pax_global_header00006660000000000000000000000064135740420500014512gustar00rootroot0000000000000052 comment=368a5b0714961953f3e3f61607fa16cb71449c1b bpfcc-0.12.0/000077500000000000000000000000001357404205000126475ustar00rootroot00000000000000bpfcc-0.12.0/.clang-format000066400000000000000000000002311357404205000152160ustar00rootroot00000000000000--- BasedOnStyle: Google AllowShortIfStatementsOnASingleLine: false AllowShortLoopsOnASingleLine: false IndentCaseLabels: false AccessModifierOffset: -1 bpfcc-0.12.0/.dockerignore000066400000000000000000000000311357404205000153150ustar00rootroot00000000000000Dockerfile* build .*.swp bpfcc-0.12.0/.gitignore000066400000000000000000000001761357404205000146430ustar00rootroot00000000000000# Editor's files *.swp *.swo *.pyc .idea *~ # Build artifacts /build/ cmake-build-debug debian/**/*.log obj-x86_64-linux-gnu bpfcc-0.12.0/.gitmodules000066400000000000000000000001361357404205000150240ustar00rootroot00000000000000[submodule "src/cc/libbpf"] path = src/cc/libbpf url = https://github.com/libbpf/libbpf.git bpfcc-0.12.0/.travis.yml000066400000000000000000000013031357404205000147550ustar00rootroot00000000000000language: python matrix: include: - name: "Check helpers on Python 2.7" python: 2.7 script: ./scripts/check-helpers.sh - name: "Python style check on Python 2.7" python: 2.7 script: ./scripts/py-style-check.sh - name: "flake8 lint on Python 2.7" python: 2.7 script: flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics - name: "flake8 lint on Python 3.7" dist: xenial # required for Python >= 3.7 python: 3.7 script: flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics allow_failures: - name: "Check helpers on Python 2.7" before_install: pip install --upgrade pip install: pip install flake8 bpfcc-0.12.0/CMakeLists.txt000066400000000000000000000101271357404205000154100ustar00rootroot00000000000000# Copyright (c) PLUMgrid, Inc. # Licensed under the Apache License, Version 2.0 (the "License") cmake_minimum_required(VERSION 2.8.7) project(bcc) if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Release) endif() enable_testing() # populate submodules (libbpf) if(NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/src/cc/libbpf/src) execute_process(COMMAND git submodule update --init --recursive WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) endif() include(cmake/GetGitRevisionDescription.cmake) include(cmake/version.cmake) include(CMakeDependentOption) include(GNUInstallDirs) include(CheckCXXCompilerFlag) include(cmake/FindCompilerFlag.cmake) option(ENABLE_LLVM_NATIVECODEGEN "Enable use of llvm nativecodegen module (needed by rw-engine)" ON) option(ENABLE_RTTI "Enable compiling with real time type information" OFF) option(ENABLE_LLVM_SHARED "Enable linking LLVM as a shared library" OFF) option(ENABLE_CLANG_JIT "Enable Loading BPF through Clang Frontend" ON) option(ENABLE_USDT "Enable User-level Statically Defined Tracing" ON) option(ENABLE_MAN "Build man pages" ON) CMAKE_DEPENDENT_OPTION(ENABLE_CPP_API "Enable C++ API" ON "ENABLE_USDT" OFF) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake) if (CMAKE_USE_LIBBPF_PACKAGE) find_package(LibBpf) endif() if(NOT PYTHON_ONLY AND ENABLE_CLANG_JIT) find_package(BISON) find_package(FLEX) find_package(LLVM REQUIRED CONFIG) message(STATUS "Found LLVM: ${LLVM_INCLUDE_DIRS} ${LLVM_PACKAGE_VERSION}") find_package(LibElf REQUIRED) # clang is linked as a library, but the library path searching is # primitively supported, unlike libLLVM set(CLANG_SEARCH "/opt/local/llvm/lib;/usr/lib/llvm-3.7/lib;${LLVM_LIBRARY_DIRS}") find_library(libclangAnalysis NAMES clangAnalysis HINTS ${CLANG_SEARCH}) find_library(libclangAST NAMES clangAST HINTS ${CLANG_SEARCH}) find_library(libclangBasic NAMES clangBasic HINTS ${CLANG_SEARCH}) find_library(libclangCodeGen NAMES clangCodeGen HINTS ${CLANG_SEARCH}) find_library(libclangDriver NAMES clangDriver HINTS ${CLANG_SEARCH}) find_library(libclangEdit NAMES clangEdit HINTS ${CLANG_SEARCH}) find_library(libclangFrontend NAMES clangFrontend HINTS ${CLANG_SEARCH}) find_library(libclangLex NAMES clangLex HINTS ${CLANG_SEARCH}) find_library(libclangParse NAMES clangParse HINTS ${CLANG_SEARCH}) find_library(libclangRewrite NAMES clangRewrite HINTS ${CLANG_SEARCH}) find_library(libclangSema NAMES clangSema HINTS ${CLANG_SEARCH}) find_library(libclangSerialization NAMES clangSerialization HINTS ${CLANG_SEARCH}) find_library(libclangASTMatchers NAMES clangASTMatchers HINTS ${CLANG_SEARCH}) if(libclangBasic STREQUAL "libclangBasic-NOTFOUND") message(FATAL_ERROR "Unable to find clang libraries") endif() FOREACH(DIR ${LLVM_INCLUDE_DIRS}) include_directories("${DIR}/../tools/clang/include") ENDFOREACH() # Set to a string path if system places kernel lib directory in # non-default location. if(NOT DEFINED BCC_KERNEL_MODULES_DIR) set(BCC_KERNEL_MODULES_DIR "/lib/modules") endif() if(NOT DEFINED BCC_PROG_TAG_DIR) set(BCC_PROG_TAG_DIR "/var/tmp/bcc") endif() # As reported in issue #735, GCC 6 has some behavioral problems when # dealing with -isystem. Hence, skip the warning optimization # altogether on that compiler. option(USINGISYSTEM "using -isystem" ON) execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION) if (USINGISYSTEM AND GCC_VERSION VERSION_LESS 6.0) # iterate over all available directories in LLVM_INCLUDE_DIRS to # generate a correctly tokenized list of parameters foreach(ONE_LLVM_INCLUDE_DIR ${LLVM_INCLUDE_DIRS}) set(CXX_ISYSTEM_DIRS "${CXX_ISYSTEM_DIRS} -isystem ${ONE_LLVM_INCLUDE_DIR}") endforeach() endif() set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD 14) endif(NOT PYTHON_ONLY AND ENABLE_CLANG_JIT) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall ${CXX_ISYSTEM_DIRS}") add_subdirectory(src) add_subdirectory(introspection) if(ENABLE_CLANG_JIT) add_subdirectory(examples) if(ENABLE_MAN) add_subdirectory(man) endif(ENABLE_MAN) add_subdirectory(tests) add_subdirectory(tools) endif(ENABLE_CLANG_JIT) bpfcc-0.12.0/CODEOWNERS000066400000000000000000000012531357404205000142430ustar00rootroot00000000000000# This file should be kept up to date with the list of maintainers responsible # for the different subdirectories within BCC. One of these people SHOULD # review code that touches the respective areas, and MUST review it if the # change is substantial or API-breaking. # see https://help.github.com/articles/about-codeowners/ for syntax # Miscellaneous * @drzaeus77 @goldshtn @yonghong-song @4ast @brendangregg # Documentation /docs/ @brendangregg @goldshtn /man/ @brendangregg @goldshtn # Tools /tools/ @brendangregg @goldshtn # Compiler, C API /src/cc/ @drzaeus77 @yonghong-song @4ast # Python API /src/python/ @drzaeus77 @goldshtn # Tests /tests/ @drzaeus77 @yonghong-song bpfcc-0.12.0/CONTRIBUTING-SCRIPTS.md000066400000000000000000000165641357404205000162410ustar00rootroot00000000000000# Contributing bcc/eBPF scripts If you want to contribute scripts to bcc, or improve your own bcc programs, great! Please read this first. _(Written by Brendan Gregg.)_ ## Type of script bcc has 2 types of scripts, in different directories: - **/examples**: intended as short examples of bcc & eBPF code. You should focus on keeping it short, neat, and documented (code comments). A submission can just be the example code. - **/tools**: intended as production safe performance and troubleshooting tools. You should focus on it being useful, tested, low overhead, documented (incl. all caveats), and easy to use. A submission should involve 4 changes: the tool, a man page, an example file, and an addition to README.md. Follow [my lead](https://github.com/brendangregg/bcc/commit/9fa156273b395cfc5505f0fff5d6b7b1396f7daa), and see the checklist below. These are run in mission critical environments as root (tech companies, financial institutions, government agencies), so if spending hours testing isn't for you, please submit your idea as an issue instead, or chat with us on irc. More detail for each below. ## Examples These are grouped into subdirectories (networking, tracing). Your example can either be a Python program with embedded C (eg, tracing/strlen_count.py), or separate Python and C files (eg, tracing/vfsreadlat.*). As said earlier: keep it short, neat, and documented (code comments). ## Tools A checklist for bcc tool development: 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 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. **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 bcc 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 command line options**. Should it have -p for filtering on a PID? -T for timestamps? -i for interval? See other tools for examples, and copy the style: the usage message should list example usage at the end. Remember to keep the tool doing one thing and doing it well. Also, if there's one option that seems to be the common case, perhaps it should just be the first argument and not need a switch (no -X). A special case of this is *stat tools, like iostat/vmstat/etc, where the convention is [interval [count]]. 1. **Concise, intuitive, self-explanatory output**. The default output should meet the common need concisely. Leave much less useful fields and data to be shown with options: -v for verbose, etc. Consider including a startup message that's self-explanatory, eg "Tracing block I/O. Output every 1 seconds. Ctrl-C to end.". 1. **Default output <80 chars wide**. 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. **Short tool name**. Follow the style of the other tools, which follow the style of other /usr/bin utilities. They are short and easy to type. No underscores. 1. **Use pep8 to check Python style**: pep8 --show-source --ignore=E123,E125,E126,E127,E128,E302 filename . Note that it misses some things, like consistent usage, so you'll still need to double check your script. 1. **Make sure your script is Python3-ready**: Adding `from __future__ import absolute_import, division, print_function, unicode_literals` helps make your script Python3-ready. 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! Perhaps it better belongs as an /example, and not a tool. 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. **Add a smoke test** to [test_tools_smoke.py](https://github.com/iovisor/bcc/blob/master/tests/python/test_tools_smoke.py), which serves as a basic check that your tool still works when we make changes to the core library. 1. If you made it this far, pull request! bpfcc-0.12.0/Dockerfile.debian000066400000000000000000000007121357404205000160620ustar00rootroot00000000000000FROM debian:stretch MAINTAINER Brenden Blanco RUN DEBIAN_RELEASE=stretch && \ # Adding non-free repo for netperf echo "deb http://deb.debian.org/debian ${DEBIAN_RELEASE} non-free" > \ /etc/apt/sources.list.d/debian-non-free.list && \ apt-get -qq update && \ apt-get -y install pbuilder aptitude COPY ./ /root/bcc WORKDIR /root/bcc RUN /usr/lib/pbuilder/pbuilder-satisfydepends && \ ./scripts/build-deb.sh bpfcc-0.12.0/Dockerfile.ubuntu000066400000000000000000000003751357404205000161670ustar00rootroot00000000000000FROM ubuntu:bionic MAINTAINER Brenden Blanco RUN apt-get -qq update && \ apt-get -y install pbuilder aptitude COPY ./ /root/bcc WORKDIR /root/bcc RUN /usr/lib/pbuilder/pbuilder-satisfydepends && \ ./scripts/build-deb.sh bpfcc-0.12.0/FAQ.txt000066400000000000000000000044251357404205000140240ustar00rootroot00000000000000Q: while running 'make test' I'm seeing: 'ImportError: No module named pyroute2' A: Install pyroute2: git clone https://github.com/svinota/pyroute2.git cd pyroute2; sudo make install Q: hello_world.py fails with: OSError: libbcc.so: cannot open shared object file: No such file or directory A: make sure to 'make install' and add the directory where libbcc.so was installed into your LD_LIBRARY_PATH export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH Q: hello_world.py fails with: ImportError: No module named bcc A: checkout "sudo make install" output to find out bpf package installation site, add it to the PYTHONPATH env variable before running the program. sudo bash -c 'PYTHONPATH=/usr/lib/python2.7/site-packages python examples/hello_world.py' Q: hello_world.py still fails with: bpf: Operation not permitted Exception: Failed to load BPF program hello A: sudo Q: hello_world.py fails with bpf: Failed to load program: Operation not permitted despite running as root, and strace shows each `bpf()` system call failing with an EPERM. A: The so-called Kernel lockdown might be the root cause. Try disabling it with the so-called sysrq mechanism: echo 1 > /proc/sys/kernel/sysrq echo x > /proc/sysrq-trigger Also see https://github.com/iovisor/bcc/issues/2525 Q: How do I fulfill the Linux kernel version requirement? A: You need to obtain a recent version of the Linux source code (please look at the README for the exact version), enable the configuration options listed in the README file, install the image, modules and headers, update your bootloader and reboot into the new kernel. If you want to compile your own kernel, you can fetch the sources from kernel.org or through your Linux distribution. To install, you need all of the following: make install make modules_install make headers_install INSTALL_HDR_PATH=/usr/local/ Q: hello_world.py fails with: ImportError: No module named past.builtins A: sudo pip install future Q: Running one of the bcc tools produces an import error: Traceback (most recent call last): File "./execsnoop", line 20, in from bcc import BPF ImportError: No module named bcc A: Make sure the python bcc bindings package (python2-bcc) is installed. bpfcc-0.12.0/INSTALL.md000066400000000000000000000413231357404205000143020ustar00rootroot00000000000000# Installing BCC * [Kernel Configuration](#kernel-configuration) * [Packages](#packages) - [Ubuntu](#ubuntu---binary) - [Fedora](#fedora---binary) - [Arch](#arch---aur) - [Gentoo](#gentoo---portage) - [openSUSE](#opensuse---binary) - [RHEL](#rhel---binary) - [Amazon Linux 1](#Amazon-Linux-1---Binary) * [Source](#source) - [Debian](#debian---source) - [Ubuntu](#ubuntu---source) - [Fedora](#fedora---source) - [openSUSE](#opensuse---source) - [Centos](#centos---source) - [Amazon Linux](#amazon-linux---source) * [Older Instructions](#older-instructions) ## Kernel Configuration In general, to use these features, a Linux kernel version 4.1 or newer is required. In addition, the kernel should have been compiled with the following flags set: ``` CONFIG_BPF=y CONFIG_BPF_SYSCALL=y # [optional, for tc filters] CONFIG_NET_CLS_BPF=m # [optional, for tc actions] CONFIG_NET_ACT_BPF=m CONFIG_BPF_JIT=y # [for Linux kernel versions 4.1 through 4.6] CONFIG_HAVE_BPF_JIT=y # [for Linux kernel versions 4.7 and later] CONFIG_HAVE_EBPF_JIT=y # [optional, for kprobes] CONFIG_BPF_EVENTS=y ``` There are a few optional kernel flags needed for running bcc networking examples on vanilla kernel: ``` CONFIG_NET_SCH_SFQ=m CONFIG_NET_ACT_POLICE=m CONFIG_NET_ACT_GACT=m CONFIG_DUMMY=m CONFIG_VXLAN=m ``` Kernel compile flags can usually be checked by looking at `/proc/config.gz` or `/boot/config-`. # Packages ## Ubuntu - Binary The stable and the nightly packages are built for Ubuntu Xenial (16.04), Ubuntu Artful (17.10) and Ubuntu Bionic (18.04). The steps are very straightforward, no need to upgrade the kernel or compile from source! **Ubuntu Packages** As of Ubuntu Bionic (18.04), versions of bcc are available in the standard Ubuntu multiverse repository. The Ubuntu packages have slightly different names: where iovisor packages use `bcc` in the name (e.g. `bcc-tools`), Ubuntu packages use `bpfcc` (e.g. `bpfcc-tools`). Source packages and the binary packages produced from them can be found at [packages.ubuntu.com](https://packages.ubuntu.com/search?suite=default§ion=all&arch=any&keywords=bpfcc&searchon=sourcenames). ```bash sudo apt-get install bpfcc-tools linux-headers-$(uname -r) ``` The tools are installed in `/sbin` (`/usr/sbin` in Ubuntu 18.04) with a `-bpfcc` extension. Try running `sudo opensnoop-bpfcc`. **_Note_**: the Ubuntu packages have different names but the package contents, in most cases, conflict and as such _cannot_ be installed alongside upstream packages. Should one choose to use Ubuntu's packages instead of the upstream iovisor packages (or vice-versa), the conflicting packages will need to be removed. The iovisor packages _do_ declare they provide the Ubuntu packages and as such may be used to satisfy dependencies. For example, should one attempt to install package `foo` which declares a dependency on `libbpfcc` while the upstream `libbcc` package is installed, `foo` should install without trouble as `libbcc` declares that it provides `libbpfcc`. That said, one should always test such a configuration in case of version incompatibilities. **Upstream Stable and Signed Packages** ```bash sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 4052245BD4284CDD echo "deb https://repo.iovisor.org/apt/$(lsb_release -cs) $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/iovisor.list sudo apt-get update sudo apt-get install bcc-tools libbcc-examples linux-headers-$(uname -r) ``` (replace `xenial` with `artful` or `bionic` as appropriate). Tools will be installed under /usr/share/bcc/tools. **Upstream Nightly Packages** ```bash echo "deb [trusted=yes] https://repo.iovisor.org/apt/xenial xenial-nightly main" | sudo tee /etc/apt/sources.list.d/iovisor.list sudo apt-get update sudo apt-get install bcc-tools libbcc-examples linux-headers-$(uname -r) ``` (replace `xenial` with `artful` or `bionic` as appropriate) ## Fedora - Binary ### Fedora 30 and newer As of Fedora 30, bcc binaries are available in the standard repository. You can install them via ```bash sudo dnf install bcc ``` **Note**: if you keep getting `Failed to load program: Operation not permitted` when trying to run the `hello_world.py` example as root then you might need to lift the so-called kernel lockdown (cf. [FAQ](https://github.com/iovisor/bcc/blob/c00d10d4552f647491395e326d2e4400f3a0b6c5/FAQ.txt#L24), [background article](https://gehrcke.de/2019/09/running-an-ebpf-program-may-require-lifting-the-kernel-lockdown)). ### Fedora 29 and older Ensure that you are running a 4.2+ kernel with `uname -r`. If not, install a 4.2+ kernel from http://alt.fedoraproject.org/pub/alt/rawhide-kernel-nodebug, for example: ```bash sudo dnf config-manager --add-repo=http://alt.fedoraproject.org/pub/alt/rawhide-kernel-nodebug/fedora-rawhide-kernel-nodebug.repo sudo dnf update # reboot ``` **Nightly Packages** Nightly bcc binary packages for Fedora 25, 26, 27, and 28 are hosted at `https://repo.iovisor.org/yum/nightly/f{25,26,27}`. To install: ```bash echo -e '[iovisor]\nbaseurl=https://repo.iovisor.org/yum/nightly/f27/$basearch\nenabled=1\ngpgcheck=0' | sudo tee /etc/yum.repos.d/iovisor.repo sudo dnf install bcc-tools kernel-headers kernel-devel ``` **Stable and Signed Packages** Stable bcc binary packages for Fedora 25, 26, 27, and 28 are hosted at `https://repo.iovisor.org/yum/main/f{25,26,27}`. ```bash echo -e '[iovisor]\nbaseurl=https://repo.iovisor.org/yum/main/f27/$basearch\nenabled=1' | sudo tee /etc/yum.repos.d/iovisor.repo sudo dnf install bcc-tools kernel-devel-$(uname -r) kernel-headers-$(uname -r) ``` ## Arch - AUR Upgrade the kernel to minimum 4.3.1-1 first; the ```CONFIG_BPF_SYSCALL=y``` configuration was not added until [this kernel release](https://bugs.archlinux.org/task/47008). Install these packages using any AUR helper such as [pacaur](https://aur.archlinux.org/packages/pacaur), [yaourt](https://aur.archlinux.org/packages/yaourt), [cower](https://aur.archlinux.org/packages/cower), etc.: ``` bcc bcc-tools python-bcc python2-bcc ``` All build and install dependencies are listed [in the PKGBUILD](https://aur.archlinux.org/cgit/aur.git/tree/PKGBUILD?h=bcc) and should install automatically. ## Gentoo - Portage First of all, upgrade the kernel of your choice to a recent version. For example: ``` emerge sys-kernel/gentoo-sources ``` Then, configure the kernel enabling the features you need. Please consider the following as a starting point: ``` CONFIG_BPF=y CONFIG_BPF_SYSCALL=y CONFIG_NET_CLS_BPF=m CONFIG_NET_ACT_BPF=m CONFIG_BPF_JIT=y CONFIG_BPF_EVENTS=y ``` Finally, you can install bcc with: ``` emerge dev-util/bcc ``` The appropriate dependencies (e.g., ```clang```, ```llvm``` with BPF backend) will be pulled automatically. ## openSUSE - Binary For openSUSE Leap 42.2 (and later) and Tumbleweed, bcc is already included in the official repo. Just install the packages with zypper. ```bash sudo zypper ref sudo zypper in bcc-tools bcc-examples ``` ## RHEL - Binary For RHEL 7.6, bcc is already included in the official yum repository as bcc-tools. As part of the install, the following dependencies are installed: bcc.x86_64 0:0.6.1-2.el7 ,llvm-private.x86_64 0:6.0.1-2.el7 ,python-bcc.x86_64 0:0.6.1-2.el7,python-netaddr.noarch 0:0.7.5-9.el7 ``` yum install bcc-tools ``` ## Amazon Linux 1 - Binary Use case 1. Install BCC for latest kernel available in repo: Tested on Amazon Linux AMI release 2018.03 (kernel 4.14.88-72.73.amzn1.x86_64) ``` sudo yum update kernel sudo yum install bcc sudo reboot ``` Use case 2. Install BCC for your AMI's default kernel (no reboot required): Tested on Amazon Linux AMI release 2018.03 (kernel 4.14.77-70.59.amzn1.x86_64) ``` sudo yum install kernel-headers-$(uname -r | cut -d'.' -f1-5) sudo yum install kernel-devel-$(uname -r | cut -d'.' -f1-5) sudo yum install bcc ``` # Source ## libbpf Submodule Since release v0.10.0, bcc starts to leverage libbpf repo (https://github.com/libbpf/libbpf) to provide wrapper functions to the kernel for bpf syscalls, uapi headers bpf.h/btf.h etc. Unfortunately, the default github release source code does not contain libbpf submodule source code and this will cause build issues. To alleviate this problem, starting at release v0.11.0, source code with corresponding libbpf submodule codes will be released as well. See https://github.com/iovisor/bcc/releases. ## Debian - Source ### Jessie #### Repositories The automated tests that run as part of the build process require `netperf`. Since netperf's license is not "certified" as an open-source license, it is in Debian's `non-free` repository. `/etc/apt/sources.list` should include the `non-free` repository and look something like this: ``` deb http://httpredir.debian.org/debian/ jessie main non-free deb-src http://httpredir.debian.org/debian/ jessie main non-free deb http://security.debian.org/ jessie/updates main non-free deb-src http://security.debian.org/ jessie/updates main non-free # wheezy-updates, previously known as 'volatile' deb http://ftp.us.debian.org/debian/ jessie-updates main non-free deb-src http://ftp.us.debian.org/debian/ jessie-updates main non-free ``` BCC also requires kernel version 4.1 or above. Those kernels are available in the `jessie-backports` repository. To add the `jessie-backports` repository to your system create the file `/etc/apt/sources.list.d/jessie-backports.list` with the following contents: ``` deb http://httpredir.debian.org/debian jessie-backports main deb-src http://httpredir.debian.org/debian jessie-backports main ``` #### Install Build Dependencies Note, check for the latest `linux-image-4.x` version in `jessie-backports` before proceeding. Also, have a look at the `Build-Depends:` section in `debian/control` file. ``` # Before you begin apt-get update # Update kernel and linux-base package apt-get -t jessie-backports install linux-base linux-image-4.9.0-0.bpo.2-amd64 linux-headers-4.9.0-0.bpo.2-amd64 # BCC build dependencies: apt-get install debhelper cmake libllvm3.8 llvm-3.8-dev libclang-3.8-dev \ libelf-dev bison flex libedit-dev clang-format-3.8 python python-netaddr \ python-pyroute2 luajit libluajit-5.1-dev arping iperf netperf ethtool \ devscripts zlib1g-dev libfl-dev ``` #### Sudo Adding eBPF probes to the kernel and removing probes from it requires root privileges. For the build to complete successfully, you must build from an account with `sudo` access. (You may also build as root, but it is bad style.) `/etc/sudoers` or `/etc/sudoers.d/build-user` should contain ``` build-user ALL = (ALL) NOPASSWD: ALL ``` or ``` build-user ALL = (ALL) ALL ``` If using the latter sudoers configuration, please keep an eye out for sudo's password prompt while the build is running. #### Build ``` cd git clone https://github.com/iovisor/bcc.git cd bcc debuild -b -uc -us ``` #### Install ``` cd .. sudo dpkg -i *bcc*.deb ``` ## Ubuntu - Source To build the toolchain from source, one needs: * LLVM 3.7.1 or newer, compiled with BPF support (default=on) * Clang, built from the same tree as LLVM * cmake (>=3.1), gcc (>=4.7), flex, bison * LuaJIT, if you want Lua support ### Install build dependencies ``` # Trusty and older VER=trusty echo "deb http://llvm.org/apt/$VER/ llvm-toolchain-$VER-3.7 main deb-src http://llvm.org/apt/$VER/ llvm-toolchain-$VER-3.7 main" | \ sudo tee /etc/apt/sources.list.d/llvm.list wget -O - http://llvm.org/apt/llvm-snapshot.gpg.key | sudo apt-key add - sudo apt-get update # For bionic sudo apt-get -y install bison build-essential cmake flex git libedit-dev \ libllvm6.0 llvm-6.0-dev libclang-6.0-dev python zlib1g-dev libelf-dev # For other versions sudo apt-get -y install bison build-essential cmake flex git libedit-dev \ libllvm3.7 llvm-3.7-dev libclang-3.7-dev python zlib1g-dev libelf-dev # For Lua support sudo apt-get -y install luajit luajit-5.1-dev ``` ### Install and compile BCC ``` git clone https://github.com/iovisor/bcc.git mkdir bcc/build; cd bcc/build cmake .. -DCMAKE_INSTALL_PREFIX=/usr make sudo make install ``` ## Fedora - Source ### Install build dependencies ``` sudo dnf install -y bison cmake ethtool flex git iperf libstdc++-static \ python-netaddr python-pip gcc gcc-c++ make zlib-devel \ elfutils-libelf-devel sudo dnf install -y luajit luajit-devel # for Lua support sudo dnf install -y \ http://repo.iovisor.org/yum/extra/mageia/cauldron/x86_64/netperf-2.7.0-1.mga6.x86_64.rpm sudo pip install pyroute2 ``` ### Install binary clang ``` # FC22 wget http://llvm.org/releases/3.7.1/clang+llvm-3.7.1-x86_64-fedora22.tar.xz sudo tar xf clang+llvm-3.7.1-x86_64-fedora22.tar.xz -C /usr/local --strip 1 # FC23 wget http://llvm.org/releases/3.9.0/clang+llvm-3.9.0-x86_64-fedora23.tar.xz sudo tar xf clang+llvm-3.9.0-x86_64-fedora23.tar.xz -C /usr/local --strip 1 # FC24 and FC25 sudo dnf install -y clang clang-devel llvm llvm-devel llvm-static ncurses-devel ``` ### Install and compile BCC ``` git clone https://github.com/iovisor/bcc.git mkdir bcc/build; cd bcc/build cmake .. -DCMAKE_INSTALL_PREFIX=/usr make sudo make install ``` ## openSUSE - Source ### Install build dependencies ``` sudo zypper in bison cmake flex gcc gcc-c++ git libelf-devel libstdc++-devel \ llvm-devel clang-devel pkg-config python-devel python-setuptools python3-devel \ python3-setuptools sudo zypper in luajit-devel # for lua support in openSUSE Leap 42.2 or later sudo zypper in lua51-luajit-devel # for lua support in openSUSE Tumbleweed ``` ### Install and compile BCC ``` git clone https://github.com/iovisor/bcc.git mkdir bcc/build; cd bcc/build cmake -DCMAKE_INSTALL_PREFIX=/usr \ -DLUAJIT_INCLUDE_DIR=`pkg-config --variable=includedir luajit` \ # for lua support .. make sudo make install cmake -DPYTHON_CMD=python3 .. # build python3 binding pushd src/python/ make sudo make install popd ``` ## Centos - Source For Centos 7.6 only ### Install build dependencies ``` sudo yum install -y epel-release sudo yum update -y sudo yum groupinstall -y "Development tools" sudo yum install -y elfutils-libelf-devel cmake3 git bison flex ncurses-devel sudo yum install -y luajit luajit-devel # for Lua support ``` ### Install and compile LLVM You could compile LLVM from source code ``` curl -LO http://releases.llvm.org/7.0.1/llvm-7.0.1.src.tar.xz curl -LO http://releases.llvm.org/7.0.1/cfe-7.0.1.src.tar.xz tar -xf cfe-7.0.1.src.tar.xz tar -xf llvm-7.0.1.src.tar.xz mkdir clang-build mkdir llvm-build cd llvm-build cmake3 -G "Unix Makefiles" -DLLVM_TARGETS_TO_BUILD="BPF;X86" \ -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr ../llvm-7.0.1.src make sudo make install cd ../clang-build cmake3 -G "Unix Makefiles" -DLLVM_TARGETS_TO_BUILD="BPF;X86" \ -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr ../cfe-7.0.1.src make sudo make install cd .. ``` or install from centos-release-scl ``` yum install -y centos-release-scl yum-config-manager --enable rhel-server-rhscl-7-rpms yum install -y devtoolset-7 llvm-toolset-7 llvm-toolset-7-llvm-devel llvm-toolset-7-llvm-static llvm-toolset-7-clang-devel source scl_source enable devtoolset-7 llvm-toolset-7 ``` For permanently enable scl environment, please check https://access.redhat.com/solutions/527703. ### Install and compile BCC ``` git clone https://github.com/iovisor/bcc.git mkdir bcc/build; cd bcc/build cmake .. -DCMAKE_INSTALL_PREFIX=/usr make sudo make install ``` ## Amazon Linux - Source Tested on Amazon Linux AMI release 2018.03 (kernel 4.14.47-56.37.amzn1.x86_64) ### Install packages required for building ``` # enable epel to get iperf, luajit, luajit-devel, cmake3 (cmake3 is required to support c++11) sudo yum-config-manager --enable epel sudo yum install -y bison cmake3 ethtool flex git iperf libstdc++-static python-netaddr gcc gcc-c++ make zlib-devel elfutils-libelf-devel sudo yum install -y luajit luajit-devel sudo yum install -y http://repo.iovisor.org/yum/extra/mageia/cauldron/x86_64/netperf-2.7.0-1.mga6.x86_64.rpm sudo pip install pyroute2 sudo yum install -y ncurses-devel ``` ### Install clang 3.7.1 pre-built binaries ``` wget http://releases.llvm.org/3.7.1/clang+llvm-3.7.1-x86_64-fedora22.tar.xz tar xf clang* (cd clang* && sudo cp -R * /usr/local/) ``` ### Build bcc ``` git clone https://github.com/iovisor/bcc.git pushd . mkdir bcc/build; cd bcc/build cmake3 .. -DCMAKE_INSTALL_PREFIX=/usr time make sudo make install popd ``` ### Setup required to run the tools ``` sudo yum -y install kernel-devel-$(uname -r) sudo mount -t debugfs debugfs /sys/kernel/debug ``` ### Test ``` sudo /usr/share/bcc/tools/execsnoop ``` # Older Instructions ## Build LLVM and Clang development libs ``` git clone https://github.com/llvm/llvm-project.git mkdir -p llvm-project/llvm/build/install cd llvm-project/llvm/build cmake -G "Ninja" -DLLVM_TARGETS_TO_BUILD="BPF;X86" \ -DLLVM_ENABLE_PROJECTS="clang" \ -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=$PWD/install .. ninja && ninja install export PATH=$PWD/install/bin:$PATH ``` bpfcc-0.12.0/LICENSE.txt000066400000000000000000000261351357404205000145010ustar00rootroot00000000000000 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. bpfcc-0.12.0/LINKS.md000066400000000000000000000134641357404205000140610ustar00rootroot00000000000000- 2018-05-03: [Linux System Monitoring with eBPF](https://www.circonus.com/2018/05/linux-system-monitoring-with-ebpf) - 2018-02-22: [Some advanced BCC topics](https://lwn.net/Articles/747640) - 2018-01-23: [BPFd: Running BCC tools remotely across systems and architectures](https://lwn.net/Articles/744522) - 2017-12-22: [An introduction to the BPF Compiler Collection](https://lwn.net/Articles/742082) - 2017-09-13: [Performance Analysis Superpowers with Linux BPF](https://www.slideshare.net/brendangregg/ossna-2017-performance-analysis-superpowers-with-linux-bpf) - 2017-07-28: [Tracing a packet journey using Linux tracepoints, perf and eBPF](https://blog.yadutaf.fr/2017/07/28/tracing-a-packet-journey-using-linux-tracepoints-perf-ebpf/) - 2017-07-13: [Performance Superpowers with Enhanced BPF](https://www.usenix.org/conference/atc17/program/presentation/gregg-superpowers) - 2017-06-28: [The BSD Packet Filter](https://speakerdeck.com/tuxology/the-bsd-packet-filter) - 2017-03-04: [Linux 4.x Tracing: Performance Analysis with bcc/BPF](https://www.slideshare.net/brendangregg/linux-4x-tracing-performance-analysis-with-bccbpf) - 2017-02-27: [Profiling a .NET Core Application on Linux](https://blogs.microsoft.co.il/sasha/2017/02/27/profiling-a-net-core-application-on-linux) - 2017-02-05: [gobpf - utilizing eBPF from Go](https://fosdem.org/2017/schedule/event/go_bpf/attachments/slides/1681/export/events/attachments/go_bpf/slides/1681/gobpf_utilizing_eBPF_from_Go_FOSDEM_2017.pdf) - 2017-01-31: [Golang bcc/BPF Function Tracing](http://www.brendangregg.com/blog/2017-01-31/golang-bcc-bpf-function-tracing.html) - 2017-01-18: [BPF: Tracing and more](https://www.slideshare.net/brendangregg/bpf-tracing-and-more) - 2016-12-09: [Linux 4.x Tracing Tools: Using BPF Superpowers](https://www.slideshare.net/brendangregg/linux-4x-tracing-tools-using-bpf-superpowers) - 2016-11-30: [Introducing gobpf - Using eBPF from Go](https://kinvolk.io/blog/2016/11/introducing-gobpf---using-ebpf-from-go) - 2016-11-30: [Linux bcc/BPF tcplife: TCP Lifespans](http://www.brendangregg.com/blog/2016-11-30/linux-bcc-tcplife.html) - 2016-10-27: [DTrace for Linux 2016](http://www.brendangregg.com/blog/2016-10-27/dtrace-for-linux-2016.html) - 2016-10-21: [Linux 4.9's Efficient BPF-based Profiler](http://www.brendangregg.com/blog/2016-10-21/linux-efficient-profiler.html) - 2016-10-15: [Linux bcc tcptop](http://www.brendangregg.com/blog/2016-10-15/linux-bcc-tcptop.html) - 2016-10-12: [Linux bcc/BPF Node.js USDT Tracing](http://www.brendangregg.com/blog/2016-10-12/linux-bcc-nodejs-usdt.html) - 2016-10-08: [Linux bcc/BPF Run Queue (Scheduler) Latency](http://www.brendangregg.com/blog/2016-10-08/linux-bcc-runqlat.html) - 2016-10-06: [Linux bcc ext4 Latency Tracing](http://www.brendangregg.com/blog/2016-10-06/linux-bcc-ext4dist-ext4slower.html) - 2016-10-04: [Installing bcc to evaluate BPF and Postgres](http://blog.gregburek.com/2016/10/04/installing-bcc-to-evaluate-bpf-and-postgres) - 2016-10-04: [Linux MySQL Slow Query Tracing with bcc/BPF](http://www.brendangregg.com/blog/2016-10-04/linux-bcc-mysqld-qslower.html) - 2016-10-01: [Linux bcc Tracing Security Capabilities](http://www.brendangregg.com/blog/2016-10-01/linux-bcc-security-capabilities.html) - 2016-09-23: [BCC – Dynamic Tracing Tools for Linux Performance Monitoring, Networking and More](http://www.tecmint.com/bcc-best-linux-performance-monitoring-tools/) - 2016-08-22: [BoF - What Can BPF Do For You?](https://events.linuxfoundation.org/sites/events/files/slides/iovisor-lc-bof-2016.pdf) - 2016-07-03: [Linux debugging tools I love](https://jvns.ca/blog/2016/07/03/debugging-tools-i-love) - 2016-06-14: [Ubuntu Xenial bcc/BPF](http://www.brendangregg.com/blog/2016-06-14/ubuntu-xenial-bcc-bpf.html) - 2016-05-26: [Linux BPF/bcc for Oracle Tracing](https://db-blog.web.cern.ch/blog/luca-canali/2016-05-linux-bpfbcc-oracle-tracing) - 2016-05-04: [Tracing your TCP IPv4 connections with eBPF and BCC from the Linux kernel JIT-VM to Splunk](https://www.splunk.com/blog/2016/05/04/tracing-your-tcp-ipv4-connections-with-ebpf-and-bcc-from-the-linux-kernel-jit-vm-to-splunk/) - 2016-03-31: [Probing the JVM with BPF/BCC](http://blogs.microsoft.co.il/sasha/2016/03/31/probing-the-jvm-with-bpfbcc/) - 2016-03-30: [How to turn any syscall into an event: Introducing eBPF Kernel probes](https://blog.yadutaf.fr/2016/03/30/turn-any-syscall-into-event-introducing-ebpf-kernel-probes) - 2016-03-30: [USDT Probe Support in BPF/BCC](http://blogs.microsoft.co.il/sasha/2016/03/30/usdt-probe-support-in-bpfbcc) - 2016-03-28: [Linux BPF/bcc Road Ahead, March 2016](http://www.brendangregg.com/blog/2016-03-28/linux-bpf-bcc-road-ahead-2016.html) - 2016-03-05: [Linux BPF Superpowers](http://www.brendangregg.com/blog/2016-03-05/linux-bpf-superpowers.html) - 2016-03-02: [Linux BPF Superpowers](https://www.slideshare.net/brendangregg/linux-bpf-superpowers) - 2016-02-14: [Two New eBPF Tools: memleak and argdist](http://blogs.microsoft.co.il/sasha/2016/02/14/two-new-ebpf-tools-memleak-and-argdist/) - 2016-02-08: [Linux eBPF/bcc uprobes](http://www.brendangregg.com/blog/2016-02-08/linux-ebpf-bcc-uprobes.html) - 2016-02-05: [Who is waking the waker? (Linux chain graph prototype)](http://www.brendangregg.com/blog/2016-02-05/ebpf-chaingraph-prototype.html) - 2016-02-01: [Linux Wakeup and Off-Wake Profiling](http://www.brendangregg.com/blog/2016-02-01/linux-wakeup-offwake-profiling.html) - 2016-01-20: [Linux eBPF Off-CPU Flame Graph](http://www.brendangregg.com/blog/2016-01-20/ebpf-offcpu-flame-graph.html) - 2016-01-18: [Linux eBPF Stack Trace Hack](http://www.brendangregg.com/blog/2016-01-18/ebpf-stack-trace-hack.html) - 2015-10-31: [tcpconnect and tcpaccept for Linux (bcc)](http://www.brendangregg.com/blog/2015-10-31/tcpconnect-tcpaccept-bcc.html) - 2015-09-22: [bcc: Taming Linux 4.3+ Tracing Superpowers](http://www.brendangregg.com/blog/2015-09-22/bcc-linux-4.3-tracing.html) bpfcc-0.12.0/QUICKSTART.md000066400000000000000000000011131357404205000146570ustar00rootroot00000000000000# Quick Start Guide A Docker container is provided for user to try out [bcc](https://github.com/iovisor/bcc). From your host shell: ```bash docker run -it --rm \ --privileged \ -v /lib/modules:/lib/modules:ro \ -v /usr/src:/usr/src:ro \ -v /etc/localtime:/etc/localtime:ro \ --workdir /usr/share/bcc/tools \ zlim/bcc ``` Now, from the container shell, you can try the various pre-installed bcc tools. For examples, please refer to the [tutorial](docs/tutorial.md#1-general-performance). If you wish to install bcc on your host, please refer to [INSTALL.md](INSTALL.md). bpfcc-0.12.0/README.md000066400000000000000000000504571357404205000141410ustar00rootroot00000000000000![BCC Logo](images/logo2.png) # BPF Compiler Collection (BCC) BCC is a toolkit for creating efficient kernel tracing and manipulation programs, and includes several useful tools and examples. It makes use of extended BPF (Berkeley Packet Filters), formally known as eBPF, a new feature that was first added to Linux 3.15. Much of what BCC uses requires Linux 4.1 and above. eBPF was [described by](https://lkml.org/lkml/2015/4/14/232) Ingo Molnár as: > One of the more interesting features in this cycle is the ability to attach eBPF programs (user-defined, sandboxed bytecode executed by the kernel) to kprobes. This allows user-defined instrumentation on a live kernel image that can never crash, hang or interfere with the kernel negatively. BCC makes BPF programs easier to write, with kernel instrumentation in C (and includes a C wrapper around LLVM), and front-ends in Python and lua. It is suited for many tasks, including performance analysis and network traffic control. ## Screenshot This example traces a disk I/O kernel function, and populates an in-kernel power-of-2 histogram of the I/O size. For efficiency, only the histogram summary is returned to user-level. ```Shell # ./bitehist.py Tracing... Hit Ctrl-C to end. ^C kbytes : count distribution 0 -> 1 : 3 | | 2 -> 3 : 0 | | 4 -> 7 : 211 |********** | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 1 | | 128 -> 255 : 800 |**************************************| ``` The above output shows a bimodal distribution, where the largest mode of 800 I/O was between 128 and 255 Kbytes in size. See the source: [bitehist.py](examples/tracing/bitehist.py). What this traces, what this stores, and how the data is presented, can be entirely customized. This shows only some of many possible capabilities. ## Installing See [INSTALL.md](INSTALL.md) for installation steps on your platform. ## FAQ See [FAQ.txt](FAQ.txt) for the most common troubleshoot questions. ## Reference guide See [docs/reference_guide.md](docs/reference_guide.md) for the reference guide to the bcc and bcc/BPF APIs. ## Contents Some of these are single files that contain both C and Python, others have a pair of .c and .py files, and some are directories of files. ### Tracing #### Examples: - examples/tracing/[bitehist.py](examples/tracing/bitehist.py): Block I/O size histogram. [Examples](examples/tracing/bitehist_example.txt). - examples/tracing/[disksnoop.py](examples/tracing/disksnoop.py): Trace block device I/O latency. [Examples](examples/tracing/disksnoop_example.txt). - examples/[hello_world.py](examples/hello_world.py): Prints "Hello, World!" for new processes. - examples/tracing/[mysqld_query.py](examples/tracing/mysqld_query.py): Trace MySQL server queries using USDT probes. [Examples](examples/tracing/mysqld_query_example.txt). - examples/tracing/[nodejs_http_server.py](examples/tracing/nodejs_http_server.py): Trace Node.js HTTP server requests using USDT probes. [Examples](examples/tracing/nodejs_http_server_example.txt). - examples/tracing/[stacksnoop](examples/tracing/stacksnoop.py): Trace a kernel function and print all kernel stack traces. [Examples](examples/tracing/stacksnoop_example.txt). - tools/[statsnoop](tools/statsnoop.py): Trace stat() syscalls. [Examples](tools/statsnoop_example.txt). - examples/tracing/[task_switch.py](examples/tracing/task_switch.py): Count task switches with from and to PIDs. - examples/tracing/[tcpv4connect.py](examples/tracing/tcpv4connect.py): Trace TCP IPv4 active connections. [Examples](examples/tracing/tcpv4connect_example.txt). - examples/tracing/[trace_fields.py](examples/tracing/trace_fields.py): Simple example of printing fields from traced events. - examples/tracing/[urandomread.py](examples/tracing/urandomread.py): A kernel tracepoint example, which traces random:urandom_read. [Examples](examples/tracing/urandomread_example.txt). - examples/tracing/[vfsreadlat.py](examples/tracing/vfsreadlat.py) examples/tracing/[vfsreadlat.c](examples/tracing/vfsreadlat.c): VFS read latency distribution. [Examples](examples/tracing/vfsreadlat_example.txt). - examples/tracing/[kvm_hypercall.py](examples/tracing/kvm_hypercall.py): Conditional static kernel tracepoints for KVM entry, exit and hypercall [Examples](examples/tracing/kvm_hypercall.txt). #### Tools:
- tools/[argdist](tools/argdist.py): Display function parameter values as a histogram or frequency count. [Examples](tools/argdist_example.txt). - tools/[bashreadline](tools/bashreadline.py): Print entered bash commands system wide. [Examples](tools/bashreadline_example.txt). - tools/[biolatency](tools/biolatency.py): Summarize block device I/O latency as a histogram. [Examples](tools/biolatency_example.txt). - tools/[biotop](tools/biotop.py): Top for disks: Summarize block device I/O by process. [Examples](tools/biotop_example.txt). - tools/[biosnoop](tools/biosnoop.py): Trace block device I/O with PID and latency. [Examples](tools/biosnoop_example.txt). - tools/[bitesize](tools/bitesize.py): Show per process I/O size histogram. [Examples](tools/bitesize_example.txt). - tools/[bpflist](tools/bpflist.py): Display processes with active BPF programs and maps. [Examples](tools/bpflist_example.txt). - tools/[btrfsdist](tools/btrfsdist.py): Summarize btrfs operation latency distribution as a histogram. [Examples](tools/btrfsdist_example.txt). - tools/[btrfsslower](tools/btrfsslower.py): Trace slow btrfs operations. [Examples](tools/btrfsslower_example.txt). - tools/[capable](tools/capable.py): Trace security capability checks. [Examples](tools/capable_example.txt). - tools/[cachestat](tools/cachestat.py): Trace page cache hit/miss ratio. [Examples](tools/cachestat_example.txt). - tools/[cachetop](tools/cachetop.py): Trace page cache hit/miss ratio by processes. [Examples](tools/cachetop_example.txt). - tools/[cpudist](tools/cpudist.py): Summarize on- and off-CPU time per task as a histogram. [Examples](tools/cpudist_example.txt) - tools/[cpuunclaimed](tools/cpuunclaimed.py): Sample CPU run queues and calculate unclaimed idle CPU. [Examples](tools/cpuunclaimed_example.txt) - tools/[criticalstat](tools/criticalstat.py): Trace and report long atomic critical sections in the kernel. [Examples](tools/criticalstat_example.txt) - tools/[dbslower](tools/dbslower.py): Trace MySQL/PostgreSQL queries slower than a threshold. [Examples](tools/dbslower_example.txt). - tools/[dbstat](tools/dbstat.py): Summarize MySQL/PostgreSQL query latency as a histogram. [Examples](tools/dbstat_example.txt). - tools/[dcsnoop](tools/dcsnoop.py): Trace directory entry cache (dcache) lookups. [Examples](tools/dcsnoop_example.txt). - tools/[dcstat](tools/dcstat.py): Directory entry cache (dcache) stats. [Examples](tools/dcstat_example.txt). - tools/[deadlock](tools/deadlock.py): Detect potential deadlocks on a running process. [Examples](tools/deadlock_example.txt). - tools/[drsnoop](tools/drsnoop.py): Trace direct reclaim events with PID and latency. [Examples](tools/drsnoop_example.txt). - tools/[execsnoop](tools/execsnoop.py): Trace new processes via exec() syscalls. [Examples](tools/execsnoop_example.txt). - tools/[exitsnoop](tools/exitsnoop.py): Trace process termination (exit and fatal signals). [Examples](tools/exitsnoop_example.txt). - tools/[ext4dist](tools/ext4dist.py): Summarize ext4 operation latency distribution as a histogram. [Examples](tools/ext4dist_example.txt). - tools/[ext4slower](tools/ext4slower.py): Trace slow ext4 operations. [Examples](tools/ext4slower_example.txt). - tools/[filelife](tools/filelife.py): Trace the lifespan of short-lived files. [Examples](tools/filelife_example.txt). - tools/[fileslower](tools/fileslower.py): Trace slow synchronous file reads and writes. [Examples](tools/fileslower_example.txt). - tools/[filetop](tools/filetop.py): File reads and writes by filename and process. Top for files. [Examples](tools/filetop_example.txt). - tools/[funccount](tools/funccount.py): Count kernel function calls. [Examples](tools/funccount_example.txt). - tools/[funclatency](tools/funclatency.py): Time functions and show their latency distribution. [Examples](tools/funclatency_example.txt). - tools/[funcslower](tools/funcslower.py): Trace slow kernel or user function calls. [Examples](tools/funcslower_example.txt). - tools/[gethostlatency](tools/gethostlatency.py): Show latency for getaddrinfo/gethostbyname[2] calls. [Examples](tools/gethostlatency_example.txt). - tools/[hardirqs](tools/hardirqs.py): Measure hard IRQ (hard interrupt) event time. [Examples](tools/hardirqs_example.txt). - tools/[inject](tools/inject.py): Targeted error injection with call chain and predicates [Examples](tools/inject_example.txt). - tools/[killsnoop](tools/killsnoop.py): Trace signals issued by the kill() syscall. [Examples](tools/killsnoop_example.txt). - tools/[klockstat](tools/klockstat.py): Traces kernel mutex lock events and display locks statistics. [Examples](tools/klockstat_example.txt). - tools/[llcstat](tools/llcstat.py): Summarize CPU cache references and misses by process. [Examples](tools/llcstat_example.txt). - tools/[mdflush](tools/mdflush.py): Trace md flush events. [Examples](tools/mdflush_example.txt). - tools/[memleak](tools/memleak.py): Display outstanding memory allocations to find memory leaks. [Examples](tools/memleak_example.txt). - tools/[mountsnoop](tools/mountsnoop.py): Trace mount and umount syscalls system-wide. [Examples](tools/mountsnoop_example.txt). - tools/[mysqld_qslower](tools/mysqld_qslower.py): Trace MySQL server queries slower than a threshold. [Examples](tools/mysqld_qslower_example.txt). - tools/[nfsslower](tools/nfsslower.py): Trace slow NFS operations. [Examples](tools/nfsslower_example.txt). - tools/[nfsdist](tools/nfsdist.py): Summarize NFS operation latency distribution as a histogram. [Examples](tools/nfsdist_example.txt). - tools/[offcputime](tools/offcputime.py): Summarize off-CPU time by kernel stack trace. [Examples](tools/offcputime_example.txt). - tools/[offwaketime](tools/offwaketime.py): Summarize blocked time by kernel off-CPU stack and waker stack. [Examples](tools/offwaketime_example.txt). - tools/[oomkill](tools/oomkill.py): Trace the out-of-memory (OOM) killer. [Examples](tools/oomkill_example.txt). - tools/[opensnoop](tools/opensnoop.py): Trace open() syscalls. [Examples](tools/opensnoop_example.txt). - tools/[pidpersec](tools/pidpersec.py): Count new processes (via fork). [Examples](tools/pidpersec_example.txt). - tools/[profile](tools/profile.py): Profile CPU usage by sampling stack traces at a timed interval. [Examples](tools/profile_example.txt). - tools/[reset-trace](tools/reset-trace.sh): Reset the state of tracing. Maintenance tool only. [Examples](tools/reset-trace_example.txt). - tools/[runqlat](tools/runqlat.py): Run queue (scheduler) latency as a histogram. [Examples](tools/runqlat_example.txt). - tools/[runqlen](tools/runqlen.py): Run queue length as a histogram. [Examples](tools/runqlen_example.txt). - tools/[runqslower](tools/runqslower.py): Trace long process scheduling delays. [Examples](tools/runqslower_example.txt). - tools/[shmsnoop](tools/shmsnoop.py): Trace System V shared memory syscalls. [Examples](tools/shmsnoop_example.txt). - tools/[sofdsnoop](tools/sofdsnoop.py): Trace FDs passed through unix sockets. [Examples](tools/sofdsnoop_example.txt). - tools/[slabratetop](tools/slabratetop.py): Kernel SLAB/SLUB memory cache allocation rate top. [Examples](tools/slabratetop_example.txt). - tools/[softirqs](tools/softirqs.py): Measure soft IRQ (soft interrupt) event time. [Examples](tools/softirqs_example.txt). - tools/[solisten](tools/solisten.py): Trace TCP socket listen. [Examples](tools/solisten_example.txt). - tools/[sslsniff](tools/sslsniff.py): Sniff OpenSSL written and readed data. [Examples](tools/sslsniff_example.txt). - tools/[stackcount](tools/stackcount.py): Count kernel function calls and their stack traces. [Examples](tools/stackcount_example.txt). - tools/[syncsnoop](tools/syncsnoop.py): Trace sync() syscall. [Examples](tools/syncsnoop_example.txt). - tools/[syscount](tools/syscount.py): Summarize syscall counts and latencies. [Examples](tools/syscount_example.txt). - tools/[tcpaccept](tools/tcpaccept.py): Trace TCP passive connections (accept()). [Examples](tools/tcpaccept_example.txt). - tools/[tcpconnect](tools/tcpconnect.py): Trace TCP active connections (connect()). [Examples](tools/tcpconnect_example.txt). - tools/[tcpconnlat](tools/tcpconnlat.py): Trace TCP active connection latency (connect()). [Examples](tools/tcpconnlat_example.txt). - tools/[tcpdrop](tools/tcpdrop.py): Trace kernel-based TCP packet drops with details. [Examples](tools/tcpdrop_example.txt). - tools/[tcplife](tools/tcplife.py): Trace TCP sessions and summarize lifespan. [Examples](tools/tcplife_example.txt). - tools/[tcpretrans](tools/tcpretrans.py): Trace TCP retransmits and TLPs. [Examples](tools/tcpretrans_example.txt). - tools/[tcpstates](tools/tcpstates.py): Trace TCP session state changes with durations. [Examples](tools/tcpstates_example.txt). - tools/[tcpsubnet](tools/tcpsubnet.py): Summarize and aggregate TCP send by subnet. [Examples](tools/tcpsubnet_example.txt). - tools/[tcptop](tools/tcptop.py): Summarize TCP send/recv throughput by host. Top for TCP. [Examples](tools/tcptop_example.txt). - tools/[tcptracer](tools/tcptracer.py): Trace TCP established connections (connect(), accept(), close()). [Examples](tools/tcptracer_example.txt). - tools/[tplist](tools/tplist.py): Display kernel tracepoints or USDT probes and their formats. [Examples](tools/tplist_example.txt). - tools/[trace](tools/trace.py): Trace arbitrary functions, with filters. [Examples](tools/trace_example.txt). - tools/[ttysnoop](tools/ttysnoop.py): Watch live output from a tty or pts device. [Examples](tools/ttysnoop_example.txt). - tools/[ucalls](tools/lib/ucalls.py): Summarize method calls or Linux syscalls in high-level languages. [Examples](tools/lib/ucalls_example.txt). - tools/[uflow](tools/lib/uflow.py): Print a method flow graph in high-level languages. [Examples](tools/lib/uflow_example.txt). - tools/[ugc](tools/lib/ugc.py): Trace garbage collection events in high-level languages. [Examples](tools/lib/ugc_example.txt). - tools/[uobjnew](tools/lib/uobjnew.py): Summarize object allocation events by object type and number of bytes allocated. [Examples](tools/lib/uobjnew_example.txt). - tools/[ustat](tools/lib/ustat.py): Collect events such as GCs, thread creations, object allocations, exceptions and more in high-level languages. [Examples](tools/lib/ustat_example.txt). - tools/[uthreads](tools/lib/uthreads.py): Trace thread creation events in Java and raw pthreads. [Examples](tools/lib/uthreads_example.txt). - tools/[vfscount](tools/vfscount.py) tools/[vfscount.c](tools/vfscount.c): Count VFS calls. [Examples](tools/vfscount_example.txt). - tools/[vfsstat](tools/vfsstat.py) tools/[vfsstat.c](tools/vfsstat.c): Count some VFS calls, with column output. [Examples](tools/vfsstat_example.txt). - tools/[wakeuptime](tools/wakeuptime.py): Summarize sleep to wakeup time by waker kernel stack. [Examples](tools/wakeuptime_example.txt). - tools/[xfsdist](tools/xfsdist.py): Summarize XFS operation latency distribution as a histogram. [Examples](tools/xfsdist_example.txt). - tools/[xfsslower](tools/xfsslower.py): Trace slow XFS operations. [Examples](tools/xfsslower_example.txt). - tools/[zfsdist](tools/zfsdist.py): Summarize ZFS operation latency distribution as a histogram. [Examples](tools/zfsdist_example.txt). - tools/[zfsslower](tools/zfsslower.py): Trace slow ZFS operations. [Examples](tools/zfsslower_example.txt). ### Networking Examples: - examples/networking/[distributed_bridge/](examples/networking/distributed_bridge): Distributed bridge example. - examples/networking/[http_filter/](examples/networking/http_filter): Simple HTTP filter example. - examples/networking/[simple_tc.py](examples/networking/simple_tc.py): Simple traffic control example. - examples/networking/[simulation.py](examples/networking/simulation.py): Simulation helper. - examples/networking/neighbor_sharing/[tc_neighbor_sharing.py](examples/networking/neighbor_sharing/tc_neighbor_sharing.py) examples/networking/neighbor_sharing/[tc_neighbor_sharing.c](examples/networking/neighbor_sharing/tc_neighbor_sharing.c): Per-IP classification and rate limiting. - examples/networking/[tunnel_monitor/](examples/networking/tunnel_monitor): Efficiently monitor traffic flows. [Example video](https://www.youtube.com/watch?v=yYy3Cwce02k). - examples/networking/vlan_learning/[vlan_learning.py](examples/networking/vlan_learning/vlan_learning.py) examples/[vlan_learning.c](examples/networking/vlan_learning/vlan_learning.c): Demux Ethernet traffic into worker veth+namespaces. ### BPF Introspection: Tools that help to introspect BPF programs. - introspection/[bps.c](introspection/bps.c): List all BPF programs loaded into the kernel. 'ps' for BPF programs. [Examples](introspection/bps_example.txt). ## Motivation BPF guarantees that the programs loaded into the kernel cannot crash, and cannot run forever, but yet BPF is general purpose enough to perform many arbitrary types of computation. Currently, it is possible to write a program in C that will compile into a valid BPF program, yet it is vastly easier to write a C program that will compile into invalid BPF (C is like that). The user won't know until trying to run the program whether it was valid or not. With a BPF-specific frontend, one should be able to write in a language and receive feedback from the compiler on the validity as it pertains to a BPF backend. This toolkit aims to provide a frontend that can only create valid BPF programs while still harnessing its full flexibility. Furthermore, current integrations with BPF have a kludgy workflow, sometimes involving compiling directly in a linux kernel source tree. This toolchain aims to minimize the time that a developer spends getting BPF compiled, and instead focus on the applications that can be written and the problems that can be solved with BPF. The features of this toolkit include: * End-to-end BPF workflow in a shared library * A modified C language for BPF backends * Integration with llvm-bpf backend for JIT * Dynamic (un)loading of JITed programs * Support for BPF kernel hooks: socket filters, tc classifiers, tc actions, and kprobes * Bindings for Python * Examples for socket filters, tc classifiers, and kprobes * Self-contained tools for tracing a running system In the future, more bindings besides python will likely be supported. Feel free to add support for the language of your choice and send a pull request! ## Tutorials - [docs/tutorial.md](docs/tutorial.md): Using bcc tools to solve performance, troubleshooting, and networking issues. - [docs/tutorial_bcc_python_developer.md](docs/tutorial_bcc_python_developer.md): Developing new bcc programs using the Python interface. ### Networking At Red Hat Summit 2015, BCC was presented as part of a [session on BPF](http://www.devnation.org/#7784f1f7513e8542e4db519e79ff5eec). A multi-host vxlan environment is simulated and a BPF program used to monitor one of the physical interfaces. The BPF program keeps statistics on the inner and outer IP addresses traversing the interface, and the userspace component turns those statistics into a graph showing the traffic distribution at multiple granularities. See the code [here](examples/networking/tunnel_monitor). [![Screenshot](http://img.youtube.com/vi/yYy3Cwce02k/0.jpg)](https://youtu.be/yYy3Cwce02k) ## Contributing Already pumped up to commit some code? Here are some resources to join the discussions in the [IOVisor](https://www.iovisor.org/) community and see what you want to work on. * _Mailing List:_ https://lists.iovisor.org/mailman/listinfo/iovisor-dev * _IRC:_ #iovisor at irc.oftc.net * _BCC Issue Tracker:_ [Github Issues](https://github.com/iovisor/bcc/issues) * _A guide for contributing scripts:_ [CONTRIBUTING-SCRIPTS.md](CONTRIBUTING-SCRIPTS.md) ## External links Looking for more information on BCC and how it's being used? You can find links to other BCC content on the web in [LINKS.md](LINKS.md). bpfcc-0.12.0/SPECS/000077500000000000000000000000001357404205000135245ustar00rootroot00000000000000bpfcc-0.12.0/SPECS/Dockerfile.fedora000066400000000000000000000014441357404205000167600ustar00rootroot00000000000000# Copyright (c) PLUMgrid, Inc. # Licensed under the Apache License, Version 2.0 (the "License") FROM fedora:rawhide MAINTAINER Brenden Blanco RUN dnf -y install bison cmake flex gcc gcc-c++ git libxml2-devel make python2-devel rpm-build wget zlib-devel WORKDIR /root RUN wget http://llvm.org/releases/3.7.1/{cfe,llvm}-3.7.1.src.tar.xz RUN tar -xf llvm-3.7.1.src.tar.xz && mkdir llvm-3.7.1.src/tools/clang && tar -xf cfe-3.7.1.src.tar.xz -C llvm-3.7.1.src/tools/clang --strip 1 && mkdir llvm-3.7.1.src/build RUN cd llvm-3.7.1.src/build && cmake .. -DCMAKE_BUILD_TYPE=Release -DLLVM_TARGETS_TO_BUILD="X86;BPF" -DCMAKE_INSTALL_PREFIX=/usr RUN cd llvm-3.7.1.src/build && make -j8 COPY . bcc WORKDIR /root/bcc RUN PATH=/root/llvm-3.7.1.src/build/bin:$PATH ./scripts/build-rpm.sh bpfcc-0.12.0/SPECS/bcc+clang.spec000066400000000000000000000053001357404205000162050ustar00rootroot00000000000000%define debug_package %{nil} %define llvmver 3.7.1 Name: bcc Version: @REVISION@ Release: @GIT_REV_COUNT@ Summary: BPF Compiler Collection (BCC) Group: Development/Languages License: ASL 2.0 URL: https://github.com/iovisor/bcc Source0: https://github.com/iovisor/bcc/archive/v%{version}.tar.gz Source1: http://llvm.org/releases/%{llvmver}/llvm-%{llvmver}.src.tar.xz Source2: http://llvm.org/releases/%{llvmver}/cfe-%{llvmver}.src.tar.xz BuildArch: x86_64 BuildRequires: bison, cmake >= 2.8.7, flex, gcc, gcc-c++, libxml2-devel, python2-devel, elfutils-libelf-devel-static %description Python bindings for BPF Compiler Collection (BCC). Control a BPF program from userspace. %prep %setup -T -b 1 -n llvm-%{llvmver}.src mkdir tools/clang tar -xvvJf %{_sourcedir}/cfe-%{llvmver}.src.tar.xz -C tools/clang --strip 1 %setup -D -n bcc %build export LD_LIBRARY_PATH="%{_builddir}/usr/lib64" export PATH="%{_builddir}/usr/bin":$PATH # build llvm pushd %{_builddir}/llvm-%{llvmver}.src mkdir build cd build cmake .. -DCMAKE_BUILD_TYPE=Release -DLLVM_TARGETS_TO_BUILD="X86;BPF" -DCMAKE_INSTALL_PREFIX=/usr make %{?_smp_mflags} make install DESTDIR="%{_builddir}" popd mkdir build pushd build cmake .. -DREVISION_LAST=%{version} -DREVISION=%{version} -DCMAKE_INSTALL_PREFIX=/usr make %{?_smp_mflags} popd %install pushd build make install/strip DESTDIR=%{buildroot} %changelog * Fri Jul 03 2015 Brenden Blanco - 0.1.1-2 - Initial RPM Release %package -n libbcc Summary: Shared Library for BPF Compiler Collection (BCC) Requires: elfutils-libelf %description -n libbcc Shared Library for BPF Compiler Collection (BCC) %package -n libbcc-examples Summary: Examples for BPF Compiler Collection (BCC) Requires: libbcc %description -n libbcc-examples Examples for BPF Compiler Collection (BCC) %package -n python-bcc Summary: Python bindings for BPF Compiler Collection (BCC) Requires: libbcc %description -n python-bcc Python bindings for BPF Compiler Collection (BCC) %package -n bcc-tools Summary: Command line tools for BPF Compiler Collection (BCC) Requires: python-bcc %description -n bcc-tools Command line tools for BPF Compiler Collection (BCC) %files -n python-bcc %{python_sitelib}/bcc* %files -n libbcc /usr/lib64/* /usr/include/bcc/* %files -n libbcc-examples /usr/share/bcc/examples/* %exclude /usr/share/bcc/examples/*.pyc %exclude /usr/share/bcc/examples/*.pyo %exclude /usr/share/bcc/examples/*/*.pyc %exclude /usr/share/bcc/examples/*/*.pyo %exclude /usr/share/bcc/examples/*/*/*.pyc %exclude /usr/share/bcc/examples/*/*/*.pyo %files -n bcc-tools /usr/share/bcc/introspection/* /usr/share/bcc/tools/* /usr/share/bcc/man/* bpfcc-0.12.0/SPECS/bcc.spec000066400000000000000000000121461357404205000151330ustar00rootroot00000000000000%bcond_with local_clang_static #lua jit not available for some architectures %ifarch ppc64 aarch64 ppc64le %{!?with_lua: %global with_lua 0} %else %{!?with_lua: %global with_lua 1} %endif # use --with shared to only link against libLLVM.so %if 0%{?fedora} >= 28 || 0%{?rhel} > 7 %bcond_without llvm_shared %else %bcond_with llvm_shared %endif # Build python3 support for distributions that have it %if 0%{?fedora} >= 28 || 0%{?rhel} > 7 %bcond_without python3 %else %bcond_with python3 %endif %if %{with python3} %global __python %{__python3} %global python_bcc python3-bcc %global python_cmds python2;python3 %else %global __python %{__python2} %global python_bcc python2-bcc %global python_cmds python2 %endif %define debug_package %{nil} Name: bcc Version: @REVISION@ Release: @GIT_REV_COUNT@ Summary: BPF Compiler Collection (BCC) Group: Development/Languages License: ASL 2.0 URL: https://github.com/iovisor/bcc Source0: bcc.tar.gz ExclusiveArch: x86_64 ppc64 aarch64 ppc64le BuildRequires: bison cmake >= 2.8.7 flex make BuildRequires: gcc gcc-c++ python2-devel elfutils-libelf-devel-static %if %{with python3} BuildRequires: python3-devel %endif %if %{with_lua} BuildRequires: luajit luajit-devel %endif %if %{without local_clang_static} BuildRequires: llvm-devel BuildRequires: clang-devel %if %{without llvm_shared} BuildRequires: llvm-static %endif %endif BuildRequires: pkgconfig ncurses-devel %description Python bindings for BPF Compiler Collection (BCC). Control a BPF program from userspace. %if %{with_lua} %global lua_include `pkg-config --variable=includedir luajit` %global lua_libs `pkg-config --variable=libdir luajit`/lib`pkg-config --variable=libname luajit`.so %global lua_config -DLUAJIT_INCLUDE_DIR=%{lua_include} -DLUAJIT_LIBRARIES=%{lua_libs} %endif %prep %setup -q -n bcc %build mkdir build pushd build cmake .. -DREVISION_LAST=%{version} -DREVISION=%{version} \ -DCMAKE_INSTALL_PREFIX=/usr \ %{?lua_config} \ -DPYTHON_CMD="%{python_cmds}" \ %{?with_llvm_shared:-DENABLE_LLVM_SHARED=1} make %{?_smp_mflags} popd %install pushd build make install/strip DESTDIR=%{buildroot} # mangle shebangs find %{buildroot}/usr/share/bcc/{tools,examples} -type f -exec \ sed -i -e '1 s|^#!/usr/bin/python$|#!'%{__python}'|' \ -e '1 s|^#!/usr/bin/env python$|#!'%{__python}'|' {} \; %package -n libbcc Summary: Shared Library for BPF Compiler Collection (BCC) Requires: elfutils-libelf %description -n libbcc Shared Library for BPF Compiler Collection (BCC) %package -n python2-bcc Summary: Python2 bindings for BPF Compiler Collection (BCC) Requires: libbcc = %{version}-%{release} %{?python_provide:%python_provide python2-bcc} %description -n python2-bcc Python bindings for BPF Compiler Collection (BCC) %if %{with python3} %package -n python3-bcc Summary: Python3 bindings for BPF Compiler Collection (BCC) Requires: libbcc = %{version}-%{release} %{?python_provide:%python_provide python3-bcc} %description -n python3-bcc Python bindings for BPF Compiler Collection (BCC) %endif %if %{with_lua} %package -n bcc-lua Summary: Standalone tool to run BCC tracers written in Lua Requires: libbcc = %{version}-%{release} %description -n bcc-lua Standalone tool to run BCC tracers written in Lua %endif %package -n libbcc-examples Summary: Examples for BPF Compiler Collection (BCC) Requires: %{python_bcc} = %{version}-%{release} %if %{with_lua} Requires: bcc-lua = %{version}-%{release} %endif %description -n libbcc-examples Examples for BPF Compiler Collection (BCC) %package -n bcc-tools Summary: Command line tools for BPF Compiler Collection (BCC) Requires: %{python_bcc} = %{version}-%{release} %description -n bcc-tools Command line tools for BPF Compiler Collection (BCC) %files -n libbcc /usr/lib64/* /usr/include/bcc/* %files -n python2-bcc %{python2_sitelib}/bcc* %if %{with python3} %files -n python3-bcc %{python3_sitelib}/bcc* %endif %if %{with_lua} %files -n bcc-lua /usr/bin/bcc-lua %endif %files -n libbcc-examples /usr/share/bcc/examples/* %exclude /usr/share/bcc/examples/*.pyc %exclude /usr/share/bcc/examples/*.pyo %exclude /usr/share/bcc/examples/*/*.pyc %exclude /usr/share/bcc/examples/*/*.pyo %exclude /usr/share/bcc/examples/*/*/*.pyc %exclude /usr/share/bcc/examples/*/*/*.pyo %files -n bcc-tools /usr/share/bcc/introspection/* /usr/share/bcc/tools/* /usr/share/bcc/man/* %post -n libbcc -p /sbin/ldconfig %postun -n libbcc -p /sbin/ldconfig %changelog * Wed Jul 18 2018 Brenden Blanco - 0.6.0-1 - Make python3 the default when possible - Add with llvm_shared conditional - Add python2/python3 package targets * Mon Nov 21 2016 William Cohen - 0.2.0-1 - Revise bcc.spec to address rpmlint issues and build properly in Fedora koji. * Mon Apr 04 2016 Vicent Marti - 0.1.4-1 - Add bcc-lua package * Sun Nov 29 2015 Brenden Blanco - 0.1.3-1 - Add bcc-tools package * Mon Oct 12 2015 Brenden Blanco - 0.1.2-1 - Add better version numbering into libbcc.so * Fri Jul 03 2015 Brenden Blanco - 0.1.1-2 - Initial RPM Release bpfcc-0.12.0/cmake/000077500000000000000000000000001357404205000137275ustar00rootroot00000000000000bpfcc-0.12.0/cmake/FindCompilerFlag.cmake000066400000000000000000000014131357404205000200750ustar00rootroot00000000000000# Copyright (c) 2017 Facebook, Inc. # Licensed under the Apache License, Version 2.0 (the "License") if (CMAKE_C_COMPILER_ID MATCHES "Clang") set(COMPILER_NOPIE_FLAG "-nopie") else() set(_backup_c_flags "${CMAKE_REQUIRED_FLAGS}") set(CMAKE_REQUIRED_FLAGS "-no-pie") CHECK_CXX_SOURCE_COMPILES("int main() {return 0;}" HAVE_NO_PIE_FLAG) if (HAVE_NO_PIE_FLAG) set(COMPILER_NOPIE_FLAG "-no-pie") else() set(COMPILER_NOPIE_FLAG "") endif() set(CMAKE_REQUIRED_FLAGS "${_backup_c_flags}") endif() # check whether reallocarray availability # this is used to satisfy reallocarray usage under src/cc/libbpf/ CHECK_CXX_SOURCE_COMPILES( " #define _GNU_SOURCE #include int main(void) { return !!reallocarray(NULL, 1, 1); } " HAVE_REALLOCARRAY_SUPPORT) bpfcc-0.12.0/cmake/FindLibBpf.cmake000066400000000000000000000027021357404205000166710ustar00rootroot00000000000000# - Try to find libbpf # Once done this will define # # LIBBPF_FOUND - system has libbpf # LIBBPF_INCLUDE_DIR - the libbpf include directory # LIBBPF_STATIC_LIBRARIES - the libbpf source directory # LIBBPF_LIBRARIES - link these to use libbpf #if (LIBBPF_LIBRARIES AND LIBBPF_INCLUDE_DIR AND LIBBPF_STATIC_LIBRARIES) # set (LibBpf_FIND_QUIETLY TRUE) #endif (LIBBPF_LIBRARIES AND LIBBPF_INCLUDE_DIR AND LIBBPF_STATIC_LIBRARIES) # You'll need following packages to be installed (Fedora names): # libbpf # libbpf-static # libbpf-devel find_path (LIBBPF_INCLUDE_DIR NAMES bpf/bpf.h bpf/btf.h bpf/libbpf.h PATHS /usr/include /usr/local/include /opt/local/include /sw/include ENV CPATH) find_library (LIBBPF_STATIC_LIBRARIES NAMES libbpf.a PATHS /usr/lib /usr/local/lib /opt/local/lib /sw/lib ENV LIBRARY_PATH ENV LD_LIBRARY_PATH) find_library (LIBBPF_LIBRARIES NAMES bpf PATHS /usr/lib /usr/local/lib /opt/local/lib /sw/lib ENV LIBRARY_PATH ENV LD_LIBRARY_PATH) include (FindPackageHandleStandardArgs) # handle the QUIETLY and REQUIRED arguments and set 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_STATIC_LIBRARIES LIBBPF_INCLUDE_DIR) mark_as_advanced(LIBBPF_INCLUDE_DIR LIBBPF_STATIC_LIBRARIES LIBBPF_LIBRARIES) bpfcc-0.12.0/cmake/FindLibElf.cmake000066400000000000000000000030471357404205000166730ustar00rootroot00000000000000# - 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 PATHS /usr/include /usr/include/libelf /usr/local/include /usr/local/include/libelf /opt/local/include /opt/local/include/libelf /sw/include /sw/include/libelf ENV CPATH) find_library (LIBELF_LIBRARIES NAMES elf PATHS /usr/lib /usr/local/lib /opt/local/lib /sw/lib ENV LIBRARY_PATH ENV LD_LIBRARY_PATH) include (FindPackageHandleStandardArgs) # handle the QUIETLY and REQUIRED arguments and set LIBELF_FOUND to TRUE if all listed variables are TRUE FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibElf DEFAULT_MSG 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) mark_as_advanced(LIBELF_INCLUDE_DIRS LIBELF_LIBRARIES ELF_GETSHDRSTRNDX) bpfcc-0.12.0/cmake/FindLuaJIT.cmake000066400000000000000000000044211357404205000166230ustar00rootroot00000000000000# Locate Lua library # This module defines # LUAJIT_FOUND, if false, do not try to link to Lua # LUAJIT_LIBRARIES # LUAJIT_INCLUDE_DIR, where to find lua.h # # Note that the expected include convention is # #include "lua.h" # and not # #include # This is because, the lua location is not standardized and may exist # in locations other than lua/ #============================================================================= # Copyright 2007-2009 Kitware, Inc. # # Distributed under the OSI-approved BSD License (the "License"); # see accompanying file Copyright.txt for details. # # This software is distributed WITHOUT ANY WARRANTY; without even the # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the License for more information. #============================================================================= # (To distributed this file outside of CMake, substitute the full # License text for the above reference.) # # ################ # 2010 - modified for cronkite to find luajit instead of lua, as it was before. # FIND_PATH(LUAJIT_INCLUDE_DIR lua.h HINTS $ENV{LUAJIT_DIR} PATH_SUFFIXES luajit-2.0 luajit2.0 luajit luajit-2.1 PATHS ~/Library/Frameworks /Library/Frameworks /usr/local /usr /sw # Fink /opt/local # DarwinPorts /opt/csw # Blastwave /opt ) FIND_LIBRARY(LUAJIT_LIBRARY NAMES libluajit-51.a libluajit-5.1.a libluajit.a libluajit-5.1.so HINTS $ENV{LUAJIT_DIR} PATH_SUFFIXES lib64 lib PATHS ~/Library/Frameworks /Library/Frameworks /usr/local /usr /sw /opt/local /opt/csw /opt ) IF(LUAJIT_LIBRARY) IF(UNIX AND NOT APPLE) FIND_LIBRARY(LUAJIT_MATH_LIBRARY m) FIND_LIBRARY(LUAJIT_DL_LIBRARY dl) SET( LUAJIT_LIBRARIES "${LUAJIT_LIBRARY};${LUAJIT_DL_LIBRARY};${LUAJIT_MATH_LIBRARY}" CACHE STRING "Lua Libraries") ELSE(UNIX AND NOT APPLE) SET( LUAJIT_LIBRARIES "${LUAJIT_LIBRARY}" CACHE STRING "Lua Libraries") ENDIF(UNIX AND NOT APPLE) ENDIF(LUAJIT_LIBRARY) INCLUDE(FindPackageHandleStandardArgs) # handle the QUIETLY and REQUIRED arguments and set LUAJIT_FOUND to TRUE if # all listed variables are TRUE FIND_PACKAGE_HANDLE_STANDARD_ARGS(LuaJIT DEFAULT_MSG LUAJIT_LIBRARIES LUAJIT_INCLUDE_DIR) MARK_AS_ADVANCED(LUAJIT_INCLUDE_DIR LUAJIT_LIBRARIES LUAJIT_LIBRARY LUAJIT_MATH_LIBRARY) bpfcc-0.12.0/cmake/GetGitRevisionDescription.cmake000066400000000000000000000073141357404205000220440ustar00rootroot00000000000000# - Returns a version string from Git # # These functions force a re-configure on each git commit so that you can # trust the values of the variables in your build system. # # get_git_head_revision( [ ...]) # # Returns the refspec and sha hash of the current head revision # # git_describe( [ ...]) # # Returns the results of git describe on the source tree, and adjusting # the output so that it tests false if an error occurs. # # git_get_exact_tag( [ ...]) # # Returns the results of git describe --exact-match on the source tree, # and adjusting the output so that it tests false if there was no exact # matching tag. # # Requires CMake 2.6 or newer (uses the 'function' command) # # Original Author: # 2009-2010 Ryan Pavlik # http://academic.cleardefinition.com # Iowa State University HCI Graduate Program/VRAC # # Copyright Iowa State University 2009-2010. # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) if(__get_git_revision_description) return() endif() set(__get_git_revision_description YES) # We must run the following at "include" time, not at function call time, # to find the path to this module rather than the path to a calling list file get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH) function(get_git_head_revision _refspecvar _hashvar) set(GIT_PARENT_DIR "${CMAKE_SOURCE_DIR}") set(GIT_DIR "${GIT_PARENT_DIR}/.git") while(NOT EXISTS "${GIT_DIR}") # .git dir not found, search parent directories set(GIT_PREVIOUS_PARENT "${GIT_PARENT_DIR}") get_filename_component(GIT_PARENT_DIR ${GIT_PARENT_DIR} PATH) if(GIT_PARENT_DIR STREQUAL GIT_PREVIOUS_PARENT) # We have reached the root directory, we are not in git set(${_refspecvar} "GITDIR-NOTFOUND" PARENT_SCOPE) set(${_hashvar} "GITDIR-NOTFOUND" PARENT_SCOPE) return() endif() set(GIT_DIR "${GIT_PARENT_DIR}/.git") endwhile() set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data") if(NOT EXISTS "${GIT_DATA}") file(MAKE_DIRECTORY "${GIT_DATA}") endif() if(NOT EXISTS "${GIT_DIR}/HEAD") return() endif() set(HEAD_FILE "${GIT_DATA}/HEAD") configure_file("${GIT_DIR}/HEAD" "${HEAD_FILE}" COPYONLY) configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in" "${GIT_DATA}/grabRef.cmake" @ONLY) include("${GIT_DATA}/grabRef.cmake") set(${_refspecvar} "${HEAD_REF}" PARENT_SCOPE) set(${_hashvar} "${HEAD_HASH}" PARENT_SCOPE) endfunction() function(git_describe _var) if(NOT GIT_FOUND) find_package(Git QUIET) endif() get_git_head_revision(refspec hash) if(NOT GIT_FOUND) set(${_var} "GIT-NOTFOUND" PARENT_SCOPE) return() endif() if(NOT hash) set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE) return() endif() # TODO sanitize #if((${ARGN}" MATCHES "&&") OR # (ARGN MATCHES "||") OR # (ARGN MATCHES "\\;")) # message("Please report the following error to the project!") # message(FATAL_ERROR "Looks like someone's doing something nefarious with git_describe! Passed arguments ${ARGN}") #endif() #message(STATUS "Arguments to execute_process: ${ARGN}") execute_process(COMMAND "${GIT_EXECUTABLE}" describe ${hash} ${ARGN} WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" RESULT_VARIABLE res OUTPUT_VARIABLE out ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if(NOT res EQUAL 0) set(out "${out}-${res}-NOTFOUND") endif() set(${_var} "${out}" PARENT_SCOPE) endfunction() function(git_get_exact_tag _var) git_describe(out --exact-match ${ARGN}) set(${_var} "${out}" PARENT_SCOPE) endfunction() bpfcc-0.12.0/cmake/GetGitRevisionDescription.cmake.in000066400000000000000000000022611357404205000224450ustar00rootroot00000000000000# # Internal file for GetGitRevisionDescription.cmake # # Requires CMake 2.6 or newer (uses the 'function' command) # # Original Author: # 2009-2010 Ryan Pavlik # http://academic.cleardefinition.com # Iowa State University HCI Graduate Program/VRAC # # Copyright Iowa State University 2009-2010. # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) set(HEAD_HASH) file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024) string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS) if(HEAD_CONTENTS MATCHES "ref") # named branch string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}") if(EXISTS "@GIT_DIR@/${HEAD_REF}") configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY) elseif(EXISTS "@GIT_DIR@/logs/${HEAD_REF}") configure_file("@GIT_DIR@/logs/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY) set(HEAD_HASH "${HEAD_REF}") endif() else() # detached HEAD configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY) endif() if(NOT HEAD_HASH) file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024) string(STRIP "${HEAD_HASH}" HEAD_HASH) endif() bpfcc-0.12.0/cmake/bump_version.cmake000066400000000000000000000010521357404205000174370ustar00rootroot00000000000000# Copyright (c) PLUMgrid, Inc. # Licensed under the Apache License, Version 2.0 (the "License") configure_file(SPECS/Dockerfile.el6.in SPECS/Dockerfile.el6 @ONLY) configure_file(SPECS/Dockerfile.el7.in SPECS/Dockerfile.el7 @ONLY) configure_file(SPECS/Dockerfile.f22.in SPECS/Dockerfile.f22 @ONLY) configure_file(SPECS/bcc.el6.spec.in SPECS/bcc.el6.spec @ONLY) configure_file(SPECS/bcc.el7.spec.in SPECS/bcc.el7.spec @ONLY) configure_file(SPECS/bcc.f22.spec.in SPECS/bcc.f22.spec @ONLY) configure_file(scripts/build-deb.sh.in scripts/build-deb.sh @ONLY) bpfcc-0.12.0/cmake/clang_libs.cmake000066400000000000000000000037061357404205000170340ustar00rootroot00000000000000if(ENABLE_LLVM_SHARED) set(llvm_libs "LLVM") else() set(llvm_raw_libs bitwriter bpfcodegen debuginfodwarf irreader linker mcjit objcarcopts option passes lto) if(ENABLE_LLVM_NATIVECODEGEN) set(llvm_raw_libs ${llvm_raw_libs} nativecodegen) endif() list(FIND LLVM_AVAILABLE_LIBS "LLVMCoverage" _llvm_coverage) if (${_llvm_coverage} GREATER -1) list(APPEND llvm_raw_libs coverage) endif() list(FIND LLVM_AVAILABLE_LIBS "LLVMCoroutines" _llvm_coroutines) if (${_llvm_coroutines} GREATER -1) list(APPEND llvm_raw_libs coroutines) endif() list(FIND LLVM_AVAILABLE_LIBS "LLVMFrontendOpenMP" _llvm_frontendOpenMP) if (${_llvm_frontendOpenMP} GREATER -1) list(APPEND llvm_raw_libs frontendopenmp) endif() if (${LLVM_PACKAGE_VERSION} VERSION_EQUAL 6 OR ${LLVM_PACKAGE_VERSION} VERSION_GREATER 6) list(APPEND llvm_raw_libs bpfasmparser) list(APPEND llvm_raw_libs bpfdisassembler) endif() llvm_map_components_to_libnames(_llvm_libs ${llvm_raw_libs}) llvm_expand_dependencies(llvm_libs ${_llvm_libs}) endif() # order is important set(clang_libs ${libclangFrontend} ${libclangSerialization} ${libclangDriver}) if (${LLVM_PACKAGE_VERSION} VERSION_EQUAL 8 OR ${LLVM_PACKAGE_VERSION} VERSION_GREATER 8) list(APPEND clang_libs ${libclangASTMatchers}) endif() list(APPEND clang_libs ${libclangParse} ${libclangSema} ${libclangCodeGen} ${libclangAnalysis} ${libclangRewrite} ${libclangEdit} ${libclangAST} ${libclangLex} ${libclangBasic}) # prune unused llvm static library stuff when linking into the new .so set(_exclude_flags) foreach(_lib ${clang_libs}) get_filename_component(_lib ${_lib} NAME) set(_exclude_flags "${_exclude_flags} -Wl,--exclude-libs=${_lib}") endforeach(_lib) set(clang_lib_exclude_flags "${_exclude_flags}") set(_exclude_flags) foreach(_lib ${llvm_libs}) get_filename_component(_lib ${_lib} NAME) set(_exclude_flags "${_exclude_flags} -Wl,--exclude-libs=lib${_lib}.a") endforeach(_lib) set(llvm_lib_exclude_flags "${_exclude_flags}") bpfcc-0.12.0/cmake/static_libstdc++.cmake000066400000000000000000000015061357404205000200540ustar00rootroot00000000000000# only turn on static-libstdc++ if also linking statically against clang string(REGEX MATCH ".*[.]a$" LIBCLANG_ISSTATIC "${libclangBasic}") # if gcc 4.9 or higher is used, static libstdc++ is a good option if (CMAKE_COMPILER_IS_GNUCC AND LIBCLANG_ISSTATIC) execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION) if (GCC_VERSION VERSION_GREATER 4.9 OR GCC_VERSION VERSION_EQUAL 4.9) execute_process(COMMAND ${CMAKE_C_COMPILER} -print-libgcc-file-name OUTPUT_VARIABLE GCC_LIB) get_filename_component(GCC_DIR "${GCC_LIB}" DIRECTORY) find_library(GCC_LIBSTDCPP libstdc++.a PATHS "${GCC_DIR}" NO_DEFAULT_PATH) if (GCC_LIBSTDCPP) message(STATUS "Using static-libstdc++") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -static-libstdc++") endif() endif() endif() bpfcc-0.12.0/cmake/version.cmake000066400000000000000000000017271357404205000164250ustar00rootroot00000000000000# Copyright (c) PLUMgrid, Inc. # Licensed under the Apache License, Version 2.0 (the "License") if(NOT REVISION) get_git_head_revision(GIT_REFSPEC GIT_SHA1) string(SUBSTRING "${GIT_SHA1}" 0 8 GIT_SHA1_SHORT) git_describe(GIT_DESCRIPTION) git_describe(GIT_TAG_LAST "--abbrev=0") git_get_exact_tag(GIT_TAG_EXACT) string(SUBSTRING "${GIT_TAG_LAST}-${GIT_SHA1_SHORT}" 1 -1 REVISION) if(GIT_TAG_EXACT) string(SUBSTRING "${GIT_TAG_EXACT}" 1 -1 REVISION) message(STATUS "Currently on Git tag ${GIT_TAG_EXACT}") else () message(STATUS "Latest recognized Git tag is ${GIT_TAG_LAST}") set(GIT_TAG_EXACT "") endif() message(STATUS "Git HEAD is ${GIT_SHA1}") # rpm/deb packaging uses this, only works on whole tag numbers if(NOT REVISION_LAST) string(SUBSTRING "${GIT_TAG_LAST}" 1 -1 REVISION_LAST) endif() else() set(REVISION_LAST "${REVISION}") endif() # strip leading 'v', and make unique for the tag message(STATUS "Revision is ${REVISION}") bpfcc-0.12.0/debian/000077500000000000000000000000001357404205000140715ustar00rootroot00000000000000bpfcc-0.12.0/debian/bcc-lua.install000066400000000000000000000000201357404205000167570ustar00rootroot00000000000000usr/bin/bcc-lua bpfcc-0.12.0/debian/bcc-tools.install000066400000000000000000000001101357404205000173360ustar00rootroot00000000000000usr/share/bcc/introspection/* usr/share/bcc/tools/* usr/share/bcc/man/* bpfcc-0.12.0/debian/changelog000066400000000000000000000105361357404205000157500ustar00rootroot00000000000000bcc (0.12.0-1) unstable; urgency=low * Support for kernel up to 5.4 * klockstat tool to track kernel mutex lock statistics * cmake option CMAKE_USE_LIBBPF_PACKAGE to build a bcc shared library linking with distro libbpf_static.a * new map.lookup_or_try_init() API to remove hidden return in map.lookup_or_init() * BPF_ARRAY_OF_MAPS and BPF_HASH_OF_MAPS support * support symbol offset for uprobe in both C++ and python API, kprobe already has the support * bug fixes for trace.py, tcpretrans.py, runqslower.py, etc. -- Yonghong Song Tue, 10 Dec 2019 17:00:00 +0000 bcc (0.11.0-1) unstable; urgency=low * Support for kernel up to 5.3 * Corresponding libbpf submodule release is v0.0.5 * Fix USDT issue with multi-threaded applications * Fixed the early return behavior of lookup_or_init * Support for nic hardware offload * Fixed and Enabled Travis CI * A lot of tools change with added new options, etc. -- Yonghong Song Tue, 03 Oct 2019 17:00:00 +0000 bcc (0.10.0-1) unstable; urgency=low * Support for kernel up to 5.1 * corresponding libbpf submodule release is v0.0.3 * support for reading kernel headers from /proc * libbpf.{a,so} renamed to libcc_bpf.{a,so} * new common options for some tools * new tool: drsnoop * s390 USDT support -- Brenden Blanco Tue, 28 May 2019 17:00:00 +0000 bcc (0.9.0-1) unstable; urgency=low * Adds support for BTF * Uses libbpf common library to wrap syscall API * Many bugfixes and new tools -- Brenden Blanco Thu, 07 Mar 2019 17:00:00 +0000 bcc (0.8.0-1) unstable; urgency=low * Support for kernel up to 5.0 -- Brenden Blanco Fri, 11 Jan 2019 17:00:00 +0000 bcc (0.7.0-1) unstable; urgency=low * Support for kernel up to 4.18 -- Brenden Blanco Tue, 04 Sep 2018 17:00:00 +0000 bcc (0.6.1-1) unstable; urgency=low * Build support for Fedora 28 and Ubuntu 18.04 * Add option to change license * Optimizations for some uses of bpf_probe_reads -- Brenden Blanco Mon, 23 Jul 2018 17:00:00 +0000 bcc (0.6.0-1) unstable; urgency=low * Support for kernel up to 4.17 * Many bugfixes * Many new tools * Improved python3 support -- Brenden Blanco Wed, 13 Jun 2018 17:00:00 +0000 bcc (0.5.0-1) unstable; urgency=low * Support for USDT in ARM64 * Bugfixes for 4.14 in some tools * Fixes for smoke test failures * Runtime memory usage reductions -- Brenden Blanco Wed, 29 Nov 2017 17:00:00 +0000 bcc (0.4.0-1) unstable; urgency=low * Bugfixes * Support for kernel up to 4.14 -- Brenden Blanco Fri, 20 Oct 2017 17:00:00 +0000 bcc (0.3.0-1) unstable; urgency=low * Many bugfixes * Many tools converted to perf ring buffer * New utilities in tools/ * capable, cpuunclaimed, dbslower, dbstat, deadlock_detector, llcstat, mountsnoop, runqlen, slabratetop, syscount, tcplife, tcptop, ttysnoop, ucalls, uflow, ugc, uobjnew, ustat, uthreads * New C++ API * Support for kernel up to 4.10 -- Brenden Blanco Thu, 09 Mar 2017 19:08:08 +0000 bcc (0.2.0-1) unstable; urgency=low * Add many new utilities in tools/ * Support for USDT * Support for lua * Many utilities converted to perf ring buffer * Support for tracepoints -- Brenden Blanco Thu, 08 Sep 2016 17:05:28 -0700 bcc (0.1.8-1) unstable; urgency=low * Add many new utilities in tools/ * wakeuptime, offwaketime, argdist, {xfs,zfs,ext4}{slower,dist}, others * Support for bpf_perf_event() * Support for public tables shared between programs * Support for up to 4.4 features * Remove external file dependencies from clang lib -- Brenden Blanco Mon, 23 Feb 2016 00:41:00 +0000 bcc (0.1.7-1) unstable; urgency=low * Tracing features and bugfixes * Built against LLVM 3.8 HEAD -- Brenden Blanco Mon, 12 Oct 2015 16:47:09 +0000 bcc (0.1.6-1) unstable; urgency=low * Stability fixes * Improvements to python API * Tracing features * Support for kernel 4.2 features -- Brenden Blanco Wed, 02 Sep 2015 16:23:19 +0000 bcc (0.1.5-1) unstable; urgency=low * Initial release -- Brenden Blanco Mon, 06 Jul 2015 18:04:28 +0000 bpfcc-0.12.0/debian/compat000066400000000000000000000000021357404205000152670ustar00rootroot000000000000009 bpfcc-0.12.0/debian/control000066400000000000000000000036471357404205000155060ustar00rootroot00000000000000Source: bcc Maintainer: Brenden Blanco Section: misc Priority: optional Standards-Version: 3.9.5 Build-Depends: debhelper (>= 9), cmake, libllvm3.7 [!arm64] | libllvm3.8 [!arm64] | libllvm6.0, llvm-3.7-dev [!arm64] | llvm-3.8-dev [!arm64] | llvm-6.0-dev, libclang-3.7-dev [!arm64] | libclang-3.8-dev [!arm64] | libclang-6.0-dev, clang-format | clang-format-3.7 [!arm64] | clang-format-3.8 [!arm64] | clang-format-6.0, libelf-dev, bison, flex, libfl-dev, libedit-dev, zlib1g-dev, git, python (>= 2.7), python-netaddr, python-pyroute2, luajit, libluajit-5.1-dev, arping, inetutils-ping | iputils-ping, iperf, netperf, ethtool, devscripts, python3, dh-python Homepage: https://github.com/iovisor/bcc Package: libbcc Architecture: all Provides: libbpfcc, libbpfcc-dev Conflicts: libbpfcc, libbpfcc-dev Depends: libc6, libstdc++6, libelf1 Description: Shared Library for BPF Compiler Collection (BCC) Shared Library for BPF Compiler Collection to control BPF programs from userspace. Package: libbcc-examples Architecture: any Depends: libbcc (= ${binary:Version}) Description: Examples for BPF Compiler Collection (BCC) Package: python-bcc Architecture: all Provides: python-bpfcc Conflicts: python-bpfcc Depends: libbcc (= ${binary:Version}), python, binutils Description: Python wrappers for BPF Compiler Collection (BCC) Package: python3-bcc Architecture: all Provides: python3-bpfcc Conflicts: python3-bpfcc Depends: libbcc (= ${binary:Version}), python3, binutils Description: Python3 wrappers for BPF Compiler Collection (BCC) Package: bcc-tools Architecture: all Provides: bpfcc-tools Conflicts: bpfcc-tools Depends: python-bcc (= ${binary:Version}) Description: Command line tools for BPF Compiler Collection (BCC) Package: bcc-lua Architecture: all Provides: bpfcc-lua Conflicts: bpfcc-lua Depends: libbcc (= ${binary:Version}) Description: Standalone tool to run BCC tracers written in Lua bpfcc-0.12.0/debian/copyright000066400000000000000000000003011357404205000160160ustar00rootroot00000000000000Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: bcc Source: https://github.com/iovisor/bcc Files: * Copyright: 2015 PLUMgrid, Inc. License: Apache-2.0 bpfcc-0.12.0/debian/docs000066400000000000000000000000361357404205000147430ustar00rootroot00000000000000FAQ.txt LICENSE.txt README.md bpfcc-0.12.0/debian/libbcc-examples.install000066400000000000000000000000311357404205000205050ustar00rootroot00000000000000usr/share/bcc/examples/* bpfcc-0.12.0/debian/libbcc.install000066400000000000000000000001021357404205000166700ustar00rootroot00000000000000usr/include/bcc/* usr/lib/*/libbcc* usr/lib/*/pkgconfig/libbcc.pc bpfcc-0.12.0/debian/python-bcc.install000066400000000000000000000000211357404205000175200ustar00rootroot00000000000000usr/lib/python2* bpfcc-0.12.0/debian/python3-bcc.install000066400000000000000000000000211357404205000176030ustar00rootroot00000000000000usr/lib/python3* bpfcc-0.12.0/debian/rules000077500000000000000000000015541357404205000151560ustar00rootroot00000000000000#!/usr/bin/make -f # -*- makefile -*- # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 DEBIAN_VERSION := $(shell dpkg-parsechangelog | sed -rne "s,^Version: (.*),\1,p") DEBIAN_REVISION := $(shell dpkg-parsechangelog | sed -rne "s,^Version: ([0-9.]+)(~|-)(.*),\3,p") UPSTREAM_VERSION := $(shell dpkg-parsechangelog | sed -rne "s,^Version: ([0-9.]+)(~|-)(.*),\1,p") %: dh $@ --buildsystem=cmake --parallel --with python2,python3 # tests cannot be run in parallel override_dh_auto_test: dh_auto_test -O--buildsystem=cmake -O--no-parallel # FIXME: LLVM_DEFINITIONS is broken somehow in LLVM cmake upstream override_dh_auto_configure: dh_auto_configure -- -DREVISION_LAST=$(UPSTREAM_VERSION) -DREVISION=$(UPSTREAM_VERSION) -DLLVM_DEFINITIONS="-D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS" -DPYTHON_CMD="python2;python3" bpfcc-0.12.0/debian/source/000077500000000000000000000000001357404205000153715ustar00rootroot00000000000000bpfcc-0.12.0/debian/source/format000066400000000000000000000000141357404205000165770ustar00rootroot000000000000003.0 (quilt) bpfcc-0.12.0/docs/000077500000000000000000000000001357404205000135775ustar00rootroot00000000000000bpfcc-0.12.0/docs/kernel-versions.md000066400000000000000000001315351357404205000172570ustar00rootroot00000000000000# BPF Features by Linux Kernel Version ## eBPF support Kernel version | Commit ---------------|------- 3.15 | [`bd4cf0ed331a`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=bd4cf0ed331a275e9bf5a49e6d0fd55dffc551b8) ## JIT compiling The list of supported architectures for your kernel can be retrieved with: git grep HAVE_EBPF_JIT arch/ Feature / Architecture | Kernel version | Commit -----------------------|----------------|------- x86\_64 | 3.16 | [`622582786c9e`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=622582786c9e041d0bd52bde201787adeab249f8) ARM64 | 3.18 | [`e54bcde3d69d`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=e54bcde3d69d40023ae77727213d14f920eb264a) s390 | 4.1 | [`054623105728`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=054623105728b06852f077299e2bf1bf3d5f2b0b) Constant blinding for JIT machines | 4.7 | [`4f3446bb809f`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=4f3446bb809f20ad56cadf712e6006815ae7a8f9) PowerPC64 | 4.8 | [`156d0e290e96`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=156d0e290e969caba25f1851c52417c14d141b24) Constant blinding - PowerPC64 | 4.9 | [`b7b7013cac55`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=b7b7013cac55d794940bd9cb7b7c55c9dececac4) Sparc64 | 4.12 | [`7a12b5031c6b`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=7a12b5031c6b947cc13918237ae652b536243b76) MIPS | 4.13 | [`f381bf6d82f0`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=f381bf6d82f032b7410185b35d000ea370ac706b) ARM32 | 4.14 | [`39c13c204bb1`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=39c13c204bb1150d401e27d41a9d8b332be47c49) x86\_32 | 4.18 | [`03f5781be2c7`](https://git.kernel.org/cgit/linux/kernel/git/davem/net-next.git/commit/?id=03f5781be2c7b7e728d724ac70ba10799cc710d7) ## Main features Several (but not all) of these _main features_ translate to an eBPF program type. The list of such program types supported in your kernel can be found in file [`include/uapi/linux/bpf.h`](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/uapi/linux/bpf.h): git grep -W 'bpf_prog_type {' include/uapi/linux/bpf.h Feature | Kernel version | Commit --------|----------------|------- `AF_PACKET` (libpcap/tcpdump, `cls_bpf` classifier, netfilter's `xt_bpf`, team driver's load-balancing mode…) | 3.15 | [`bd4cf0ed331a`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=bd4cf0ed331a275e9bf5a49e6d0fd55dffc551b8) Kernel helpers | 3.15 | [`bd4cf0ed331a`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=bd4cf0ed331a275e9bf5a49e6d0fd55dffc551b8) `bpf()` syscall | 3.18 | [`99c55f7d47c0`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=99c55f7d47c0dc6fc64729f37bf435abf43f4c60) Tables (_a.k.a._ Maps; details below) | 3.18 | [`99c55f7d47c0`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=99c55f7d47c0dc6fc64729f37bf435abf43f4c60) BPF attached to sockets | 3.19 | [`89aa075832b0`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=89aa075832b0da4402acebd698d0411dcc82d03e) BPF attached to `kprobes` | 4.1 | [`2541517c32be`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=2541517c32be2531e0da59dfd7efc1ce844644f5) `cls_bpf` / `act_bpf` for `tc` | 4.1 | [`e2e9b6541dd4`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=e2e9b6541dd4b31848079da80fe2253daaafb549) Tail calls | 4.2 | [`04fd61ab36ec`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=04fd61ab36ec065e194ab5e74ae34a5240d992bb) Non-root programs on sockets | 4.4 | [`1be7f75d1668`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=1be7f75d1668d6296b80bf35dcf6762393530afc) Persistent maps and programs (virtual FS) | 4.4 | [`b2197755b263`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=b2197755b2633e164a439682fb05a9b5ea48f706) `tc`'s `direct-action` (`da`) mode | 4.4 | [`045efa82ff56`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=045efa82ff563cd4e656ca1c2e354fa5bf6bbda4) `tc`'s `clsact` qdisc | 4.5 | [`1f211a1b929c`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=1f211a1b929c804100e138c5d3d656992cfd5622) BPF attached to tracepoints | 4.7 | [`98b5c2c65c29`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=98b5c2c65c2951772a8fc661f50d675e450e8bce) Direct packet access | 4.7 | [`969bf05eb3ce`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=969bf05eb3cedd5a8d4b7c346a85c2ede87a6d6d) XDP (see below) | 4.8 | [`6a773a15a1e8`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=6a773a15a1e8874e5eccd2f29190c31085912c95) BPF attached to perf events | 4.9 | [`0515e5999a46`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=0515e5999a466dfe6e1924f460da599bb6821487) Hardware offload for `tc`'s `cls_bpf` | 4.9 | [`332ae8e2f6ec`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=332ae8e2f6ecda5e50c5c62ed62894963e3a83f5) Verifier exposure and internal hooks | 4.9 | [`13a27dfc6697`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=13a27dfc669724564aafa2699976ee756029fed2) BPF attached to cgroups for socket filtering | 4.10 | [`0e33661de493`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=0e33661de493db325435d565a4a722120ae4cbf3) Lightweight tunnel encapsulation | 4.10 | [`3a0af8fd61f9`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=3a0af8fd61f90920f6fa04e4f1e9a6a73c1b4fd2) **e**BPF support for `xt_bpf` module (iptables) | 4.10 | [`2c16d6033264`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=2c16d60332643e90d4fa244f4a706c454b8c7569) BPF program tag | 4.10 | [`7bd509e311f4`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=7bd509e311f408f7a5132fcdde2069af65fa05ae) Tracepoints to debug BPF | 4.11 (removed in 4.18) | [`a67edbf4fb6d`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=a67edbf4fb6deadcfe57a04a134abed4a5ba3bb5) [`4d220ed0f814`](https://git.kernel.org/cgit/linux/kernel/git/bpf/bpf-next.git/commit/?id=4d220ed0f8140c478ab7b0a14d96821da639b646) Testing / benchmarking BPF programs | 4.12 | [`1cf1cae963c2`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=1cf1cae963c2e6032aebe1637e995bc2f5d330f4) BPF programs and maps IDs | 4.13 | [`dc4bb0e23561`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=dc4bb0e2356149aee4cdae061936f3bbdd45595c) BPF support for `sock_ops` | 4.13 | [`40304b2a1567`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=40304b2a1567fecc321f640ee4239556dd0f3ee0) BPF support for skbs on sockets | 4.14 | [`b005fd189cec`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=b005fd189cec9407b700599e1e80e0552446ee79) bpftool utility in kernel sources | 4.15 | [`71bb428fe2c1`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=71bb428fe2c19512ac671d5ee16ef3e73e1b49a8) BPF attached to cgroups as device controller | 4.15 | [`ebc614f68736`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=ebc614f687369f9df99828572b1d85a7c2de3d92) bpf2bpf function calls | 4.16 | [`cc8b0b92a169`](https://github.com/torvalds/linux/commit/cc8b0b92a1699bc32f7fec71daa2bfc90de43a4d) BPF used for monitoring socket RX/TX data | 4.17 | [`4f738adba30a`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=4f738adba30a7cfc006f605707e7aee847ffefa0) BPF attached to raw tracepoints | 4.17 | [`c4f6699dfcb8`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=c4f6699dfcb8558d138fe838f741b2c10f416cf9) BPF attached to `bind()` system call | 4.17 | [`4fbac77d2d09`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=4fbac77d2d092b475dda9eea66da674369665427) BPF Type Format (BTF) | 4.18 | [`69b693f0aefa`](https://git.kernel.org/cgit/linux/kernel/git/davem/net-next.git/commit/?id=69b693f0aefa0ed521e8bd02260523b5ae446ad7) AF_XDP | 4.18 | [`fbfc504a24f5`](https://git.kernel.org/cgit/linux/kernel/git/davem/net-next.git/commit/?id=fbfc504a24f53f7ebe128ab55cb5dba634f4ece8) bpfilter | 4.18 | [`d2ba09c17a06`](https://git.kernel.org/cgit/linux/kernel/git/davem/net-next.git/commit/?id=d2ba09c17a0647f899d6c20a11bab9e6d3382f07) End.BPF action for seg6local LWT | 4.18 | [`004d4b274e2a`](https://git.kernel.org/cgit/linux/kernel/git/davem/net-next.git/commit/?id=004d4b274e2a1a895a0e5dc66158b90a7d463d44) BPF attached to LIRC devices | 4.18 | [`f4364dcfc86d`](https://git.kernel.org/cgit/linux/kernel/git/bpf/bpf-next.git/commit/?id=f4364dcfc86df7c1ca47b256eaf6b6d0cdd0d936) BPF socket reuseport | 4.19 | [`2dbb9b9e6df6`](https://github.com/torvalds/linux/commit/2dbb9b9e6df67d444fbe425c7f6014858d337adf) BPF flow dissector | 4.20 | [`d58e468b1112`](https://github.com/torvalds/linux/commit/d58e468b1112dcd1d5193c0a89ff9f98b5a3e8b9) BPF cgroup sysctl | 5.2 | [`7b146cebe30c`](https://github.com/torvalds/linux/commit/7b146cebe30cb481b0f70d85779da938da818637) BPF raw tracepoint writable | 5.2 | [`9df1c28bb752`](https://github.com/torvalds/linux/commit/9df1c28bb75217b244257152ab7d788bb2a386d0) ## Tables (_a.k.a._ Maps) The list of map types supported in your kernel can be found in file [`include/uapi/linux/bpf.h`](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/uapi/linux/bpf.h): git grep -W 'bpf_map_type {' include/uapi/linux/bpf.h Table type | Kernel version | Commit -----------|----------------|------- Hash | 3.19 | [`0f8e4bd8a1fc`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=0f8e4bd8a1fc8c4185f1630061d0a1f2d197a475) Array | 3.19 | [`28fbcfa08d8e`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=28fbcfa08d8ed7c5a50d41a0433aad222835e8e3) Tail call (`PROG_ARRAY`) | 4.2 | [`04fd61ab36ec`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=04fd61ab36ec065e194ab5e74ae34a5240d992bb) Perf events | 4.3 | [`ea317b267e9d`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=ea317b267e9d03a8241893aa176fba7661d07579) Per-CPU hash | 4.6 | [`824bd0ce6c7c`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=824bd0ce6c7c43a9e1e210abf124958e54d88342) Per-CPU array | 4.6 | [`a10423b87a7e`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=a10423b87a7eae75da79ce80a8d9475047a674ee) Stack trace | 4.6 | [`d5a3b1f69186`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=d5a3b1f691865be576c2bffa708549b8cdccda19) Pre-alloc maps memory | 4.6 | [`6c9059817432`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=6c90598174322b8888029e40dd84a4eb01f56afe) cgroup array | 4.8 | [`4ed8ec521ed5`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=4ed8ec521ed57c4e207ad464ca0388776de74d4b) LRU hash | 4.10 | [`29ba732acbee`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=29ba732acbeece1e34c68483d1ec1f3720fa1bb3) [`3a08c2fd7634`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=3a08c2fd763450a927d1130de078d6f9e74944fb) LRU per-CPU hash | 4.10 | [`8f8449384ec3`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=8f8449384ec364ba2a654f11f94e754e4ff719e0) [`961578b63474`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=961578b63474d13ad0e2f615fcc2901c5197dda6) LPM trie (longest-prefix match) | 4.11 | [`b95a5c4db09b`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=b95a5c4db09bc7c253636cb84dc9b12c577fd5a0) Array of maps | 4.12 | [`56f668dfe00d`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=56f668dfe00dcf086734f1c42ea999398fad6572) Hash of maps | 4.12 | [`bcc6b1b7ebf8`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=bcc6b1b7ebf857a9fe56202e2be3361131588c15) Netdevice references | 4.14 | [`546ac1ffb70d`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=546ac1ffb70d25b56c1126940e5ec639c4dd7413) Socket references (array) | 4.14 | [`174a79ff9515`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=174a79ff9515f400b9a6115643dafd62a635b7e6) CPU references | 4.15 | [`6710e1126934`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=6710e1126934d8b4372b4d2f9ae1646cd3f151bf) AF_XDP socket (XSK) references | 4.18 | [`fbfc504a24f5`](https://git.kernel.org/cgit/linux/kernel/git/davem/net-next.git/commit/?id=fbfc504a24f53f7ebe128ab55cb5dba634f4ece8) Socket references (hashmap) | 4.18 | [`81110384441a`](https://git.kernel.org/cgit/linux/kernel/git/davem/net-next.git/commit/?id=81110384441a59cff47430f20f049e69b98c17f4) cgroup storage | 4.19 | [`de9cbbaadba5`](https://github.com/torvalds/linux/commit/de9cbbaadba5adf88a19e46df61f7054000838f6) reuseport sockarray | 4.19 | [`5dc4c4b7d4e8`](https://github.com/torvalds/linux/commit/5dc4c4b7d4e8115e7cde96a030f98cb3ab2e458c) precpu cgroup storage | 4.20 | [`b741f1630346`](https://github.com/torvalds/linux/commit/b741f1630346defcbc8cc60f1a2bdae8b3b0036f) queue | 4.20 | [`f1a2e44a3aec`](https://github.com/torvalds/linux/commit/f1a2e44a3aeccb3ff18d3ccc0b0203e70b95bd92) stack | 4.20 | [`f1a2e44a3aec`](https://github.com/torvalds/linux/commit/f1a2e44a3aeccb3ff18d3ccc0b0203e70b95bd92) socket local storage | 5.2 | [`6ac99e8f23d4`](https://github.com/torvalds/linux/commit/6ac99e8f23d4b10258406ca0dd7bffca5f31da9d) ## XDP An approximate list of drivers or components supporting XDP programs for your kernel can be retrieved with: git grep -l XDP_SETUP_PROG drivers/ Feature / Driver | Kernel version | Commit -----------------|----------------|------- XDP core architecture | 4.8 | [`6a773a15a1e8`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=6a773a15a1e8874e5eccd2f29190c31085912c95) Action: drop | 4.8 | [`6a773a15a1e8`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=6a773a15a1e8874e5eccd2f29190c31085912c95) Action: pass on to stack | 4.8 | [`6a773a15a1e8`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=6a773a15a1e8874e5eccd2f29190c31085912c95) Action: direct forwarding (on same port) | 4.8 | [`6ce96ca348a9`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=6ce96ca348a9e949f8c43f4d3e98db367d93cffd) Direct packet data write | 4.8 | [`4acf6c0b84c9`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=4acf6c0b84c91243c705303cd9ff16421914150d) Mellanox `mlx4` driver | 4.8 | [`47a38e155037`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=47a38e155037f417c5740e24ccae6482aedf4b68) Mellanox `mlx5` driver | 4.9 | [`86994156c736`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=86994156c736978d113e7927455d4eeeb2128b9f) Netronome `nfp` driver | 4.10 | [`ecd63a0217d5`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=ecd63a0217d5f1e8a92f7516f5586d1177b95de2) QLogic (Cavium) `qed*` drivers | 4.10 | [`496e05170958`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=496e051709588f832d7a6a420f44f8642b308a87) `virtio_net` driver | 4.10 | [`f600b6905015`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=f600b690501550b94e83e07295d9c8b9c4c39f4e) Broadcom `bnxt_en` driver | 4.11 | [`c6d30e8391b8`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=c6d30e8391b85e00eb544e6cf047ee0160ee9938) Intel `ixgbe*` drivers | 4.12 | [`924708081629`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=9247080816297de4e31abb684939c0e53e3a8a67) Cavium `thunderx` driver | 4.12 | [`05c773f52b96`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=05c773f52b96ef3fbc7d9bfa21caadc6247ef7a8) Generic XDP | 4.12 | [`b5cdae3291f7`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=b5cdae3291f7be7a34e75affe4c0ec1f7f328b64) Intel `i40e` driver | 4.13 | [`0c8493d90b6b`](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?h=0c8493d90b6bb0f5c4fe9217db8f7203f24c0f28) Action: redirect | 4.14 | [`6453073987ba`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=6453073987ba392510ab6c8b657844a9312c67f7) Support for tap | 4.14 | [`761876c857cb`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=761876c857cb2ef8489fbee01907151da902af91) Support for veth | 4.14 | [`d445516966dc`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=d445516966dcb2924741b13b27738b54df2af01a) Intel `ixgbevf` driver | 4.17 | [`c7aec59657b6`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=c7aec59657b60f3a29fc7d3274ebefd698879301) Freescale `dpaa2` driver | 5.0 | [`7e273a8ebdd3`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=7e273a8ebdd3b83f94eb8b49fc8ee61464f47cc2) Socionext `netsec` driver | 5.3 | [`ba2b232108d3`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=ba2b232108d3c2951bab02930a00f23b0cffd5af) TI `cpsw` driver | 5.3 | [`9ed4050c0d75`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=9ed4050c0d75768066a07cf66eef4f8dc9d79b52) Intel `e1000` driver | | [Not upstream yet](https://git.kernel.org/pub/scm/linux/kernel/git/ast/bpf.git/commit/?h=xdp&id=0afee87cfc800bf3317f4dc8847e6f36539b820c) Intel `e1000e` driver | | [Not planned for upstream at this time](https://github.com/adjavon/e1000e_xdp) ## Helpers The list of helpers supported in your kernel can be found in file [`include/uapi/linux/bpf.h`](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/uapi/linux/bpf.h): git grep ' FN(' include/uapi/linux/bpf.h Alphabetical order Helper | Kernel version | License | Commit | -------|----------------|---------|--------| `BPF_FUNC_bind()` | 4.17 | | [`d74bad4e74ee`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=d74bad4e74ee373787a9ae24197c17b7cdc428d5) | `BPF_FUNC_clone_redirect()` | 4.2 | | [`3896d655f4d4`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=3896d655f4d491c67d669a15f275a39f713410f8) `BPF_FUNC_csum_diff()` | 4.6 | | [`7d672345ed29`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=7d672345ed295b1356a5d9f7111da1d1d7d65867) `BPF_FUNC_csum_update()` | 4.9 | | [`36bbef52c7eb`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=36bbef52c7eb646ed6247055a2acd3851e317857) `BPF_FUNC_current_task_under_cgroup()` | 4.9 | | [`60d20f9195b2`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=60d20f9195b260bdf0ac10c275ae9f6016f9c069) `BPF_FUNC_fib_lookup()` | 4.18 | GPL | [`87f5fc7e48dd`](https://git.kernel.org/cgit/linux/kernel/git/davem/net-next.git/commit/?id=87f5fc7e48dd3175b30dd03b41564e1a8e136323) `BPF_FUNC_get_cgroup_classid()` | 4.3 | | [`8d20aabe1c76`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=8d20aabe1c76cccac544d9fcc3ad7823d9e98a2d) `BPF_FUNC_get_current_cgroup_id()` | 4.18 | | [`bf6fa2c893c5`](https://github.com/torvalds/linux/commit/bf6fa2c893c5237b48569a13fa3c673041430b6c) `BPF_FUNC_get_current_comm()` | 4.2 | | [`ffeedafbf023`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=ffeedafbf0236f03aeb2e8db273b3e5ae5f5bc89) `BPF_FUNC_get_current_pid_tgid()` | 4.2 | | [`ffeedafbf023`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=ffeedafbf0236f03aeb2e8db273b3e5ae5f5bc89) `BPF_FUNC_get_current_task()` | 4.8 | GPL | [`606274c5abd8`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=606274c5abd8e245add01bc7145a8cbb92b69ba8) `BPF_FUNC_get_current_uid_gid()` | 4.2 | | [`ffeedafbf023`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=ffeedafbf0236f03aeb2e8db273b3e5ae5f5bc89) `BPF_FUNC_get_hash_recalc()` | 4.8 | | [`13c5c240f789`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=13c5c240f789bbd2bcacb14a23771491485ae61f) `BPF_FUNC_get_listener_sock()` | 5.1 | | [`dbafd7ddd623`](https://kernel.googlesource.com/pub/scm/linux/kernel/git/davem/net-next/+/dbafd7ddd62369b2f3926ab847cbf8fc40e800b7) `BPF_FUNC_get_local_storage()` | 4.19 | | [`cd3394317653`](https://github.com/torvalds/linux/commit/cd3394317653837e2eb5c5d0904a8996102af9fc) `BPF_FUNC_get_numa_node_id()` | 4.10 | | [`2d0e30c30f84`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=2d0e30c30f84d08dc16f0f2af41f1b8a85f0755e) `BPF_FUNC_get_prandom_u32()` | 4.1 | | [`03e69b508b6f`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=03e69b508b6f7c51743055c9f61d1dfeadf4b635) `BPF_FUNC_get_route_realm()` | 4.4 | | [`c46646d0484f`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=c46646d0484f5d08e2bede9b45034ba5b8b489cc) `BPF_FUNC_get_smp_processor_id()` | 4.1 | | [`c04167ce2ca0`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=c04167ce2ca0ecaeaafef006cb0d65cf01b68e42) `BPF_FUNC_get_socket_cookie()` | 4.12 | | [`91b8270f2a4d`](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=91b8270f2a4d1d9b268de90451cdca63a70052d6) `BPF_FUNC_get_socket_uid()` | 4.12 | | [`6acc5c291068`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=6acc5c2910689fc6ee181bf63085c5efff6a42bd) `BPF_FUNC_get_stack()` | 4.18 | GPL | [`de2ff05f48af`](https://git.kernel.org/cgit/linux/kernel/git/davem/net-next.git/commit/?id=de2ff05f48afcde816ff4edb217417f62f624ab5) `BPF_FUNC_get_stackid()` | 4.6 | GPL | [`d5a3b1f69186`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=d5a3b1f691865be576c2bffa708549b8cdccda19) `BPF_FUNC_getsockopt()` | 4.15 | | [`cd86d1fd2102`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=cd86d1fd21025fdd6daf23d1288da405e7ad0ec6) `BPF_FUNC_ktime_get_ns()` | 4.1 | GPL | [`d9847d310ab4`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=d9847d310ab4003725e6ed1822682e24bd406908) `BPF_FUNC_l3_csum_replace()` | 4.1 | | [`91bc4822c3d6`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=91bc4822c3d61b9bb7ef66d3b77948a4f9177954) `BPF_FUNC_l4_csum_replace()` | 4.1 | | [`91bc4822c3d6`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=91bc4822c3d61b9bb7ef66d3b77948a4f9177954) `BPF_FUNC_lwt_push_encap()` | 4.18 | | [`fe94cc290f53`](https://git.kernel.org/cgit/linux/kernel/git/davem/net-next.git/commit/?id=fe94cc290f535709d3c5ebd1e472dfd0aec7ee79) `BPF_FUNC_lwt_seg6_action()` | 4.18 | | [`fe94cc290f53`](https://git.kernel.org/cgit/linux/kernel/git/davem/net-next.git/commit/?id=fe94cc290f535709d3c5ebd1e472dfd0aec7ee79) `BPF_FUNC_lwt_seg6_adjust_srh()` | 4.18 | | [`fe94cc290f53`](https://git.kernel.org/cgit/linux/kernel/git/davem/net-next.git/commit/?id=fe94cc290f535709d3c5ebd1e472dfd0aec7ee79) `BPF_FUNC_lwt_seg6_store_bytes()` | 4.18 | | [`fe94cc290f53`](https://git.kernel.org/cgit/linux/kernel/git/davem/net-next.git/commit/?id=fe94cc290f535709d3c5ebd1e472dfd0aec7ee79) `BPF_FUNC_map_delete_elem()` | 3.19 | | [`d0003ec01c66`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=d0003ec01c667b731c139e23de3306a8b328ccf5) `BPF_FUNC_map_lookup_elem()` | 3.19 | | [`d0003ec01c66`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=d0003ec01c667b731c139e23de3306a8b328ccf5) `BPF_FUNC_map_peek_elem()` | 4.20 | | [`f1a2e44a3aec`](https://github.com/torvalds/linux/commit/f1a2e44a3aeccb3ff18d3ccc0b0203e70b95bd92) `BPF_FUNC_map_pop_elem()` | 4.20 | | [`f1a2e44a3aec`](https://github.com/torvalds/linux/commit/f1a2e44a3aeccb3ff18d3ccc0b0203e70b95bd92) `BPF_FUNC_map_push_elem()` | 4.20 | | [`f1a2e44a3aec`](https://github.com/torvalds/linux/commit/f1a2e44a3aeccb3ff18d3ccc0b0203e70b95bd92) `BPF_FUNC_map_update_elem()` | 3.19 | | [`d0003ec01c66`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=d0003ec01c667b731c139e23de3306a8b328ccf5) `BPF_FUNC_msg_apply_bytes()` | 4.17 | | [`2a100317c9eb`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=2a100317c9ebc204a166f16294884fbf9da074ce) `BPF_FUNC_msg_cork_bytes()` | 4.17 | | [`91843d540a13`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=91843d540a139eb8070bcff8aa10089164436deb) `BPF_FUNC_msg_pop_data()` | 5.0 | | [`7246d8ed4dcc`](https://github.com/torvalds/linux/commit/7246d8ed4dcce23f7509949a77be15fa9f0e3d28) `BPF_FUNC_msg_pull_data()` | 4.17 | | [`015632bb30da`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=015632bb30daaaee64e1bcac07570860e0bf3092) `BPF_FUNC_msg_push_data()` | 4.20 | | [`6fff607e2f14`](https://github.com/torvalds/linux/commit/6fff607e2f14bd7c63c06c464a6f93b8efbabe28) `BPF_FUNC_msg_redirect_hash()` | 4.18 | | [`81110384441a`](https://git.kernel.org/cgit/linux/kernel/git/davem/net-next.git/commit/?id=81110384441a59cff47430f20f049e69b98c17f4) `BPF_FUNC_msg_redirect_map()` | 4.17 | | [`4f738adba30a`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=4f738adba30a7cfc006f605707e7aee847ffefa0) `BPF_FUNC_perf_event_output()` | 4.4 | GPL | [`a43eec304259`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=a43eec304259a6c637f4014a6d4767159b6a3aa3) `BPF_FUNC_perf_event_read()` | 4.3 | GPL | [`35578d798400`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=35578d7984003097af2b1e34502bc943d40c1804) `BPF_FUNC_perf_event_read_value()` | 4.15 | GPL | [`908432ca84fc`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=908432ca84fc229e906ba164219e9ad0fe56f755) `BPF_FUNC_perf_prog_read_value()` | 4.15 | GPL | [`4bebdc7a85aa`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=4bebdc7a85aa400c0222b5329861e4ad9252f1e5) `BPF_FUNC_probe_read()` | 4.1 | GPL | [`2541517c32be`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=2541517c32be2531e0da59dfd7efc1ce844644f5) `BPF_FUNC_probe_read_kernel()` | 5.5 | GPL | [`6ae08ae3dea2`](https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git/commit/?id=6ae08ae3dea2cfa03dd3665a3c8475c2d429ef47) `BPF_FUNC_probe_read_kernel_str()` | 5.5 | GPL | [`6ae08ae3dea2`](https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git/commit/?id=6ae08ae3dea2cfa03dd3665a3c8475c2d429ef47) `BPF_FUNC_probe_read_user()` | 5.5 | GPL | [`6ae08ae3dea2`](https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git/commit/?id=6ae08ae3dea2cfa03dd3665a3c8475c2d429ef47) `BPF_FUNC_probe_read_user_str()` | 5.5 | GPL | [`6ae08ae3dea2`](https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git/commit/?id=6ae08ae3dea2cfa03dd3665a3c8475c2d429ef47) `BPF_FUNC_probe_read_str()` | 4.11 | GPL | [`a5e8c07059d0`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=a5e8c07059d0f0b31737408711d44794928ac218) `BPF_FUNC_probe_write_user()` | 4.8 | GPL | [`96ae52279594`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=96ae52279594470622ff0585621a13e96b700600) `BPF_FUNC_rc_keydown()` | 4.18 | GPL | [`f4364dcfc86d`](https://git.kernel.org/cgit/linux/kernel/git/bpf/bpf-next.git/commit/?id=f4364dcfc86df7c1ca47b256eaf6b6d0cdd0d936) `BPF_FUNC_rc_pointer_rel()` | 5.0 | GPL | [`01d3240a04f4`](https://github.com/torvalds/linux/commit/01d3240a04f4c09392e13c77b54d4423ebce2d72) `BPF_FUNC_rc_repeat()` | 4.18 | GPL | [`f4364dcfc86d`](https://git.kernel.org/cgit/linux/kernel/git/bpf/bpf-next.git/commit/?id=f4364dcfc86df7c1ca47b256eaf6b6d0cdd0d936) `BPF_FUNC_redirect()` | 4.4 | | [`27b29f63058d`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=27b29f63058d26c6c1742f1993338280d5a41dc6) `BPF_FUNC_redirect_map()` | 4.14 | | [`97f91a7cf04f`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=97f91a7cf04ff605845c20948b8a80e54cbd3376) `BPF_FUNC_send_signal()` | 5.3 | | [`8b401f9ed244`](https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git/commit/?id=8b401f9ed2441ad9e219953927a842d24ed051fc) `BPF_FUNC_set_hash()` | 4.13 | | [`ded092cd73c2`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=ded092cd73c2c56a394b936f86897f29b2e131c0) `BPF_FUNC_set_hash_invalid()` | 4.9 | | [`7a4b28c6cc9f`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=7a4b28c6cc9ffac50f791b99cc7e46106436e5d8) `BPF_FUNC_setsockopt()` | 4.13 | | [`8c4b4c7e9ff0`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=8c4b4c7e9ff0447995750d9329949fa082520269) `BPF_FUNC_sk_fullsock()` | 5.1 | | [`46f8bc92758c`](https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git/commit/?id=46f8bc92758c6259bcf945e9216098661c1587cd) `BPF_FUNC_sk_lookup_tcp()` | 4.20 | | [`6acc9b432e67`](https://github.com/torvalds/linux/commit/6acc9b432e6714d72d7d77ec7c27f6f8358d0c71) `BPF_FUNC_sk_lookup_udp()` | 4.20 | | [`6acc9b432e67`](https://github.com/torvalds/linux/commit/6acc9b432e6714d72d7d77ec7c27f6f8358d0c71) `BPF_FUNC_sk_redirect_hash()` | 4.18 | | [`81110384441a`](https://git.kernel.org/cgit/linux/kernel/git/davem/net-next.git/commit/?id=81110384441a59cff47430f20f049e69b98c17f4) `BPF_FUNC_sk_redirect_map()` | 4.14 | | [`174a79ff9515`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=174a79ff9515f400b9a6115643dafd62a635b7e6) `BPF_FUNC_sk_release()` | 4.20 | | [`6acc9b432e67`](https://github.com/torvalds/linux/commit/6acc9b432e6714d72d7d77ec7c27f6f8358d0c71) `BPF_FUNC_sk_select_reuseport()` | 4.19 | | [`2dbb9b9e6df6`](https://github.com/torvalds/linux/commit/2dbb9b9e6df67d444fbe425c7f6014858d337adf) `BPF_FUNC_sk_storage_delete()` | 5.2 | | [`6ac99e8f23d4`](https://github.com/torvalds/linux/commit/6ac99e8f23d4b10258406ca0dd7bffca5f31da9d) `BPF_FUNC_sk_storage_get()` | 5.2 | | [`6ac99e8f23d4`](https://github.com/torvalds/linux/commit/6ac99e8f23d4b10258406ca0dd7bffca5f31da9d) `BPF_FUNC_skb_adjust_room()` | 4.13 | | [`2be7e212d541`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=2be7e212d5419a400d051c84ca9fdd083e5aacac) `BPF_FUNC_skb_ancestor_cgroup_id()` | 4.19 | | [`7723628101aa`](https://github.com/torvalds/linux/commit/7723628101aaeb1d723786747529b4ea65c5b5c5) `BPF_FUNC_skb_change_head()` | 4.10 | | [`3a0af8fd61f9`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=3a0af8fd61f90920f6fa04e4f1e9a6a73c1b4fd2) `BPF_FUNC_skb_change_proto()` | 4.8 | | [`6578171a7ff0`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=6578171a7ff0c31dc73258f93da7407510abf085) `BPF_FUNC_skb_change_tail()` | 4.9 | | [`5293efe62df8`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=5293efe62df81908f2e90c9820c7edcc8e61f5e9) `BPF_FUNC_skb_change_type()` | 4.8 | | [`d2485c4242a8`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=d2485c4242a826fdf493fd3a27b8b792965b9b9e) `BPF_FUNC_skb_cgroup_id()` | 4.18 | | [`cb20b08ead40`](https://github.com/torvalds/linux/commit/cb20b08ead401fd17627a36f035c0bf5bfee5567) `BPF_FUNC_skb_ecn_set_ce()` | 5.1 | | [`f7c917ba11a6`](https://github.com/torvalds/linux/commit/f7c917ba11a67632a8452ea99fe132f626a7a2cc) `BPF_FUNC_skb_get_tunnel_key()` | 4.3 | | [`d3aa45ce6b94`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=d3aa45ce6b94c65b83971257317867db13e5f492) `BPF_FUNC_skb_get_tunnel_opt()` | 4.6 | | [`14ca0751c96f`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=14ca0751c96f8d3d0f52e8ed3b3236f8b34d3460) `BPF_FUNC_skb_get_xfrm_state()` | 4.18 | | [`12bed760a78d`](https://git.kernel.org/cgit/linux/kernel/git/davem/net-next.git/commit/?id=12bed760a78da6e12ac8252fec64d019a9eac523) `BPF_FUNC_skb_load_bytes()` | 4.5 | | [`05c74e5e53f6`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=05c74e5e53f6cb07502c3e6a820f33e2777b6605) `BPF_FUNC_skb_load_bytes_relative()` | 4.18 | | [`4e1ec56cdc59`](https://git.kernel.org/cgit/linux/kernel/git/davem/net-next.git/commit/?id=4e1ec56cdc59746943b2acfab3c171b930187bbe) `BPF_FUNC_skb_output()` | 5.5 | | [`a7658e1a4164`](https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git/commit/?id=a7658e1a4164ce2b9eb4a11aadbba38586e93bd6) `BPF_FUNC_skb_pull_data()` | 4.9 | | [`36bbef52c7eb`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=36bbef52c7eb646ed6247055a2acd3851e317857) `BPF_FUNC_skb_set_tunnel_key()` | 4.3 | | [`d3aa45ce6b94`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=d3aa45ce6b94c65b83971257317867db13e5f492) `BPF_FUNC_skb_set_tunnel_opt()` | 4.6 | | [`14ca0751c96f`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=14ca0751c96f8d3d0f52e8ed3b3236f8b34d3460) `BPF_FUNC_skb_store_bytes()` | 4.1 | | [`91bc4822c3d6`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=91bc4822c3d61b9bb7ef66d3b77948a4f9177954) `BPF_FUNC_skb_under_cgroup()` | 4.8 | | [`4a482f34afcc`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=4a482f34afcc162d8456f449b137ec2a95be60d8) `BPF_FUNC_skb_vlan_pop()` | 4.3 | | [`4e10df9a60d9`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=4e10df9a60d96ced321dd2af71da558c6b750078) `BPF_FUNC_skb_vlan_push()` | 4.3 | | [`4e10df9a60d9`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=4e10df9a60d96ced321dd2af71da558c6b750078) `BPF_FUNC_skc_lookup_tcp()` | 5.2 | | [`edbf8c01de5a`](https://kernel.googlesource.com/pub/scm/linux/kernel/git/davem/net-next/+/edbf8c01de5a104a71ed6df2bf6421ceb2836a8e) `BPF_FUNC_sock_hash_update()` | 4.18 | | [`81110384441a`](https://git.kernel.org/cgit/linux/kernel/git/davem/net-next.git/commit/?id=81110384441a59cff47430f20f049e69b98c17f4) `BPF_FUNC_sock_map_update()` | 4.14 | | [`174a79ff9515`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=174a79ff9515f400b9a6115643dafd62a635b7e6) `BPF_FUNC_spin_lock()` | 5.1 | | [`d83525ca62cf`](https://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next.git/commit/?id=d83525ca62cf8ebe3271d14c36fb900c294274a2) `BPF_FUNC_spin_unlock()` | 5.1 | | [`d83525ca62cf`](https://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next.git/commit/?id=d83525ca62cf8ebe3271d14c36fb900c294274a2) `BPF_FUNC_strtol()` | 5.2 | | [`d7a4cb9b6705`](https://kernel.googlesource.com/pub/scm/linux/kernel/git/davem/net-next/+/d7a4cb9b6705a89937d12c8158a35a3145dc967a) `BPF_FUNC_strtoul()` | 5.2 | | [`d7a4cb9b6705`](https://kernel.googlesource.com/pub/scm/linux/kernel/git/davem/net-next/+/d7a4cb9b6705a89937d12c8158a35a3145dc967a) `BPF_FUNC_sysctl_get_current_value()` | 5.2 | | [`1d11b3016cec`](https://kernel.googlesource.com/pub/scm/linux/kernel/git/davem/net-next/+/1d11b3016cec4ed9770b98e82a61708c8f4926e7) `BPF_FUNC_sysctl_get_name()` | 5.2 | | [`808649fb787d`](https://kernel.googlesource.com/pub/scm/linux/kernel/git/davem/net-next/+/808649fb787d918a48a360a668ee4ee9023f0c11) `BPF_FUNC_sysctl_get_new_value()` | 5.2 | | [`4e63acdff864`](https://kernel.googlesource.com/pub/scm/linux/kernel/git/davem/net-next/+/4e63acdff864654cee0ac5aaeda3913798ee78f6) `BPF_FUNC_sysctl_set_new_value()` | 5.2 | | [`4e63acdff864`](https://kernel.googlesource.com/pub/scm/linux/kernel/git/davem/net-next/+/4e63acdff864654cee0ac5aaeda3913798ee78f6) `BPF_FUNC_tail_call()` | 4.2 | | [`04fd61ab36ec`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=04fd61ab36ec065e194ab5e74ae34a5240d992bb) `BPF_FUNC_tcp_check_syncookie()` | 5.2 | | [`399040847084`](https://kernel.googlesource.com/pub/scm/linux/kernel/git/davem/net-next/+/399040847084a69f345e0a52fd62f04654e0fce3) `BPF_FUNC_tcp_gen_syncookie()` | 5.3 | | [`70d66244317e`](https://github.com/torvalds/linux/commit/70d66244317e958092e9c971b08dd5b7fd29d9cb#diff-05da4bf36c7fbcd176254e1615d98b28) `BPF_FUNC_tcp_sock()` | 5.1 | | [`655a51e536c0`](https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git/commit/?id=655a51e536c09d15ffa3603b1b6fce2b45b85a1f) `BPF_FUNC_trace_printk()` | 4.1 | GPL | [`9c959c863f82`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=9c959c863f8217a2ff3d7c296e8223654d240569) `BPF_FUNC_xdp_adjust_head()` | 4.10 | | [`17bedab27231`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=17bedab2723145d17b14084430743549e6943d03) `BPF_FUNC_xdp_adjust_meta()` | 4.15 | | [`de8f3a83b0a0`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=de8f3a83b0a0fddb2cf56e7a718127e9619ea3da) `BPF_FUNC_xdp_adjust_tail()` | 4.18 | | [`b32cc5b9a346`](https://git.kernel.org/cgit/linux/kernel/git/davem/net-next.git/commit/?id=b32cc5b9a346319c171e3ad905e0cddda032b5eb) `BPF_FUNC_override_return()` | 4.16 | GPL | [`9802d86585db`](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=9802d86585db91655c7d1929a4f6bbe0952ea88e) `BPF_FUNC_sock_ops_cb_flags_set()` | 4.16 | | [`b13d88072172`](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=b13d880721729384757f235166068c315326f4a1) Note: GPL-only BPF helpers require a GPL-compatible license. The current licenses considered GPL-compatible by the kernel are: * GPL * GPL v2 * GPL and additional rights * Dual BSD/GPL * Dual MIT/GPL * Dual MPL/GPL Check the list of GPL-compatible licenses in your [kernel source code](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/linux/license.h). ## Program Types The list of program types and supported helper functions can be retrieved with: git grep -W 'func_proto(enum bpf_func_id func_id' kernel/ net/ drivers/ |Program Type| Helper Functions| |------------|-----------------| |`BPF_PROG_TYPE_SOCKET_FILTER`|`BPF_FUNC_skb_load_bytes()`
`BPF_FUNC_skb_load_bytes_relative()`
`BPF_FUNC_get_socket_cookie()`
`BPF_FUNC_get_socket_uid()`
`BPF_FUNC_perf_event_output()`
`Base functions`| |`BPF_PROG_TYPE_KPROBE`|`BPF_FUNC_perf_event_output()`
`BPF_FUNC_get_stackid()`
`BPF_FUNC_get_stack()`
`BPF_FUNC_perf_event_read_value()`
`BPF_FUNC_override_return()`
`Tracing functions`| |`BPF_PROG_TYPE_SCHED_CLS`
`BPF_PROG_TYPE_SCHED_ACT`|`BPF_FUNC_skb_store_bytes()`
`BPF_FUNC_skb_load_bytes()`
`BPF_FUNC_skb_load_bytes_relative()`
`BPF_FUNC_skb_pull_data()`
`BPF_FUNC_csum_diff()`
`BPF_FUNC_csum_update()`
`BPF_FUNC_l3_csum_replace()`
`BPF_FUNC_l4_csum_replace()`
`BPF_FUNC_clone_redirect()`
`BPF_FUNC_get_cgroup_classid()`
`BPF_FUNC_skb_vlan_push()`
`BPF_FUNC_skb_vlan_pop()`
`BPF_FUNC_skb_change_proto()`
`BPF_FUNC_skb_change_type()`
`BPF_FUNC_skb_adjust_room()`
`BPF_FUNC_skb_change_tail()`
`BPF_FUNC_skb_get_tunnel_key()`
`BPF_FUNC_skb_set_tunnel_key()`
`BPF_FUNC_skb_get_tunnel_opt()`
`BPF_FUNC_skb_set_tunnel_opt()`
`BPF_FUNC_redirect()`
`BPF_FUNC_get_route_realm()`
`BPF_FUNC_get_hash_recalc()`
`BPF_FUNC_set_hash_invalid()`
`BPF_FUNC_set_hash()`
`BPF_FUNC_perf_event_output()`
`BPF_FUNC_get_smp_processor_id()`
`BPF_FUNC_skb_under_cgroup()`
`BPF_FUNC_get_socket_cookie()`
`BPF_FUNC_get_socket_uid()`
`BPF_FUNC_fib_lookup()`
`BPF_FUNC_skb_get_xfrm_state()`
`BPF_FUNC_skb_cgroup_id()`
`Base functions`| |`BPF_PROG_TYPE_TRACEPOINT`|`BPF_FUNC_perf_event_output()`
`BPF_FUNC_get_stackid()`
`BPF_FUNC_get_stack()`
`Tracing functions`| |`BPF_PROG_TYPE_XDP`| `BPF_FUNC_perf_event_output()`
`BPF_FUNC_get_smp_processor_id()`
`BPF_FUNC_csum_diff()`
`BPF_FUNC_xdp_adjust_head()`
`BPF_FUNC_xdp_adjust_meta()`
`BPF_FUNC_redirect()`
`BPF_FUNC_redirect_map()`
`BPF_FUNC_xdp_adjust_tail()`
`BPF_FUNC_fib_lookup()`
`Base functions`| |`BPF_PROG_TYPE_PERF_EVENT`| `BPF_FUNC_perf_event_output()`
`BPF_FUNC_get_stackid()`
`BPF_FUNC_get_stack()`
`BPF_FUNC_perf_prog_read_value()`
`Tracing functions`| |`BPF_PROG_TYPE_CGROUP_SKB`|`BPF_FUNC_skb_load_bytes()`
`BPF_FUNC_skb_load_bytes_relative()`
`BPF_FUNC_get_socket_cookie()`
`BPF_FUNC_get_socket_uid()`
`Base functions`| |`BPF_PROG_TYPE_CGROUP_SOCK`|`BPF_FUNC_get_current_uid_gid()`
`Base functions`| |`BPF_PROG_TYPE_LWT_IN`|`BPF_FUNC_lwt_push_encap()`
`LWT functions`
`Base functions`| |`BPF_PROG_TYPE_LWT_OUT`| `LWT functions`
`Base functions`| |`BPF_PROG_TYPE_LWT_XMIT`| `BPF_FUNC_skb_get_tunnel_key()`
`BPF_FUNC_skb_set_tunnel_key()`
`BPF_FUNC_skb_get_tunnel_opt()`
`BPF_FUNC_skb_set_tunnel_opt()`
`BPF_FUNC_redirect()`
`BPF_FUNC_clone_redirect()`
`BPF_FUNC_skb_change_tail()`
`BPF_FUNC_skb_change_head()`
`BPF_FUNC_skb_store_bytes()`
`BPF_FUNC_csum_update()`
`BPF_FUNC_l3_csum_replace()`
`BPF_FUNC_l4_csum_replace()`
`BPF_FUNC_set_hash_invalid()`
`LWT functions`| |`BPF_PROG_TYPE_SOCK_OPS`|`BPF_FUNC_setsockopt()`
`BPF_FUNC_getsockopt()`
`BPF_FUNC_sock_ops_cb_flags_set()`
`BPF_FUNC_sock_map_update()`
`BPF_FUNC_sock_hash_update()`
`BPF_FUNC_get_socket_cookie()`
`Base functions`| |`BPF_PROG_TYPE_SK_SKB`|`BPF_FUNC_skb_store_bytes()`
`BPF_FUNC_skb_load_bytes()`
`BPF_FUNC_skb_pull_data()`
`BPF_FUNC_skb_change_tail()`
`BPF_FUNC_skb_change_head()`
`BPF_FUNC_get_socket_cookie()`
`BPF_FUNC_get_socket_uid()`
`BPF_FUNC_sk_redirect_map()`
`BPF_FUNC_sk_redirect_hash()`
`BPF_FUNC_sk_lookup_tcp()`
`BPF_FUNC_sk_lookup_udp()`
`BPF_FUNC_sk_release()`
`Base functions`| |`BPF_PROG_TYPE_CGROUP_DEVICE`|`BPF_FUNC_map_lookup_elem()`
`BPF_FUNC_map_update_elem()`
`BPF_FUNC_map_delete_elem()`
`BPF_FUNC_get_current_uid_gid()`
`BPF_FUNC_trace_printk()`| |`BPF_PROG_TYPE_SK_MSG`|`BPF_FUNC_msg_redirect_map()`
`BPF_FUNC_msg_redirect_hash()`
`BPF_FUNC_msg_apply_bytes()`
`BPF_FUNC_msg_cork_bytes()`
`BPF_FUNC_msg_pull_data()`
`BPF_FUNC_msg_push_data()`
`BPF_FUNC_msg_pop_data()`
`Base functions`| |`BPF_PROG_TYPE_RAW_TRACEPOINT`|`BPF_FUNC_perf_event_output()`
`BPF_FUNC_get_stackid()`
`BPF_FUNC_get_stack()`
`BPF_FUNC_skb_output()`
`Tracing functions`| |`BPF_PROG_TYPE_CGROUP_SOCK_ADDR`|`BPF_FUNC_get_current_uid_gid()`
`BPF_FUNC_bind()`
`BPF_FUNC_get_socket_cookie()`
`Base functions`| |`BPF_PROG_TYPE_LWT_SEG6LOCAL`|`BPF_FUNC_lwt_seg6_store_bytes()`
`BPF_FUNC_lwt_seg6_action()`
`BPF_FUNC_lwt_seg6_adjust_srh()`
`LWT functions`| |`BPF_PROG_TYPE_LIRC_MODE2`|`BPF_FUNC_rc_repeat()`
`BPF_FUNC_rc_keydown()`
`BPF_FUNC_rc_pointer_rel()`
`BPF_FUNC_map_lookup_elem()`
`BPF_FUNC_map_update_elem()`
`BPF_FUNC_map_delete_elem()`
`BPF_FUNC_ktime_get_ns()`
`BPF_FUNC_tail_call()`
`BPF_FUNC_get_prandom_u32()`
`BPF_FUNC_trace_printk()`| |`BPF_PROG_TYPE_SK_REUSEPORT`|`BPF_FUNC_sk_select_reuseport()`
`BPF_FUNC_skb_load_bytes()`
`BPF_FUNC_load_bytes_relative()`
`Base functions`| |`BPF_PROG_TYPE_FLOW_DISSECTOR`|`BPF_FUNC_skb_load_bytes()`
`Base functions`| |Function Group| Functions| |------------------|-------| |`Base functions`| `BPF_FUNC_map_lookup_elem()`
`BPF_FUNC_map_update_elem()`
`BPF_FUNC_map_delete_elem()`
`BPF_FUNC_map_peek_elem()`
`BPF_FUNC_map_pop_elem()`
`BPF_FUNC_map_push_elem()`
`BPF_FUNC_get_prandom_u32()`
`BPF_FUNC_get_smp_processor_id()`
`BPF_FUNC_get_numa_node_id()`
`BPF_FUNC_tail_call()`
`BPF_FUNC_ktime_get_ns()`
`BPF_FUNC_trace_printk()`
`BPF_FUNC_spin_lock()`
`BPF_FUNC_spin_unlock()` | |`Tracing functions`|`BPF_FUNC_map_lookup_elem()`
`BPF_FUNC_map_update_elem()`
`BPF_FUNC_map_delete_elem()`
`BPF_FUNC_probe_read()`
`BPF_FUNC_ktime_get_ns()`
`BPF_FUNC_tail_call()`
`BPF_FUNC_get_current_pid_tgid()`
`BPF_FUNC_get_current_task()`
`BPF_FUNC_get_current_uid_gid()`
`BPF_FUNC_get_current_comm()`
`BPF_FUNC_trace_printk()`
`BPF_FUNC_get_smp_processor_id()`
`BPF_FUNC_get_numa_node_id()`
`BPF_FUNC_perf_event_read()`
`BPF_FUNC_probe_write_user()`
`BPF_FUNC_current_task_under_cgroup()`
`BPF_FUNC_get_prandom_u32()`
`BPF_FUNC_probe_read_str()`
`BPF_FUNC_get_current_cgroup_id()`
`BPF_FUNC_send_signal()`
`BPF_FUNC_probe_read_kernel()`
`BPF_FUNC_probe_read_kernel_str()`
`BPF_FUNC_probe_read_user()`
`BPF_FUNC_probe_read_user_str()`| |`LWT functions`| `BPF_FUNC_skb_load_bytes()`
`BPF_FUNC_skb_pull_data()`
`BPF_FUNC_csum_diff()`
`BPF_FUNC_get_cgroup_classid()`
`BPF_FUNC_get_route_realm()`
`BPF_FUNC_get_hash_recalc()`
`BPF_FUNC_perf_event_output()`
`BPF_FUNC_get_smp_processor_id()`
`BPF_FUNC_skb_under_cgroup()`| bpfcc-0.12.0/docs/reference_guide.md000066400000000000000000002046431357404205000172450ustar00rootroot00000000000000# bcc Reference Guide Intended for search (Ctrl-F) and reference. For tutorials, start with [tutorial.md](tutorial.md). This guide is incomplete. If something feels missing, check the bcc and kernel source. And if you confirm we're missing something, please send a pull request to fix it, and help out everyone. ## Contents - [BPF C](#bpf-c) - [Events & Arguments](#events--arguments) - [1. kprobes](#1-kprobes) - [2. kretprobes](#2-kretprobes) - [3. Tracepoints](#3-tracepoints) - [4. uprobes](#4-uprobes) - [5. uretprobes](#5-uretprobes) - [6. USDT probes](#6-usdt-probes) - [7. Raw Tracepoints](#7-raw-tracepoints) - [8. system call tracepoints](#8-system-call-tracepoints) - [Data](#data) - [1. bpf_probe_read()](#1-bpf_probe_read) - [2. bpf_probe_read_str()](#2-bpf_probe_read_str) - [3. bpf_ktime_get_ns()](#3-bpf_ktime_get_ns) - [4. bpf_get_current_pid_tgid()](#4-bpf_get_current_pid_tgid) - [5. bpf_get_current_uid_gid()](#5-bpf_get_current_uid_gid) - [6. bpf_get_current_comm()](#6-bpf_get_current_comm) - [7. bpf_get_current_task()](#7-bpf_get_current_task) - [8. bpf_log2l()](#8-bpf_log2l) - [9. bpf_get_prandom_u32()](#9-bpf_get_prandom_u32) - [Debugging](#debugging) - [1. bpf_override_return()](#1-bpf_override_return) - [Output](#output) - [1. bpf_trace_printk()](#1-bpf_trace_printk) - [2. BPF_PERF_OUTPUT](#2-bpf_perf_output) - [3. perf_submit()](#3-perf_submit) - [Maps](#maps) - [1. BPF_TABLE](#1-bpf_table) - [2. BPF_HASH](#2-bpf_hash) - [3. BPF_ARRAY](#3-bpf_array) - [4. BPF_HISTOGRAM](#4-bpf_histogram) - [5. BPF_STACK_TRACE](#5-bpf_stack_trace) - [6. BPF_PERF_ARRAY](#6-bpf_perf_array) - [7. BPF_PERCPU_ARRAY](#7-bpf_percpu_array) - [8. BPF_LPM_TRIE](#8-bpf_lpm_trie) - [9. BPF_PROG_ARRAY](#9-bpf_prog_array) - [10. BPF_DEVMAP](#10-bpf_devmap) - [11. BPF_CPUMAP](#11-bpf_cpumap) - [12. map.lookup()](#12-maplookup) - [13. map.lookup_or_try_init()](#13-maplookup_or_try_init) - [14. map.delete()](#14-mapdelete) - [15. map.update()](#15-mapupdate) - [16. map.insert()](#16-mapinsert) - [17. map.increment()](#17-mapincrement) - [18. map.get_stackid()](#18-mapget_stackid) - [19. map.perf_read()](#19-mapperf_read) - [20. map.call()](#20-mapcall) - [21. map.redirect_map()](#21-mapredirect_map) - [Licensing](#licensing) - [bcc Python](#bcc-python) - [Initialization](#initialization) - [1. BPF](#1-bpf) - [2. USDT](#2-usdt) - [Events](#events) - [1. attach_kprobe()](#1-attach_kprobe) - [2. attach_kretprobe()](#2-attach_kretprobe) - [3. attach_tracepoint()](#3-attach_tracepoint) - [4. attach_uprobe()](#4-attach_uprobe) - [5. attach_uretprobe()](#5-attach_uretprobe) - [6. USDT.enable_probe()](#6-usdtenable_probe) - [7. attach_raw_tracepoint()](#7-attach_raw_tracepoint) - [Debug Output](#debug-output) - [1. trace_print()](#1-trace_print) - [2. trace_fields()](#2-trace_fields) - [Output](#output) - [1. perf_buffer_poll()](#1-perf_buffer_poll) - [Maps](#maps) - [1. get_table()](#1-get_table) - [2. open_perf_buffer()](#2-open_perf_buffer) - [3. items()](#3-items) - [4. values()](#4-values) - [5. clear()](#5-clear) - [6. print_log2_hist()](#6-print_log2_hist) - [7. print_linear_hist()](#6-print_linear_hist) - [Helpers](#helpers) - [1. ksym()](#1-ksym) - [2. ksymname()](#2-ksymname) - [3. sym()](#3-sym) - [4. num_open_kprobes()](#4-num_open_kprobes) - [5. get_syscall_fnname()](#5-get_syscall_fnname) - [BPF Errors](#bpf-errors) - [1. Invalid mem access](#1-invalid-mem-access) - [2. Cannot call GPL only function from proprietary program](#2-cannot-call-gpl-only-function-from-proprietary-program) - [Environment Variables](#envvars) - [1. kernel source directory](#1-kernel-source-directory) - [2. kernel version overriding](#2-kernel-version-overriding) # BPF C This section describes the C part of a bcc program. ## Events & Arguments ### 1. kprobes Syntax: kprobe__*kernel_function_name* ```kprobe__``` is a special prefix that creates a kprobe (dynamic tracing of a kernel function call) for the kernel function name provided as the remainder. You can also use kprobes by declaring a normal C function, then using the Python ```BPF.attach_kprobe()``` (covered later) to associate it with a kernel function. Arguments are specified on the function declaration: kprobe__*kernel_function_name*(struct pt_regs *ctx [, *argument1* ...]) For example: ```C int kprobe__tcp_v4_connect(struct pt_regs *ctx, struct sock *sk) [...] } ``` This instruments the tcp_v4_connect() kernel function using a kprobe, with the following arguments: - ```struct pt_regs *ctx```: Registers and BPF context. - ```struct sock *sk```: First argument to tcp_v4_connect(). The first argument is always ```struct pt_regs *```, the remainder are the arguments to the function (they don't need to be specified, if you don't intend to use them). Examples in situ: [code](https://github.com/iovisor/bcc/blob/4afa96a71c5dbfc4c507c3355e20baa6c184a3a8/examples/tracing/tcpv4connect.py#L28) ([output](https://github.com/iovisor/bcc/blob/5bd0eb21fd148927b078deb8ac29fff2fb044b66/examples/tracing/tcpv4connect_example.txt#L8)), [code](https://github.com/iovisor/bcc/commit/310ab53710cfd46095c1f6b3e44f1dbc8d1a41d8#diff-8cd1822359ffee26e7469f991ce0ef00R26) ([output](https://github.com/iovisor/bcc/blob/3b9679a3bd9b922c736f6061dc65cb56de7e0250/examples/tracing/bitehist_example.txt#L6)) ### 2. kretprobes Syntax: kretprobe__*kernel_function_name* ```kretprobe__``` is a special prefix that creates a kretprobe (dynamic tracing of a kernel function return) for the kernel function name provided as the remainder. You can also use kretprobes by declaring a normal C function, then using the Python ```BPF.attach_kretprobe()``` (covered later) to associate it with a kernel function. Return value is available as ```PT_REGS_RC(ctx)```, given a function declaration of: kretprobe__*kernel_function_name*(struct pt_regs *ctx) For example: ```C int kretprobe__tcp_v4_connect(struct pt_regs *ctx) { int ret = PT_REGS_RC(ctx); [...] } ``` This instruments the return of the tcp_v4_connect() kernel function using a kretprobe, and stores the return value in ```ret```. Examples in situ: [code](https://github.com/iovisor/bcc/blob/4afa96a71c5dbfc4c507c3355e20baa6c184a3a8/examples/tracing/tcpv4connect.py#L38) ([output](https://github.com/iovisor/bcc/blob/5bd0eb21fd148927b078deb8ac29fff2fb044b66/examples/tracing/tcpv4connect_example.txt#L8)) ### 3. Tracepoints Syntax: TRACEPOINT_PROBE(*category*, *event*) This is a macro that instruments the tracepoint defined by *category*:*event*. Arguments are available in an ```args``` struct, which are the tracepoint arguments. One way to list these is to cat the relevant format file under /sys/kernel/debug/tracing/events/*category*/*event*/format. The ```args``` struct can be used in place of ``ctx`` in each functions requiring a context as an argument. This includes notably [perf_submit()](#3-perf_submit). For example: ```C TRACEPOINT_PROBE(random, urandom_read) { // args is from /sys/kernel/debug/tracing/events/random/urandom_read/format bpf_trace_printk("%d\\n", args->got_bits); return 0; } ``` This instruments the random:urandom_read tracepoint, and prints the tracepoint argument ```got_bits```. Examples in situ: [code](https://github.com/iovisor/bcc/blob/a4159da8c4ea8a05a3c6e402451f530d6e5a8b41/examples/tracing/urandomread.py#L19) ([output](https://github.com/iovisor/bcc/commit/e422f5e50ecefb96579b6391a2ada7f6367b83c4#diff-41e5ecfae4a3b38de5f4e0887ed160e5R10)), [search /examples](https://github.com/iovisor/bcc/search?q=TRACEPOINT_PROBE+path%3Aexamples&type=Code), [search /tools](https://github.com/iovisor/bcc/search?q=TRACEPOINT_PROBE+path%3Atools&type=Code) ### 4. uprobes These are instrumented by declaring a normal function in C, then associating it as a uprobe probe in Python via ```BPF.attach_uprobe()``` (covered later). Arguments can be examined using ```PT_REGS_PARM``` macros. For example: ```C int count(struct pt_regs *ctx) { char buf[64]; bpf_probe_read(&buf, sizeof(buf), (void *)PT_REGS_PARM1(ctx)); bpf_trace_printk("%s %d", buf, PT_REGS_PARM2(ctx)); return(0); } ``` This reads the first argument as a string, and then prints it with the second argument as an integer. Examples in situ: [code](https://github.com/iovisor/bcc/blob/4afa96a71c5dbfc4c507c3355e20baa6c184a3a8/examples/tracing/strlen_count.py#L26) ### 5. uretprobes These are instrumented by declaring a normal function in C, then associating it as a uretprobe probe in Python via ```BPF.attach_uretprobe()``` (covered later). Return value is available as ```PT_REGS_RC(ctx)```, given a function declaration of: *function_name*(struct pt_regs *ctx) For example: ```C BPF_HISTOGRAM(dist); int count(struct pt_regs *ctx) { dist.increment(PT_REGS_RC(ctx)); return 0; } ``` This increments the bucket in the ```dist``` histogram that is indexed by the return value. Examples in situ: [code](https://github.com/iovisor/bcc/blob/4afa96a71c5dbfc4c507c3355e20baa6c184a3a8/examples/tracing/strlen_hist.py#L39) ([output](https://github.com/iovisor/bcc/blob/4afa96a71c5dbfc4c507c3355e20baa6c184a3a8/examples/tracing/strlen_hist.py#L15)), [code](https://github.com/iovisor/bcc/blob/4afa96a71c5dbfc4c507c3355e20baa6c184a3a8/tools/bashreadline.py) ([output](https://github.com/iovisor/bcc/commit/aa87997d21e5c1a6a20e2c96dd25eb92adc8e85d#diff-2fd162f9e594206f789246ce97d62cf0R7)) ### 6. USDT probes These are User Statically-Defined Tracing (USDT) probes, which may be placed in some applications or libraries to provide a user-level equivalent of tracepoints. The primary BPF method provided for USDT support method is ```enable_probe()```. USDT probes are instrumented by declaring a normal function in C, then associating it as a USDT probe in Python via ```USDT.enable_probe()```. Arguments can be read via: bpf_usdt_readarg(*index*, ctx, &addr) For example: ```C int do_trace(struct pt_regs *ctx) { uint64_t addr; char path[128]; bpf_usdt_readarg(6, ctx, &addr); bpf_probe_read(&path, sizeof(path), (void *)addr); bpf_trace_printk("path:%s\\n", path); return 0; }; ``` This reads the sixth USDT argument, and then pulls it in as a string to ```path```. When initializing USDTs via the third argument of ```BPF::init``` in the C API, if any USDT fails to ```init```, entire ```BPF::init``` will fail. If you're OK with some USDTs failing to ```init```, use ```BPF::init_usdt``` before calling ```BPF::init```. Examples in situ: [code](https://github.com/iovisor/bcc/commit/4f88a9401357d7b75e917abd994aa6ea97dda4d3#diff-04a7cad583be5646080970344c48c1f4R24), [search /examples](https://github.com/iovisor/bcc/search?q=bpf_usdt_readarg+path%3Aexamples&type=Code), [search /tools](https://github.com/iovisor/bcc/search?q=bpf_usdt_readarg+path%3Atools&type=Code) ### 7. Raw Tracepoints Syntax: RAW_TRACEPOINT_PROBE(*event*) This is a macro that instruments the raw tracepoint defined by *event*. The argument is a pointer to struct ```bpf_raw_tracepoint_args```, which is defined in [bpf.h](https://github.com/iovisor/bcc/blob/master/src/cc/compat/linux/virtual_bpf.h). The struct field ```args``` contains all parameters of the raw tracepoint where you can found at linux tree [include/trace/events](https://github.com/torvalds/linux/tree/master/include/trace/events) directory. For example: ```C RAW_TRACEPOINT_PROBE(sched_switch) { // TP_PROTO(bool preempt, struct task_struct *prev, struct task_struct *next) struct task_struct *prev = (struct task_struct *)ctx->args[1]; struct task_struct *next= (struct task_struct *)ctx->args[2]; s32 prev_tgid, next_tgid; bpf_probe_read(&prev_tgid, sizeof(prev->tgid), &prev->tgid); bpf_probe_read(&next_tgid, sizeof(next->tgid), &next->tgid); bpf_trace_printk("%d -> %d\\n", prev_tgid, next_tgid); } ``` This instruments the sched:sched_switch tracepoint, and prints the prev and next tgid. Examples in situ: [search /tools](https://github.com/iovisor/bcc/search?q=RAW_TRACEPOINT_PROBE+path%3Atools&type=Code) ### 8. system call tracepoints Syntax: ```syscall__SYSCALLNAME``` ```syscall__``` is a special prefix that creates a kprobe for the system call name provided as the remainder. You can use it by declaring a normal C function, then using the Python ```BPF.get_syscall_name(SYSCALLNAME)``` and ```BPF.attach_kprobe()``` to associate it. Arguments are specified on the function declaration: ```syscall__SYSCALLNAME(struct pt_regs *ctx, [, argument1 ...])```. For example: ```C int syscall__execve(struct pt_regs *ctx, const char __user *filename, const char __user *const __user *__argv, const char __user *const __user *__envp) { [...] } ``` This instruments the execve system call. The first argument is always ```struct pt_regs *```, the remainder are the arguments to the function (they don't need to be specified, if you don't intend to use them). Corresponding Python code: ```Python b = BPF(text=bpf_text) execve_fnname = b.get_syscall_name("execve") b.attach_kprobe(event=execve_fnname, fn_name="syscall__execve") ``` Examples in situ: [code](https://github.com/iovisor/bcc/blob/552658edda09298afdccc8a4b5e17311a2d8a771/tools/execsnoop.py#L101) ([output](https://github.com/iovisor/bcc/blob/552658edda09298afdccc8a4b5e17311a2d8a771/tools/execsnoop_example.txt#L8)) ## Data ### 1. bpf_probe_read() Syntax: ```int bpf_probe_read(void *dst, int size, const void *src)``` Return: 0 on success This copies a memory location to the BPF stack, so that BPF can later operate on it. For safety, all memory reads must pass through bpf_probe_read(). This happens automatically in some cases, such as dereferencing kernel variables, as bcc will rewrite the BPF program to include the necessary bpf_probe_reads(). Examples in situ: [search /examples](https://github.com/iovisor/bcc/search?q=bpf_probe_read+path%3Aexamples&type=Code), [search /tools](https://github.com/iovisor/bcc/search?q=bpf_probe_read+path%3Atools&type=Code) ### 2. bpf_probe_read_str() Syntax: ```int bpf_probe_read_str(void *dst, int size, const void *src)``` Return: - \> 0 length of the string including the trailing NULL on success - \< 0 error This copies a `NULL` terminated string from memory location to BPF stack, so that BPF can later operate on it. In case the string length is smaller than size, the target is not padded with further `NULL` bytes. In case the string length is larger than size, just `size - 1` bytes are copied and the last byte is set to `NULL`. Examples in situ: [search /examples](https://github.com/iovisor/bcc/search?q=bpf_probe_read_str+path%3Aexamples&type=Code), [search /tools](https://github.com/iovisor/bcc/search?q=bpf_probe_read_str+path%3Atools&type=Code) ### 3. bpf_ktime_get_ns() Syntax: ```u64 bpf_ktime_get_ns(void)``` Return: current time in nanoseconds Examples in situ: [search /examples](https://github.com/iovisor/bcc/search?q=bpf_ktime_get_ns+path%3Aexamples&type=Code), [search /tools](https://github.com/iovisor/bcc/search?q=bpf_ktime_get_ns+path%3Atools&type=Code) ### 4. bpf_get_current_pid_tgid() Syntax: ```u64 bpf_get_current_pid_tgid(void)``` Return: ```current->tgid << 32 | current->pid``` Returns the process ID in the lower 32 bits (kernel's view of the PID, which in user space is usually presented as the thread ID), and the thread group ID in the upper 32 bits (what user space often thinks of as the PID). By directly setting this to a u32, we discard the upper 32 bits. Examples in situ: [search /examples](https://github.com/iovisor/bcc/search?q=bpf_get_current_pid_tgid+path%3Aexamples&type=Code), [search /tools](https://github.com/iovisor/bcc/search?q=bpf_get_current_pid_tgid+path%3Atools&type=Code) ### 5. bpf_get_current_uid_gid() Syntax: ```u64 bpf_get_current_uid_gid(void)``` Return: ```current_gid << 32 | current_uid``` Returns the user ID and group IDs. Examples in situ: [search /examples](https://github.com/iovisor/bcc/search?q=bpf_get_current_uid_gid+path%3Aexamples&type=Code), [search /tools](https://github.com/iovisor/bcc/search?q=bpf_get_current_uid_gid+path%3Atools&type=Code) ### 6. bpf_get_current_comm() Syntax: ```bpf_get_current_comm(char *buf, int size_of_buf)``` Return: 0 on success Populates the first argument address with the current process name. It should be a pointer to a char array of at least size TASK_COMM_LEN, which is defined in linux/sched.h. For example: ```C #include int do_trace(struct pt_regs *ctx) { char comm[TASK_COMM_LEN]; bpf_get_current_comm(&comm, sizeof(comm)); [...] ``` Examples in situ: [search /examples](https://github.com/iovisor/bcc/search?q=bpf_get_current_comm+path%3Aexamples&type=Code), [search /tools](https://github.com/iovisor/bcc/search?q=bpf_get_current_comm+path%3Atools&type=Code) ### 7. bpf_get_current_task() Syntax: ```bpf_get_current_task()``` Return: current task as a pointer to struct task_struct. Returns a pointer to the current task's task_struct object. This helper can be used to compute the on-CPU time for a process, identify kernel threads, get the current CPU's run queue, or retrieve many other pieces of information. With Linux 4.13, due to issues with field randomization, you may need two #define directives before the includes: ```C #define randomized_struct_fields_start struct { #define randomized_struct_fields_end }; #include int do_trace(void *ctx) { struct task_struct *t = (struct task_struct *)bpf_get_current_task(); [...] ``` Examples in situ: [search /examples](https://github.com/iovisor/bcc/search?q=bpf_get_current_task+path%3Aexamples&type=Code), [search /tools](https://github.com/iovisor/bcc/search?q=bpf_get_current_task+path%3Atools&type=Code) ### 8. bpf_log2l() Syntax: ```unsigned int bpf_log2l(unsigned long v)``` Returns the log-2 of the provided value. This is often used to create indexes for histograms, to construct power-of-2 histograms. Examples in situ: [search /examples](https://github.com/iovisor/bcc/search?q=bpf_log2l+path%3Aexamples&type=Code), [search /tools](https://github.com/iovisor/bcc/search?q=bpf_log2l+path%3Atools&type=Code) ### 9. bpf_get_prandom_u32() Syntax: ```u32 bpf_get_prandom_u32()``` Returns a pseudo-random u32. Example in situ: [search /examples](https://github.com/iovisor/bcc/search?q=bpf_get_prandom_u32+path%3Aexamples&type=Code), [search /tools](https://github.com/iovisor/bcc/search?q=bpf_get_prandom_u32+path%3Atools&type=Code) ## Debugging ### 1. bpf_override_return() Syntax: ```int bpf_override_return(struct pt_regs *, unsigned long rc)``` Return: 0 on success When used in a program attached to a function entry kprobe, causes the execution of the function to be skipped, immediately returning `rc` instead. This is used for targeted error injection. bpf_override_return will only work when the kprobed function is whitelisted to allow error injections. Whitelisting entails tagging a function with `ALLOW_ERROR_INJECTION()` in the kernel source tree; see `io_ctl_init` for an example. If the kprobed function is not whitelisted, the bpf program will fail to attach with ` ioctl(PERF_EVENT_IOC_SET_BPF): Invalid argument` ```C int kprobe__io_ctl_init(void *ctx) { bpf_override_return(ctx, -ENOMEM); return 0; } ``` ## Output ### 1. bpf_trace_printk() Syntax: ```int bpf_trace_printk(const char *fmt, ...)``` Return: 0 on success A simple kernel facility for printf() to the common trace_pipe (/sys/kernel/debug/tracing/trace_pipe). This is ok for some quick examples, but has limitations: 3 args max, 1 %s only, and trace_pipe is globally shared, so concurrent programs will have clashing output. A better interface is via BPF_PERF_OUTPUT(). Note that calling this helper is made simpler than the original kernel version, which has ```fmt_size``` as the second parameter. Examples in situ: [search /examples](https://github.com/iovisor/bcc/search?q=bpf_trace_printk+path%3Aexamples&type=Code), [search /tools](https://github.com/iovisor/bcc/search?q=bpf_trace_printk+path%3Atools&type=Code) ### 2. BPF_PERF_OUTPUT Syntax: ```BPF_PERF_OUTPUT(name)``` Creates a BPF table for pushing out custom event data to user space via a perf ring buffer. This is the preferred method for pushing per-event data to user space. For example: ```C struct data_t { u32 pid; u64 ts; char comm[TASK_COMM_LEN]; }; BPF_PERF_OUTPUT(events); int hello(struct pt_regs *ctx) { struct data_t data = {}; data.pid = bpf_get_current_pid_tgid(); data.ts = bpf_ktime_get_ns(); bpf_get_current_comm(&data.comm, sizeof(data.comm)); events.perf_submit(ctx, &data, sizeof(data)); return 0; } ``` The output table is named ```events```, and data is pushed to it via ```events.perf_submit()```. Examples in situ: [search /examples](https://github.com/iovisor/bcc/search?q=BPF_PERF_OUTPUT+path%3Aexamples&type=Code), [search /tools](https://github.com/iovisor/bcc/search?q=BPF_PERF_OUTPUT+path%3Atools&type=Code) ### 3. perf_submit() Syntax: ```int perf_submit((void *)ctx, (void *)data, u32 data_size)``` Return: 0 on success A method of a BPF_PERF_OUTPUT table, for submitting custom event data to user space. See the BPF_PERF_OUTPUT entry. (This ultimately calls bpf_perf_event_output().) The ```ctx``` parameter is provided in [kprobes](#1-kprobes) or [kretprobes](#2-kretprobes). For ```SCHED_CLS``` or ```SOCKET_FILTER``` programs, the ```struct __sk_buff *skb``` must be used instead. Examples in situ: [search /examples](https://github.com/iovisor/bcc/search?q=perf_submit+path%3Aexamples&type=Code), [search /tools](https://github.com/iovisor/bcc/search?q=perf_submit+path%3Atools&type=Code) ## Maps Maps are BPF data stores, and are the basis for higher level object types including tables, hashes, and histograms. ### 1. BPF_TABLE Syntax: ```BPF_TABLE(_table_type, _key_type, _leaf_type, _name, _max_entries)``` Creates a map named ```_name```. Most of the time this will be used via higher-level macros, like BPF_HASH, BPF_HIST, etc. Methods (covered later): map.lookup(), map.lookup_or_try_init(), map.delete(), map.update(), map.insert(), map.increment(). Examples in situ: [search /examples](https://github.com/iovisor/bcc/search?q=BPF_TABLE+path%3Aexamples&type=Code), [search /tools](https://github.com/iovisor/bcc/search?q=BPF_TABLE+path%3Atools&type=Code) #### Pinned Maps Maps that were pinned to the BPF filesystem can be accessed through an extended syntax: ```BPF_TABLE_PINNED(_table_type, _key_type, _leaf_type, _name, _max_entries, "/sys/fs/bpf/xyz")``` The type information is not enforced and the actual map type depends on the map that got pinned to the location. For example: ```C BPF_TABLE_PINNED("hash", u64, u64, ids, 1024, "/sys/fs/bpf/ids"); ``` ### 2. BPF_HASH Syntax: ```BPF_HASH(name [, key_type [, leaf_type [, size]]])``` Creates a hash map (associative array) named ```name```, with optional parameters. Defaults: ```BPF_HASH(name, key_type=u64, leaf_type=u64, size=10240)``` For example: ```C BPF_HASH(start, struct request *); ``` This creates a hash named ```start``` where the key is a ```struct request *```, and the value defaults to u64. This hash is used by the disksnoop.py example for saving timestamps for each I/O request, where the key is the pointer to struct request, and the value is the timestamp. Methods (covered later): map.lookup(), map.lookup_or_try_init(), map.delete(), map.update(), map.insert(), map.increment(). Examples in situ: [search /examples](https://github.com/iovisor/bcc/search?q=BPF_HASH+path%3Aexamples&type=Code), [search /tools](https://github.com/iovisor/bcc/search?q=BPF_HASH+path%3Atools&type=Code) ### 3. BPF_ARRAY Syntax: ```BPF_ARRAY(name [, leaf_type [, size]])``` Creates an int-indexed array which is optimized for fastest lookup and update, named ```name```, with optional parameters. Defaults: ```BPF_ARRAY(name, leaf_type=u64, size=10240)``` For example: ```C BPF_ARRAY(counts, u64, 32); ``` This creates an array named ```counts``` where with 32 buckets and 64-bit integer values. This array is used by the funccount.py example for saving call count of each function. Methods (covered later): map.lookup(), map.update(), map.increment(). Note that all array elements are pre-allocated with zero values and can not be deleted. Examples in situ: [search /examples](https://github.com/iovisor/bcc/search?q=BPF_ARRAY+path%3Aexamples&type=Code), [search /tools](https://github.com/iovisor/bcc/search?q=BPF_ARRAY+path%3Atools&type=Code) ### 4. BPF_HISTOGRAM Syntax: ```BPF_HISTOGRAM(name [, key_type [, size ]])``` Creates a histogram map named ```name```, with optional parameters. Defaults: ```BPF_HISTOGRAM(name, key_type=int, size=64)``` For example: ```C BPF_HISTOGRAM(dist); ``` This creates a histogram named ```dist```, which defaults to 64 buckets indexed by keys of type int. Methods (covered later): map.increment(). Examples in situ: [search /examples](https://github.com/iovisor/bcc/search?q=BPF_HISTOGRAM+path%3Aexamples&type=Code), [search /tools](https://github.com/iovisor/bcc/search?q=BPF_HISTOGRAM+path%3Atools&type=Code) ### 5. BPF_STACK_TRACE Syntax: ```BPF_STACK_TRACE(name, max_entries)``` Creates stack trace map named ```name```, with a maximum entry count provided. These maps are used to store stack traces. For example: ```C BPF_STACK_TRACE(stack_traces, 1024); ``` This creates stack trace map named ```stack_traces```, with a maximum number of stack trace entries of 1024. Methods (covered later): map.get_stackid(). Examples in situ: [search /examples](https://github.com/iovisor/bcc/search?q=BPF_STACK_TRACE+path%3Aexamples&type=Code), [search /tools](https://github.com/iovisor/bcc/search?q=BPF_STACK_TRACE+path%3Atools&type=Code) ### 6. BPF_PERF_ARRAY Syntax: ```BPF_PERF_ARRAY(name, max_entries)``` Creates perf array named ```name```, with a maximum entry count provided, which must be equal to the number of system cpus. These maps are used to fetch hardware performance counters. For example: ```C text=""" BPF_PERF_ARRAY(cpu_cycles, NUM_CPUS); """ b = bcc.BPF(text=text, cflags=["-DNUM_CPUS=%d" % multiprocessing.cpu_count()]) b["cpu_cycles"].open_perf_event(b["cpu_cycles"].HW_CPU_CYCLES) ``` This creates a perf array named ```cpu_cycles```, with number of entries equal to the number of cpus/cores. The array is configured so that later calling map.perf_read() will return a hardware-calculated counter of the number of cycles elapsed from some point in the past. Only one type of hardware counter may be configured per table at a time. Methods (covered later): map.perf_read(). Examples in situ: [search /tests](https://github.com/iovisor/bcc/search?q=BPF_PERF_ARRAY+path%3Atests&type=Code) ### 7. BPF_PERCPU_ARRAY Syntax: ```BPF_PERCPU_ARRAY(name [, leaf_type [, size]])``` Creates NUM_CPU int-indexed arrays which are optimized for fastest lookup and update, named ```name```, with optional parameters. Each CPU will have a separate copy of this array. The copies are not kept synchronized in any way. Note that due to limits defined in the kernel (in linux/mm/percpu.c), the ```leaf_type``` cannot have a size of more than 32KB. In other words, ```BPF_PERCPU_ARRAY``` elements cannot be larger than 32KB in size. Defaults: ```BPF_PERCPU_ARRAY(name, leaf_type=u64, size=10240)``` For example: ```C BPF_PERCPU_ARRAY(counts, u64, 32); ``` This creates NUM_CPU arrays named ```counts``` where with 32 buckets and 64-bit integer values. Methods (covered later): map.lookup(), map.update(), map.increment(). Note that all array elements are pre-allocated with zero values and can not be deleted. Examples in situ: [search /examples](https://github.com/iovisor/bcc/search?q=BPF_PERCPU_ARRAY+path%3Aexamples&type=Code), [search /tools](https://github.com/iovisor/bcc/search?q=BPF_PERCPU_ARRAY+path%3Atools&type=Code) ### 8. BPF_LPM_TRIE Syntax: `BPF_LPM_TRIE(name [, key_type [, leaf_type [, size]]])` Creates a longest prefix match trie map named `name`, with optional parameters. Defaults: `BPF_LPM_TRIE(name, key_type=u64, leaf_type=u64, size=10240)` For example: ```c BPF_LPM_TRIE(trie, struct key_v6); ``` This creates an LPM trie map named `trie` where the key is a `struct key_v6`, and the value defaults to u64. Methods (covered later): map.lookup(), map.lookup_or_try_init(), map.delete(), map.update(), map.insert(), map.increment(). Examples in situ: [search /examples](https://github.com/iovisor/bcc/search?q=BPF_LPM_TRIE+path%3Aexamples&type=Code), [search /tools](https://github.com/iovisor/bcc/search?q=BPF_LPM_TRIE+path%3Atools&type=Code) ### 9. BPF_PROG_ARRAY Syntax: ```BPF_PROG_ARRAY(name, size)``` This creates a program array named ```name``` with ```size``` entries. Each entry of the array is either a file descriptor to a bpf program or ```NULL```. The array acts as a jump table so that bpf programs can "tail-call" other bpf programs. Methods (covered later): map.call(). Examples in situ: [search /examples](https://github.com/iovisor/bcc/search?q=BPF_PROG_ARRAY+path%3Aexamples&type=Code), [search /tests](https://github.com/iovisor/bcc/search?q=BPF_PROG_ARRAY+path%3Atests&type=Code), [assign fd](https://github.com/iovisor/bcc/blob/master/examples/networking/tunnel_monitor/monitor.py#L24-L26) ### 10. BPF_DEVMAP Syntax: ```BPF_DEVMAP(name, size)``` This creates a device map named ```name``` with ```size``` entries. Each entry of the map is an `ifindex` to a network interface. This map is only used in XDP. For example: ```C BPF_DEVMAP(devmap, 10); ``` Methods (covered later): map.redirect_map(). Examples in situ: [search /examples](https://github.com/iovisor/bcc/search?q=BPF_DEVMAP+path%3Aexamples&type=Code), ### 11. BPF_CPUMAP Syntax: ```BPF_CPUMAP(name, size)``` This creates a cpu map named ```name``` with ```size``` entries. The index of the map represents the CPU id and each entry is the size of the ring buffer allocated for the CPU. This map is only used in XDP. For example: ```C BPF_CPUMAP(cpumap, 16); ``` Methods (covered later): map.redirect_map(). Examples in situ: [search /examples](https://github.com/iovisor/bcc/search?q=BPF_CPUMAP+path%3Aexamples&type=Code), ### 12. BPF_ARRAY_OF_MAPS Syntax: ```BPF_ARRAY_OF_MAPS(name, inner_map_name, size)``` This creates an array map with a map-in-map type (BPF_MAP_TYPE_HASH_OF_MAPS) map named ```name``` with ```size``` entries. The inner map meta data is provided by map ```inner_map_name``` and can be most of array or hash maps except ```BPF_MAP_TYPE_PROG_ARRAY```, ```BPF_MAP_TYPE_CGROUP_STORAGE``` and ```BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE```. For example: ```C BPF_TABLE("hash", int, int, ex1, 1024); BPF_TABLE("hash", int, int, ex2, 1024); BPF_ARRAY_OF_MAPS(maps_array, "ex1", 10); ``` ### 13. BPF_HASH_OF_MAPS Syntax: ```BPF_HASH_OF_MAPS(name, inner_map_name, size)``` This creates a hash map with a map-in-map type (BPF_MAP_TYPE_HASH_OF_MAPS) map named ```name``` with ```size``` entries. The inner map meta data is provided by map ```inner_map_name``` and can be most of array or hash maps except ```BPF_MAP_TYPE_PROG_ARRAY```, ```BPF_MAP_TYPE_CGROUP_STORAGE``` and ```BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE```. For example: ```C BPF_ARRAY(ex1, int, 1024); BPF_ARRAY(ex2, int, 1024); BPF_HASH_OF_MAPS(maps_hash, "ex1", 10); ``` ### 14. map.lookup() Syntax: ```*val map.lookup(&key)``` Lookup the key in the map, and return a pointer to its value if it exists, else NULL. We pass the key in as an address to a pointer. Examples in situ: [search /examples](https://github.com/iovisor/bcc/search?q=lookup+path%3Aexamples&type=Code), [search /tools](https://github.com/iovisor/bcc/search?q=lookup+path%3Atools&type=Code) ### 15. map.lookup_or_try_init() Syntax: ```*val map.lookup_or_try_init(&key, &zero)``` Lookup the key in the map, and return a pointer to its value if it exists, else initialize the key's value to the second argument. This is often used to initialize values to zero. If the key cannot be inserted (e.g. the map is full) then NULL is returned. Examples in situ: [search /examples](https://github.com/iovisor/bcc/search?q=lookup_or_try_init+path%3Aexamples&type=Code), [search /tools](https://github.com/iovisor/bcc/search?q=lookup_or_try_init+path%3Atools&type=Code) Note: The old map.lookup_or_init() may cause return from the function, so lookup_or_try_init() is recommended as it does not have this side effect. ### 16. map.delete() Syntax: ```map.delete(&key)``` Delete the key from the hash. Examples in situ: [search /examples](https://github.com/iovisor/bcc/search?q=delete+path%3Aexamples&type=Code), [search /tools](https://github.com/iovisor/bcc/search?q=delete+path%3Atools&type=Code) ### 17. map.update() Syntax: ```map.update(&key, &val)``` Associate the value in the second argument to the key, overwriting any previous value. Examples in situ: [search /examples](https://github.com/iovisor/bcc/search?q=update+path%3Aexamples&type=Code), [search /tools](https://github.com/iovisor/bcc/search?q=update+path%3Atools&type=Code) ### 18. map.insert() Syntax: ```map.insert(&key, &val)``` Associate the value in the second argument to the key, only if there was no previous value. Examples in situ: [search /examples](https://github.com/iovisor/bcc/search?q=insert+path%3Aexamples&type=Code), [search /tools](https://github.com/iovisor/bcc/search?q=insert+path%3Atools&type=Code) ### 19. map.increment() Syntax: ```map.increment(key[, increment_amount])``` Increments the key's value by `increment_amount`, which defaults to 1. Used for histograms. Examples in situ: [search /examples](https://github.com/iovisor/bcc/search?q=increment+path%3Aexamples&type=Code), [search /tools](https://github.com/iovisor/bcc/search?q=increment+path%3Atools&type=Code) ### 20. map.get_stackid() Syntax: ```int map.get_stackid(void *ctx, u64 flags)``` This walks the stack found via the struct pt_regs in ```ctx```, saves it in the stack trace map, and returns a unique ID for the stack trace. Examples in situ: [search /examples](https://github.com/iovisor/bcc/search?q=get_stackid+path%3Aexamples&type=Code), [search /tools](https://github.com/iovisor/bcc/search?q=get_stackid+path%3Atools&type=Code) ### 21. map.perf_read() Syntax: ```u64 map.perf_read(u32 cpu)``` This returns the hardware performance counter as configured in [5. BPF_PERF_ARRAY](#5-bpf_perf_array) Examples in situ: [search /tests](https://github.com/iovisor/bcc/search?q=perf_read+path%3Atests&type=Code) ### 22. map.call() Syntax: ```void map.call(void *ctx, int index)``` This invokes ```bpf_tail_call()``` to tail-call the bpf program which the ```index``` entry in [9. BPF_PROG_ARRAY](#9-bpf_prog_array) points to. A tail-call is different from the normal call. It reuses the current stack frame after jumping to another bpf program and never goes back. If the ```index``` entry is empty, it won't jump anywhere and the program execution continues as normal. For example: ```C BPF_PROG_ARRAY(prog_array, 10); int tail_call(void *ctx) { bpf_trace_printk("Tail-call\n"); return 0; } int do_tail_call(void *ctx) { bpf_trace_printk("Original program\n"); prog_array.call(ctx, 2); return 0; } ``` ```Python b = BPF(src_file="example.c") tail_fn = b.load_func("tail_call", BPF.KPROBE) prog_array = b.get_table("prog_array") prog_array[c_int(2)] = c_int(tail_fn.fd) b.attach_kprobe(event="some_kprobe_event", fn_name="do_tail_call") ``` This assigns ```tail_call()``` to ```prog_array[2]```. In the end of ```do_tail_call()```, ```prog_array.call(ctx, 2)``` tail-calls ```tail_call()``` and executes it. **NOTE:** To prevent infinite loop, the maximum number of tail-calls is 32 ([```MAX_TAIL_CALL_CNT```](https://github.com/torvalds/linux/search?l=C&q=MAX_TAIL_CALL_CNT+path%3Ainclude%2Flinux&type=Code)). Examples in situ: [search /examples](https://github.com/iovisor/bcc/search?l=C&q=call+path%3Aexamples&type=Code), [search /tests](https://github.com/iovisor/bcc/search?l=C&q=call+path%3Atests&type=Code) ### 23. map.redirect_map() Syntax: ```int map.redirect_map(int index, int flags)``` This redirects the incoming packets based on the ```index``` entry. If the map is [10. BPF_DEVMAP](#10-bpf_devmap), the packet will be sent to the transmit queue of the network interface that the entry points to. If the map is [11. BPF_CPUMAP](#11-bpf_cpumap), the packet will be sent to the ring buffer of the ```index``` CPU and be processed by the CPU later. If the packet is redirected successfully, the function will return XDP_REDIRECT. Otherwise, it will return XDP_ABORTED to discard the packet. For example: ```C BPF_DEVMAP(devmap, 1); int redirect_example(struct xdp_md *ctx) { return devmap.redirect_map(0, 0); } int xdp_dummy(struct xdp_md *ctx) { return XDP_PASS; } ``` ```Python ip = pyroute2.IPRoute() idx = ip.link_lookup(ifname="eth1")[0] b = bcc.BPF(src_file="example.c") devmap = b.get_table("devmap") devmap[c_uint32(0)] = c_int(idx) in_fn = b.load_func("redirect_example", BPF.XDP) out_fn = b.load_func("xdp_dummy", BPF.XDP) b.attach_xdp("eth0", in_fn, 0) b.attach_xdp("eth1", out_fn, 0) ``` Examples in situ: [search /examples](https://github.com/iovisor/bcc/search?l=C&q=redirect_map+path%3Aexamples&type=Code), ## Licensing Depending on which [BPF helpers](kernel-versions.md#helpers) are used, a GPL-compatible license is required. The special BCC macro `BPF_LICENSE` specifies the license of the BPF program. You can set the license as a comment in your source code, but the kernel has a special interface to specify it programmatically. If you need to use GPL-only helpers, it is recommended to specify the macro in your C code so that the kernel can understand it: ```C // SPDX-License-Identifier: GPL-2.0+ #define BPF_LICENSE GPL ``` Otherwise, the kernel may reject loading your program (see the [error description](#2-cannot-call-gpl-only-function-from-proprietary-program) below). Note that it supports multiple words and quotes are not necessary: ```C // SPDX-License-Identifier: GPL-2.0+ OR BSD-2-Clause #define BPF_LICENSE Dual BSD/GPL ``` Check the [BPF helpers reference](kernel-versions.md#helpers) to see which helpers are GPL-only and what the kernel understands as GPL-compatible. **If the macro is not specified, BCC will automatically define the license of the program as GPL.** # bcc Python ## Initialization Constructors. ### 1. BPF Syntax: ```BPF({text=BPF_program | src_file=filename} [, usdt_contexts=[USDT_object, ...]] [, cflags=[arg1, ...]] [, debug=int])``` Creates a BPF object. This is the main object for defining a BPF program, and interacting with its output. Exactly one of `text` or `src_file` must be supplied (not both). The `cflags` specifies additional arguments to be passed to the compiler, for example `-DMACRO_NAME=value` or `-I/include/path`. The arguments are passed as an array, with each element being an additional argument. Note that strings are not split on whitespace, so each argument must be a different element of the array, e.g. `["-include", "header.h"]`. The `debug` flags control debug output, and can be or'ed together: - `DEBUG_LLVM_IR = 0x1` compiled LLVM IR - `DEBUG_BPF = 0x2` loaded BPF bytecode and register state on branches - `DEBUG_PREPROCESSOR = 0x4` pre-processor result - `DEBUG_SOURCE = 0x8` ASM instructions embedded with source - `DEBUG_BPF_REGISTER_STATE = 0x10` register state on all instructions in addition to DEBUG_BPF Examples: ```Python # define entire BPF program in one line: BPF(text='int do_trace(void *ctx) { bpf_trace_printk("hit!\\n"); return 0; }'); # define program as a variable: prog = """ int hello(void *ctx) { bpf_trace_printk("Hello, World!\\n"); return 0; } """ b = BPF(text=prog) # source a file: b = BPF(src_file = "vfsreadlat.c") # include a USDT object: u = USDT(pid=int(pid)) [...] b = BPF(text=bpf_text, usdt_contexts=[u]) # add include paths: u = BPF(text=prog, cflags=["-I/path/to/include"]) ``` Examples in situ: [search /examples](https://github.com/iovisor/bcc/search?q=BPF+path%3Aexamples+language%3Apython&type=Code), [search /tools](https://github.com/iovisor/bcc/search?q=BPF+path%3Atools+language%3Apython&type=Code) ### 2. USDT Syntax: ```USDT({pid=pid | path=path})``` Creates an object to instrument User Statically-Defined Tracing (USDT) probes. Its primary method is ```enable_probe()```. Arguments: - pid: attach to this process ID. - path: instrument USDT probes from this binary path. Examples: ```Python # include a USDT object: u = USDT(pid=int(pid)) [...] b = BPF(text=bpf_text, usdt_contexts=[u]) ``` Examples in situ: [search /examples](https://github.com/iovisor/bcc/search?q=USDT+path%3Aexamples+language%3Apython&type=Code), [search /tools](https://github.com/iovisor/bcc/search?q=USDT+path%3Atools+language%3Apython&type=Code) ## Events ### 1. attach_kprobe() Syntax: ```BPF.attach_kprobe(event="event", fn_name="name")``` Instruments the kernel function ```event()``` using kernel dynamic tracing of the function entry, and attaches our C defined function ```name()``` to be called when the kernel function is called. For example: ```Python b.attach_kprobe(event="sys_clone", fn_name="do_trace") ``` This will instrument the kernel ```sys_clone()``` function, which will then run our BPF defined ```do_trace()``` function each time it is called. You can call attach_kprobe() more than once, and attach your BPF function to multiple kernel functions. See the previous kprobes section for how to instrument arguments from BPF. Examples in situ: [search /examples](https://github.com/iovisor/bcc/search?q=attach_kprobe+path%3Aexamples+language%3Apython&type=Code), [search /tools](https://github.com/iovisor/bcc/search?q=attach_kprobe+path%3Atools+language%3Apython&type=Code) ### 2. attach_kretprobe() Syntax: ```BPF.attach_kretprobe(event="event", fn_name="name" [, maxactive=int])``` Instruments the return of the kernel function ```event()``` using kernel dynamic tracing of the function return, and attaches our C defined function ```name()``` to be called when the kernel function returns. For example: ```Python b.attach_kretprobe(event="vfs_read", fn_name="do_return") ``` This will instrument the kernel ```vfs_read()``` function, which will then run our BPF defined ```do_return()``` function each time it is called. You can call attach_kretprobe() more than once, and attach your BPF function to multiple kernel function returns. When a kretprobe is installed on a kernel function, there is a limit on how many parallel calls it can catch. You can change that limit with ```maxactive```. See the kprobes documentation for its default value. See the previous kretprobes section for how to instrument the return value from BPF. Examples in situ: [search /examples](https://github.com/iovisor/bcc/search?q=attach_kretprobe+path%3Aexamples+language%3Apython&type=Code), [search /tools](https://github.com/iovisor/bcc/search?q=attach_kretprobe+path%3Atools+language%3Apython&type=Code) ### 3. attach_tracepoint() Syntax: ```BPF.attach_tracepoint(tp="tracepoint", fn_name="name")``` Instruments the kernel tracepoint described by ```tracepoint```, and when hit, runs the BPF function ```name()```. This is an explicit way to instrument tracepoints. The ```TRACEPOINT_PROBE``` syntax, covered in the earlier tracepoints section, is an alternate method with the advantage of auto-declaring an ```args``` struct containing the tracepoint arguments. With ```attach_tracepoint()```, the tracepoint arguments need to be declared in the BPF program. For example: ```Python # define BPF program bpf_text = """ #include struct urandom_read_args { // from /sys/kernel/debug/tracing/events/random/urandom_read/format u64 __unused__; u32 got_bits; u32 pool_left; u32 input_left; }; int printarg(struct urandom_read_args *args) { bpf_trace_printk("%d\\n", args->got_bits); return 0; }; """ # load BPF program b = BPF(text=bpf_text) b.attach_tracepoint("random:urandom_read", "printarg") ``` Notice how the first argument to ```printarg()``` is now our defined struct. Examples in situ: [code](https://github.com/iovisor/bcc/blob/a4159da8c4ea8a05a3c6e402451f530d6e5a8b41/examples/tracing/urandomread-explicit.py#L41), [search /examples](https://github.com/iovisor/bcc/search?q=attach_tracepoint+path%3Aexamples+language%3Apython&type=Code), [search /tools](https://github.com/iovisor/bcc/search?q=attach_tracepoint+path%3Atools+language%3Apython&type=Code) ### 4. attach_uprobe() Syntax: ```BPF.attach_uprobe(name="location", sym="symbol", fn_name="name" [, sym_off=int])```, ```BPF.attach_uprobe(name="location", sym_re="regex", fn_name="name")```, ```BPF.attach_uprobe(name="location", addr=int, fn_name="name")``` Instruments the user-level function ```symbol()``` from either the library or binary named by ```location``` using user-level dynamic tracing of the function entry, and attach our C defined function ```name()``` to be called whenever the user-level function is called. If ```sym_off``` is given, the function is attached to the offset within the symbol. The real address ```addr``` may be supplied in place of ```sym```, in which case ```sym``` must be set to its default value. If the file is a non-PIE executable, ```addr``` must be a virtual address, otherwise it must be an offset relative to the file load address. Instead of a symbol name, a regular expression can be provided in ```sym_re```. The uprobe will then attach to symbols that match the provided regular expression. Libraries can be given in the name argument without the lib prefix, or with the full path (/usr/lib/...). Binaries can be given only with the full path (/bin/sh). For example: ```Python b.attach_uprobe(name="c", sym="strlen", fn_name="count") ``` This will instrument ```strlen()``` function from libc, and call our BPF function ```count()``` when it is called. Note how the "lib" in "libc" is not necessary to specify. Other examples: ```Python b.attach_uprobe(name="c", sym="getaddrinfo", fn_name="do_entry") b.attach_uprobe(name="/usr/bin/python", sym="main", fn_name="do_main") ``` You can call attach_uprobe() more than once, and attach your BPF function to multiple user-level functions. See the previous uprobes section for how to instrument arguments from BPF. Examples in situ: [search /examples](https://github.com/iovisor/bcc/search?q=attach_uprobe+path%3Aexamples+language%3Apython&type=Code), [search /tools](https://github.com/iovisor/bcc/search?q=attach_uprobe+path%3Atools+language%3Apython&type=Code) ### 5. attach_uretprobe() Syntax: ```BPF.attach_uretprobe(name="location", sym="symbol", fn_name="name")``` Instruments the return of the user-level function ```symbol()``` from either the library or binary named by ```location``` using user-level dynamic tracing of the function return, and attach our C defined function ```name()``` to be called whenever the user-level function returns. For example: ```Python b.attach_uretprobe(name="c", sym="strlen", fn_name="count") ``` This will instrument ```strlen()``` function from libc, and call our BPF function ```count()``` when it returns. Other examples: ```Python b.attach_uretprobe(name="c", sym="getaddrinfo", fn_name="do_return") b.attach_uretprobe(name="/usr/bin/python", sym="main", fn_name="do_main") ``` You can call attach_uretprobe() more than once, and attach your BPF function to multiple user-level functions. See the previous uretprobes section for how to instrument the return value from BPF. Examples in situ: [search /examples](https://github.com/iovisor/bcc/search?q=attach_uretprobe+path%3Aexamples+language%3Apython&type=Code), [search /tools](https://github.com/iovisor/bcc/search?q=attach_uretprobe+path%3Atools+language%3Apython&type=Code) ### 6. USDT.enable_probe() Syntax: ```USDT.enable_probe(probe=probe, fn_name=name)``` Attaches a BPF C function ```name``` to the USDT probe ```probe```. Example: ```Python # enable USDT probe from given PID u = USDT(pid=int(pid)) u.enable_probe(probe="http__server__request", fn_name="do_trace") ``` To check if your binary has USDT probes, and what they are, you can run ```readelf -n binary``` and check the stap debug section. Examples in situ: [search /examples](https://github.com/iovisor/bcc/search?q=enable_probe+path%3Aexamples+language%3Apython&type=Code), [search /tools](https://github.com/iovisor/bcc/search?q=enable_probe+path%3Atools+language%3Apython&type=Code) ### 7. attach_raw_tracepoint() Syntax: ```BPF.attach_raw_tracepoint(tp="tracepoint", fn_name="name")``` Instruments the kernel raw tracepoint described by ```tracepoint``` (```event``` only, no ```category```), and when hit, runs the BPF function ```name()```. This is an explicit way to instrument tracepoints. The ```RAW_TRACEPOINT_PROBE``` syntax, covered in the earlier raw tracepoints section, is an alternate method. For example: ```Python b.attach_raw_tracepoint("sched_swtich", "do_trace") ``` Examples in situ: [search /tools](https://github.com/iovisor/bcc/search?q=attach_raw_tracepoint+path%3Atools+language%3Apython&type=Code) ## Debug Output ### 1. trace_print() Syntax: ```BPF.trace_print(fmt="fields")``` This method continually reads the globally shared /sys/kernel/debug/tracing/trace_pipe file and prints its contents. This file can be written to via BPF and the bpf_trace_printk() function, however, that method has limitations, including a lack of concurrent tracing support. The BPF_PERF_OUTPUT mechanism, covered earlier, is preferred. Arguments: - ```fmt```: optional, and can contain a field formatting string. It defaults to ```None```. Examples: ```Python # print trace_pipe output as-is: b.trace_print() # print PID and message: b.trace_print(fmt="{1} {5}") ``` Examples in situ: [search /examples](https://github.com/iovisor/bcc/search?q=trace_print+path%3Aexamples+language%3Apython&type=Code), [search /tools](https://github.com/iovisor/bcc/search?q=trace_print+path%3Atools+language%3Apython&type=Code) ### 2. trace_fields() Syntax: ```BPF.trace_fields(nonblocking=False)``` This method reads one line from the globally shared /sys/kernel/debug/tracing/trace_pipe file and returns it as fields. This file can be written to via BPF and the bpf_trace_printk() function, however, that method has limitations, including a lack of concurrent tracing support. The BPF_PERF_OUTPUT mechanism, covered earlier, is preferred. Arguments: - ```nonblocking```: optional, defaults to ```False```. When set to ```True```, the program will not block waiting for input. Examples: ```Python while 1: try: (task, pid, cpu, flags, ts, msg) = b.trace_fields() except ValueError: continue [...] ``` Examples in situ: [search /examples](https://github.com/iovisor/bcc/search?q=trace_fields+path%3Aexamples+language%3Apython&type=Code), [search /tools](https://github.com/iovisor/bcc/search?q=trace_fields+path%3Atools+language%3Apython&type=Code) ## Output Normal output from a BPF program is either: - per-event: using PERF_EVENT_OUTPUT, open_perf_buffer(), and perf_buffer_poll(). - map summary: using items(), or print_log2_hist(), covered in the Maps section. ### 1. perf_buffer_poll() Syntax: ```BPF.perf_buffer_poll(timeout=T)``` This polls from all open perf ring buffers, calling the callback function that was provided when calling open_perf_buffer for each entry. The timeout parameter is optional and measured in milliseconds. In its absence, polling continues indefinitely. Example: ```Python # loop with callback to print_event b["events"].open_perf_buffer(print_event) while 1: try: b.perf_buffer_poll() except KeyboardInterrupt: exit(); ``` Examples in situ: [code](https://github.com/iovisor/bcc/blob/v0.9.0/examples/tracing/hello_perf_output.py#L55), [search /examples](https://github.com/iovisor/bcc/search?q=perf_buffer_poll+path%3Aexamples+language%3Apython&type=Code), [search /tools](https://github.com/iovisor/bcc/search?q=perf_buffer_poll+path%3Atools+language%3Apython&type=Code) ## Maps Maps are BPF data stores, and are used in bcc to implement a table, and then higher level objects on top of tables, including hashes and histograms. ### 1. get_table() Syntax: ```BPF.get_table(name)``` Returns a table object. This is no longer used, as tables can now be read as items from BPF. Eg: ```BPF[name]```. Examples: ```Python counts = b.get_table("counts") counts = b["counts"] ``` These are equivalent. ### 2. open_perf_buffer() Syntax: ```table.open_perf_buffers(callback, page_cnt=N, lost_cb=None)``` This operates on a table as defined in BPF as BPF_PERF_OUTPUT(), and associates the callback Python function ```callback``` to be called when data is available in the perf ring buffer. This is part of the recommended mechanism for transferring per-event data from kernel to user space. The size of the perf ring buffer can be specified via the ```page_cnt``` parameter, which must be a power of two number of pages and defaults to 8. If the callback is not processing data fast enough, some submitted data may be lost. ```lost_cb``` will be called to log / monitor the lost count. If ```lost_cb``` is the default ```None``` value, it will just print a line of message to ```stderr```. Example: ```Python # process event def print_event(cpu, data, size): event = ct.cast(data, ct.POINTER(Data)).contents [...] # loop with callback to print_event b["events"].open_perf_buffer(print_event) while 1: try: b.perf_buffer_poll() except KeyboardInterrupt: exit() ``` Note that the data structure transferred will need to be declared in C in the BPF program. For example: ```C // define output data structure in C struct data_t { u32 pid; u64 ts; char comm[TASK_COMM_LEN]; }; BPF_PERF_OUTPUT(events); [...] ``` In Python, you can either let bcc generate the data structure from C declaration automatically (recommanded): ```Python def print_event(cpu, data, size): event = b["events"].event(data) [...] ``` or define it manually: ```Python # define output data structure in Python TASK_COMM_LEN = 16 # linux/sched.h class Data(ct.Structure): _fields_ = [("pid", ct.c_ulonglong), ("ts", ct.c_ulonglong), ("comm", ct.c_char * TASK_COMM_LEN)] def print_event(cpu, data, size): event = ct.cast(data, ct.POINTER(Data)).contents [...] ``` Examples in situ: [code](https://github.com/iovisor/bcc/blob/v0.9.0/examples/tracing/hello_perf_output.py#L52), [search /examples](https://github.com/iovisor/bcc/search?q=open_perf_buffer+path%3Aexamples+language%3Apython&type=Code), [search /tools](https://github.com/iovisor/bcc/search?q=open_perf_buffer+path%3Atools+language%3Apython&type=Code) ### 3. items() Syntax: ```table.items()``` Returns an array of the keys in a table. This can be used with BPF_HASH maps to fetch, and iterate, over the keys. Example: ```Python # print output print("%10s %s" % ("COUNT", "STRING")) counts = b.get_table("counts") for k, v in sorted(counts.items(), key=lambda counts: counts[1].value): print("%10d \"%s\"" % (v.value, k.c.encode('string-escape'))) ``` This example also uses the ```sorted()``` method to sort by value. Examples in situ: [search /examples](https://github.com/iovisor/bcc/search?q=items+path%3Aexamples+language%3Apython&type=Code), [search /tools](https://github.com/iovisor/bcc/search?q=items+path%3Atools+language%3Apython&type=Code) ### 4. values() Syntax: ```table.values()``` Returns an array of the values in a table. ### 5. clear() Syntax: ```table.clear()``` Clears the table: deletes all entries. Example: ```Python # print map summary every second: while True: time.sleep(1) print("%-8s\n" % time.strftime("%H:%M:%S"), end="") dist.print_log2_hist(sym + " return:") dist.clear() ``` Examples in situ: [search /examples](https://github.com/iovisor/bcc/search?q=clear+path%3Aexamples+language%3Apython&type=Code), [search /tools](https://github.com/iovisor/bcc/search?q=clear+path%3Atools+language%3Apython&type=Code) ### 6. print_log2_hist() Syntax: ```table.print_log2_hist(val_type="value", section_header="Bucket ptr", section_print_fn=None)``` Prints a table as a log2 histogram in ASCII. The table must be stored as log2, which can be done using the BPF function ```bpf_log2l()```. Arguments: - val_type: optional, column header. - section_header: if the histogram has a secondary key, multiple tables will print and section_header can be used as a header description for each. - section_print_fn: if section_print_fn is not None, it will be passed the bucket value. Example: ```Python b = BPF(text=""" BPF_HISTOGRAM(dist); int kprobe__blk_account_io_completion(struct pt_regs *ctx, struct request *req) { dist.increment(bpf_log2l(req->__data_len / 1024)); return 0; } """) [...] b["dist"].print_log2_hist("kbytes") ``` Output: ``` kbytes : count distribution 0 -> 1 : 3 | | 2 -> 3 : 0 | | 4 -> 7 : 211 |********** | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 1 | | 128 -> 255 : 800 |**************************************| ``` This output shows a multi-modal distribution, with the largest mode of 128->255 kbytes and a count of 800. This is an efficient way to summarize data, as the summarization is performed in-kernel, and only the count column is passed to user space. Examples in situ: [search /examples](https://github.com/iovisor/bcc/search?q=print_log2_hist+path%3Aexamples+language%3Apython&type=Code), [search /tools](https://github.com/iovisor/bcc/search?q=print_log2_hist+path%3Atools+language%3Apython&type=Code) ### 6. print_linear_hist() Syntax: ```table.print_linear_hist(val_type="value", section_header="Bucket ptr", section_print_fn=None)``` Prints a table as a linear histogram in ASCII. This is intended to visualize small integer ranges, eg, 0 to 100. Arguments: - val_type: optional, column header. - section_header: if the histogram has a secondary key, multiple tables will print and section_header can be used as a header description for each. - section_print_fn: if section_print_fn is not None, it will be passed the bucket value. Example: ```Python b = BPF(text=""" BPF_HISTOGRAM(dist); int kprobe__blk_account_io_completion(struct pt_regs *ctx, struct request *req) { dist.increment(req->__data_len / 1024); return 0; } """) [...] b["dist"].print_linear_hist("kbytes") ``` Output: ``` kbytes : count distribution 0 : 3 |****** | 1 : 0 | | 2 : 0 | | 3 : 0 | | 4 : 19 |****************************************| 5 : 0 | | 6 : 0 | | 7 : 0 | | 8 : 4 |******** | 9 : 0 | | 10 : 0 | | 11 : 0 | | 12 : 0 | | 13 : 0 | | 14 : 0 | | 15 : 0 | | 16 : 2 |**** | [...] ``` This is an efficient way to summarize data, as the summarization is performed in-kernel, and only the values in the count column are passed to user space. Examples in situ: [search /examples](https://github.com/iovisor/bcc/search?q=print_linear_hist+path%3Aexamples+language%3Apython&type=Code), [search /tools](https://github.com/iovisor/bcc/search?q=print_linear_hist+path%3Atools+language%3Apython&type=Code) ## Helpers Some helper methods provided by bcc. Note that since we're in Python, we can import any Python library and their methods, including, for example, the libraries: argparse, collections, ctypes, datetime, re, socket, struct, subprocess, sys, and time. ### 1. ksym() Syntax: ```BPF.ksym(addr)``` Translate a kernel memory address into a kernel function name, which is returned. Example: ```Python print("kernel function: " + b.ksym(addr)) ``` Examples in situ: [search /examples](https://github.com/iovisor/bcc/search?q=ksym+path%3Aexamples+language%3Apython&type=Code), [search /tools](https://github.com/iovisor/bcc/search?q=ksym+path%3Atools+language%3Apython&type=Code) ### 2. ksymname() Syntax: ```BPF.ksymname(name)``` Translate a kernel name into an address. This is the reverse of ksym. Returns -1 when the function name is unknown. Example: ```Python print("kernel address: %x" % b.ksymname("vfs_read")) ``` Examples in situ: [search /examples](https://github.com/iovisor/bcc/search?q=ksymname+path%3Aexamples+language%3Apython&type=Code), [search /tools](https://github.com/iovisor/bcc/search?q=ksymname+path%3Atools+language%3Apython&type=Code) ### 3. sym() Syntax: ```BPF.sym(addr, pid, show_module=False, show_offset=False)``` Translate a memory address into a function name for a pid, which is returned. A pid of less than zero will access the kernel symbol cache. The `show_module` and `show_offset` parameters control whether the module in which the symbol lies should be displayed, and whether the instruction offset from the beginning of the symbol should be displayed. These extra parameters default to `False`. Example: ```Python print("function: " + b.sym(addr, pid)) ``` Examples in situ: [search /examples](https://github.com/iovisor/bcc/search?q=sym+path%3Aexamples+language%3Apython&type=Code), [search /tools](https://github.com/iovisor/bcc/search?q=sym+path%3Atools+language%3Apython&type=Code) ### 4. num_open_kprobes() Syntax: ```BPF.num_open_kprobes()``` Returns the number of open k[ret]probes. Can be useful for scenarios where event_re is used while attaching and detaching probes. Excludes perf_events readers. Example: ```Python b.attach_kprobe(event_re=pattern, fn_name="trace_count") matched = b.num_open_kprobes() if matched == 0: print("0 functions matched by \"%s\". Exiting." % args.pattern) exit() ``` Examples in situ: [search /examples](https://github.com/iovisor/bcc/search?q=num_open_kprobes+path%3Aexamples+language%3Apython&type=Code), [search /tools](https://github.com/iovisor/bcc/search?q=num_open_kprobes+path%3Atools+language%3Apython&type=Code) ### 5. get_syscall_fnname() Syntax: ```BPF.get_syscall_fnname(name : str)``` Return the corresponding kernel function name of the syscall. This helper function will try different prefixes and use the right one to concatenate with the syscall name. Note that the return value may vary in different versions of linux kernel and sometimes it will causing trouble. (see [#2590](https://github.com/iovisor/bcc/issues/2590)) Example: ```Python print("The function name of %s in kernel is %s" % ("clone", b.get_syscall_fnname("clone"))) # sys_clone or __x64_sys_clone or ... ``` Examples in situ: [search /examples](https://github.com/iovisor/bcc/search?q=get_syscall_fnname+path%3Aexamples+language%3Apython&type=Code), [search /tools](https://github.com/iovisor/bcc/search?q=get_syscall_fnname+path%3Atools+language%3Apython&type=Code) # BPF Errors See the "Understanding eBPF verifier messages" section in the kernel source under Documentation/networking/filter.txt. ## 1. Invalid mem access This can be due to trying to read memory directly, instead of operating on memory on the BPF stack. All memory reads must be passed via bpf_probe_read() to copy memory into the BPF stack, which can be automatic by the bcc rewriter in some cases of simple dereferencing. bpf_probe_read() does all the required checks. Example: ``` bpf: Permission denied 0: (bf) r6 = r1 1: (79) r7 = *(u64 *)(r6 +80) 2: (85) call 14 3: (bf) r8 = r0 [...] 23: (69) r1 = *(u16 *)(r7 +16) R7 invalid mem access 'inv' Traceback (most recent call last): File "./tcpaccept", line 179, in b = BPF(text=bpf_text) File "/usr/lib/python2.7/dist-packages/bcc/__init__.py", line 172, in __init__ self._trace_autoload() File "/usr/lib/python2.7/dist-packages/bcc/__init__.py", line 612, in _trace_autoload fn = self.load_func(func_name, BPF.KPROBE) File "/usr/lib/python2.7/dist-packages/bcc/__init__.py", line 212, in load_func raise Exception("Failed to load BPF program %s" % func_name) Exception: Failed to load BPF program kretprobe__inet_csk_accept ``` ## 2. Cannot call GPL only function from proprietary program This error happens when a GPL-only helper is called from a non-GPL BPF program. To fix this error, do not use GPL-only helpers from a proprietary BPF program, or relicense the BPF program under a GPL-compatible license. Check which [BPF helpers](https://github.com/iovisor/bcc/blob/master/docs/kernel-versions.md#helpers) are GPL-only, and what licenses are considered GPL-compatible. Example calling `bpf_get_stackid()`, a GPL-only BPF helper, from a proprietary program (`#define BPF_LICENSE Proprietary`): ``` bpf: Failed to load program: Invalid argument [...] 8: (85) call bpf_get_stackid#27 cannot call GPL only function from proprietary program ``` # Environment Variables ## 1. Kernel source directory eBPF program compilation needs kernel sources or kernel headers with headers compiled. In case your kernel sources are at a non-standard location where BCC cannot find then, its possible to provide BCC the absolute path of the location by setting `BCC_KERNEL_SOURCE` to it. ## 2. Kernel version overriding By default, BCC stores the `LINUX_VERSION_CODE` in the generated eBPF object which is then passed along to the kernel when the eBPF program is loaded. Sometimes this is quite inconvenient especially when the kernel is slightly updated such as an LTS kernel release. Its extremely unlikely the slight mismatch would cause any issues with the loaded eBPF program. By setting `BCC_LINUX_VERSION_CODE` to the version of the kernel that's running, the check for verifying the kernel version can be bypassed. This is needed for programs that use kprobes. This needs to be encoded in the format: `(VERSION * 65536) + (PATCHLEVEL * 256) + SUBLEVEL`. For example, if the running kernel is `4.9.10`, then can set `export BCC_LINUX_VERSION_CODE=264458` to override the kernel version check successfully. bpfcc-0.12.0/docs/tutorial.md000066400000000000000000000454721357404205000160000ustar00rootroot00000000000000# bcc Tutorial This tutorial covers how to use [bcc](https://github.com/iovisor/bcc) tools to quickly solve performance, troubleshooting, and networking issues. If you want to develop new bcc tools, see [tutorial_bcc_python_developer.md](tutorial_bcc_python_developer.md) for that tutorial. It is assumed for this tutorial that bcc is already installed, and you can run tools like execsnoop successfully. See [INSTALL.md](../INSTALL.md). This uses enhancements added to the Linux 4.x series. ## Observability Some quick wins. ### 0. Before bcc Before using bcc, you should start with the Linux basics. One reference is the [Linux Performance Analysis in 60s](http://techblog.netflix.com/2015/11/linux-performance-analysis-in-60s.html) post, which covers these commands: 1. uptime 1. dmesg | tail 1. vmstat 1 1. mpstat -P ALL 1 1. pidstat 1 1. iostat -xz 1 1. free -m 1. sar -n DEV 1 1. sar -n TCP,ETCP 1 1. top ### 1. General Performance Here is a generic checklist for performance investigations with bcc, first as a list, then in detail: 1. execsnoop 1. opensnoop 1. ext4slower (or btrfs\*, xfs\*, zfs\*) 1. biolatency 1. biosnoop 1. cachestat 1. tcpconnect 1. tcpaccept 1. tcpretrans 1. runqlat 1. profile These tools may be installed on your system under /usr/share/bcc/tools, or you can run them from the bcc github repo under /tools where they have a .py extension. Browse the 50+ tools available for more analysis options. #### 1.1 execsnoop ``` # ./execsnoop PCOMM PID RET ARGS supervise 9660 0 ./run supervise 9661 0 ./run mkdir 9662 0 /bin/mkdir -p ./main run 9663 0 ./run [...] ``` execsnoop prints one line of output for each new process. Check for short-lived processes. These can consume CPU resources, but not show up in most monitoring tools that periodically take snapshots of which processes are running. It works by tracing exec(), not the fork(), so it will catch many types of new processes but not all (eg, it won't see an application launching working processes, that doesn't exec() anything else). More [examples](../tools/execsnoop_example.txt). #### 1.2. opensnoop ``` # ./opensnoop PID COMM FD ERR PATH 1565 redis-server 5 0 /proc/1565/stat 1565 redis-server 5 0 /proc/1565/stat 1565 redis-server 5 0 /proc/1565/stat 1603 snmpd 9 0 /proc/net/dev 1603 snmpd 11 0 /proc/net/if_inet6 1603 snmpd -1 2 /sys/class/net/eth0/device/vendor 1603 snmpd 11 0 /proc/sys/net/ipv4/neigh/eth0/retrans_time_ms 1603 snmpd 11 0 /proc/sys/net/ipv6/neigh/eth0/retrans_time_ms 1603 snmpd 11 0 /proc/sys/net/ipv6/conf/eth0/forwarding [...] ``` opensnoop prints one line of output for each open() syscall, including details. Files that are opened can tell you a lot about how applications work: identifying their data files, config files, and log files. Sometimes applications can misbehave, and perform poorly, when they are constantly attempting to read files that do not exist. opensnoop gives you a quick look. More [examples](../tools/opensnoop_example.txt). #### 1.3. ext4slower (or btrfs\*, xfs\*, zfs\*) ``` # ./ext4slower Tracing ext4 operations slower than 10 ms TIME COMM PID T BYTES OFF_KB LAT(ms) FILENAME 06:35:01 cron 16464 R 1249 0 16.05 common-auth 06:35:01 cron 16463 R 1249 0 16.04 common-auth 06:35:01 cron 16465 R 1249 0 16.03 common-auth 06:35:01 cron 16465 R 4096 0 10.62 login.defs 06:35:01 cron 16464 R 4096 0 10.61 login.defs ``` ext4slower traces the ext4 file system and times common operations, and then only prints those that exceed a threshold. This is great for identifying or exonerating one type of performance issue: show individually slow disk i/O via the file system. Disks process I/O asynchronously, and it can be difficult to associate latency at that layer with the latency applications experience. Tracing higher up in the kernel stack, at the VFS -> file system interface, will more closely match what an application suffers. Use this tool to identify if file system latency exceeds a given threshold. Similar tools exist in bcc for other file systems: btrfsslower, xfsslower, and zfsslower. There is also fileslower, which works at the VFS layer and traces everything (although at some higher overhead). More [examples](../tools/ext4slower_example.txt). #### 1.4. biolatency ``` # ./biolatency Tracing block device I/O... Hit Ctrl-C to end. ^C usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 1 | | 128 -> 255 : 12 |******** | 256 -> 511 : 15 |********** | 512 -> 1023 : 43 |******************************* | 1024 -> 2047 : 52 |**************************************| 2048 -> 4095 : 47 |********************************** | 4096 -> 8191 : 52 |**************************************| 8192 -> 16383 : 36 |************************** | 16384 -> 32767 : 15 |********** | 32768 -> 65535 : 2 |* | 65536 -> 131071 : 2 |* | ``` biolatency traces disk I/O latency (time from device issue to completion), and when the tool ends (Ctrl-C, or a given interval), it prints a histogram summary of the latency. This is great for understanding disk I/O latency beyond the average times given by tools like iostat. I/O latency outliers will be visible at the end of the distribution, as well as multi-mode distributions. More [examples](../tools/biolatency_example.txt). #### 1.5. biosnoop ``` # ./biosnoop TIME(s) COMM PID DISK T SECTOR BYTES LAT(ms) 0.000004001 supervise 1950 xvda1 W 13092560 4096 0.74 0.000178002 supervise 1950 xvda1 W 13092432 4096 0.61 0.001469001 supervise 1956 xvda1 W 13092440 4096 1.24 0.001588002 supervise 1956 xvda1 W 13115128 4096 1.09 1.022346001 supervise 1950 xvda1 W 13115272 4096 0.98 1.022568002 supervise 1950 xvda1 W 13188496 4096 0.93 [...] ``` biosnoop prints a line of output for each disk I/O, with details including latency (time from device issue to completion). This allows you to examine disk I/O in more detail, and look for time-ordered patterns (eg, reads queueing behind writes). Note that the output will be verbose if your system performs disk I/O at a high rate. More [examples](../tools/biosnoop_example.txt). #### 1.6. cachestat ``` # ./cachestat HITS MISSES DIRTIES READ_HIT% WRITE_HIT% BUFFERS_MB CACHED_MB 1074 44 13 94.9% 2.9% 1 223 2195 170 8 92.5% 6.8% 1 143 182 53 56 53.6% 1.3% 1 143 62480 40960 20480 40.6% 19.8% 1 223 7 2 5 22.2% 22.2% 1 223 348 0 0 100.0% 0.0% 1 223 [...] ``` cachestat prints a one line summary every second (or every custom interval) showing statistics from the file system cache. Use this to identify a low cache hit ratio, and a high rate of misses: which gives one lead for performance tuning. More [examples](../tools/cachestat_example.txt). #### 1.7. tcpconnect ``` # ./tcpconnect PID COMM IP SADDR DADDR DPORT 1479 telnet 4 127.0.0.1 127.0.0.1 23 1469 curl 4 10.201.219.236 54.245.105.25 80 1469 curl 4 10.201.219.236 54.67.101.145 80 1991 telnet 6 ::1 ::1 23 2015 ssh 6 fe80::2000:bff:fe82:3ac fe80::2000:bff:fe82:3ac 22 [...] ``` tcpconnect prints one line of output for every active TCP connection (eg, via connect()), with details including source and destination addresses. Look for unexpected connections that may point to inefficiencies in application configuration, or an intruder. More [examples](../tools/tcpconnect_example.txt). #### 1.8. tcpaccept ``` # ./tcpaccept PID COMM IP RADDR LADDR LPORT 907 sshd 4 192.168.56.1 192.168.56.102 22 907 sshd 4 127.0.0.1 127.0.0.1 22 5389 perl 6 1234:ab12:2040:5020:2299:0:5:0 1234:ab12:2040:5020:2299:0:5:0 7001 [...] ``` tcpaccept prints one line of output for every passive TCP connection (eg, via accept()), with details including source and destination addresses. Look for unexpected connections that may point to inefficiencies in application configuration, or an intruder. More [examples](../tools/tcpaccept_example.txt). #### 1.9. tcpretrans ``` # ./tcpretrans TIME PID IP LADDR:LPORT T> RADDR:RPORT STATE 01:55:05 0 4 10.153.223.157:22 R> 69.53.245.40:34619 ESTABLISHED 01:55:05 0 4 10.153.223.157:22 R> 69.53.245.40:34619 ESTABLISHED 01:55:17 0 4 10.153.223.157:22 R> 69.53.245.40:22957 ESTABLISHED [...] ``` tcprerans prints one line of output for every TCP retransmit packet, with details including source and destination addresses, and kernel state of the TCP connection. TCP retransmissions cause latency and throughput issues. For ESTABLISHED retransmits, look for patterns with networks. For SYN_SENT, this may point to target kernel CPU saturation and kernel packet drops. More [examples](../tools/tcpretrans_example.txt). #### 1.10. runqlat ``` # ./runqlat Tracing run queue latency... Hit Ctrl-C to end. ^C usecs : count distribution 0 -> 1 : 233 |*********** | 2 -> 3 : 742 |************************************ | 4 -> 7 : 203 |********** | 8 -> 15 : 173 |******** | 16 -> 31 : 24 |* | 32 -> 63 : 0 | | 64 -> 127 : 30 |* | 128 -> 255 : 6 | | 256 -> 511 : 3 | | 512 -> 1023 : 5 | | 1024 -> 2047 : 27 |* | 2048 -> 4095 : 30 |* | 4096 -> 8191 : 20 | | 8192 -> 16383 : 29 |* | 16384 -> 32767 : 809 |****************************************| 32768 -> 65535 : 64 |*** | ``` runqlat times how long threads were waiting on the CPU run queues, and prints this as a histogram. This can help quantify time lost waiting for a turn on CPU, during periods of CPU saturation. More [examples](../tools/runqlat_example.txt). #### 1.11. profile ``` # ./profile Sampling at 49 Hertz of all threads by user + kernel stack... Hit Ctrl-C to end. ^C 00007f31d76c3251 [unknown] 47a2c1e752bf47f7 [unknown] - sign-file (8877) 1 ffffffff813d0af8 __clear_user ffffffff813d5277 iov_iter_zero ffffffff814ec5f2 read_iter_zero ffffffff8120be9d __vfs_read ffffffff8120c385 vfs_read ffffffff8120d786 sys_read ffffffff817cc076 entry_SYSCALL_64_fastpath 00007fc5652ad9b0 read - dd (25036) 4 0000000000400542 func_a 0000000000400598 main 00007f12a133e830 __libc_start_main 083e258d4c544155 [unknown] - func_ab (13549) 5 [...] ffffffff8105eb66 native_safe_halt ffffffff8103659e default_idle ffffffff81036d1f arch_cpu_idle ffffffff810bba5a default_idle_call ffffffff810bbd07 cpu_startup_entry ffffffff8104df55 start_secondary - swapper/1 (0) 75 ``` profile is a CPU profiler, which takes samples of stack traces at timed intervals, and prints a summary of unique stack traces and a count of their occurrence. Use this tool to understand the code paths that are consuming CPU resources. More [examples](../tools/profile_example.txt). ### 2. Observatility with Generic Tools In addition to the above tools for performance tuning, below is a checklist for bcc generic tools, first as a list, and in detail: 1. trace 1. argdist 1. funccount These generic tools may be useful to provide visibility to solve your specific problems. #### 2.1. trace ##### Example 1 Suppose you want to track file ownership change. There are three syscalls, `chown`, `fchown` and `lchown` which users can use to change file ownership. The corresponding syscall entry is `SyS_[f|l]chown`. The following command can be used to print out syscall parameters and the calling process user id. You can use `id` command to find the uid of a particular user. ``` $ trace.py \ 'p::SyS_chown "file = %s, to_uid = %d, to_gid = %d, from_uid = %d", arg1, arg2, arg3, $uid' \ 'p::SyS_fchown "fd = %d, to_uid = %d, to_gid = %d, from_uid = %d", arg1, arg2, arg3, $uid' \ 'p::SyS_lchown "file = %s, to_uid = %d, to_gid = %d, from_uid = %d", arg1, arg2, arg3, $uid' PID TID COMM FUNC - 1269255 1269255 python3.6 SyS_lchown file = /tmp/dotsync-usisgezu/tmp, to_uid = 128203, to_gid = 100, from_uid = 128203 1269441 1269441 zstd SyS_chown file = /tmp/dotsync-vic7ygj0/dotsync-package.zst, to_uid = 128203, to_gid = 100, from_uid = 128203 1269255 1269255 python3.6 SyS_lchown file = /tmp/dotsync-a40zd7ev/tmp, to_uid = 128203, to_gid = 100, from_uid = 128203 1269442 1269442 zstd SyS_chown file = /tmp/dotsync-gzp413o_/dotsync-package.zst, to_uid = 128203, to_gid = 100, from_uid = 128203 1269255 1269255 python3.6 SyS_lchown file = /tmp/dotsync-whx4fivm/tmp/.bash_profile, to_uid = 128203, to_gid = 100, from_uid = 128203 ``` ##### Example 2 Suppose you want to count nonvoluntary context switches (`nvcsw`) in your bpf based performance monitoring tools and you do not know what is the proper method. `/proc//status` already tells you the number (`nonvoluntary_ctxt_switches`) for a pid and you can use `trace.py` to do a quick experiment to verify your method. With kernel source code, the `nvcsw` is counted at file `linux/kernel/sched/core.c` function `__schedule` and under condition ``` !(!preempt && prev->state) // i.e., preempt || !prev->state ``` The `__schedule` function is marked as `notrace`, and the best place to evaluate the above condition seems in `sched/sched_switch` tracepoint called inside function `__schedule` and defined in `linux/include/trace/events/sched.h`. `trace.py` already has `args` being the pointer to the tracepoint `TP_STRUCT__entry`. The above condition in function `__schedule` can be represented as ``` args->prev_state == TASK_STATE_MAX || args->prev_state == 0 ``` The below command can be used to count the involuntary context switches (per process or per pid) and compare to `/proc//status` or `/proc//task//status` for correctness, as in typical cases, involuntary context switches are not very common. ``` $ trace.py -p 1134138 't:sched:sched_switch (args->prev_state == TASK_STATE_MAX || args->prev_state == 0)' PID TID COMM FUNC 1134138 1134140 contention_test sched_switch 1134138 1134142 contention_test sched_switch ... $ trace.py -L 1134140 't:sched:sched_switch (args->prev_state == TASK_STATE_MAX || args->prev_state == 0)' PID TID COMM FUNC 1134138 1134140 contention_test sched_switch 1134138 1134140 contention_test sched_switch ... ``` ##### Example 3 This example is related to issue [1231](https://github.com/iovisor/bcc/issues/1231) and [1516](https://github.com/iovisor/bcc/issues/1516) where uprobe does not work at all in certain cases. First, you can do a `strace` as below ``` $ strace trace.py 'r:bash:readline "%s", retval' ... perf_event_open(0x7ffd968212f0, -1, 0, -1, 0x8 /* PERF_FLAG_??? */) = -1 EIO (Input/output error) ... ``` The `perf_event_open` syscall returns `-EIO`. Digging into kernel uprobe related codes in `/kernel/trace` and `/kernel/events` directories to search `EIO`, the function `uprobe_register` is the most suspicious. Let us find whether this function is called or not and what is the return value if it is called. In one terminal using the following command to print out the return value of uprobe_register, ``` $ trace.py 'r::uprobe_register "ret = %d", retval' ``` In another terminal run the same bash uretprobe tracing example, and you should get ``` $ trace.py 'r::uprobe_register "ret = %d", retval' PID TID COMM FUNC - 1041401 1041401 python2.7 uprobe_register ret = -5 ``` The `-5` error code is EIO. This confirms that the following code in function `uprobe_register` is the most suspicious culprit. ``` if (!inode->i_mapping->a_ops->readpage && !shmem_mapping(inode->i_mapping)) return -EIO; ``` The `shmem_mapping` function is defined as ``` bool shmem_mapping(struct address_space *mapping) { return mapping->a_ops == &shmem_aops; } ``` To confirm the theory, find what is `inode->i_mapping->a_ops` with the following command ``` $ trace.py -I 'linux/fs.h' 'p::uprobe_register(struct inode *inode) "a_ops = %llx", inode->i_mapping->a_ops' PID TID COMM FUNC - 814288 814288 python2.7 uprobe_register a_ops = ffffffff81a2adc0 ^C$ grep ffffffff81a2adc0 /proc/kallsyms ffffffff81a2adc0 R empty_aops ``` The kernel symbol `empty_aops` does not have `readpage` defined and hence the above suspicious condition is true. Further examining the kernel source code shows that `overlayfs` does not provide its own `a_ops` while some other file systems (e.g., ext4) define their own `a_ops` (e.g., `ext4_da_aops`), and `ext4_da_aops` defines `readpage`. Hence, uprobe works fine on ext4 while not on overlayfs. More [examples](../tools/trace_example.txt). #### 2.2. argdist More [examples](../tools/argdist_example.txt). #### 2.3. funccount More [examples](../tools/funccount_example.txt). ## Networking To do. bpfcc-0.12.0/docs/tutorial_bcc_python_developer.md000066400000000000000000000720021357404205000222420ustar00rootroot00000000000000# bcc Python Developer Tutorial This tutorial is about developing [bcc](https://github.com/iovisor/bcc) tools and programs using the Python interface. There are two parts: observability then networking. Snippets are taken from various programs in bcc: see their files for licences. Also see the bcc developer's [reference_guide.md](reference_guide.md), and a tutorial for end-users of tools: [tutorial.md](tutorial.md). There is also a lua interface for bcc. ## Observability This observability tutorial contains 17 lessons, and 46 enumerated things to learn. ### Lesson 1. Hello World Start by running [examples/hello_world.py](../examples/hello_world.py), while running some commands (eg, "ls") in another session. It should print "Hello, World!" for new processes. If not, start by fixing bcc: see [INSTALL.md](../INSTALL.md). ``` # ./examples/hello_world.py bash-13364 [002] d... 24573433.052937: : Hello, World! bash-13364 [003] d... 24573436.642808: : Hello, World! [...] ``` Here's the code for hello_world.py: ```Python from bcc import BPF BPF(text='int kprobe__sys_clone(void *ctx) { bpf_trace_printk("Hello, World!\\n"); return 0; }').trace_print() ``` There are six things to learn from this: 1. ```text='...'```: This defines a BPF program inline. The program is written in C. 1. ```kprobe__sys_clone()```: This is a short-cut for kernel dynamic tracing via kprobes. If the C function begins with ``kprobe__``, the rest is treated as a kernel function name to instrument, in this case, ```sys_clone()```. 1. ```void *ctx```: ctx has arguments, but since we aren't using them here, we'll just cast it to ```void *```. 1. ```bpf_trace_printk()```: A simple kernel facility for printf() to the common trace_pipe (/sys/kernel/debug/tracing/trace_pipe). This is ok for some quick examples, but has limitations: 3 args max, 1 %s only, and trace_pipe is globally shared, so concurrent programs will have clashing output. A better interface is via BPF_PERF_OUTPUT(), covered later. 1. ```return 0;```: Necessary formality (if you want to know why, see [#139](https://github.com/iovisor/bcc/issues/139)). 1. ```.trace_print()```: A bcc routine that reads trace_pipe and prints the output. ### Lesson 2. sys_sync() Write a program that traces the sys_sync() kernel function. Print "sys_sync() called" when it runs. Test by running ```sync``` in another session while tracing. The hello_world.py program has everything you need for this. Improve it by printing "Tracing sys_sync()... Ctrl-C to end." when the program first starts. Hint: it's just Python. ### Lesson 3. hello_fields.py This program is in [examples/tracing/hello_fields.py](../examples/tracing/trace_fields.py). Sample output (run commands in another session): ``` # ./examples/tracing/hello_fields.py TIME(s) COMM PID MESSAGE 24585001.174885999 sshd 1432 Hello, World! 24585001.195710000 sshd 15780 Hello, World! 24585001.991976000 systemd-udevd 484 Hello, World! 24585002.276147000 bash 15787 Hello, World! ``` Code: ```Python from bcc import BPF # define BPF program prog = """ int hello(void *ctx) { bpf_trace_printk("Hello, World!\\n"); return 0; } """ # load BPF program b = BPF(text=prog) b.attach_kprobe(event=b.get_syscall_fnname("clone"), fn_name="hello") # header print("%-18s %-16s %-6s %s" % ("TIME(s)", "COMM", "PID", "MESSAGE")) # format output while 1: try: (task, pid, cpu, flags, ts, msg) = b.trace_fields() except ValueError: continue print("%-18.9f %-16s %-6d %s" % (ts, task, pid, msg)) ``` This is similar to hello_world.py, and traces new processes via sys_clone() again, but has a few more things to learn: 1. ```prog =```: This time we declare the C program as a variable, and later refer to it. This is useful if you want to add some string substitutions based on command line arguments. 1. ```hello()```: Now we're just declaring a C function, instead of the ```kprobe__``` shortcut. We'll refer to this later. All C functions declared in the BPF program are expected to be executed on a probe, hence they all need to take a ```pt_reg* ctx``` as first argument. If you need to define some helper function that will not be executed on a probe, they need to be defined as ```static inline``` in order to be inlined by the compiler. Sometimes you would also need to add ```_always_inline``` function attribute to it. 1. ```b.attach_kprobe(event=b.get_syscall_fnname("clone"), fn_name="hello")```: Creates a kprobe for the kernel clone system call function, which will execute our defined hello() function. You can call attach_kprobe() more than once, and attach your C function to multiple kernel functions. 1. ```b.trace_fields()```: Returns a fixed set of fields from trace_pipe. Similar to trace_print(), this is handy for hacking, but for real tooling we should switch to BPF_PERF_OUTPUT(). ### Lesson 4. sync_timing.py Remember the days of sysadmins typing ```sync``` three times on a slow console before ```reboot```, to give the first asynchronous sync time to complete? Then someone thought ```sync;sync;sync``` was clever, to run them all on one line, which became industry practice despite defeating the original purpose! And then sync became synchronous, so more reasons it was silly. Anyway. The following example times how quickly the ```do_sync``` function is called, and prints output if it has been called more recently than one second ago. A ```sync;sync;sync``` will print output for the 2nd and 3rd sync's: ``` # ./examples/tracing/sync_timing.py Tracing for quick sync's... Ctrl-C to end At time 0.00 s: multiple syncs detected, last 95 ms ago At time 0.10 s: multiple syncs detected, last 96 ms ago ``` This program is [examples/tracing/sync_timing.py](../examples/tracing/sync_timing.py): ```Python from __future__ import print_function from bcc import BPF # load BPF program b = BPF(text=""" #include BPF_HASH(last); int do_trace(struct pt_regs *ctx) { u64 ts, *tsp, delta, key = 0; // attempt to read stored timestamp tsp = last.lookup(&key); if (tsp != 0) { delta = bpf_ktime_get_ns() - *tsp; if (delta < 1000000000) { // output if time is less than 1 second bpf_trace_printk("%d\\n", delta / 1000000); } last.delete(&key); } // update stored timestamp ts = bpf_ktime_get_ns(); last.update(&key, &ts); return 0; } """) b.attach_kprobe(event=b.get_syscall_fnname("sync"), fn_name="do_trace") print("Tracing for quick sync's... Ctrl-C to end") # format output start = 0 while 1: (task, pid, cpu, flags, ts, ms) = b.trace_fields() if start == 0: start = ts ts = ts - start print("At time %.2f s: multiple syncs detected, last %s ms ago" % (ts, ms)) ``` Things to learn: 1. ```bpf_ktime_get_ns()```: Returns the time as nanoseconds. 1. ```BPF_HASH(last)```: Creates a BPF map object that is a hash (associative array), called "last". We didn't specify any further arguments, so it defaults to key and value types of u64. 1. ```key = 0```: We'll only store one key/value pair in this hash, where the key is hardwired to zero. 1. ```last.lookup(&key)```: Lookup the key in the hash, and return a pointer to its value if it exists, else NULL. We pass the key in as an address to a pointer. 1. ```last.delete(&key)```: Delete the key from the hash. This is currently required because of [a kernel bug in `.update()`](https://git.kernel.org/cgit/linux/kernel/git/davem/net.git/commit/?id=a6ed3ea65d9868fdf9eff84e6fe4f666b8d14b02). 1. ```last.update(&key, &ts)```: Associate the value in the 2nd argument to the key, overwriting any previous value. This records the timestamp. ### Lesson 5. sync_count.py Modify the sync_timing.py program (prior lesson) to store the count of all kernel sync system calls (both fast and slow), and print it with the output. This count can be recorded in the BPF program by adding a new key index to the existing hash. ### Lesson 6. disksnoop.py Browse the [examples/tracing/disksnoop.py](../examples/tracing/disksnoop.py) program to see what is new. Here is some sample output: ``` # ./disksnoop.py TIME(s) T BYTES LAT(ms) 16458043.436012 W 4096 3.13 16458043.437326 W 4096 4.44 16458044.126545 R 4096 42.82 16458044.129872 R 4096 3.24 [...] ``` And a code snippet: ```Python [...] REQ_WRITE = 1 # from include/linux/blk_types.h # load BPF program b = BPF(text=""" #include #include BPF_HASH(start, struct request *); void trace_start(struct pt_regs *ctx, struct request *req) { // stash start timestamp by request ptr u64 ts = bpf_ktime_get_ns(); start.update(&req, &ts); } void trace_completion(struct pt_regs *ctx, struct request *req) { u64 *tsp, delta; tsp = start.lookup(&req); if (tsp != 0) { delta = bpf_ktime_get_ns() - *tsp; bpf_trace_printk("%d %x %d\\n", req->__data_len, req->cmd_flags, delta / 1000); start.delete(&req); } } """) b.attach_kprobe(event="blk_start_request", fn_name="trace_start") b.attach_kprobe(event="blk_mq_start_request", fn_name="trace_start") b.attach_kprobe(event="blk_account_io_completion", fn_name="trace_completion") [...] ``` Things to learn: 1. ```REQ_WRITE```: We're defining a kernel constant in the Python program because we'll use it there later. If we were using REQ_WRITE in the BPF program, it should just work (without needing to be defined) with the appropriate #includes. 1. ```trace_start(struct pt_regs *ctx, struct request *req)```: This function will later be attached to kprobes. The arguments to kprobe functions are ```struct pt_regs *ctx```, for registers and BPF context, and then the actual arguments to the function. We'll attach this to blk_start_request(), where the first argument is ```struct request *```. 1. ```start.update(&req, &ts)```: We're using the pointer to the request struct as a key in our hash. What? This is commonplace in tracing. Pointers to structs turn out to be great keys, as they are unique: two structs can't have the same pointer address. (Just be careful about when it gets free'd and reused.) So what we're really doing is tagging the request struct, which describes the disk I/O, with our own timestamp, so that we can time it. There's two common keys used for storing timestamps: pointers to structs, and, thread IDs (for timing function entry to return). 1. ```req->__data_len```: We're dereferencing members of ```struct request```. See its definition in the kernel source for what members are there. bcc actually rewrites these expressions to be a series of ```bpf_probe_read()``` calls. Sometimes bcc can't handle a complex dereference, and you need to call ```bpf_probe_read()``` directly. This is a pretty interesting program, and if you can understand all the code, you'll understand many important basics. We're still using the bpf_trace_printk() hack, so let's fix that next. ### Lesson 7. hello_perf_output.py Let's finally stop using bpf_trace_printk() and use the proper BPF_PERF_OUTPUT() interface. This will also mean we stop getting the free trace_field() members like PID and timestamp, and will need to fetch them directly. Sample output while commands are run in another session: ``` # ./hello_perf_output.py TIME(s) COMM PID MESSAGE 0.000000000 bash 22986 Hello, perf_output! 0.021080275 systemd-udevd 484 Hello, perf_output! 0.021359520 systemd-udevd 484 Hello, perf_output! 0.021590610 systemd-udevd 484 Hello, perf_output! [...] ``` Code is [examples/tracing/hello_perf_output.py](../examples/tracing/hello_perf_output.py): ```Python from bcc import BPF # define BPF program prog = """ #include // define output data structure in C struct data_t { u32 pid; u64 ts; char comm[TASK_COMM_LEN]; }; BPF_PERF_OUTPUT(events); int hello(struct pt_regs *ctx) { struct data_t data = {}; data.pid = bpf_get_current_pid_tgid(); data.ts = bpf_ktime_get_ns(); bpf_get_current_comm(&data.comm, sizeof(data.comm)); events.perf_submit(ctx, &data, sizeof(data)); return 0; } """ # load BPF program b = BPF(text=prog) b.attach_kprobe(event=b.get_syscall_fnname("clone"), fn_name="hello") # header print("%-18s %-16s %-6s %s" % ("TIME(s)", "COMM", "PID", "MESSAGE")) # process event start = 0 def print_event(cpu, data, size): global start event = b["events"].event(data) if start == 0: start = event.ts time_s = (float(event.ts - start)) / 1000000000 print("%-18.9f %-16s %-6d %s" % (time_s, event.comm, event.pid, "Hello, perf_output!")) # loop with callback to print_event b["events"].open_perf_buffer(print_event) while 1: b.perf_buffer_poll() ``` Things to learn: 1. ```struct data_t```: This defines the C struct we'll use to pass data from kernel to user space. 1. ```BPF_PERF_OUTPUT(events)```: This names our output channel "events". 1. ```struct data_t data = {};```: Create an empty data_t struct that we'll then populate. 1. ```bpf_get_current_pid_tgid()```: Returns the process ID in the lower 32 bits (kernel's view of the PID, which in user space is usually presented as the thread ID), and the thread group ID in the upper 32 bits (what user space often thinks of as the PID). By directly setting this to a u32, we discard the upper 32 bits. Should you be presenting the PID or the TGID? For a multi-threaded app, the TGID will be the same, so you need the PID to differentiate them, if that's what you want. It's also a question of expectations for the end user. 1. ```bpf_get_current_comm()```: Populates the first argument address with the current process name. 1. ```events.perf_submit()```: Submit the event for user space to read via a perf ring buffer. 1. ```def print_event()```: Define a Python function that will handle reading events from the ```events``` stream. 1. ```b["events"].event(data)```: Now get the event as a Python object, auto-generated from the C declaration. 1. ```b["events"].open_perf_buffer(print_event)```: Associate the Python ```print_event``` function with the ```events``` stream. 1. ```while 1: b.perf_buffer_poll()```: Block waiting for events. ### Lesson 8. sync_perf_output.py Rewrite sync_timing.py, from a prior lesson, to use ```BPF_PERF_OUTPUT```. ### Lesson 9. bitehist.py The following tool records a histogram of disk I/O sizes. Sample output: ``` # ./bitehist.py Tracing... Hit Ctrl-C to end. ^C kbytes : count distribution 0 -> 1 : 3 | | 2 -> 3 : 0 | | 4 -> 7 : 211 |********** | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 1 | | 128 -> 255 : 800 |**************************************| ``` Code is [examples/tracing/bitehist.py](../examples/tracing/bitehist.py): ```Python from __future__ import print_function from bcc import BPF from time import sleep # load BPF program b = BPF(text=""" #include #include BPF_HISTOGRAM(dist); int kprobe__blk_account_io_completion(struct pt_regs *ctx, struct request *req) { dist.increment(bpf_log2l(req->__data_len / 1024)); return 0; } """) # header print("Tracing... Hit Ctrl-C to end.") # trace until Ctrl-C try: sleep(99999999) except KeyboardInterrupt: print() # output b["dist"].print_log2_hist("kbytes") ``` A recap from earlier lessons: - ```kprobe__```: This prefix means the rest will be treated as a kernel function name that will be instrumented using kprobe. - ```struct pt_regs *ctx, struct request *req```: Arguments to kprobe. The ```ctx``` is registers and BPF context, the ```req``` is the first argument to the instrumented function: ```blk_account_io_completion()```. - ```req->__data_len```: Dereferencing that member. New things to learn: 1. ```BPF_HISTOGRAM(dist)```: Defines a BPF map object that is a histogram, and names it "dist". 1. ```dist.increment()```: Increments the histogram bucket index provided as first argument by one by default. Optionally, custom increments can be passed as the second argument. 1. ```bpf_log2l()```: Returns the log-2 of the provided value. This becomes the index of our histogram, so that we're constructing a power-of-2 histogram. 1. ```b["dist"].print_log2_hist("kbytes")```: Prints the "dist" histogram as power-of-2, with a column header of "kbytes". The only data transferred from kernel to user space is the bucket counts, making this efficient. ### Lesson 10. disklatency.py Write a program that times disk I/O, and prints a histogram of their latency. Disk I/O instrumentation and timing can be found in the disksnoop.py program from a prior lesson, and histogram code can be found in bitehist.py from a prior lesson. ### Lesson 11. vfsreadlat.py This example is split into separate Python and C files. Example output: ``` # ./vfsreadlat.py 1 Tracing... Hit Ctrl-C to end. usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 2 |*********** | 4 -> 7 : 7 |****************************************| 8 -> 15 : 4 |********************** | usecs : count distribution 0 -> 1 : 29 |****************************************| 2 -> 3 : 28 |************************************** | 4 -> 7 : 4 |***** | 8 -> 15 : 8 |*********** | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 0 | | 128 -> 255 : 0 | | 256 -> 511 : 2 |** | 512 -> 1023 : 0 | | 1024 -> 2047 : 0 | | 2048 -> 4095 : 0 | | 4096 -> 8191 : 4 |***** | 8192 -> 16383 : 6 |******** | 16384 -> 32767 : 9 |************ | 32768 -> 65535 : 6 |******** | 65536 -> 131071 : 2 |** | usecs : count distribution 0 -> 1 : 11 |****************************************| 2 -> 3 : 2 |******* | 4 -> 7 : 10 |************************************ | 8 -> 15 : 8 |***************************** | 16 -> 31 : 1 |*** | 32 -> 63 : 2 |******* | [...] ``` Browse the code in [examples/tracing/vfsreadlat.py](../examples/tracing/vfsreadlat.py) and [examples/tracing/vfsreadlat.c](../examples/tracing/vfsreadlat.c). Things to learn: 1. ```b = BPF(src_file = "vfsreadlat.c")```: Read the BPF C program from a separate source file. 1. ```b.attach_kretprobe(event="vfs_read", fn_name="do_return")```: Attaches the BPF C function ```do_return()``` to the return of the kernel function ```vfs_read()```. This is a kretprobe: instrumenting the return from a function, rather than its entry. 1. ```b["dist"].clear()```: Clears the histogram. ### Lesson 12. urandomread.py Tracing while a ```dd if=/dev/urandom of=/dev/null bs=8k count=5``` is run: ``` # ./urandomread.py TIME(s) COMM PID GOTBITS 24652832.956994001 smtp 24690 384 24652837.726500999 dd 24692 65536 24652837.727111001 dd 24692 65536 24652837.727703001 dd 24692 65536 24652837.728294998 dd 24692 65536 24652837.728888001 dd 24692 65536 ``` Hah! I caught smtp by accident. Code is [examples/tracing/urandomread.py](../examples/tracing/urandomread.py): ```Python from __future__ import print_function from bcc import BPF # load BPF program b = BPF(text=""" TRACEPOINT_PROBE(random, urandom_read) { // args is from /sys/kernel/debug/tracing/events/random/urandom_read/format bpf_trace_printk("%d\\n", args->got_bits); return 0; } """) # header print("%-18s %-16s %-6s %s" % ("TIME(s)", "COMM", "PID", "GOTBITS")) # format output while 1: try: (task, pid, cpu, flags, ts, msg) = b.trace_fields() except ValueError: continue print("%-18.9f %-16s %-6d %s" % (ts, task, pid, msg)) ``` Things to learn: 1. ```TRACEPOINT_PROBE(random, urandom_read)```: Instrument the kernel tracepoint ```random:urandom_read```. These have a stable API, and thus are recommend to use instead of kprobes, wherever possible. You can run ```perf list``` for a list of tracepoints. Linux >= 4.7 is required to attach BPF programs to tracepoints. 1. ```args->got_bits```: ```args``` is auto-populated to be a structure of the tracepoint arguments. The comment above says where you can see that structure. Eg: ``` # cat /sys/kernel/debug/tracing/events/random/urandom_read/format name: urandom_read ID: 972 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 got_bits; offset:8; size:4; signed:1; field:int pool_left; offset:12; size:4; signed:1; field:int input_left; offset:16; size:4; signed:1; print fmt: "got_bits %d nonblocking_pool_entropy_left %d input_entropy_left %d", REC->got_bits, REC->pool_left, REC->input_left ``` In this case, we were printing the ```got_bits``` member. ### Lesson 13. disksnoop.py fixed Convert disksnoop.py from a previous lesson to use the ```block:block_rq_issue``` and ```block:block_rq_complete``` tracepoints. ### Lesson 14. strlen_count.py This program instruments a user-level function, the ```strlen()``` library function, and frequency counts its string argument. Example output: ``` # ./strlen_count.py Tracing strlen()... Hit Ctrl-C to end. ^C COUNT STRING 1 " " 1 "/bin/ls" 1 "." 1 "cpudist.py.1" 1 ".bashrc" 1 "ls --color=auto" 1 "key_t" [...] 10 "a7:~# " 10 "/root" 12 "LC_ALL" 12 "en_US.UTF-8" 13 "en_US.UTF-8" 20 "~" 70 "#%^,~:-=?+/}" 340 "\x01\x1b]0;root@bgregg-test: ~\x07\x02root@bgregg-test:~# " ``` These are various strings that are being processed by this library function while tracing, along with their frequency counts. ```strlen()``` was called on "LC_ALL" 12 times, for example. Code is [examples/tracing/strlen_count.py](../examples/tracing/strlen_count.py): ```Python from __future__ import print_function from bcc import BPF from time import sleep # load BPF program b = BPF(text=""" #include struct key_t { char c[80]; }; BPF_HASH(counts, struct key_t); int count(struct pt_regs *ctx) { if (!PT_REGS_PARM1(ctx)) return 0; struct key_t key = {}; u64 zero = 0, *val; bpf_probe_read(&key.c, sizeof(key.c), (void *)PT_REGS_PARM1(ctx)); // could also use `counts.increment(key)` val = counts.lookup_or_try_init(&key, &zero); if (val) { (*val)++; } return 0; }; """) b.attach_uprobe(name="c", sym="strlen", fn_name="count") # header print("Tracing strlen()... Hit Ctrl-C to end.") # sleep until Ctrl-C try: sleep(99999999) except KeyboardInterrupt: pass # print output print("%10s %s" % ("COUNT", "STRING")) counts = b.get_table("counts") for k, v in sorted(counts.items(), key=lambda counts: counts[1].value): print("%10d \"%s\"" % (v.value, k.c.encode('string-escape'))) ``` Things to learn: 1. ```PT_REGS_PARM1(ctx)```: This fetches the first argument to ```strlen()```, which is the string. 1. ```b.attach_uprobe(name="c", sym="strlen", fn_name="count")```: Attach to library "c" (if this is the main program, use its pathname), instrument the user-level function ```strlen()```, and on execution call our C function ```count()```. ### Lesson 15. nodejs_http_server.py This program instruments a user statically-defined tracing (USDT) probe, which is the user-level version of a kernel tracepoint. Sample output: ``` # ./nodejs_http_server.py 24728 TIME(s) COMM PID ARGS 24653324.561322998 node 24728 path:/index.html 24653335.343401998 node 24728 path:/images/welcome.png 24653340.510164998 node 24728 path:/images/favicon.png ``` Relevant code from [examples/tracing/nodejs_http_server.py](../examples/tracing/nodejs_http_server.py): ```Python from __future__ import print_function from bcc import BPF, USDT import sys if len(sys.argv) < 2: print("USAGE: nodejs_http_server PID") exit() pid = sys.argv[1] debug = 0 # load BPF program bpf_text = """ #include int do_trace(struct pt_regs *ctx) { uint64_t addr; char path[128]={0}; bpf_usdt_readarg(6, ctx, &addr); bpf_probe_read(&path, sizeof(path), (void *)addr); bpf_trace_printk("path:%s\\n", path); return 0; }; """ # enable USDT probe from given PID u = USDT(pid=int(pid)) u.enable_probe(probe="http__server__request", fn_name="do_trace") if debug: print(u.get_text()) print(bpf_text) # initialize BPF b = BPF(text=bpf_text, usdt_contexts=[u]) ``` Things to learn: 1. ```bpf_usdt_readarg(6, ctx, &addr)```: Read the address of argument 6 from the USDT probe into ```addr```. 1. ```bpf_probe_read(&path, sizeof(path), (void *)addr)```: Now the string ```addr``` points to into our ```path``` variable. 1. ```u = USDT(pid=int(pid))```: Initialize USDT tracing for the given PID. 1. ```u.enable_probe(probe="http__server__request", fn_name="do_trace")```: Attach our ```do_trace()``` BPF C function to the Node.js ```http__server__request``` USDT probe. 1. ```b = BPF(text=bpf_text, usdt_contexts=[u])```: Need to pass in our USDT object, ```u```, to BPF object creation. ### Lesson 16. task_switch.c This is an older tutorial included as a bonus lesson. Use this for recap and to reinforce what you've already learned. This is a slightly more complex tracing example than Hello World. This program will be invoked for every task change in the kernel, and record in a BPF map the new and old pids. The C program below introduces a new concept: the prev argument. This argument is treated specially by the BCC frontend, such that accesses to this variable are read from the saved context that is passed by the kprobe infrastructure. The prototype of the args starting from position 1 should match the prototype of the kernel function being kprobed. If done so, the program will have seamless access to the function parameters. ```c #include #include struct key_t { u32 prev_pid; u32 curr_pid; }; BPF_HASH(stats, struct key_t, u64, 1024); int count_sched(struct pt_regs *ctx, struct task_struct *prev) { struct key_t key = {}; u64 zero = 0, *val; key.curr_pid = bpf_get_current_pid_tgid(); key.prev_pid = prev->pid; // could also use `stats.increment(key);` val = stats.lookup_or_try_init(&key, &zero); if (val) { (*val)++; } return 0; } ``` The userspace component loads the file shown above, and attaches it to the `finish_task_switch` kernel function. The `[]` operator of the BPF object gives access to each BPF_HASH in the program, allowing pass-through access to the values residing in the kernel. Use the object as you would any other python dict object: read, update, and deletes are all allowed. ```python from bcc import BPF from time import sleep b = BPF(src_file="task_switch.c") b.attach_kprobe(event="finish_task_switch", fn_name="count_sched") # generate many schedule events for i in range(0, 100): sleep(0.01) for k, v in b["stats"].items(): print("task_switch[%5d->%5d]=%u" % (k.prev_pid, k.curr_pid, v.value)) ``` These programs can be found in the files [examples/tracing/task_switch.c](../examples/tracing/task_switch.c) and [examples/tracing/task_switch.py](../examples/tracing/task_switch.py) respectively. ### Lesson 17. Further Study For further study, see Sasha Goldshtein's [linux-tracing-workshop](https://github.com/goldshtn/linux-tracing-workshop), which contains additional labs. There are also many tools in bcc /tools to study. Please read [CONTRIBUTING-SCRIPTS.md](../CONTRIBUTING-SCRIPTS.md) if you wish to contrubite tools to bcc. At the bottom of the main [README.md](../README.md), you'll also find methods for contacting us. Good luck, and happy tracing! ## Networking To do. bpfcc-0.12.0/examples/000077500000000000000000000000001357404205000144655ustar00rootroot00000000000000bpfcc-0.12.0/examples/CMakeLists.txt000066400000000000000000000004161357404205000172260ustar00rootroot00000000000000set(EXAMPLE_PROGRAMS hello_world.py) install(PROGRAMS ${EXAMPLE_PROGRAMS} DESTINATION share/bcc/examples) if(ENABLE_CLANG_JIT) if(ENABLE_USDT) add_subdirectory(cpp) endif(ENABLE_USDT) add_subdirectory(lua) add_subdirectory(networking) add_subdirectory(tracing) endif() bpfcc-0.12.0/examples/cpp/000077500000000000000000000000001357404205000152475ustar00rootroot00000000000000bpfcc-0.12.0/examples/cpp/CGroupTest.cc000066400000000000000000000043241357404205000176200ustar00rootroot00000000000000/* * CGroupTest Demonstrate how to use BPF cgroup API to collect file open event * * Basic example of cgroup and BPF kprobes. * * USAGE: CGroupTest cgroup2_path * * EXAMPLES: * 1. Create a directory under cgroup2 mountpoint: * $ sudo mkdir /sys/fs/cgroup/unified/test * 2. Add current bash into the testing cgroup: * $ sudo echo $$ | sudo tee -a /sys/fs/cgroup/unified/test/cgroup.procs * 3. Open another bash window, and start CGroupTest as: * $ sudo ./examples/cpp/CGroupTest /sys/fs/cgroup/unified/test * 4. Run file open activity from previous bash window should be printed. * * Copyright (c) Jinshan Xiong * Licensed under the Apache License, Version 2.0 (the "License") */ #include #include #include #include #include #include #include "BPF.h" const std::string BPF_PROGRAM = R"( #include #include #include BPF_CGROUP_ARRAY(cgroup, 1); int on_vfs_open(struct pt_regs *ctx, struct path *path) { if (cgroup.check_current_task(0) > 0) bpf_trace_printk("file '%s' was opened!\n", path->dentry->d_name.name); return 0; } )"; int main(int argc, char** argv) { if (argc != 2) { std::cerr << argv[0] << ": requires _one_ cgroup path" << std::endl; return 1; } ebpf::BPF bpf; auto init_res = bpf.init(BPF_PROGRAM); if (init_res.code() != 0) { std::cerr << init_res.msg() << std::endl; return 1; } auto cgroup_array = bpf.get_cgroup_array("cgroup"); auto update_res = cgroup_array.update_value(0, argv[1]); if (update_res.code() != 0) { std::cerr << update_res.msg() << std::endl; return 1; } auto attach_res = bpf.attach_kprobe("vfs_open", "on_vfs_open"); if (attach_res.code() != 0) { std::cerr << attach_res.msg() << std::endl; return 1; } std::ifstream pipe("/sys/kernel/debug/tracing/trace_pipe"); std::string line; std::cout << "Started tracing open event, hit Ctrl-C to terminate." << std::endl; while (std::getline(pipe, line)) std::cout << line << std::endl; auto detach_res = bpf.detach_kprobe("vfs_open"); if (detach_res.code() != 0) { std::cerr << detach_res.msg() << std::endl; return 1; } return 0; } bpfcc-0.12.0/examples/cpp/CMakeLists.txt000066400000000000000000000036571357404205000200220ustar00rootroot00000000000000# Copyright (c) Facebook, Inc. # Licensed under the Apache License, Version 2.0 (the "License") include_directories(${CMAKE_SOURCE_DIR}/src/cc) include_directories(${CMAKE_SOURCE_DIR}/src/cc/api) include_directories(${CMAKE_SOURCE_DIR}/src/cc/libbpf/include/uapi) option(INSTALL_CPP_EXAMPLES "Install C++ examples. Those binaries are statically linked and can take plenty of disk space" OFF) add_executable(HelloWorld HelloWorld.cc) target_link_libraries(HelloWorld bcc-static) add_executable(CPUDistribution CPUDistribution.cc) target_link_libraries(CPUDistribution bcc-static) add_executable(RecordMySQLQuery RecordMySQLQuery.cc) target_link_libraries(RecordMySQLQuery bcc-static) add_executable(TCPSendStack TCPSendStack.cc) target_link_libraries(TCPSendStack bcc-static) add_executable(RandomRead RandomRead.cc) target_link_libraries(RandomRead bcc-static) add_executable(LLCStat LLCStat.cc) target_link_libraries(LLCStat bcc-static) add_executable(FollyRequestContextSwitch FollyRequestContextSwitch.cc) target_link_libraries(FollyRequestContextSwitch bcc-static) add_executable(UseExternalMap UseExternalMap.cc) target_link_libraries(UseExternalMap bcc-static) add_executable(CGroupTest CGroupTest.cc) target_link_libraries(CGroupTest bcc-static) if(INSTALL_CPP_EXAMPLES) install (TARGETS HelloWorld DESTINATION share/bcc/examples/cpp) install (TARGETS CPUDistribution DESTINATION share/bcc/examples/cpp) install (TARGETS RecordMySQLQuery DESTINATION share/bcc/examples/cpp) install (TARGETS TCPSendStack DESTINATION share/bcc/examples/cpp) install (TARGETS RandomRead DESTINATION share/bcc/examples/cpp) install (TARGETS LLCStat DESTINATION share/bcc/examples/cpp) install (TARGETS FollyRequestContextSwitch DESTINATION share/bcc/examples/cpp) install (TARGETS UseExternalMap DESTINATION share/bcc/examples/cpp) install (TARGETS CGroupTest DESTINATION share/bcc/examples/cpp) endif(INSTALL_CPP_EXAMPLES) add_subdirectory(pyperf) bpfcc-0.12.0/examples/cpp/CPUDistribution.cc000066400000000000000000000047071357404205000206150ustar00rootroot00000000000000/* * CPUDistribution Show load distribution across CPU cores during a period of * time. For Linux, uses BCC, eBPF. Embedded C. * * Basic example of BCC and kprobes. * * USAGE: CPUDistribution [duration] * * Copyright (c) Facebook, Inc. * Licensed under the Apache License, Version 2.0 (the "License") */ #include #include #include #include #include #include "BPF.h" const std::string BPF_PROGRAM = R"( #include #include BPF_HASH(pid_to_cpu, pid_t, int); BPF_HASH(pid_to_ts, pid_t, uint64_t); BPF_HASH(cpu_time, int, uint64_t); int task_switch_event(struct pt_regs *ctx, struct task_struct *prev) { pid_t prev_pid = prev->pid; int* prev_cpu = pid_to_cpu.lookup(&prev_pid); uint64_t* prev_ts = pid_to_ts.lookup(&prev_pid); pid_t cur_pid = bpf_get_current_pid_tgid(); int cur_cpu = bpf_get_smp_processor_id(); uint64_t cur_ts = bpf_ktime_get_ns(); uint64_t this_cpu_time = 0; if (prev_ts) { pid_to_ts.delete(&prev_pid); this_cpu_time = (cur_ts - *prev_ts); } if (prev_cpu) { pid_to_cpu.delete(&prev_pid); if (this_cpu_time > 0) { int cpu_key = *prev_cpu; uint64_t* history_time = cpu_time.lookup(&cpu_key); if (history_time) this_cpu_time += *history_time; cpu_time.update(&cpu_key, &this_cpu_time); } } pid_to_cpu.update(&cur_pid, &cur_cpu); pid_to_ts.update(&cur_pid, &cur_ts); return 0; } )"; int main(int argc, char** argv) { ebpf::BPF bpf; auto init_res = bpf.init(BPF_PROGRAM); if (init_res.code() != 0) { std::cerr << init_res.msg() << std::endl; return 1; } auto attach_res = bpf.attach_kprobe("finish_task_switch", "task_switch_event"); if (attach_res.code() != 0) { std::cerr << attach_res.msg() << std::endl; return 1; } int probe_time = 10; if (argc == 2) { probe_time = atoi(argv[1]); } std::cout << "Probing for " << probe_time << " seconds" << std::endl; sleep(probe_time); auto table = bpf.get_hash_table("cpu_time"); auto num_cores = sysconf(_SC_NPROCESSORS_ONLN); for (int i = 0; i < num_cores; i++) { std::cout << "CPU " << std::setw(2) << i << " worked for "; std::cout << (table[i] / 1000000.0) << " ms." << std::endl; } auto detach_res = bpf.detach_kprobe("finish_task_switch"); if (detach_res.code() != 0) { std::cerr << detach_res.msg() << std::endl; return 1; } return 0; } bpfcc-0.12.0/examples/cpp/FollyRequestContextSwitch.cc000066400000000000000000000062561357404205000227540ustar00rootroot00000000000000/* * FollyRequestContextSwitch Monitor RequestContext switch events for any binary * uses the class from [folly](http://bit.ly/2h6S1yx). * For Linux, uses BCC, eBPF. Embedded C. * * Basic example of using USDT with BCC. * * USAGE: FollyRequestContextSwitch PATH_TO_BINARY * * Copyright (c) Facebook, Inc. * Licensed under the Apache License, Version 2.0 (the "License") */ #include #include #include #include #include "BPF.h" const std::string BPF_PROGRAM = R"( #include #include struct event_t { int pid; char name[16]; uint64_t old_addr; uint64_t new_addr; }; BPF_PERF_OUTPUT(events); int on_context_switch(struct pt_regs *ctx) { struct event_t event = {}; event.pid = bpf_get_current_pid_tgid(); bpf_get_current_comm(&event.name, sizeof(event.name)); bpf_usdt_readarg(1, ctx, &event.old_addr); bpf_usdt_readarg(2, ctx, &event.new_addr); events.perf_submit(ctx, &event, sizeof(event)); return 0; } )"; // Define the same struct to use in user space. struct event_t { int pid; char name[16]; uint64_t old_addr; uint64_t new_addr; }; void handle_output(void* cb_cookie, void* data, int data_size) { auto event = static_cast(data); std::cout << "PID " << event->pid << " (" << event->name << ") "; std::cout << "folly::RequestContext switch from " << event->old_addr << " to " << event->new_addr << std::endl; } std::function shutdown_handler; void signal_handler(int s) { shutdown_handler(s); } int main(int argc, char** argv) { std::string binary; pid_t pid = -1; for (int i = 0; i < argc; i++) { if (strncmp(argv[i], "--pid", 5) == 0) { pid = std::stoi(argv[i + 1]); i++; continue; } if (strncmp(argv[i], "--binary", 8) == 0) { binary = argv[i + 1]; i++; continue; } } if (pid <= 0 && binary.empty()) { std::cout << "Must specify at least one of binary or PID:" << std::endl << "FollyRequestContextSwitch [--pid PID] [--binary BINARY]" << std::endl; exit(1); } ebpf::USDT u(binary, pid, "folly", "request_context_switch_before", "on_context_switch"); ebpf::BPF* bpf = new ebpf::BPF(); auto init_res = bpf->init(BPF_PROGRAM, {}, {u}); if (init_res.code() != 0) { std::cerr << init_res.msg() << std::endl; return 1; } auto attach_res = bpf->attach_usdt(u); if (attach_res.code() != 0) { std::cerr << attach_res.msg() << std::endl; return 1; } else { std::cout << "Attached to USDT " << u; } auto open_res = bpf->open_perf_buffer("events", &handle_output); if (open_res.code() != 0) { std::cerr << open_res.msg() << std::endl; return 1; } shutdown_handler = [&](int s) { std::cerr << "Terminating..." << std::endl; bpf->detach_usdt(u); delete bpf; exit(0); }; signal(SIGINT, signal_handler); std::cout << "Started tracing, hit Ctrl-C to terminate." << std::endl; auto perf_buffer = bpf->get_perf_buffer("events"); if (perf_buffer) while (true) // 100ms timeout perf_buffer->poll(100); return 0; } bpfcc-0.12.0/examples/cpp/HelloWorld.cc000066400000000000000000000024031357404205000176300ustar00rootroot00000000000000/* * Copyright (c) Facebook, Inc. * Licensed under the Apache License, Version 2.0 (the "License") */ #include #include #include #include #include "BPF.h" const std::string BPF_PROGRAM = R"( int on_sys_clone(void *ctx) { bpf_trace_printk("Hello, World! Here I did a sys_clone call!\n"); return 0; } )"; int main() { ebpf::BPF bpf; auto init_res = bpf.init(BPF_PROGRAM); if (init_res.code() != 0) { std::cerr << init_res.msg() << std::endl; return 1; } std::ifstream pipe("/sys/kernel/debug/tracing/trace_pipe"); std::string line; std::string clone_fnname = bpf.get_syscall_fnname("clone"); auto attach_res = bpf.attach_kprobe(clone_fnname, "on_sys_clone"); if (attach_res.code() != 0) { std::cerr << attach_res.msg() << std::endl; return 1; } while (true) { if (std::getline(pipe, line)) { std::cout << line << std::endl; // Detach the probe if we got at least one line. auto detach_res = bpf.detach_kprobe(clone_fnname); if (detach_res.code() != 0) { std::cerr << detach_res.msg() << std::endl; return 1; } break; } else { std::cout << "Waiting for a sys_clone event" << std::endl; sleep(1); } } return 0; } bpfcc-0.12.0/examples/cpp/LLCStat.cc000066400000000000000000000064371357404205000170360ustar00rootroot00000000000000/* * LLCStat Show LLC hit ratio for each process on each CPU core. * For Linux, uses BCC, eBPF. Embedded C. * * Basic example of BCC timed sampling perf event. * * USAGE: LLCStat [duration] * * Copyright (c) Facebook, Inc. * Licensed under the Apache License, Version 2.0 (the "License") */ #include #include #include #include #include #include "BPF.h" const std::string BPF_PROGRAM = R"( #include #include struct event_t { int cpu; int pid; char name[16]; }; BPF_HASH(ref_count, struct event_t); BPF_HASH(miss_count, struct event_t); static inline __attribute__((always_inline)) void get_key(struct event_t* key) { key->cpu = bpf_get_smp_processor_id(); key->pid = bpf_get_current_pid_tgid(); bpf_get_current_comm(&(key->name), sizeof(key->name)); } int on_cache_miss(struct bpf_perf_event_data *ctx) { struct event_t key = {}; get_key(&key); u64 zero = 0, *val; val = miss_count.lookup_or_try_init(&key, &zero); if (val) { (*val) += ctx->sample_period; } return 0; } int on_cache_ref(struct bpf_perf_event_data *ctx) { struct event_t key = {}; get_key(&key); u64 zero = 0, *val; val = ref_count.lookup_or_try_init(&key, &zero); if (val) { (*val) += ctx->sample_period; } return 0; } )"; struct event_t { int cpu; int pid; char name[16]; }; int main(int argc, char** argv) { ebpf::BPF bpf; auto init_res = bpf.init(BPF_PROGRAM); if (init_res.code() != 0) { std::cerr << init_res.msg() << std::endl; return 1; } auto attach_ref_res = bpf.attach_perf_event(PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_REFERENCES, "on_cache_ref", 100, 0); if (attach_ref_res.code() != 0) { std::cerr << attach_ref_res.msg() << std::endl; return 1; } auto attach_miss_res = bpf.attach_perf_event( PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_MISSES, "on_cache_miss", 100, 0); if (attach_miss_res.code() != 0) { std::cerr << attach_miss_res.msg() << std::endl; return 1; } int probe_time = 10; if (argc == 2) { probe_time = atoi(argv[1]); } std::cout << "Probing for " << probe_time << " seconds" << std::endl; sleep(probe_time); bpf.detach_perf_event(PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_REFERENCES); bpf.detach_perf_event(PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_MISSES); auto refs = bpf.get_hash_table("ref_count"); auto misses = bpf.get_hash_table("miss_count"); for (auto it : refs.get_table_offline()) { uint64_t hit; try { auto miss = misses[it.first]; hit = miss <= it.second ? it.second - miss : 0; } catch (...) { hit = it.second; } double ratio = (double(hit) / double(it.second)) * 100.0; std::cout << "PID " << std::setw(8) << std::setfill(' ') << it.first.pid; std::cout << std::setw(20) << std::setfill(' ') << std::left << " (" + std::string(it.first.name) + ") " << std::right; std::cout << "on CPU " << std::setw(2) << std::setfill(' ') << it.first.cpu; std::cout << " Hit Rate " << std::setprecision(4) << ratio << "% "; std::cout << "(" << hit << "/" << it.second << ")" << std::endl; } return 0; } bpfcc-0.12.0/examples/cpp/RandomRead.cc000066400000000000000000000067451357404205000176060ustar00rootroot00000000000000/* * RandomRead Monitor random number read events. * For Linux, uses BCC, eBPF. Embedded C. * * Basic example of BCC Tracepoint and perf buffer. * * USAGE: RandomRead * * Copyright (c) Facebook, Inc. * Licensed under the Apache License, Version 2.0 (the "License") */ #include #include #include #include "BPF.h" const std::string BPF_PROGRAM = R"( #include #include #ifndef CGROUP_FILTER #define CGROUP_FILTER 0 #endif struct urandom_read_args { // See /sys/kernel/debug/tracing/events/random/urandom_read/format uint64_t common__unused; int got_bits; int pool_left; int input_left; }; struct event_t { int pid; char comm[16]; int cpu; int got_bits; }; BPF_PERF_OUTPUT(events); BPF_CGROUP_ARRAY(cgroup, 1); int on_urandom_read(struct urandom_read_args* attr) { if (CGROUP_FILTER && (cgroup.check_current_task(0) != 1)) return 0; struct event_t event = {}; event.pid = bpf_get_current_pid_tgid(); bpf_get_current_comm(&event.comm, sizeof(event.comm)); event.cpu = bpf_get_smp_processor_id(); event.got_bits = attr->got_bits; events.perf_submit(attr, &event, sizeof(event)); return 0; } )"; // Define the same struct to use in user space. struct event_t { int pid; char comm[16]; int cpu; int got_bits; }; void handle_output(void* cb_cookie, void* data, int data_size) { auto event = static_cast(data); std::cout << "PID: " << event->pid << " (" << event->comm << ") on CPU " << event->cpu << " read " << event->got_bits << " bits" << std::endl; } ebpf::BPF* bpf; void signal_handler(int s) { std::cerr << "Terminating..." << std::endl; delete bpf; exit(0); } void usage(void) { std::cerr << "USAGE: RandomRead [{-r|-u} [cgroup2_path]]" << std::endl; } int main(int argc, char** argv) { if (argc > 3) { usage(); return 1; } bool allow_rlimit = true; if (argc >= 2) { // Set a small rlimit for MEMLOCK struct rlimit rlim_new = {4096, 4096}; setrlimit(RLIMIT_MEMLOCK, &rlim_new); if (strcmp(argv[1], "-r") == 0) { allow_rlimit = false; } else if (strcmp(argv[1], "-u") == 0) { allow_rlimit = true; } else { usage(); return 1; } } std::vector cflags = {}; if (argc == 3) cflags.emplace_back("-DCGROUP_FILTER=1"); bpf = new ebpf::BPF(0, nullptr, true, "", allow_rlimit); auto init_res = bpf->init(BPF_PROGRAM, cflags, {}); if (init_res.code() != 0) { std::cerr << init_res.msg() << std::endl; return 1; } if (argc == 3) { auto cgroup_array = bpf->get_cgroup_array("cgroup"); auto update_res = cgroup_array.update_value(0, argv[2]); if (update_res.code() != 0) { std::cerr << update_res.msg() << std::endl; return 1; } } auto attach_res = bpf->attach_tracepoint("random:urandom_read", "on_urandom_read"); if (attach_res.code() != 0) { std::cerr << attach_res.msg() << std::endl; return 1; } auto open_res = bpf->open_perf_buffer("events", &handle_output); if (open_res.code() != 0) { std::cerr << open_res.msg() << std::endl; return 1; } // done with all initial work, free bcc memory if (bpf->free_bcc_memory()) { std::cerr << "Failed to free llvm/clang memory" << std::endl; return 1; } signal(SIGINT, signal_handler); std::cout << "Started tracing, hit Ctrl-C to terminate." << std::endl; while (true) bpf->poll_perf_buffer("events"); return 0; } bpfcc-0.12.0/examples/cpp/RecordMySQLQuery.cc000066400000000000000000000050671357404205000207200ustar00rootroot00000000000000/* * RecordMySQLQuery Record MySQL queries by probing the alloc_query() function * in mysqld. For Linux, uses BCC, eBPF. Embedded C. * * Basic example of BCC and uprobes. * * Copyright (c) Facebook, Inc. * Licensed under the Apache License, Version 2.0 (the "License") */ #include #include #include #include #include #include "BPF.h" const std::string BPF_PROGRAM = R"( #include struct query_probe_t { uint64_t ts; pid_t pid; char query[100]; }; BPF_HASH(queries, struct query_probe_t, int); int probe_mysql_query(struct pt_regs *ctx, void* thd, char* query, size_t len) { if (query) { struct query_probe_t key = {}; key.ts = bpf_ktime_get_ns(); key.pid = bpf_get_current_pid_tgid(); bpf_probe_read_str(&key.query, sizeof(key.query), query); int one = 1; queries.update(&key, &one); } return 0; } )"; const std::string ALLOC_QUERY_FUNC = "_Z11alloc_queryP3THDPKcj"; // Define the same struct to use in user space. struct query_probe_t { uint64_t ts; pid_t pid; char query[100]; }; int main(int argc, char** argv) { if (argc < 2) { std::cout << "USAGE: RecordMySQLQuery PATH_TO_MYSQLD [duration]" << std::endl; exit(1); } std::string mysql_path(argv[1]); std::cout << "Using mysqld path: " << mysql_path << std::endl; ebpf::BPF bpf; auto init_res = bpf.init(BPF_PROGRAM); if (init_res.code() != 0) { std::cerr << init_res.msg() << std::endl; return 1; } auto attach_res = bpf.attach_uprobe(mysql_path, ALLOC_QUERY_FUNC, "probe_mysql_query"); if (attach_res.code() != 0) { std::cerr << attach_res.msg() << std::endl; return 1; } int probe_time = 10; if (argc >= 3) probe_time = atoi(argv[2]); std::cout << "Probing for " << probe_time << " seconds" << std::endl; sleep(probe_time); auto table_handle = bpf.get_hash_table("queries"); auto table = table_handle.get_table_offline(); std::sort( table.begin(), table.end(), [](std::pair a, std::pair b) { return a.first.ts < b.first.ts; }); std::cout << table.size() << " queries recorded:" << std::endl; for (auto it : table) { std::cout << "Time: " << it.first.ts << " PID: " << it.first.pid << " Query: " << it.first.query << std::endl; } auto detach_res = bpf.detach_uprobe(mysql_path, ALLOC_QUERY_FUNC); if (detach_res.code() != 0) { std::cerr << detach_res.msg() << std::endl; return 1; } return 0; } bpfcc-0.12.0/examples/cpp/TCPSendStack.cc000066400000000000000000000071251357404205000200110ustar00rootroot00000000000000/* * TCPSendStack Summarize tcp_sendmsg() calling stack traces. * For Linux, uses BCC, eBPF. Embedded C. * * Basic example of BCC in-kernel stack trace dedup. * * USAGE: TCPSendStack [duration] * * Copyright (c) Facebook, Inc. * Licensed under the Apache License, Version 2.0 (the "License") */ #include #include #include #include "BPF.h" const std::string BPF_PROGRAM = R"( #include #include struct stack_key_t { int pid; char name[16]; int user_stack; int kernel_stack; }; BPF_STACK_TRACE(stack_traces, 16384); BPF_HASH(counts, struct stack_key_t, uint64_t); int on_tcp_send(struct pt_regs *ctx) { struct stack_key_t key = {}; key.pid = bpf_get_current_pid_tgid() >> 32; bpf_get_current_comm(&key.name, sizeof(key.name)); key.kernel_stack = stack_traces.get_stackid(ctx, 0); key.user_stack = stack_traces.get_stackid(ctx, BPF_F_USER_STACK); u64 zero = 0, *val; val = counts.lookup_or_try_init(&key, &zero); if (val) { (*val)++; } return 0; } )"; // Define the same struct to use in user space. struct stack_key_t { int pid; char name[16]; int user_stack; int kernel_stack; }; int main(int argc, char** argv) { ebpf::BPF bpf; auto init_res = bpf.init(BPF_PROGRAM); if (init_res.code() != 0) { std::cerr << init_res.msg() << std::endl; return 1; } auto attach_res = bpf.attach_kprobe("tcp_sendmsg", "on_tcp_send"); if (attach_res.code() != 0) { std::cerr << attach_res.msg() << std::endl; return 1; } int probe_time = 10; if (argc == 2) { probe_time = atoi(argv[1]); } std::cout << "Probing for " << probe_time << " seconds" << std::endl; sleep(probe_time); auto detach_res = bpf.detach_kprobe("tcp_sendmsg"); if (detach_res.code() != 0) { std::cerr << detach_res.msg() << std::endl; return 1; } auto table = bpf.get_hash_table("counts").get_table_offline(); std::sort( table.begin(), table.end(), [](std::pair a, std::pair b) { return a.second < b.second; }); auto stacks = bpf.get_stack_table("stack_traces"); int lost_stacks = 0; for (auto it : table) { std::cout << "PID: " << it.first.pid << " (" << it.first.name << ") " << "made " << it.second << " TCP sends on following stack: " << std::endl; if (it.first.kernel_stack >= 0) { std::cout << " Kernel Stack:" << std::endl; auto syms = stacks.get_stack_symbol(it.first.kernel_stack, -1); for (auto sym : syms) std::cout << " " << sym << std::endl; } else { // -EFAULT normally means the stack is not availiable and not an error if (it.first.kernel_stack != -EFAULT) { lost_stacks++; std::cout << " [Lost Kernel Stack" << it.first.kernel_stack << "]" << std::endl; } } if (it.first.user_stack >= 0) { std::cout << " User Stack:" << std::endl; auto syms = stacks.get_stack_symbol(it.first.user_stack, it.first.pid); for (auto sym : syms) std::cout << " " << sym << std::endl; } else { // -EFAULT normally means the stack is not availiable and not an error if (it.first.user_stack != -EFAULT) { lost_stacks++; std::cout << " [Lost User Stack " << it.first.user_stack << "]" << std::endl; } } } if (lost_stacks > 0) std::cout << "Total " << lost_stacks << " stack-traces lost due to " << "hash collision or stack table full" << std::endl; return 0; } bpfcc-0.12.0/examples/cpp/UseExternalMap.cc000066400000000000000000000075651357404205000204700ustar00rootroot00000000000000/* * UseExternalMap shows how to access an external map through * C++ interface. The external map could be a pinned map. * This example simulates the pinned map through a locally * created map by calling libbpf bcc_create_map. * * Copyright (c) Facebook, Inc. * Licensed under the Apache License, Version 2.0 (the "License") */ #include #include #include "BPF.h" // Used by C++ get hash_table struct sched_switch_info { int prev_pid; int next_pid; char prev_comm[16]; char next_comm[16]; }; #define CHECK(condition, msg) \ ({ \ if (condition) { \ std::cerr << msg << std::endl; \ return 1; \ } \ }) const std::string BPF_PROGRAM = R"( #include struct sched_switch_info { int prev_pid; int next_pid; char prev_comm[16]; char next_comm[16]; }; BPF_TABLE("extern", u32, u32, control, 1); BPF_HASH(counts, struct sched_switch_info, u32); int on_sched_switch(struct tracepoint__sched__sched_switch *args) { struct sched_switch_info key = {}; u32 zero = 0, *val; /* only do something when control is on */ val = control.lookup(&zero); if (!val || *val == 0) return 0; /* record sched_switch info in counts table */ key.prev_pid = args->prev_pid; key.next_pid = args->next_pid; __builtin_memcpy(&key.prev_comm, args->prev_comm, 16); __builtin_memcpy(&key.next_comm, args->next_comm, 16); val = counts.lookup_or_try_init(&key, &zero); if (val) { (*val)++; } return 0; } )"; static void print_counts(ebpf::BPF *bpfp, std::string msg) { auto counts_table_hdl = bpfp->get_hash_table("counts"); printf("%s\n", msg.c_str()); printf("%-8s %-16s %-8s %-16s %-4s\n", "PREV_PID", "PREV_COMM", "CURR_PID", "CURR_COMM", "CNT"); for (auto it : counts_table_hdl.get_table_offline()) { printf("%-8d (%-16s) ==> %-8d (%-16s): %-4d\n", it.first.prev_pid, it.first.prev_comm, it.first.next_pid, it.first.next_comm, it.second); } } int main() { int ctrl_map_fd; uint32_t val; // create a map through bcc_create_map, bcc knows nothing about this map. ctrl_map_fd = bcc_create_map(BPF_MAP_TYPE_ARRAY, "control", sizeof(uint32_t), sizeof(uint32_t), 1, 0); CHECK(ctrl_map_fd < 0, "bcc_create_map failure"); // populate control map into TableStorage std::unique_ptr local_ts = ebpf::createSharedTableStorage(); ebpf::Path global_path({"control"}); ebpf::TableDesc table_desc("control", ebpf::FileDesc(ctrl_map_fd), BPF_MAP_TYPE_ARRAY, sizeof(uint32_t), sizeof(uint32_t), 1, 0); local_ts->Insert(global_path, std::move(table_desc)); // constructor with the pre-populated table storage ebpf::BPF bpf(0, &*local_ts); auto res = bpf.init(BPF_PROGRAM); CHECK(res.code(), res.msg()); // attach to the tracepoint sched:sched_switch res = bpf.attach_tracepoint("sched:sched_switch", "on_sched_switch"); CHECK(res.code(), res.msg()); // wait for some scheduling events sleep(1); auto control_table_hdl = bpf.get_array_table("control"); res = control_table_hdl.get_value(0, val); CHECK(res.code() || val != 0, res.msg()); // we should not see any events here print_counts(&bpf, "events with control off:"); printf("\n"); // change the control to on so bpf program starts to count events val = 1; res = control_table_hdl.update_value(0, val); CHECK(res.code(), res.msg()); // verify we get the control on back val = 0; res = control_table_hdl.get_value(0, val); CHECK(res.code() || val != 1, res.msg()); // wait for some scheduling events sleep(1); // we should see a bunch of events here print_counts(&bpf, "events with control on:"); return 0; } bpfcc-0.12.0/examples/cpp/pyperf/000077500000000000000000000000001357404205000165545ustar00rootroot00000000000000bpfcc-0.12.0/examples/cpp/pyperf/CMakeLists.txt000066400000000000000000000010501357404205000213100ustar00rootroot00000000000000# Copyright (c) Facebook, Inc. # Licensed under the Apache License, Version 2.0 (the "License") include_directories(${CMAKE_SOURCE_DIR}/src/cc) include_directories(${CMAKE_SOURCE_DIR}/src/cc/api) include_directories(${CMAKE_SOURCE_DIR}/src/cc/libbpf/include/uapi) add_executable(PyPerf PyPerf.cc PyPerfUtil.cc PyPerfBPFProgram.cc PyPerfLoggingHelper.cc PyPerfDefaultPrinter.cc Py36Offsets.cc) target_link_libraries(PyPerf bcc-static) if(INSTALL_CPP_EXAMPLES) install (TARGETS PyPerf DESTINATION share/bcc/examples/cpp) endif(INSTALL_CPP_EXAMPLES) bpfcc-0.12.0/examples/cpp/pyperf/Py36Offsets.cc000066400000000000000000000023651357404205000211640ustar00rootroot00000000000000/* * Copyright (c) Facebook, Inc. * Licensed under the Apache License, Version 2.0 (the "License") */ #include "PyPerfType.h" namespace ebpf { namespace pyperf { extern const OffsetConfig kPy36OffsetConfig = { .PyObject_type = 8, // offsetof(PyObject, ob_type) .PyTypeObject_name = 24, // offsetof(PyTypeObject, tp_name) .PyThreadState_frame = 24, // offsetof(PyThreadState, frame) .PyThreadState_thread = 152, // offsetof(PyThreadState, thread_id) .PyFrameObject_back = 24, // offsetof(PyFrameObject, f_back) .PyFrameObject_code = 32, // offsetof(PyFrameObject, f_code) .PyFrameObject_lineno = 124, // offsetof(PyFrameObject, f_lineno) .PyFrameObject_localsplus = 376, // offsetof(PyFrameObject, f_localsplus) .PyCodeObject_filename = 96, // offsetof(PyCodeObject, co_filename) .PyCodeObject_name = 104, // offsetof(PyCodeObject, co_name) .PyCodeObject_varnames = 64, // offsetof(PyCodeObject, co_varnames) .PyTupleObject_item = 24, // offsetof(PyTupleObject, ob_item) .String_data = 48, // sizeof(PyASCIIObject) .String_size = 16, // offsetof(PyVarObject, ob_size) }; } } // namespace ebpf bpfcc-0.12.0/examples/cpp/pyperf/PyPerf.cc000066400000000000000000000063471357404205000203020ustar00rootroot00000000000000/* * PyPerf Profile Python Processes with Python stack-trace. * For Linux, uses BCC, eBPF. Embedded C. * * Example of using BPF to profile Python Processes with Python stack-trace. * * USAGE: PyPerf [-d|--duration DURATION_MS] [-c|--sample-rate SAMPLE_RATE] * [-v|--verbosity LOG_VERBOSITY] * * Copyright (c) Facebook, Inc. * Licensed under the Apache License, Version 2.0 (the "License") */ #include #include #include #include #include "PyPerfDefaultPrinter.h" #include "PyPerfLoggingHelper.h" #include "PyPerfUtil.h" int main(int argc, char** argv) { // Argument parsing helpers int pos = 1; auto parseIntArg = [&](std::vector argNames, uint64_t& target) { std::string arg(argv[pos]); for (const auto& name : argNames) { if (arg == name) { if (pos == argc) { std::fprintf(stderr, "Expect value after %s\n", arg.c_str()); std::exit(1); } pos++; std::string value(argv[pos]); try { target = std::stoi(value); } catch (const std::exception& e) { std::fprintf(stderr, "Expect integer value after %s, got %s: %s\n", arg.c_str(), value.c_str(), e.what()); std::exit(1); } return true; } } return false; }; auto parseBoolArg = [&](std::vector argNames, bool& target) { std::string arg(argv[pos]); for (const auto& name : argNames) { if (arg == ("--" + name)) { target = true; return true; } if (arg == "--no-" + name) { target = false; return true; } } return false; }; // Default argument values uint64_t sampleRate = 1000000; uint64_t durationMs = 1000; uint64_t verbosityLevel = 0; bool showGILState = true; bool showThreadState = true; bool showPthreadIDState = false; while (true) { if (pos >= argc) { break; } bool found = false; found = found || parseIntArg({"-c", "--sample-rate"}, sampleRate); found = found || parseIntArg({"-d", "--duration"}, durationMs); found = found || parseIntArg({"-v", "--verbose"}, verbosityLevel); found = found || parseBoolArg({"show-gil-state"}, showGILState); found = found || parseBoolArg({"show-thread-state"}, showThreadState); found = found || parseBoolArg({"show-pthread-id-state"}, showPthreadIDState); if (!found) { std::fprintf(stderr, "Unexpected argument: %s\n", argv[pos]); std::exit(1); } pos++; } ebpf::pyperf::setVerbosity(verbosityLevel); ebpf::pyperf::logInfo(1, "Profiling Sample Rate: %" PRIu64 "\n", sampleRate); ebpf::pyperf::logInfo(1, "Profiling Duration: %" PRIu64 "ms\n", durationMs); ebpf::pyperf::logInfo(1, "Showing GIL state: %d\n", showGILState); ebpf::pyperf::logInfo(1, "Showing Thread state: %d\n", showThreadState); ebpf::pyperf::logInfo(1, "Showing Pthread ID state: %d\n", showPthreadIDState); ebpf::pyperf::PyPerfUtil util; util.init(); ebpf::pyperf::PyPerfDefaultPrinter printer(showGILState, showThreadState, showPthreadIDState); util.profile(sampleRate, durationMs, &printer); return 0; } bpfcc-0.12.0/examples/cpp/pyperf/PyPerfBPFProgram.cc000066400000000000000000000364151357404205000221610ustar00rootroot00000000000000/* * Copyright (c) Facebook, Inc. * Licensed under the Apache License, Version 2.0 (the "License") */ #include namespace ebpf { namespace pyperf { extern const std::string PYPERF_BPF_PROGRAM = R"( #include #include #define PYTHON_STACK_FRAMES_PER_PROG 25 #define PYTHON_STACK_PROG_CNT 3 #define STACK_MAX_LEN (PYTHON_STACK_FRAMES_PER_PROG * PYTHON_STACK_PROG_CNT) #define CLASS_NAME_LEN 32 #define FUNCTION_NAME_LEN 64 #define FILE_NAME_LEN 128 #define TASK_COMM_LEN 16 enum { STACK_STATUS_COMPLETE = 0, STACK_STATUS_ERROR = 1, STACK_STATUS_TRUNCATED = 2, }; enum { GIL_STATE_NO_INFO = 0, GIL_STATE_ERROR = 1, GIL_STATE_UNINITIALIZED = 2, GIL_STATE_NOT_LOCKED = 3, GIL_STATE_THIS_THREAD = 4, GIL_STATE_GLOBAL_CURRENT_THREAD = 5, GIL_STATE_OTHER_THREAD = 6, GIL_STATE_NULL = 7, }; enum { THREAD_STATE_UNKNOWN = 0, THREAD_STATE_MATCH = 1, THREAD_STATE_MISMATCH = 2, THREAD_STATE_THIS_THREAD_NULL = 3, THREAD_STATE_GLOBAL_CURRENT_THREAD_NULL = 4, THREAD_STATE_BOTH_NULL = 5, }; enum { PTHREAD_ID_UNKNOWN = 0, PTHREAD_ID_MATCH = 1, PTHREAD_ID_MISMATCH = 2, PTHREAD_ID_THREAD_STATE_NULL = 3, PTHREAD_ID_NULL = 4, PTHREAD_ID_ERROR = 5, }; typedef struct { int64_t PyObject_type; int64_t PyTypeObject_name; int64_t PyThreadState_frame; int64_t PyThreadState_thread; int64_t PyFrameObject_back; int64_t PyFrameObject_code; int64_t PyFrameObject_lineno; int64_t PyFrameObject_localsplus; int64_t PyCodeObject_filename; int64_t PyCodeObject_name; int64_t PyCodeObject_varnames; int64_t PyTupleObject_item; int64_t String_data; int64_t String_size; } OffsetConfig; typedef struct { uintptr_t current_state_addr; // virtual address of _PyThreadState_Current uintptr_t tls_key_addr; // virtual address of autoTLSkey for pthreads TLS uintptr_t gil_locked_addr; // virtual address of gil_locked uintptr_t gil_last_holder_addr; // virtual address of gil_last_holder OffsetConfig offsets; } PidData; typedef struct { char classname[CLASS_NAME_LEN]; char name[FUNCTION_NAME_LEN]; char file[FILE_NAME_LEN]; // NOTE: PyFrameObject also has line number but it is typically just the // first line of that function and PyCode_Addr2Line needs to be called // to get the actual line } Symbol; typedef struct { uint32_t pid; uint32_t tid; char comm[TASK_COMM_LEN]; uint8_t thread_state_match; uint8_t gil_state; uint8_t pthread_id_match; uint8_t stack_status; // instead of storing symbol name here directly, we add it to another // hashmap with Symbols and only store the ids here int64_t stack_len; int32_t stack[STACK_MAX_LEN]; } Event; #define _STR_CONCAT(str1, str2) str1##str2 #define STR_CONCAT(str1, str2) _STR_CONCAT(str1, str2) #define FAIL_COMPILATION_IF(condition) \ typedef struct { \ char _condition_check[1 - 2 * !!(condition)]; \ } STR_CONCAT(compile_time_condition_check, __COUNTER__); // See comments in get_frame_data FAIL_COMPILATION_IF(sizeof(Symbol) == sizeof(struct bpf_perf_event_value)) typedef struct { OffsetConfig offsets; uint64_t cur_cpu; int64_t symbol_counter; void* frame_ptr; int64_t python_stack_prog_call_cnt; Event event; } sample_state_t; BPF_PERCPU_ARRAY(state_heap, sample_state_t, 1); BPF_HASH(symbols, Symbol, int32_t, __SYMBOLS_SIZE__); BPF_HASH(pid_config, pid_t, PidData); BPF_PROG_ARRAY(progs, 1); BPF_PERF_OUTPUT(events); static inline __attribute__((__always_inline__)) void* get_thread_state( void* tls_base, PidData* pid_data) { // Python sets the thread_state using pthread_setspecific with the key // stored in a global variable autoTLSkey. // We read the value of the key from the global variable and then read // the value in the thread-local storage. This relies on pthread implementation. // This is basically the same as running the following in GDB: // p *(PyThreadState*)((struct pthread*)pthread_self())-> // specific_1stblock[autoTLSkey]->data int key; bpf_probe_read(&key, sizeof(key), (void*)pid_data->tls_key_addr); // This assumes autoTLSkey < 32, which means that the TLS is stored in // pthread->specific_1stblock[autoTLSkey] // 0x310 is offsetof(struct pthread, specific_1stblock), // 0x10 is sizeof(pthread_key_data) // 0x8 is offsetof(struct pthread_key_data, data) // 'struct pthread' is not in the public API so we have to hardcode // the offsets here void* thread_state; bpf_probe_read( &thread_state, sizeof(thread_state), tls_base + 0x310 + key * 0x10 + 0x08); return thread_state; } static inline __attribute__((__always_inline__)) int submit_sample( struct pt_regs* ctx, sample_state_t* state) { events.perf_submit(ctx, &state->event, sizeof(Event)); return 0; } // this function is trivial, but we need to do map lookup in separate function, // because BCC doesn't allow direct map calls (including lookups) from inside // a macro (which we want to do in GET_STATE() macro below) static inline __attribute__((__always_inline__)) sample_state_t* get_state() { int zero = 0; return state_heap.lookup(&zero); } #define GET_STATE() \ sample_state_t* state = get_state(); \ if (!state) { \ return 0; /* should never happen */ \ } static inline __attribute__((__always_inline__)) int get_thread_state_match( void* this_thread_state, void* global_thread_state) { if (this_thread_state == 0 && global_thread_state == 0) { return THREAD_STATE_BOTH_NULL; } if (this_thread_state == 0) { return THREAD_STATE_THIS_THREAD_NULL; } if (global_thread_state == 0) { return THREAD_STATE_GLOBAL_CURRENT_THREAD_NULL; } if (this_thread_state == global_thread_state) { return THREAD_STATE_MATCH; } else { return THREAD_STATE_MISMATCH; } } static inline __attribute__((__always_inline__)) int get_gil_state( void* this_thread_state, void* global_thread_state, PidData* pid_data) { // Get information of GIL state if (pid_data->gil_locked_addr == 0 || pid_data->gil_last_holder_addr == 0) { return GIL_STATE_NO_INFO; } int gil_locked = 0; void* gil_thread_state = 0; if (bpf_probe_read( &gil_locked, sizeof(gil_locked), (void*)pid_data->gil_locked_addr)) { return GIL_STATE_ERROR; } switch (gil_locked) { case -1: return GIL_STATE_UNINITIALIZED; case 0: return GIL_STATE_NOT_LOCKED; case 1: // GIL is held by some Thread bpf_probe_read( &gil_thread_state, sizeof(void*), (void*)pid_data->gil_last_holder_addr); if (gil_thread_state == this_thread_state) { return GIL_STATE_THIS_THREAD; } else if (gil_thread_state == global_thread_state) { return GIL_STATE_GLOBAL_CURRENT_THREAD; } else if (gil_thread_state == 0) { return GIL_STATE_NULL; } else { return GIL_STATE_OTHER_THREAD; } default: return GIL_STATE_ERROR; } } static inline __attribute__((__always_inline__)) int get_pthread_id_match(void* thread_state, void* tls_base, PidData* pid_data) { if (thread_state == 0) { return PTHREAD_ID_THREAD_STATE_NULL; } uint64_t pthread_self, pthread_created; bpf_probe_read( &pthread_created, sizeof(pthread_created), thread_state + pid_data->offsets.PyThreadState_thread); if (pthread_created == 0) { return PTHREAD_ID_NULL; } // 0x10 = offsetof(struct pthread, header.self) bpf_probe_read(&pthread_self, sizeof(pthread_self), tls_base + 0x10); if (pthread_self == 0) { return PTHREAD_ID_ERROR; } if (pthread_self == pthread_created) { return PTHREAD_ID_MATCH; } else { return PTHREAD_ID_MISMATCH; } } int on_event(struct pt_regs* ctx) { uint64_t pid_tgid = bpf_get_current_pid_tgid(); pid_t pid = (pid_t)(pid_tgid >> 32); PidData* pid_data = pid_config.lookup(&pid); if (!pid_data) { return 0; } GET_STATE(); state->offsets = pid_data->offsets; state->cur_cpu = bpf_get_smp_processor_id(); state->python_stack_prog_call_cnt = 0; Event* event = &state->event; event->pid = pid; event->tid = (pid_t)pid_tgid; bpf_get_current_comm(&event->comm, sizeof(event->comm)); // Get pointer of global PyThreadState, which should belong to the Thread // currently holds the GIL void* global_current_thread = (void*)0; bpf_probe_read( &global_current_thread, sizeof(global_current_thread), (void*)pid_data->current_state_addr); struct task_struct* task = (struct task_struct*)bpf_get_current_task(); #if __x86_64__ // thread_struct->fs was renamed to fsbase in // https://github.com/torvalds/linux/commit/296f781a4b7801ad9c1c0219f9e87b6c25e196fe // so depending on kernel version, we need to account for that #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 7, 0) void* tls_base = (void*)task->thread.fs; #else void* tls_base = (void*)task->thread.fsbase; #endif #elif __aarch64__ void* tls_base = (void*)task->thread.tp_value; #else #error "Unsupported platform" #endif // Read PyThreadState of this Thread from TLS void* thread_state = get_thread_state(tls_base, pid_data); // Check for matching between TLS PyThreadState and // the global _PyThreadState_Current event->thread_state_match = get_thread_state_match(thread_state, global_current_thread); // Read GIL state event->gil_state = get_gil_state(thread_state, global_current_thread, pid_data); // Check for matching between pthread ID created current PyThreadState and // pthread of actual current pthread event->pthread_id_match = get_pthread_id_match(thread_state, tls_base, pid_data); // pre-initialize event struct in case any subprogram below fails event->stack_status = STACK_STATUS_COMPLETE; event->stack_len = 0; if (thread_state != 0) { // Get pointer to top frame from PyThreadState bpf_probe_read( &state->frame_ptr, sizeof(void*), thread_state + pid_data->offsets.PyThreadState_frame); // jump to reading first set of Python frames progs.call(ctx, PYTHON_STACK_PROG_IDX); // we won't ever get here } return submit_sample(ctx, state); } static inline __attribute__((__always_inline__)) void get_names( void* cur_frame, void* code_ptr, OffsetConfig* offsets, Symbol* symbol, void* ctx) { // Figure out if we want to parse class name, basically checking the name of // the first argument, // ((PyTupleObject*)$frame->f_code->co_varnames)->ob_item[0] // If it's 'self', we get the type and it's name, if it's cls, we just get // the name. This is not perfect but there is no better way to figure this // out from the code object. void* args_ptr; bpf_probe_read( &args_ptr, sizeof(void*), code_ptr + offsets->PyCodeObject_varnames); bpf_probe_read( &args_ptr, sizeof(void*), args_ptr + offsets->PyTupleObject_item); bpf_probe_read_str( &symbol->name, sizeof(symbol->name), args_ptr + offsets->String_data); // compare strings as ints to save instructions char self_str[4] = {'s', 'e', 'l', 'f'}; char cls_str[4] = {'c', 'l', 's', '\0'}; bool first_self = *(int32_t*)symbol->name == *(int32_t*)self_str; bool first_cls = *(int32_t*)symbol->name == *(int32_t*)cls_str; // We re-use the same Symbol instance across loop iterations, which means // we will have left-over data in the struct. Although this won't affect // correctness of the result because we have '\0' at end of the strings read, // it would affect effectiveness of the deduplication. // Helper bpf_perf_prog_read_value clears the buffer on error, so here we // (ab)use this behavior to clear the memory. It requires the size of Symbol // to be different from struct bpf_perf_event_value, which we check at // compilation time using the FAIL_COMPILATION_IF macro. bpf_perf_prog_read_value(ctx, symbol, sizeof(Symbol)); // Read class name from $frame->f_localsplus[0]->ob_type->tp_name. if (first_self || first_cls) { void* ptr; bpf_probe_read( &ptr, sizeof(void*), cur_frame + offsets->PyFrameObject_localsplus); if (first_self) { // we are working with an instance, first we need to get type bpf_probe_read(&ptr, sizeof(void*), ptr + offsets->PyObject_type); } bpf_probe_read(&ptr, sizeof(void*), ptr + offsets->PyTypeObject_name); bpf_probe_read_str(&symbol->classname, sizeof(symbol->classname), ptr); } void* pystr_ptr; // read PyCodeObject's filename into symbol bpf_probe_read( &pystr_ptr, sizeof(void*), code_ptr + offsets->PyCodeObject_filename); bpf_probe_read_str( &symbol->file, sizeof(symbol->file), pystr_ptr + offsets->String_data); // read PyCodeObject's name into symbol bpf_probe_read( &pystr_ptr, sizeof(void*), code_ptr + offsets->PyCodeObject_name); bpf_probe_read_str( &symbol->name, sizeof(symbol->name), pystr_ptr + offsets->String_data); } // get_frame_data reads current PyFrameObject filename/name and updates // stack_info->frame_ptr with pointer to next PyFrameObject static inline __attribute__((__always_inline__)) bool get_frame_data( void** frame_ptr, OffsetConfig* offsets, Symbol* symbol, // ctx is only used to call helper to clear symbol, see documentation below void* ctx) { void* cur_frame = *frame_ptr; if (!cur_frame) { return false; } void* code_ptr; // read PyCodeObject first, if that fails, then no point reading next frame bpf_probe_read( &code_ptr, sizeof(void*), cur_frame + offsets->PyFrameObject_code); if (!code_ptr) { return false; } get_names(cur_frame, code_ptr, offsets, symbol, ctx); // read next PyFrameObject pointer, update in place bpf_probe_read( frame_ptr, sizeof(void*), cur_frame + offsets->PyFrameObject_back); return true; } // To avoid duplicate ids, every CPU needs to use different ids when inserting // into the hashmap. NUM_CPUS is defined at PyPerf backend side and passed // through CFlag. static inline __attribute__((__always_inline__)) int64_t get_symbol_id( sample_state_t* state, Symbol* sym) { int32_t* symbol_id_ptr = symbols.lookup(sym); if (symbol_id_ptr) { return *symbol_id_ptr; } // the symbol is new, bump the counter int32_t symbol_id = state->symbol_counter * NUM_CPUS + state->cur_cpu; state->symbol_counter++; symbols.update(sym, &symbol_id); return symbol_id; } int read_python_stack(struct pt_regs* ctx) { GET_STATE(); state->python_stack_prog_call_cnt++; Event* sample = &state->event; Symbol sym = {}; bool last_res = false; #pragma unroll for (int i = 0; i < PYTHON_STACK_FRAMES_PER_PROG; i++) { last_res = get_frame_data(&state->frame_ptr, &state->offsets, &sym, ctx); if (last_res) { uint32_t symbol_id = get_symbol_id(state, &sym); int64_t cur_len = sample->stack_len; if (cur_len >= 0 && cur_len < STACK_MAX_LEN) { sample->stack[cur_len] = symbol_id; sample->stack_len++; } } } if (!state->frame_ptr) { sample->stack_status = STACK_STATUS_COMPLETE; } else { if (!last_res) { sample->stack_status = STACK_STATUS_ERROR; } else { sample->stack_status = STACK_STATUS_TRUNCATED; } } if (sample->stack_status == STACK_STATUS_TRUNCATED && state->python_stack_prog_call_cnt < PYTHON_STACK_PROG_CNT) { // read next batch of frames progs.call(ctx, PYTHON_STACK_PROG_IDX); } return submit_sample(ctx, state); } )"; } } // namespace ebpf bpfcc-0.12.0/examples/cpp/pyperf/PyPerfDefaultPrinter.cc000066400000000000000000000074441357404205000231520ustar00rootroot00000000000000/* * Copyright (c) Facebook, Inc. * Licensed under the Apache License, Version 2.0 (the "License") */ #include #include #include "PyPerfDefaultPrinter.h" #include "PyPerfUtil.h" namespace ebpf { namespace pyperf { const static std::string kLostSymbol = "[Lost Symbol]"; const static std::string kIncompleteStack = "[Truncated Stack]"; const static std::string kErrorStack = "[Stack Error]"; const static std::string kNonPythonStack = "[Non-Python Code]"; const static std::map kGILStateValues = { {GIL_STATE_NO_INFO, "No GIL Info"}, {GIL_STATE_ERROR, "Error Reading GIL State"}, {GIL_STATE_UNINITIALIZED, "GIL Uninitialized"}, {GIL_STATE_NOT_LOCKED, "GIL Not Locked"}, {GIL_STATE_THIS_THREAD, "GIL on This Thread"}, {GIL_STATE_GLOBAL_CURRENT_THREAD, "GIL on Global _PyThreadState_Current Thread"}, {GIL_STATE_OTHER_THREAD, "GIL on Unexpected Thread"}, {GIL_STATE_NULL, "GIL State Empty"}}; const static std::map kThreadStateValues = { {THREAD_STATE_UNKNOWN, "ThreadState Unknown"}, {THREAD_STATE_MATCH, "TLS ThreadState is Global _PyThreadState_Current"}, {THREAD_STATE_MISMATCH, "TLS ThreadState is not Global _PyThreadState_Current"}, {THREAD_STATE_THIS_THREAD_NULL, "TLS ThreadState is NULL"}, {THREAD_STATE_GLOBAL_CURRENT_THREAD_NULL, "Global _PyThreadState_Current is NULL"}, {THREAD_STATE_BOTH_NULL, "Both TLS ThreadState and Global _PyThreadState_Current is NULL"}, }; const static std::map kPthreadIDStateValues = { {PTHREAD_ID_UNKNOWN, "Pthread ID Unknown"}, {PTHREAD_ID_MATCH, "System Pthread ID is Python ThreadState Pthread ID"}, {PTHREAD_ID_MISMATCH, "System Pthread ID is not Python ThreadState Pthread ID"}, {PTHREAD_ID_THREAD_STATE_NULL, "No Pthread ID: TLS ThreadState is NULL"}, {PTHREAD_ID_NULL, "Pthread ID on TLS ThreadState is NULL"}, {PTHREAD_ID_ERROR, "Error Reading System Pthread ID"}}; void PyPerfDefaultPrinter::processSamples( const std::vector& samples, PyPerfUtil* util) { auto symbols = util->getSymbolMapping(); uint32_t lostSymbols = 0; uint32_t truncatedStack = 0; for (auto& sample : samples) { if (sample.threadStateMatch != THREAD_STATE_THIS_THREAD_NULL && sample.threadStateMatch != THREAD_STATE_BOTH_NULL) { for (const auto stackId : sample.pyStackIds) { auto symbIt = symbols.find(stackId); if (symbIt != symbols.end()) { std::printf(" %s\n", symbIt->second.c_str()); } else { std::printf(" %s\n", kLostSymbol.c_str()); lostSymbols++; } } switch (sample.stackStatus) { case STACK_STATUS_TRUNCATED: std::printf(" %s\n", kIncompleteStack.c_str()); truncatedStack++; break; case STACK_STATUS_ERROR: std::printf(" %s\n", kErrorStack.c_str()); break; default: break; } } else { std::printf(" %s\n", kNonPythonStack.c_str()); } std::printf("PID: %d TID: %d (%s)\n", sample.pid, sample.tid, sample.comm.c_str()); if (showGILState_) std::printf("GIL State: %s\n", kGILStateValues.at(sample.gilState)); if (showThreadState_) std::printf("Thread State: %s\n", kThreadStateValues.at(sample.threadStateMatch)); if (showPthreadIDState_) std::printf("Pthread ID State: %s\n", kPthreadIDStateValues.at(sample.pthreadIDMatch)); std::printf("\n"); } std::printf("%d samples collected\n", util->getTotalSamples()); std::printf("%d samples lost\n", util->getLostSamples()); std::printf("%d samples with truncated stack\n", truncatedStack); std::printf("%d times Python symbol lost\n", lostSymbols); } } // namespace pyperf } // namespace ebpf bpfcc-0.12.0/examples/cpp/pyperf/PyPerfDefaultPrinter.h000066400000000000000000000013621357404205000230050ustar00rootroot00000000000000/* * Copyright (c) Facebook, Inc. * Licensed under the Apache License, Version 2.0 (the "License") */ #pragma once #include "PyPerfSampleProcessor.h" namespace ebpf { namespace pyperf { class PyPerfDefaultPrinter : public PyPerfSampleProcessor { public: PyPerfDefaultPrinter(bool showGILState, bool showThreadState, bool showPthreadIDState) : showGILState_(showGILState), showThreadState_(showThreadState), showPthreadIDState_(showPthreadIDState) {} void processSamples(const std::vector& samples, PyPerfUtil* util) override; private: bool showGILState_; bool showThreadState_; bool showPthreadIDState_; }; } // namespace pyperf } // namespace ebpf bpfcc-0.12.0/examples/cpp/pyperf/PyPerfLoggingHelper.cc000066400000000000000000000011061357404205000227350ustar00rootroot00000000000000/* * Copyright (c) Facebook, Inc. * Licensed under the Apache License, Version 2.0 (the "License") */ #include #include #include "PyPerfLoggingHelper.h" namespace ebpf { namespace pyperf { static uint64_t setVerbosityLevel = 0; void setVerbosity(uint64_t verbosityLevel) { setVerbosityLevel = verbosityLevel; } void logInfo(uint64_t logLevel, const char* fmt, ...) { if (logLevel > setVerbosityLevel) { return; } va_list va; va_start(va, fmt); std::vfprintf(stderr, fmt, va); va_end(va); } } // namespace pyperf } // namespace ebpf bpfcc-0.12.0/examples/cpp/pyperf/PyPerfLoggingHelper.h000066400000000000000000000005011357404205000225750ustar00rootroot00000000000000/* * Copyright (c) Facebook, Inc. * Licensed under the Apache License, Version 2.0 (the "License") */ #pragma once #include namespace ebpf { namespace pyperf { void setVerbosity(uint64_t verbosityLevel); void logInfo(uint64_t logLevel, const char* fmt, ...); } // namespace pyperf } // namespace ebpf bpfcc-0.12.0/examples/cpp/pyperf/PyPerfSampleProcessor.h000066400000000000000000000006601357404205000231760ustar00rootroot00000000000000/* * Copyright (c) Facebook, Inc. * Licensed under the Apache License, Version 2.0 (the "License") */ #pragma once #include #include "PyPerfType.h" namespace ebpf { namespace pyperf { class PyPerfUtil; class PyPerfSampleProcessor { public: virtual void processSamples(const std::vector& samples, PyPerfUtil* util) = 0; }; } // namespace pyperf } // namespace ebpf bpfcc-0.12.0/examples/cpp/pyperf/PyPerfType.h000066400000000000000000000063171357404205000210030ustar00rootroot00000000000000/* * Copyright (c) Facebook, Inc. * Licensed under the Apache License, Version 2.0 (the "License") */ #pragma once #include #include #include #include #define PYTHON_STACK_FRAMES_PER_PROG 25 #define PYTHON_STACK_PROG_CNT 3 #define STACK_MAX_LEN (PYTHON_STACK_FRAMES_PER_PROG * PYTHON_STACK_PROG_CNT) #define CLASS_NAME_LEN 32 #define FUNCTION_NAME_LEN 64 #define FILE_NAME_LEN 128 #define TASK_COMM_LEN 16 namespace ebpf { namespace pyperf { enum { STACK_STATUS_COMPLETE = 0, STACK_STATUS_ERROR = 1, STACK_STATUS_TRUNCATED = 2, }; enum { GIL_STATE_NO_INFO = 0, GIL_STATE_ERROR = 1, GIL_STATE_UNINITIALIZED = 2, GIL_STATE_NOT_LOCKED = 3, GIL_STATE_THIS_THREAD = 4, GIL_STATE_GLOBAL_CURRENT_THREAD = 5, GIL_STATE_OTHER_THREAD = 6, GIL_STATE_NULL = 7, }; enum { THREAD_STATE_UNKNOWN = 0, THREAD_STATE_MATCH = 1, THREAD_STATE_MISMATCH = 2, THREAD_STATE_THIS_THREAD_NULL = 3, THREAD_STATE_GLOBAL_CURRENT_THREAD_NULL = 4, THREAD_STATE_BOTH_NULL = 5, }; enum { PTHREAD_ID_UNKNOWN = 0, PTHREAD_ID_MATCH = 1, PTHREAD_ID_MISMATCH = 2, PTHREAD_ID_THREAD_STATE_NULL = 3, PTHREAD_ID_NULL = 4, PTHREAD_ID_ERROR = 5, }; typedef struct { int64_t PyObject_type; int64_t PyTypeObject_name; int64_t PyThreadState_frame; int64_t PyThreadState_thread; int64_t PyFrameObject_back; int64_t PyFrameObject_code; int64_t PyFrameObject_lineno; int64_t PyFrameObject_localsplus; int64_t PyCodeObject_filename; int64_t PyCodeObject_name; int64_t PyCodeObject_varnames; int64_t PyTupleObject_item; int64_t String_data; int64_t String_size; } OffsetConfig; typedef struct { uintptr_t current_state_addr; // virtual address of _PyThreadState_Current uintptr_t tls_key_addr; // virtual address of autoTLSkey for pthreads TLS uintptr_t gil_locked_addr; // virtual address of gil_locked uintptr_t gil_last_holder_addr; // virtual address of gil_last_holder OffsetConfig offsets; } PidData; typedef struct { char classname[CLASS_NAME_LEN]; char name[FUNCTION_NAME_LEN]; char file[FILE_NAME_LEN]; // NOTE: PyFrameObject also has line number but it is typically just the // first line of that function and PyCode_Addr2Line needs to be called // to get the actual line } Symbol; typedef struct { uint32_t pid; uint32_t tid; char comm[TASK_COMM_LEN]; uint8_t thread_state_match; uint8_t gil_state; uint8_t pthread_id_match; uint8_t stack_status; // instead of storing symbol name here directly, we add it to another // hashmap with Symbols and only store the ids here int64_t stack_len; int32_t stack[STACK_MAX_LEN]; } Event; struct PyPerfSample { pid_t pid; pid_t tid; std::string comm; uint8_t threadStateMatch; uint8_t gilState; uint8_t pthreadIDMatch; uint8_t stackStatus; std::vector pyStackIds; explicit PyPerfSample(const Event* raw, int rawSize) : pid(raw->pid), tid(raw->tid), comm(raw->comm), threadStateMatch(raw->thread_state_match), gilState(raw->gil_state), pthreadIDMatch(raw->pthread_id_match), stackStatus(raw->stack_status), pyStackIds(raw->stack, raw->stack + raw->stack_len) {} }; } // namespace pyperf } // namespace ebpf bpfcc-0.12.0/examples/cpp/pyperf/PyPerfUtil.cc000066400000000000000000000260731357404205000211360ustar00rootroot00000000000000/* * Copyright (c) Facebook, Inc. * Licensed under the Apache License, Version 2.0 (the "License") */ #include #include #include #include #include #include #include #include #include #include #include #include "PyPerfLoggingHelper.h" #include "PyPerfUtil.h" #include "bcc_elf.h" #include "bcc_proc.h" #include "bcc_syms.h" namespace ebpf { namespace pyperf { extern OffsetConfig kPy36OffsetConfig; extern std::string PYPERF_BPF_PROGRAM; const static int kPerfBufSizePages = 32; const static std::string kPidCfgTableName("pid_config"); const static std::string kProgsTableName("progs"); const static std::string kSamplePerfBufName("events"); const static std::string kOnEventFuncName("on_event"); const static std::string kPythonStackFuncName("read_python_stack"); const static std::string kPythonStackProgIdxFlag("-DPYTHON_STACK_PROG_IDX="); const static int kPythonStackProgIdx = 0; const static std::string kNumCpusFlag("-DNUM_CPUS="); const static std::string kSymbolsHashSizeFlag("-D__SYMBOLS_SIZE__="); const static int kSymbolsHashSize = 16384; namespace { bool getRunningPids(std::vector& output) { auto dir = ::opendir("/proc/"); if (!dir) { std::fprintf(stderr, "Open /proc failed: %d\n", errno); return false; } dirent* result = nullptr; do { if ((result = readdir(dir))) { std::string basename = result->d_name; if (basename == "." || basename == "..") { continue; } std::string fullpath = "/proc/" + basename; struct stat st; if (::stat(fullpath.c_str(), &st) != 0 || !S_ISDIR(st.st_mode)) { continue; } try { auto pid = std::stoi(basename); output.push_back(pid); } catch (const std::exception& e) { continue; } } } while (result); if (::closedir(dir) == -1) { std::fprintf(stderr, "Close /proc failed: %d\n", errno); return false; } return true; } typedef struct { int pid; bool found; uint64_t st; uint64_t en; } FindPythonPathHelper; const static std::string kPy36LibName = "libpython3.6"; int findPythonPathCallback(mod_info *mod, int, void* payload) { auto helper = static_cast(payload); std::string file = mod->name; auto pos = file.rfind("/"); if (pos != std::string::npos) { file = file.substr(pos + 1); } if (file.find(kPy36LibName) == 0) { logInfo(1, "Found Python library %s loaded at %lx-%lx for PID %d\n", mod->name, mod->start_addr, mod->end_addr, helper->pid); helper->found = true; helper->st = mod->start_addr; helper->en = mod->end_addr; return -1; } return 0; } bool allAddrFound(const PidData& data) { return (data.current_state_addr > 0) && (data.tls_key_addr > 0) && (data.gil_locked_addr > 0) && (data.gil_last_holder_addr > 0); } int getAddrOfPythonBinaryCallback(const char* name, uint64_t addr, uint64_t, void* payload) { PidData& data = *static_cast(payload); auto checkAndGetAddr = [&](uintptr_t& targetAddr, const char* targetName) { if (targetAddr == 0 && std::strcmp(name, targetName) == 0) { targetAddr = addr; } }; checkAndGetAddr(data.tls_key_addr, "autoTLSkey"); checkAndGetAddr(data.current_state_addr, "_PyThreadState_Current"); checkAndGetAddr(data.gil_locked_addr, "gil_locked"); checkAndGetAddr(data.gil_last_holder_addr, "gil_last_holder"); if (allAddrFound(data)) { return -1; } return 0; } bool getAddrOfPythonBinary(const std::string& path, PidData& data) { std::memset(&data, 0, sizeof(data)); struct bcc_symbol_option option = {.use_debug_file = 0, .check_debug_file_crc = 0, .lazy_symbolize = 1, .use_symbol_type = (1 << STT_OBJECT)}; bcc_elf_foreach_sym(path.c_str(), &getAddrOfPythonBinaryCallback, &option, &data); return allAddrFound(data); } } // namespace void handleSampleCallback(void* cb_cookie, void* raw_data, int data_size) { auto profiler = static_cast(cb_cookie); profiler->handleSample(raw_data, data_size); } void handleLostSamplesCallback(void* cb_cookie, uint64_t lost_cnt) { auto profiler = static_cast(cb_cookie); profiler->handleLostSamples(lost_cnt); } PyPerfUtil::PyPerfResult PyPerfUtil::init() { std::vector cflags; cflags.emplace_back(kNumCpusFlag + std::to_string(::sysconf(_SC_NPROCESSORS_ONLN))); cflags.emplace_back(kSymbolsHashSizeFlag + std::to_string(kSymbolsHashSize)); cflags.emplace_back(kPythonStackProgIdxFlag + std::to_string(kPythonStackProgIdx)); auto initRes = bpf_.init(PYPERF_BPF_PROGRAM, cflags); if (initRes.code() != 0) { std::fprintf(stderr, "Failed to compiled PyPerf BPF programs: %s\n", initRes.msg().c_str()); return PyPerfResult::INIT_FAIL; } int progFd = -1; auto loadRes = bpf_.load_func(kPythonStackFuncName, BPF_PROG_TYPE_PERF_EVENT, progFd); if (loadRes.code() != 0) { std::fprintf(stderr, "Failed to load BPF program %s: %s\n", kPythonStackFuncName.c_str(), loadRes.msg().c_str()); return PyPerfResult::INIT_FAIL; } auto progTable = bpf_.get_prog_table(kProgsTableName); auto updateRes = progTable.update_value(kPythonStackProgIdx, progFd); if (updateRes.code() != 0) { std::fprintf(stderr, "Failed to set BPF program %s FD %d to program table: %s\n", kPythonStackFuncName.c_str(), progFd, updateRes.msg().c_str()); return PyPerfResult::INIT_FAIL; } std::vector pids; if (!getRunningPids(pids)) { std::fprintf(stderr, "Failed getting running Processes\n"); return PyPerfResult::INIT_FAIL; } // Populate config for each Python Process auto pid_hash = bpf_.get_hash_table(kPidCfgTableName); PidData pidData; for (const auto pid : pids) { if (!tryTargetPid(pid, pidData)) { // Not a Python Process continue; } pid_hash.update_value(pid, pidData); } // Open perf buffer auto openRes = bpf_.open_perf_buffer( kSamplePerfBufName, &handleSampleCallback, &handleLostSamplesCallback, this, kPerfBufSizePages); if (openRes.code() != 0) { std::fprintf(stderr, "Unable to open Perf Buffer: %s\n", openRes.msg().c_str()); return PyPerfResult::PERF_BUF_OPEN_FAIL; } initCompleted_ = true; return PyPerfResult::SUCCESS; } void PyPerfUtil::handleSample(const void* data, int dataSize) { const Event* raw = static_cast(data); samples_.emplace_back(raw, dataSize); totalSamples_++; } void PyPerfUtil::handleLostSamples(int lostCnt) { lostSamples_ += lostCnt; } PyPerfUtil::PyPerfResult PyPerfUtil::profile(int64_t sampleRate, int64_t durationMs, PyPerfSampleProcessor* processor) { if (!initCompleted_) { std::fprintf(stderr, "PyPerfUtil::init not invoked or failed\n"); return PyPerfResult::NO_INIT; } // Attach to CPU cycles auto attachRes = bpf_.attach_perf_event(0, 0, kOnEventFuncName, sampleRate, 0); if (attachRes.code() != 0) { std::fprintf(stderr, "Attach to CPU cycles event failed: %s\n", attachRes.msg().c_str()); return PyPerfResult::EVENT_ATTACH_FAIL; } logInfo(2, "Attached to profiling event\n"); // Get Perf Buffer and poll in a loop for a given duration auto perfBuffer = bpf_.get_perf_buffer(kSamplePerfBufName); if (!perfBuffer) { std::fprintf(stderr, "Failed to get Perf Buffer: %s\n", kSamplePerfBufName.c_str()); return PyPerfResult::PERF_BUF_OPEN_FAIL; } logInfo(2, "Started polling Perf Buffer\n"); auto start = std::chrono::steady_clock::now(); while (std::chrono::steady_clock::now() < start + std::chrono::milliseconds(durationMs)) { perfBuffer->poll(50 /* 50ms timeout */); } logInfo(2, "Profiling duration finished\n"); // Detach the event auto detachRes = bpf_.detach_perf_event(0, 0); if (detachRes.code() != 0) { std::fprintf(stderr, "Detach CPU cycles event failed: %s\n", detachRes.msg().c_str()); return PyPerfResult::EVENT_DETACH_FAIL; } logInfo(2, "Detached from profiling event\n"); // Drain remaining samples logInfo(2, "Draining remaining samples\n"); while (perfBuffer->poll(0) > 0) { } logInfo(2, "Finished draining remaining samples\n"); processor->processSamples(samples_, this); return PyPerfResult::SUCCESS; } std::unordered_map PyPerfUtil::getSymbolMapping() { auto symbolTable = bpf_.get_hash_table("symbols"); std::unordered_map symbols; for (auto& x : symbolTable.get_table_offline()) { auto symbolName = getSymbolName(x.first); logInfo(2, "Symbol ID %d is %s\n", x.second, symbolName.c_str()); symbols.emplace(x.second, std::move(symbolName)); } logInfo(1, "Total %d unique Python symbols\n", symbols.size()); return symbols; } std::string PyPerfUtil::getSymbolName(Symbol& sym) const { std::string nameStr = std::string(sym.name).substr(0, FUNCTION_NAME_LEN); std::string classStr = std::string(sym.classname).substr(0, CLASS_NAME_LEN); if (classStr.size() > 0) { nameStr = classStr + "." + nameStr; } std::string file = std::string(sym.file).substr(0, FILE_NAME_LEN); if (file.empty()) { return nameStr; } if (file[0] == '/') { file = file.substr(1); } if (file.find("./") == 0) { file = file.substr(2); } if (file.find(".py", file.size() - 3) == (file.size() - 3)) { file = file.substr(0, file.size() - 3); } std::replace(file.begin(), file.end(), '/', '.'); return file + "." + nameStr; } bool PyPerfUtil::tryTargetPid(int pid, PidData& data) { FindPythonPathHelper helper{pid, false, 0, 0}; bcc_procutils_each_module(pid, &findPythonPathCallback, &helper); if (!helper.found) { logInfo(2, "PID %d does not contain Python library\n", pid); return false; } char path[256]; int res = std::snprintf(path, sizeof(path), "/proc/%d/map_files/%lx-%lx", pid, helper.st, helper.en); if (res < 0 || size_t(res) >= sizeof(path)) { return false; } if (!getAddrOfPythonBinary(path, data)) { std::fprintf( stderr, "Failed getting addresses in potential Python library in PID %d\n", pid); return false; } data.offsets = kPy36OffsetConfig; data.current_state_addr += helper.st; logInfo(2, "PID %d has _PyThreadState_Current at %lx\n", pid, data.current_state_addr); data.tls_key_addr += helper.st; logInfo(2, "PID %d has autoTLSKey at %lx\n", pid, data.current_state_addr); data.gil_locked_addr += helper.st; logInfo(2, "PID %d has gil_locked at %lx\n", pid, data.current_state_addr); data.gil_last_holder_addr += helper.st; logInfo(2, "PID %d has gil_last_holder at %lx\n", pid, data.current_state_addr); return true; } } // namespace pyperf } // namespace ebpf bpfcc-0.12.0/examples/cpp/pyperf/PyPerfUtil.h000066400000000000000000000026511357404205000207740ustar00rootroot00000000000000/* * Copyright (c) Facebook, Inc. * Licensed under the Apache License, Version 2.0 (the "License") */ #pragma once #include #include #include #include #include #include "BPF.h" #include "PyPerfSampleProcessor.h" #include "PyPerfType.h" namespace ebpf { namespace pyperf { class PyPerfUtil { public: enum class PyPerfResult : int { SUCCESS = 0, INIT_FAIL, PERF_BUF_OPEN_FAIL, NO_INIT, EVENT_ATTACH_FAIL, EVENT_DETACH_FAIL }; // init must be invoked exactly once before invoking profile PyPerfResult init(); PyPerfResult profile(int64_t sampleRate, int64_t durationMs, PyPerfSampleProcessor* processor); std::unordered_map getSymbolMapping(); uint32_t getTotalSamples() const { return totalSamples_; } uint32_t getLostSamples() const { return lostSamples_; } private: uint32_t totalSamples_ = 0, lostSamples_ = 0; ebpf::BPF bpf_{0, nullptr, false, "", true}; std::vector samples_; bool initCompleted_{false}; void handleSample(const void* data, int dataSize); void handleLostSamples(int lostCnt); friend void handleLostSamplesCallback(void*, uint64_t); friend void handleSampleCallback(void*, void*, int); std::string getSymbolName(Symbol& sym) const; bool tryTargetPid(int pid, PidData& data); }; } // namespace pyperf } // namespace ebpf bpfcc-0.12.0/examples/hello_world.py000077500000000000000000000007131357404205000173550ustar00rootroot00000000000000#!/usr/bin/python # Copyright (c) PLUMgrid, Inc. # Licensed under the Apache License, Version 2.0 (the "License") # run in project examples directory with: # sudo ./hello_world.py" # see trace_fields.py for a longer example from bcc import BPF # This may not work for 4.17 on x64, you need replace kprobe__sys_clone with kprobe____x64_sys_clone BPF(text='int kprobe__sys_clone(void *ctx) { bpf_trace_printk("Hello, World!\\n"); return 0; }').trace_print() bpfcc-0.12.0/examples/lua/000077500000000000000000000000001357404205000152465ustar00rootroot00000000000000bpfcc-0.12.0/examples/lua/CMakeLists.txt000066400000000000000000000002601357404205000200040ustar00rootroot00000000000000file(GLOB C_FILES *.c) file(GLOB LUA_FILES *.lua) install(FILES ${C_FILES} DESTINATION share/bcc/examples/lua) install(PROGRAMS ${LUA_FILES} DESTINATION share/bcc/examples/lua)bpfcc-0.12.0/examples/lua/bashreadline.c000066400000000000000000000006731357404205000200410ustar00rootroot00000000000000#include struct str_t { u64 pid; char str[80]; }; BPF_PERF_OUTPUT(events); int printret(struct pt_regs *ctx) { struct str_t data = {}; u32 pid; if (!PT_REGS_RC(ctx)) return 0; pid = bpf_get_current_pid_tgid(); data.pid = pid; bpf_probe_read(&data.str, sizeof(data.str), (void *)PT_REGS_RC(ctx)); events.perf_submit(ctx, &data, sizeof(data)); return 0; }; bpfcc-0.12.0/examples/lua/bashreadline.lua000077500000000000000000000021231357404205000203730ustar00rootroot00000000000000#!/usr/bin/env bcc-lua --[[ Copyright 2016 GitHub, Inc 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. ]] local ffi = require("ffi") return function(BPF) local b = BPF:new{src_file="bashreadline.c", debug=0} b:attach_uprobe{name="/bin/bash", sym="readline", fn_name="printret", retprobe=true} local function print_readline(cpu, event) print("%-9s %-6d %s" % {os.date("%H:%M:%S"), tonumber(event.pid), ffi.string(event.str)}) end b:get_table("events"):open_perf_buffer(print_readline, "struct { uint64_t pid; char str[80]; }", nil) print("%-9s %-6s %s" % {"TIME", "PID", "COMMAND"}) b:perf_buffer_poll_loop() end bpfcc-0.12.0/examples/lua/kprobe-latency.lua000066400000000000000000000047341357404205000207000ustar00rootroot00000000000000#!/usr/bin/env bcc-lua --[[ Copyright 2016 Marek Vavrusa 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. ]] -- This example program measures latency of block device operations and plots it -- in a histogram. It is similar to BPF example: -- https://github.com/torvalds/linux/blob/master/samples/bpf/tracex3_kern.c local ffi = require('ffi') local bpf = require('bpf') local S = require('syscall') -- Shared part of the program local bins = 100 local map = bpf.map('hash', 512, ffi.typeof('uint64_t'), ffi.typeof('uint64_t')) local lat_map = bpf.map('array', bins) -- Kernel-space part of the program local trace_start = bpf.kprobe('myprobe:blk_start_request', function (ptregs) map[ptregs.parm1] = time() end, false, -1, 0) local trace_end = bpf.kprobe('myprobe2:blk_account_io_completion', function (ptregs) -- The lines below are computing index -- using log10(x)*10 = log2(x)*10/log2(10) = log2(x)*3 -- index = 29 ~ 1 usec -- index = 59 ~ 1 msec -- index = 89 ~ 1 sec -- index = 99 ~ 10sec or more local delta = time() - map[ptregs.parm1] local index = 3 * math.log2(delta) if index >= bins then index = bins-1 end xadd(lat_map[index], 1) return true end, false, -1, 0) -- User-space part of the program pcall(function() local counter = 0 local sym = {' ',' ','.','.','*','*','o','o','O','O','#','#'} while true do -- Print header once in a while if counter % 50 == 0 then print('|1us |10us |100us |1ms |10ms |100ms |1s |10s') counter = 0 end counter = counter + 1 -- Collect all events local hist, events = {}, 0 for i=29,bins-1 do local v = tonumber(lat_map[i] or 0) if v > 0 then hist[i] = hist[i] or 0 + v events = events + v end end -- Print histogram symbols based on relative frequency local s = '' for i=29,bins-1 do if hist[i] then local c = math.ceil((hist[i] / (events + 1)) * #sym) s = s .. sym[c] else s = s .. ' ' end end print(s .. string.format(' ; %d events', events)) S.sleep(1) end end)bpfcc-0.12.0/examples/lua/kprobe-write.lua000066400000000000000000000021451357404205000203650ustar00rootroot00000000000000#!/usr/bin/env bcc-lua --[[ Copyright 2016 Marek Vavrusa 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. ]] -- Simple tracing example that executes a program on -- return from sys_write() and tracks the number of hits local ffi = require('ffi') local bpf = require('bpf') local S = require('syscall') -- Shared part of the program local map = bpf.map('array', 1) -- Kernel-space part of the program local probe = bpf.kprobe('myprobe:sys_write', function (ptregs) xadd(map[0], 1) end, true) -- User-space part of the program pcall(function() for _ = 1, 10 do print('hits: ', tonumber(map[0])) S.sleep(1) end end) bpfcc-0.12.0/examples/lua/memleak.lua000077500000000000000000000140641357404205000173740ustar00rootroot00000000000000#!/usr/bin/env bcc-lua --[[ Copyright 2016 GitHub, Inc 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. ]] local bpf_source = [[ #include struct alloc_info_t { u64 size; u64 timestamp_ns; int stack_id; }; BPF_HASH(sizes, u64); BPF_HASH(allocs, u64, struct alloc_info_t); BPF_STACK_TRACE(stack_traces, 10240); int alloc_enter(struct pt_regs *ctx, size_t size) { SIZE_FILTER if (SAMPLE_EVERY_N > 1) { u64 ts = bpf_ktime_get_ns(); if (ts % SAMPLE_EVERY_N != 0) return 0; } u64 pid = bpf_get_current_pid_tgid(); u64 size64 = size; sizes.update(&pid, &size64); if (SHOULD_PRINT) bpf_trace_printk("alloc entered, size = %u\n", size); return 0; } int alloc_exit(struct pt_regs *ctx) { u64 address = PT_REGS_RC(ctx); u64 pid = bpf_get_current_pid_tgid(); u64* size64 = sizes.lookup(&pid); struct alloc_info_t info = {0}; if (size64 == 0) return 0; // missed alloc entry info.size = *size64; sizes.delete(&pid); info.timestamp_ns = bpf_ktime_get_ns(); info.stack_id = stack_traces.get_stackid(ctx, STACK_FLAGS); allocs.update(&address, &info); if (SHOULD_PRINT) { bpf_trace_printk("alloc exited, size = %lu, result = %lx\n", info.size, address); } return 0; } int free_enter(struct pt_regs *ctx, void *address) { u64 addr = (u64)address; struct alloc_info_t *info = allocs.lookup(&addr); if (info == 0) return 0; allocs.delete(&addr); if (SHOULD_PRINT) { bpf_trace_printk("free entered, address = %lx, size = %lu\n", address, info->size); } return 0; } ]] return function(BPF, utils) local parser = utils.argparse("memleak", "Catch memory leaks") parser:flag("-t --trace") parser:flag("-a --show-allocs") parser:option("-p --pid"):convert(tonumber) parser:option("-i --interval", "", 5):convert(tonumber) parser:option("-o --older", "", 500):convert(tonumber) parser:option("-s --sample-rate", "", 1):convert(tonumber) parser:option("-z --min-size", ""):convert(tonumber) parser:option("-Z --max-size", ""):convert(tonumber) parser:option("-T --top", "", 10):convert(tonumber) local args = parser:parse() local size_filter = "" if args.min_size and args.max_size then size_filter = "if (size < %d || size > %d) return 0;" % {args.min_size, args.max_size} elseif args.min_size then size_filter = "if (size < %d) return 0;" % args.min_size elseif args.max_size then size_filter = "if (size > %d) return 0;" % args.max_size end local stack_flags = "0" if args.pid then stack_flags = stack_flags .. "|BPF_F_USER_STACK" end local text = bpf_source text = text:gsub("SIZE_FILTER", size_filter) text = text:gsub("STACK_FLAGS", stack_flags) text = text:gsub("SHOULD_PRINT", args.trace and "1" or "0") text = text:gsub("SAMPLE_EVERY_N", tostring(args.sample_rate)) local bpf = BPF:new{text=text, debug=0} local syms = nil local min_age_ns = args.older * 1e6 if args.pid then print("Attaching to malloc and free in pid %d, Ctrl+C to quit." % args.pid) bpf:attach_uprobe{name="c", sym="malloc", fn_name="alloc_enter", pid=args.pid} bpf:attach_uprobe{name="c", sym="malloc", fn_name="alloc_exit", pid=args.pid, retprobe=true} bpf:attach_uprobe{name="c", sym="free", fn_name="free_enter", pid=args.pid} else print("Attaching to kmalloc and kfree, Ctrl+C to quit.") bpf:attach_kprobe{event="__kmalloc", fn_name="alloc_enter"} bpf:attach_kprobe{event="__kmalloc", fn_name="alloc_exit", retprobe=true} -- TODO bpf:attach_kprobe{event="kfree", fn_name="free_enter"} end local syms = BPF.SymbolCache(args.pid) local allocs = bpf:get_table("allocs") local stack_traces = bpf:get_table("stack_traces") local function resolve(addr) local sym = syms:resolve(addr) if args.pid == nil then sym = sym .. " [kernel]" end return string.format("%s (%p)", sym, addr) end local function print_outstanding() local alloc_info = {} local now = utils.posix.time_ns() print("[%s] Top %d stacks with outstanding allocations:" % {os.date("%H:%M:%S"), args.top}) for address, info in allocs:items() do if now - min_age_ns >= tonumber(info.timestamp_ns) then local stack_id = tonumber(info.stack_id) if stack_id >= 0 then if alloc_info[stack_id] then local s = alloc_info[stack_id] s.count = s.count + 1 s.size = s.size + tonumber(info.size) else local stack = stack_traces:get(stack_id, resolve) alloc_info[stack_id] = { stack=stack, count=1, size=tonumber(info.size) } end end if args.show_allocs then print("\taddr = %p size = %s" % {address, tonumber(info.size)}) end end end local top = table.values(alloc_info) table.sort(top, function(a, b) return a.size > b.size end) for n, alloc in ipairs(top) do print("\t%d bytes in %d allocations from stack\n\t\t%s" % {alloc.size, alloc.count, table.concat(alloc.stack, "\n\t\t")}) if n == args.top then break end end end if args.trace then local pipe = bpf:pipe() while true do print(pipe:trace_fields()) end else while true do utils.posix.sleep(args.interval) syms:refresh() print_outstanding() end end end bpfcc-0.12.0/examples/lua/offcputime.lua000077500000000000000000000060521357404205000201200ustar00rootroot00000000000000#!/usr/bin/env bcc-lua --[[ Copyright 2016 GitHub, Inc 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. ]] local program = [[ #include #include #define MINBLOCK_US 1 struct key_t { char name[TASK_COMM_LEN]; int stack_id; }; BPF_HASH(counts, struct key_t); BPF_HASH(start, u32); BPF_STACK_TRACE(stack_traces, 10240); int oncpu(struct pt_regs *ctx, struct task_struct *prev) { u32 pid; u64 ts, *tsp; // record previous thread sleep time if (FILTER) { pid = prev->pid; ts = bpf_ktime_get_ns(); start.update(&pid, &ts); } // calculate current thread's delta time pid = bpf_get_current_pid_tgid(); tsp = start.lookup(&pid); if (tsp == 0) return 0; // missed start or filtered u64 delta = bpf_ktime_get_ns() - *tsp; start.delete(&pid); delta = delta / 1000; if (delta < MINBLOCK_US) return 0; // create map key u64 zero = 0, *val; struct key_t key = {}; int stack_flags = 0; /* if (!(prev->flags & PF_KTHREAD)) stack_flags |= BPF_F_USER_STACK; */ bpf_get_current_comm(&key.name, sizeof(key.name)); key.stack_id = stack_traces.get_stackid(ctx, stack_flags); val = counts.lookup_or_try_init(&key, &zero); if (val) { (*val) += delta; } return 0; } ]] return function(BPF, utils) local ffi = require("ffi") local parser = utils.argparse("offcputime", "Summarize off-cpu time") parser:flag("-u --user-only") parser:option("-p --pid"):convert(tonumber) parser:flag("-f --folded") parser:option("-d --duration", "duration to trace for", 9999999):convert(tonumber) local args = parser:parse() local ksym = BPF.SymbolCache() local filter = "1" local MAXDEPTH = 20 if args.pid then filter = "pid == %d" % args.pid elseif args.user_only then filter = "!(prev->flags & PF_KTHREAD)" end local text = program:gsub("FILTER", filter) local b = BPF:new{text=text} b:attach_kprobe{event="finish_task_switch", fn_name="oncpu"} if BPF.num_open_kprobes() == 0 then print("no functions matched. quitting...") return end print("Sleeping for %d seconds..." % args.duration) pcall(utils.posix.sleep, args.duration) print("Tracing...") local counts = b:get_table("counts") local stack_traces = b:get_table("stack_traces") for k, v in counts:items() do for addr in stack_traces:walk(tonumber(k.stack_id)) do print(" %-16p %s" % {addr, ksym:resolve(addr)}) end print(" %-16s %s" % {"-", ffi.string(k.name)}) print(" %d\n" % tonumber(v)) end end bpfcc-0.12.0/examples/lua/sock-parse-dns.lua000066400000000000000000000032361357404205000206060ustar00rootroot00000000000000#!/usr/bin/env bcc-lua --[[ Copyright 2016 Marek Vavrusa 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. ]] -- Simple parsing example of UDP/DNS that counts frequency of QTYPEs. -- It shows how to parse packet variable-length packet structures. local ffi = require("ffi") local bpf = require("bpf") local S = require("syscall") -- Shared part of the program local map = assert(bpf.map('array', 256)) -- Kernel-space part of the program local prog = bpf.socket('lo', function (skb) local ip = pkt.ip -- Accept only UDP messages if ip.proto ~= c.ip.proto_udp then return false end local udp = ip.udp -- Only messages >12 octets (DNS header) if udp.length < 12 then return false end -- Unroll QNAME (up to 2 labels) udp = udp.data + 12 local label = udp[0] if label > 0 then udp = udp + label + 1 label = udp[0] if label > 0 then udp = udp + label + 1 end end -- Track QTYPE (low types) if udp[0] == 0 then local qtype = udp[2] -- Low octet from QTYPE xadd(map[qtype], 1) end end) -- User-space part of the program for _ = 1, 10 do for k,v in map.pairs,map,0 do v = tonumber(v) if v > 0 then print(string.format('TYPE%d: %d', k, v)) end end S.sleep(1) endbpfcc-0.12.0/examples/lua/sock-parse-http.lua000066400000000000000000000037521357404205000210040ustar00rootroot00000000000000#!/usr/bin/env bcc-lua --[[ Copyright 2016 Marek Vavrusa 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. ]] -- Simple parsing example of TCP/HTTP that counts frequency of types of requests -- and shows more complicated pattern matching constructions and slices. -- Rewrite of a BCC example: -- https://github.com/iovisor/bcc/blob/master/examples/networking/http_filter/http-parse-simple.c local ffi = require("ffi") local bpf = require("bpf") local S = require("syscall") -- Shared part of the program local map = bpf.map('hash', 64) -- Kernel-space part of the program local prog = bpf.socket('lo', function (skb) -- Only ingress so we don't count twice on loopback if skb.ingress_ifindex == 0 then return end local data = pkt.ip.tcp.data -- Get TCP protocol dissector -- Continue only if we have 7 bytes of TCP data if data + 7 > skb.len then return end -- Fetch 4 bytes of TCP data and compare local h = data(0, 4) if h == 'HTTP' or h == 'GET ' or h == 'POST' or h == 'PUT ' or h == 'HEAD' or h == 'DELE' then -- If hash key doesn't exist, create it -- otherwise increment counter local v = map[h] if not v then map[h] = 1 else xadd(map[h], 1) end end end) -- User-space part of the program for _ = 1, 10 do local strkey = ffi.new('uint32_t [1]') local s = '' for k,v in map.pairs,map,0 do strkey[0] = bpf.ntoh(k) s = s..string.format('%s %d ', ffi.string(strkey, 4):match '^%s*(.-)%s*$', tonumber(v)) end if #s > 0 then print(s..'messages') end S.sleep(1) endbpfcc-0.12.0/examples/lua/sock-proto.lua000066400000000000000000000026341357404205000200560ustar00rootroot00000000000000#!/usr/bin/env bcc-lua --[[ Copyright 2016 Marek Vavrusa 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. ]] -- This program looks at IP, UDP and ICMP packets and -- increments counter for each packet of given type seen -- Rewrite of https://github.com/torvalds/linux/blob/master/samples/bpf/sock_example.c local ffi = require("ffi") local bpf = require("bpf") local S = require("syscall") -- Shared part of the program local map = bpf.map('hash', 256) map[1], map[6], map[17] = 0, 0, 0 -- Kernel-space part of the program bpf.socket('lo', function (skb) local proto = pkt.ip.proto -- Get byte (ip.proto) from frame at [23] xadd(map[proto], 1) -- Atomic `map[proto] += 1` end) -- User-space part of the program for _ = 1, 10 do local icmp, udp, tcp = map[1], map[17], map[6] print(string.format('TCP %d UDP %d ICMP %d packets', tonumber(tcp or 0), tonumber(udp or 0), tonumber(icmp or 0))) S.sleep(1) endbpfcc-0.12.0/examples/lua/sock-protolen.lua000066400000000000000000000030011357404205000205420ustar00rootroot00000000000000#!/usr/bin/env bcc-lua --[[ Copyright 2016 Marek Vavrusa 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. ]] -- This program counts total bytes received per-protocol in 64-bit counters. -- The map backend is array in this case to avoid key allocations. -- increments counter for each packet of given type seen -- Rewrite of https://github.com/torvalds/linux/blob/master/samples/bpf/sock_example.c local ffi = require("ffi") local bpf = require("bpf") local S = require("syscall") -- Shared part of the program local map = bpf.map('array', 256, ffi.typeof('uint32_t'), ffi.typeof('uint64_t')) -- Kernel-space part of the program bpf.socket('lo', function (skb) local proto = pkt.ip.proto -- Get byte (ip.proto) from frame at [23] xadd(map[proto], skb.len) -- Atomic `map[proto] += ` end) -- User-space part of the program for _ = 1, 10 do local icmp, udp, tcp = map[1], map[17], map[6] print(string.format('TCP %d UDP %d ICMP %d bytes', tonumber(tcp or 0), tonumber(udp or 0), tonumber(icmp or 0))) S.sleep(1) endbpfcc-0.12.0/examples/lua/strlen_count.lua000077500000000000000000000024241357404205000204750ustar00rootroot00000000000000#!/usr/bin/env bcc-lua --[[ Copyright 2016 GitHub, Inc 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. ]] assert(arg[1], "usage: strlen_count PID") local program = string.gsub([[ #include int printarg(struct pt_regs *ctx) { if (!PT_REGS_PARM1(ctx)) return 0; u32 pid = bpf_get_current_pid_tgid(); if (pid != PID) return 0; char str[128] = {}; bpf_probe_read(&str, sizeof(str), (void *)PT_REGS_PARM1(ctx)); bpf_trace_printk("strlen(\"%s\")\n", &str); return 0; }; ]], "PID", arg[1]) return function(BPF) local b = BPF:new{text=program, debug=0} b:attach_uprobe{name="c", sym="strlen", fn_name="printarg"} local pipe = b:pipe() while true do local task, pid, cpu, flags, ts, msg = pipe:trace_fields() print("%-18.9f %-16s %-6d %s" % {ts, task, pid, msg}) end end bpfcc-0.12.0/examples/lua/task_switch.lua000077500000000000000000000026041357404205000203010ustar00rootroot00000000000000#!/usr/bin/env bcc-lua --[[ Copyright 2016 GitHub, Inc 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. ]] local program = [[ #include #include struct key_t { u32 prev_pid; u32 curr_pid; }; // map_type, key_type, leaf_type, table_name, num_entry BPF_HASH(stats, struct key_t, u64, 1024); int count_sched(struct pt_regs *ctx, struct task_struct *prev) { struct key_t key = {}; u64 zero = 0, *val; key.curr_pid = bpf_get_current_pid_tgid(); key.prev_pid = prev->pid; val = stats.lookup_or_try_init(&key, &zero); if (val) { (*val)++; } return 0; } ]] return function(BPF) local b = BPF:new{text=program, debug=0} b:attach_kprobe{event="finish_task_switch", fn_name="count_sched"} print("Press any key...") io.read() local t = b:get_table("stats") for k, v in t:items() do print("task_switch[%d -> %d] = %d" % {k.prev_pid, k.curr_pid, tonumber(v)}) end end bpfcc-0.12.0/examples/lua/tracepoint-offcputime.lua000066400000000000000000000051321357404205000222610ustar00rootroot00000000000000#!/usr/bin/env bcc-lua --[[ Copyright 2016 Marek Vavrusa 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. ]] -- Summarize off-CPU time by stack trace -- Related tool: https://github.com/iovisor/bcc/blob/master/tools/offcputime.py local ffi = require('ffi') local bpf = require('bpf') local S = require('syscall') -- Create BPF maps -- TODO: made smaller to fit default memory limits local key_t = 'struct { char name[16]; int32_t stack_id; }' local starts = assert(bpf.map('hash', 128, ffi.typeof('uint32_t'), ffi.typeof('uint64_t'))) local counts = assert(bpf.map('hash', 128, ffi.typeof(key_t), ffi.typeof('uint64_t'))) local stack_traces = assert(bpf.map('stack_trace', 16)) -- Open tracepoint and attach BPF program -- The 'arg' parses tracepoint format automatically local tp = bpf.tracepoint('sched/sched_switch', function (arg) -- Update previous thread sleep time local pid = arg.prev_pid local now = time() starts[pid] = now -- Calculate current thread's delta time pid = arg.next_pid local from = starts[pid] if not from then return 0 end local delta = (now - from) / 1000 starts[pid] = nil -- Check if the delta is below 1us if delta < 1 then return end -- Create key for this thread local key = ffi.new(key_t) comm(key.name) key.stack_id = stack_id(stack_traces, BPF.F_FAST_STACK_CMP) -- Update current thread off cpu time with delta local val = counts[key] if not val then counts[key] = 0 end xadd(counts[key], delta) end, 0, -1) -- Helper: load kernel symbols ffi.cdef 'unsigned long long strtoull(const char *, char **, int);' local ksyms = {} for l in io.lines('/proc/kallsyms') do local addr, sym = l:match '(%w+) %w (%S+)' if addr then ksyms[ffi.C.strtoull(addr, nil, 16)] = sym end end -- User-space part of the program while true do for k,v in counts.pairs,counts,nil do local s = '' local traces = stack_traces[k.stack_id] if traces then for i, ip in ipairs(traces) do s = s .. string.format(" %-16p %s", ip, ksyms[ip]) end end s = s .. string.format(" %-16s %s", "-", ffi.string(k.name)) s = s .. string.format(" %d", tonumber(v)) print(s) end S.sleep(1) end bpfcc-0.12.0/examples/lua/uprobe-readline-perf.lua000066400000000000000000000036071357404205000217660ustar00rootroot00000000000000#!/usr/bin/env bcc-lua --[[ Copyright 2016 Marek Vavrusa 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. ]] -- Trace readline() call from all bash instances (print bash commands from all running shells). -- This is rough equivallent to `bashreadline` with output through perf event API. -- Source: http://www.brendangregg.com/blog/2016-02-08/linux-ebpf-bcc-uprobes.html local ffi = require('ffi') local bpf = require('bpf') local S = require('syscall') -- Perf event map local sample_t = 'struct { uint64_t pid; char str[80]; }' local events = bpf.map('perf_event_array') -- Kernel-space part of the program local probe = bpf.uprobe('/bin/bash:readline', function (ptregs) local sample = ffi.new(sample_t) sample.pid = pid_tgid() ffi.copy(sample.str, ffi.cast('char *', ptregs.ax)) -- Cast `ax` to string pointer and copy to buffer perf_submit(events, sample) -- Write buffer to perf event map end, true, -1, 0) -- User-space part of the program local log = events:reader(nil, 0, sample_t) -- Must specify PID or CPU_ID to observe print(' TASK-PID TIMESTAMP FUNCTION') print(' | | | |') while true do log:block() -- Wait until event reader is readable for _,e in log:read() do -- Collect available reader events print(string.format('%12s%-16s %-10s %s', '', tonumber(e.pid), os.date("%H:%M:%S"), ffi.string(e.str))) end end bpfcc-0.12.0/examples/lua/uprobe-readline.lua000066400000000000000000000030241357404205000210250ustar00rootroot00000000000000#!/usr/bin/env bcc-lua --[[ Copyright 2016 Marek Vavrusa 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. ]] -- Trace readline() call from all bash instances (print bash commands from all running shells). -- This is rough equivallent to `bashreadline` -- Source: http://www.brendangregg.com/blog/2016-02-08/linux-ebpf-bcc-uprobes.html local ffi = require('ffi') local bpf = require('bpf') local S = require('syscall') -- Kernel-space part of the program local probe = bpf.uprobe('/bin/bash:readline', function (ptregs) local line = ffi.new('char [40]') -- Create a 40 byte buffer on stack ffi.copy(line, ffi.cast('char *', ptregs.ax)) -- Cast `ax` to string pointer and copy to buffer print('%s\n', line) -- Print to trace_pipe end, true, -1, 0) -- User-space part of the program local ok, err = pcall(function() local log = bpf.tracelog() print(' TASK-PID CPU# TIMESTAMP FUNCTION') print(' | | | | |') while true do print(log:read()) end end) bpfcc-0.12.0/examples/lua/uprobe-tailkt.lua000066400000000000000000000047731357404205000205460ustar00rootroot00000000000000#!/usr/bin/env bcc-lua --[[ Copyright 2016 Marek Vavrusa 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. ]] -- Trace operations on keys matching given pattern in KyotoTycoon daemon. -- This can show you if certain keys were modified or read during the lifetime -- even if KT doesn't support this. It also shows how to attach to C++ mangled symbols. local ffi = require('ffi') local bpf = require('bpf') local S = require('syscall') local function help(err) print(string.format('%s [get|set] [key]', arg[0])) if err then print('error: '..err) end os.exit(1) end -- Accept the same format as ktremotemgr for clarity: local writeable, watch_key, klen = 'any', arg[2] or '*', 80 if arg[1] == 'get' then writeable = 0 elseif arg[1] == 'set' then writeable = 1 elseif arg[1] == '-h' or arg[1] == '--help' then help() elseif arg[1] and arg[1] ~= 'any' then help(string.format('bad cmd: "%s"', arg[1])) end if watch_key ~= '*' then klen = #watch_key end -- Find a good entrypoint that has both key and differentiates read/write in KT -- That is going to serve as an attachment point for BPF program -- ABI: bool accept(void *this, const char* kbuf, size_t ksiz, Visitor* visitor, bool writable) local key_type = string.format('char [%d]', klen) local probe = bpf.uprobe('/usr/local/bin/ktserver:kyotocabinet::StashDB::accept', function (ptregs) -- Watch either get/set or both if writeable ~= 'any' then if ptregs.parm5 ~= writeable then return end end local line = ffi.new(key_type) ffi.copy(line, ffi.cast('char *', ptregs.parm2)) -- Check if we're looking for specific key if watch_key ~= '*' then if ptregs.parm3 ~= klen then return false end if line ~= watch_key then return false end end print('%s write:%d\n', line, ptregs.parm5) end, false, -1, 0) -- User-space part of the program local ok, err = pcall(function() local log = bpf.tracelog() print(' TASK-PID CPU# TIMESTAMP FUNCTION') print(' | | | | |') while true do print(log:read()) end end) bpfcc-0.12.0/examples/lua/usdt_ruby.lua000077500000000000000000000022711357404205000177760ustar00rootroot00000000000000#!/usr/bin/env bcc-lua --[[ Copyright 2016 GitHub, Inc 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. ]] local program = [[ #include int trace_method(struct pt_regs *ctx) { uint64_t addr; bpf_usdt_readarg(2, ctx, &addr); char fn_name[128] = {}; bpf_probe_read(&fn_name, sizeof(fn_name), (void *)addr); bpf_trace_printk("%s(...)\n", fn_name); return 0; }; ]] return function(BPF, util) if not arg[1] then print("usage: rubysyms.lua PID") return end local u = util.USDT:new{pid=tonumber(arg[1])} u:enable_probe{probe="method__entry", fn_name="trace_method"} local b = BPF:new{text=program, usdt=u} local pipe = b:pipe() while true do print(pipe:trace_fields()) end end bpfcc-0.12.0/examples/networking/000077500000000000000000000000001357404205000166545ustar00rootroot00000000000000bpfcc-0.12.0/examples/networking/CMakeLists.txt000066400000000000000000000006551357404205000214220ustar00rootroot00000000000000set(EXAMPLE_FILES simulation.py) set(EXAMPLE_PROGRAMS simple_tc.py tc_perf_event.py) install(FILES ${EXAMPLE_FILES} DESTINATION share/bcc/examples/networking) install(PROGRAMS ${EXAMPLE_PROGRAMS} DESTINATION share/bcc/examples/networking) add_subdirectory(distributed_bridge) add_subdirectory(neighbor_sharing) add_subdirectory(vlan_learning) add_subdirectory(tunnel_monitor) add_subdirectory(http_filter) add_subdirectory(xdp) bpfcc-0.12.0/examples/networking/distributed_bridge/000077500000000000000000000000001357404205000225125ustar00rootroot00000000000000bpfcc-0.12.0/examples/networking/distributed_bridge/CMakeLists.txt000066400000000000000000000004571357404205000252600ustar00rootroot00000000000000set(EXAMPLE_FILES simulation.py tunnel.c tunnel_mesh.c) set(EXAMPLE_PROGRAMS main.py tunnel_mesh.py tunnel.py) install(FILES ${EXAMPLE_FILES} DESTINATION share/bcc/examples/networking/distributed_bridge) install(PROGRAMS ${EXAMPLE_PROGRAMS} DESTINATION share/bcc/examples/networking/distributed_bridge) bpfcc-0.12.0/examples/networking/distributed_bridge/main.py000077500000000000000000000056471357404205000240270ustar00rootroot00000000000000#!/usr/bin/python # Copyright (c) PLUMgrid, Inc. # Licensed under the Apache License, Version 2.0 (the "License") from sys import argv from builtins import input from pyroute2 import IPRoute, NetNS, IPDB, NSPopen from simulation import Simulation from subprocess import PIPE, call, Popen import re multicast = 1 dhcp = 0 gretap = 0 if "mesh" in argv: multicast = 0 if "dhcp" in argv: dhcp = 1 multicast = 0 if "gretap" in argv: gretap = 1 multicast = 0 print("multicast %d dhcp %d gretap %d" % (multicast, dhcp, gretap)) ipr = IPRoute() ipdb = IPDB(nl=ipr) num_hosts = 3 null = open("/dev/null", "w") class TunnelSimulation(Simulation): def __init__(self, ipdb): super(TunnelSimulation, self).__init__(ipdb) def start(self): # each entry is tuple of ns_ipdb, out_ifc, in_ifc host_info = [] for i in range(0, num_hosts): print("Launching host %i of %i" % (i + 1, num_hosts)) ipaddr = "172.16.1.%d/24" % (100 + i) host_info.append(self._create_ns("host%d" % i, ipaddr=ipaddr, disable_ipv6=True)) if multicast: cmd = ["python", "tunnel.py", str(i)] else: cmd = ["python", "tunnel_mesh.py", str(num_hosts), str(i), str(dhcp), str(gretap)] p = NSPopen(host_info[i][0].nl.netns, cmd, stdin=PIPE) self.processes.append(p) with self.ipdb.create(ifname="br-fabric", kind="bridge") as br: for host in host_info: br.add_port(host[1]) br.up() # get host0 bridge ip's host0_br_ips = [] if dhcp == 1: print("Waiting for host0 br1/br2 ip addresses available") for j in range(0, 2): interface = host_info[0][0].interfaces["br%d" % j] interface.wait_ip("99.1.0.0", 16, timeout=60) host0_br_ips = [x[0] for x in interface.ipaddr if x[0].startswith("99.1")] else: host0_br_ips.append("99.1.0.1") host0_br_ips.append("99.1.1.1") # traffic test print("Validating connectivity") for i in range(1, num_hosts): for j in range(0, 2): interface = host_info[i][0].interfaces["br%d" % j] interface.wait_ip("99.1.0.0", 16, timeout=60) print("VNI%d between host0 and host%d" % (10000 + j, i)) call(["ip", "netns", "exec", "host%d" % i, "ping", host0_br_ips[j], "-c", "3", "-i", "0.2", "-q"]) try: sim = TunnelSimulation(ipdb) sim.start() input("Press enter to quit:") for p in sim.processes: p.communicate(b"\n") except: if "sim" in locals(): for p in sim.processes: p.kill(); p.wait(); p.release() finally: if "br-fabric" in ipdb.interfaces: ipdb.interfaces["br-fabric"].remove().commit() if "sim" in locals(): sim.release() ipdb.release() null.close() bpfcc-0.12.0/examples/networking/distributed_bridge/simulation.py000077700000000000000000000000001357404205000302142../simulation.pyustar00rootroot00000000000000bpfcc-0.12.0/examples/networking/distributed_bridge/tunnel.c000066400000000000000000000045651357404205000241750ustar00rootroot00000000000000// Copyright (c) PLUMgrid, Inc. // Licensed under the Apache License, Version 2.0 (the "License") #include BPF_HASH(vni2if, u32, int, 1024); struct vni_key { u64 mac; int ifindex; int pad; }; struct host { u32 tunnel_id; u32 remote_ipv4; u64 rx_pkts; u64 tx_pkts; }; BPF_HASH(mac2host, struct vni_key, struct host); struct config { int tunnel_ifindex; }; BPF_HASH(conf, int, struct config, 1); // Handle packets from the encap device, demux into the dest tenant int handle_ingress(struct __sk_buff *skb) { u8 *cursor = 0; struct ethernet_t *ethernet = cursor_advance(cursor, sizeof(*ethernet)); struct bpf_tunnel_key tkey = {}; bpf_skb_get_tunnel_key(skb, &tkey, offsetof(struct bpf_tunnel_key, remote_ipv6[1]), 0); int *ifindex = vni2if.lookup(&tkey.tunnel_id); if (ifindex) { //bpf_trace_printk("ingress tunnel_id=%d ifindex=%d\n", tkey.tunnel_id, *ifindex); struct vni_key vk = {ethernet->src, *ifindex, 0}; struct host *src_host = mac2host.lookup_or_try_init(&vk, &(struct host){tkey.tunnel_id, tkey.remote_ipv4, 0, 0}); if (src_host) { lock_xadd(&src_host->rx_pkts, 1); } bpf_clone_redirect(skb, *ifindex, 1/*ingress*/); } else { bpf_trace_printk("ingress invalid tunnel_id=%d\n", tkey.tunnel_id); } return 1; } // Handle packets from the tenant, mux into the encap device int handle_egress(struct __sk_buff *skb) { u8 *cursor = 0; int one = 1; struct config *cfg = conf.lookup(&one); if (!cfg) return 1; struct ethernet_t *ethernet = cursor_advance(cursor, sizeof(*ethernet)); struct vni_key vk = {ethernet->dst, skb->ifindex, 0}; struct host *dst_host = mac2host.lookup(&vk); struct bpf_tunnel_key tkey = {}; if (dst_host) { u32 zero = 0; tkey.tunnel_id = dst_host->tunnel_id; tkey.remote_ipv4 = dst_host->remote_ipv4; bpf_skb_set_tunnel_key(skb, &tkey, offsetof(struct bpf_tunnel_key, remote_ipv6[1]), 0); lock_xadd(&dst_host->tx_pkts, 1); } else { struct bpf_tunnel_key tkey = {}; vk.mac = 0xFFFFFFFFFFFFull; dst_host = mac2host.lookup(&vk); if (!dst_host) return 1; tkey.tunnel_id = dst_host->tunnel_id; tkey.remote_ipv4 = dst_host->remote_ipv4; bpf_skb_set_tunnel_key(skb, &tkey, offsetof(struct bpf_tunnel_key, remote_ipv6[1]), 0); } bpf_clone_redirect(skb, cfg->tunnel_ifindex, 0/*egress*/); return 1; } bpfcc-0.12.0/examples/networking/distributed_bridge/tunnel.py000077500000000000000000000050561357404205000244020ustar00rootroot00000000000000#!/usr/bin/python # Copyright (c) PLUMgrid, Inc. # Licensed under the Apache License, Version 2.0 (the "License") from sys import argv from bcc import BPF from builtins import input from ctypes import c_int, c_uint from http.server import HTTPServer, SimpleHTTPRequestHandler import json from netaddr import EUI, IPAddress from pyroute2 import IPRoute, NetNS, IPDB, NSPopen from socket import htons, AF_INET from threading import Thread from subprocess import call host_id = int(argv[1]) b = BPF(src_file="tunnel.c") ingress_fn = b.load_func("handle_ingress", BPF.SCHED_CLS) egress_fn = b.load_func("handle_egress", BPF.SCHED_CLS) mac2host = b.get_table("mac2host") vni2if = b.get_table("vni2if") conf = b.get_table("conf") ipr = IPRoute() ipdb = IPDB(nl=ipr) ifc = ipdb.interfaces.eth0 mcast = IPAddress("239.1.1.1") # ifcs to cleanup at the end ifc_gc = [] def run(): ipdb.routes.add({"dst": "224.0.0.0/4", "oif": ifc.index}).commit() with ipdb.create(ifname="vxlan0", kind="vxlan", vxlan_id=0, vxlan_link=ifc, vxlan_port=4789, vxlan_group=str(mcast), vxlan_flowbased=True, vxlan_collect_metadata=True, vxlan_learning=False) as vx: vx.up() ifc_gc.append(vx.ifname) conf[c_int(1)] = c_int(vx.index) ipr.tc("add", "ingress", vx.index, "ffff:") ipr.tc("add-filter", "bpf", vx.index, ":1", fd=ingress_fn.fd, name=ingress_fn.name, parent="ffff:", action="drop", classid=1) for i in range(0, 2): vni = 10000 + i with ipdb.create(ifname="br%d" % i, kind="bridge") as br: v = ipdb.create(ifname="dummy%d" % i, kind="dummy").up().commit() mcast_key = mac2host.Key(0xFFFFFFFFFFFF, v.index, 0) mcast_leaf = mac2host.Leaf(vni, mcast.value, 0, 0) mac2host[mcast_key] = mcast_leaf ipr.tc("add", "sfq", v.index, "1:") ipr.tc("add-filter", "bpf", v.index, ":1", fd=egress_fn.fd, name=egress_fn.name, parent="1:", action="drop", classid=1) br.add_port(v) br.up() ifc_gc.append(v.ifname) ifc_gc.append(br.ifname) vni2if[c_uint(vni)] = c_int(v.index) ipaddr = "99.1.%d.%d/24" % (i, host_id + 1) br.add_ip(ipaddr) try: run() ipdb.release() input("") print("---") for k, v in mac2host.items(): print(EUI(k.mac), k.ifindex, IPAddress(v.remote_ipv4), v.tunnel_id, v.rx_pkts, v.tx_pkts) finally: for v in ifc_gc: call(["ip", "link", "del", v]) bpfcc-0.12.0/examples/networking/distributed_bridge/tunnel_mesh.c000066400000000000000000000034531357404205000252040ustar00rootroot00000000000000// Copyright (c) PLUMgrid, Inc. // Licensed under the Apache License, Version 2.0 (the "License") #include struct config { int tunnel_ifindex; }; BPF_HASH(conf, int, struct config, 1); struct tunnel_key { u32 tunnel_id; u32 remote_ipv4; }; BPF_HASH(tunkey2if, struct tunnel_key, int, 1024); BPF_HASH(if2tunkey, int, struct tunnel_key, 1024); // Handle packets from the encap device, demux into the dest tenant int handle_ingress(struct __sk_buff *skb) { struct bpf_tunnel_key tkey = {}; struct tunnel_key key; bpf_skb_get_tunnel_key(skb, &tkey, offsetof(struct bpf_tunnel_key, remote_ipv6[1]), 0); key.tunnel_id = tkey.tunnel_id; key.remote_ipv4 = tkey.remote_ipv4; int *ifindex = tunkey2if.lookup(&key); if (ifindex) { //bpf_trace_printk("ingress tunnel_id=%d remote_ip=%08x ifindex=%d\n", // key.tunnel_id, key.remote_ipv4, *ifindex); // mark from external skb->tc_index = 1; bpf_clone_redirect(skb, *ifindex, 1/*ingress*/); } else { bpf_trace_printk("ingress invalid tunnel_id=%d\n", key.tunnel_id); } return 1; } // Handle packets from the tenant, mux into the encap device int handle_egress(struct __sk_buff *skb) { int ifindex = skb->ifindex; struct bpf_tunnel_key tkey = {}; struct tunnel_key *key_p; int one = 1; struct config *cfg = conf.lookup(&one); if (!cfg) return 1; if (skb->tc_index) { //bpf_trace_printk("from external\n"); // don't send it back out to encap device return 1; } key_p = if2tunkey.lookup(&ifindex); if (key_p) { tkey.tunnel_id = key_p->tunnel_id; tkey.remote_ipv4 = key_p->remote_ipv4; bpf_skb_set_tunnel_key(skb, &tkey, offsetof(struct bpf_tunnel_key, remote_ipv6[1]), 0); bpf_clone_redirect(skb, cfg->tunnel_ifindex, 0/*egress*/); } return 1; } bpfcc-0.12.0/examples/networking/distributed_bridge/tunnel_mesh.py000066400000000000000000000122611357404205000254070ustar00rootroot00000000000000#!/usr/bin/python # Copyright (c) PLUMgrid, Inc. # Licensed under the Apache License, Version 2.0 (the "License") from sys import argv from bcc import BPF from builtins import input from ctypes import c_int, c_uint from http.server import HTTPServer, SimpleHTTPRequestHandler import json from netaddr import EUI, IPAddress from pyroute2 import IPRoute, NetNS, IPDB, NSPopen from socket import htons, AF_INET from threading import Thread from subprocess import call, Popen, PIPE num_hosts = int(argv[1]) host_id = int(argv[2]) dhcp = int(argv[3]) gretap = int(argv[4]) b = BPF(src_file="tunnel_mesh.c") ingress_fn = b.load_func("handle_ingress", BPF.SCHED_CLS) egress_fn = b.load_func("handle_egress", BPF.SCHED_CLS) tunkey2if = b.get_table("tunkey2if") if2tunkey = b.get_table("if2tunkey") conf = b.get_table("conf") ipr = IPRoute() ipdb = IPDB(nl=ipr) ifc = ipdb.interfaces.eth0 # ifcs to cleanup at the end ifc_gc = [] # dhcp server and client processes d_serv = [] d_client = [] def run(): if gretap: with ipdb.create(ifname="gretap1", kind="gretap", gre_ikey=0, gre_okey=0, gre_local='172.16.1.%d' % (100 + host_id), gre_ttl=16, gre_collect_metadata=1) as vx: vx.up() ifc_gc.append(vx.ifname) else: with ipdb.create(ifname="vxlan0", kind="vxlan", vxlan_id=0, vxlan_link=ifc, vxlan_port=4789, vxlan_collect_metadata=True, vxlan_learning=False) as vx: vx.up() ifc_gc.append(vx.ifname) conf[c_int(1)] = c_int(vx.index) ipr.tc("add", "ingress", vx.index, "ffff:") ipr.tc("add-filter", "bpf", vx.index, ":1", fd=ingress_fn.fd, name=ingress_fn.name, parent="ffff:", action="drop", classid=1) for j in range(0, 2): vni = 10000 + j with ipdb.create(ifname="br%d" % j, kind="bridge") as br: for i in range(0, num_hosts): if i != host_id: v = ipdb.create(ifname="dummy%d%d" % (j , i), kind="dummy").up().commit() ipaddr = "172.16.1.%d" % (100 + i) tunkey2if_key = tunkey2if.Key(vni) tunkey2if_key.remote_ipv4 = IPAddress(ipaddr) tunkey2if_leaf = tunkey2if.Leaf(v.index) tunkey2if[tunkey2if_key] = tunkey2if_leaf if2tunkey_key = if2tunkey.Key(v.index) if2tunkey_leaf = if2tunkey.Leaf(vni) if2tunkey_leaf.remote_ipv4 = IPAddress(ipaddr) if2tunkey[if2tunkey_key] = if2tunkey_leaf ipr.tc("add", "sfq", v.index, "1:") ipr.tc("add-filter", "bpf", v.index, ":1", fd=egress_fn.fd, name=egress_fn.name, parent="1:", action="drop", classid=1) br.add_port(v) br.up() ifc_gc.append(v.ifname) if dhcp == 0: ipaddr = "99.1.%d.%d/24" % (j, host_id + 1) br.add_ip(ipaddr) ifc_gc.append(br.ifname) # dhcp server only runs on host 0 if dhcp == 1 and host_id == 0: for j in range(0, 2): v1 = "dhcp%d_v1" % j v2 = "dhcp%d_v2" % j br = ipdb.interfaces["br%d" % j] with ipdb.create(ifname=v1, kind="veth", peer=v2) as v: v.up() br.add_port(ipdb.interfaces[v1]).commit() dhcp_v2 = ipdb.interfaces[v2] dhcp_v2.add_ip("99.1.%d.1/24" % j).up().commit() call(["/bin/rm", "-f", "/tmp/dnsmasq.%d.leases" % j]) cmd = ["dnsmasq", "-d", "--bind-interfaces", "--strict-order", "--conf-file=", "--dhcp-range", "99.1.%d.2,99.1.%d.254,255.255.255.0,12h" % (j, j), "--dhcp-no-override", "--except-interface=lo", "--interface=dhcp%d_v2" % j, "--dhcp-authoritative", "--dhcp-leasefile=/tmp/dnsmasq.%d.leases" % j] d_serv.append(Popen(cmd, stdout=PIPE, stderr=PIPE)) # dhcp client to assign ip address for each bridge if dhcp == 1: for j in range(0, 2): call(["/bin/rm", "-rf", "/tmp/dhcp_%d_%d" % (host_id, j)]) call(["mkdir", "/tmp/dhcp_%d_%d" % (host_id, j)]) call(["touch", "/tmp/dhcp_%d_%d/dhclient.conf" % (host_id, j)]) call(["touch", "/tmp/dhcp_%d_%d/dhclient.lease" % (host_id, j)]) cmd = ["dhclient", "-d", "br%d" % j, "-cf", "/tmp/dhcp_%d_%d/dhclient.conf" % (host_id, j), "-lf", "/tmp/dhcp_%d_%d/dhclient.lease" % (host_id, j)] d_client.append(Popen(cmd, stdout=PIPE, stderr=PIPE)) # make sure we get address for eth0 retry = -1 while retry < 0: check = Popen(["ip", "addr", "show", "br%d" % j], stdout=PIPE, stderr=PIPE) out = check.stdout.read() checkip = b"99.1.%d" % j retry = out.find(checkip) try: run() input("") finally: for v in ifc_gc: call(["ip", "link", "del", v]) ipdb.release() for p in d_client: p.kill() for p in d_serv: p.kill() bpfcc-0.12.0/examples/networking/dns_matching/000077500000000000000000000000001357404205000213125ustar00rootroot00000000000000bpfcc-0.12.0/examples/networking/dns_matching/dns_matching.c000066400000000000000000000044771357404205000241300ustar00rootroot00000000000000/* * dns_matching.c Drop DNS packets requesting DNS name contained in hash map * For Linux, uses BCC, eBPF. See .py file. * * Copyright (c) 2016 Rudi Floren. * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. * * 11-May-2016 Rudi Floren Created this. */ #include #include #include #include #include #include #include #define ETH_LEN 14 struct dns_hdr_t { uint16_t id; uint16_t flags; uint16_t qdcount; uint16_t ancount; uint16_t nscount; uint16_t arcount; } BPF_PACKET_HEADER; struct dns_query_flags_t { uint16_t qtype; uint16_t qclass; } BPF_PACKET_HEADER; struct dns_char_t { char c; } BPF_PACKET_HEADER; struct Key { unsigned char p[255]; }; struct Leaf { // Not really needed in this example unsigned char p[4]; }; BPF_HASH(cache, struct Key, struct Leaf, 128); int dns_matching(struct __sk_buff *skb) { u8 *cursor = 0; struct Key key = {}; // Check of ethernet/IP frame. struct ethernet_t *ethernet = cursor_advance(cursor, sizeof(*ethernet)); if(ethernet->type == ETH_P_IP) { // Check for UDP. struct ip_t *ip = cursor_advance(cursor, sizeof(*ip)); u16 hlen_bytes = ip->hlen << 2; if(ip->nextp == IPPROTO_UDP) { // Check for Port 53, DNS packet. struct udp_t *udp = cursor_advance(cursor, sizeof(*udp)); if(udp->dport == 53){ struct dns_hdr_t *dns_hdr = cursor_advance(cursor, sizeof(*dns_hdr)); // Do nothing if packet is not a request. if((dns_hdr->flags >>15) != 0) { // Exit if this packet is not a request. return -1; } u16 i = 0; struct dns_char_t *c; #pragma unroll for(i = 0; i<255;i++){ c = cursor_advance(cursor, 1); if (c->c == 0) break; key.p[i] = c->c; } struct Leaf * lookup_leaf = cache.lookup(&key); // If DNS name is contained in our map, keep the packet if(lookup_leaf) { bpf_trace_printk("Matched1\n"); return -1; } } } } // Drop the packet return 0; } bpfcc-0.12.0/examples/networking/dns_matching/dns_matching.py000077500000000000000000000063421357404205000243320ustar00rootroot00000000000000#!/usr/bin/python from __future__ import print_function from bcc import BPF from ctypes import * import os import sys import fcntl import dnslib import argparse def encode_dns(name): if len(name) + 1 > 255: raise Exception("DNS Name too long.") b = bytearray() for element in name.split('.'): sublen = len(element) if sublen > 63: raise ValueError('DNS label %s is too long' % element) b.append(sublen) b.extend(element.encode('ascii')) b.append(0) # Add 0-len octet label for the root server return b def add_cache_entry(cache, name): key = cache.Key() key_len = len(key.p) name_buffer = encode_dns(name) # Pad the buffer with null bytes if it is too short name_buffer.extend((0,) * (key_len - len(name_buffer))) key.p = (c_ubyte * key_len).from_buffer(name_buffer) leaf = cache.Leaf() leaf.p = (c_ubyte * 4).from_buffer(bytearray(4)) cache[key] = leaf parser = argparse.ArgumentParser(usage='For detailed information about usage,\ try with -h option') req_args = parser.add_argument_group("Required arguments") req_args.add_argument("-i", "--interface", type=str, default="", help="Interface name, defaults to all if unspecified.") req_args.add_argument("-d", "--domains", type=str, required=True, nargs="+", help='List of domain names separated by space. For example: -d abc.def xyz.mno') args = parser.parse_args() # initialize BPF - load source code from http-parse-simple.c bpf = BPF(src_file = "dns_matching.c", debug=0) # print(bpf.dump_func("dns_test")) #load eBPF program http_filter of type SOCKET_FILTER into the kernel eBPF vm #more info about eBPF program types #http://man7.org/linux/man-pages/man2/bpf.2.html function_dns_matching = bpf.load_func("dns_matching", BPF.SOCKET_FILTER) #create raw socket, bind it to user provided interface #attach bpf program to socket created BPF.attach_raw_socket(function_dns_matching, args.interface) # Get the table. cache = bpf.get_table("cache") # Add cache entries for e in args.domains: print(">>>> Adding map entry: ", e) add_cache_entry(cache, e) print("\nTry to lookup some domain names using nslookup from another terminal.") print("For example: nslookup foo.bar") print("\nBPF program will filter-in DNS packets which match with map entries.") print("Packets received by user space program will be printed here") print("\nHit Ctrl+C to end...") socket_fd = function_dns_matching.sock fl = fcntl.fcntl(socket_fd, fcntl.F_GETFL) fcntl.fcntl(socket_fd, fcntl.F_SETFL, fl & (~os.O_NONBLOCK)) while 1: #retrieve raw packet from socket try: packet_str = os.read(socket_fd, 2048) except KeyboardInterrupt: sys.exit(0) packet_bytearray = bytearray(packet_str) ETH_HLEN = 14 UDP_HLEN = 8 #IP HEADER #calculate ip header length ip_header_length = packet_bytearray[ETH_HLEN] #load Byte ip_header_length = ip_header_length & 0x0F #mask bits 0..3 ip_header_length = ip_header_length << 2 #shift to obtain length #calculate payload offset payload_offset = ETH_HLEN + ip_header_length + UDP_HLEN payload = packet_bytearray[payload_offset:] # pass the payload to dnslib for parsing dnsrec = dnslib.DNSRecord.parse(payload) print (dnsrec.questions, "\n") bpfcc-0.12.0/examples/networking/http_filter/000077500000000000000000000000001357404205000212005ustar00rootroot00000000000000bpfcc-0.12.0/examples/networking/http_filter/CMakeLists.txt000066400000000000000000000004331357404205000237400ustar00rootroot00000000000000set(FILES http-parse-complete.c http-parse-simple.c README.md) set(PROGRAMS http-parse-complete.py http-parse-simple.py) install(FILES ${FILES} DESTINATION share/bcc/examples/networking/http_filter) install(PROGRAMS ${PROGRAMS} DESTINATION share/bcc/examples/networking/http_filter) bpfcc-0.12.0/examples/networking/http_filter/README.md000066400000000000000000000040671357404205000224660ustar00rootroot00000000000000# HTTP Filter eBPF application that parses HTTP packets and extracts (and prints on screen) the URL contained in the GET/POST request. [eBPF HTTP Filter - Short Presentation](https://github.com/iovisor/bpf-docs/blob/master/ebpf_http_filter.pdf) ## Usage Example $ sudo python http-parse-complete.py GET /pipermail/iovisor-dev/ HTTP/1.1 HTTP/1.1 200 OK GET /favicon.ico HTTP/1.1 HTTP/1.1 404 Not Found GET /pipermail/iovisor-dev/2016-January/thread.html HTTP/1.1 HTTP/1.1 200 OK GET /pipermail/iovisor-dev/2016-January/000046.html HTTP/1.1 HTTP/1.1 200 OK ## Implementation overview The implementation is split in two portions: the former that exploits eBPF code, the latter that performs some additional processing in user space (the python wrapper). ### First part: eBPF filter This component filters IP and TCP packets containing the "HTTP", "GET", "POST" strings in their payload and all subsequent packets belonging to the same session, having the same (ip.src,ip.dst,port.src,port.dst) tuple. The program is loaded as PROG_TYPE_SOCKET_FILTER and attached to a socket, bind to eth0. Matching packets are forwarded to user space, the others are dropped by the filter. ### Second part: python code in user space The Python script reads filtered raw packets from the socket, if necessary reassembles packets belonging to the same session, and prints on stdout the first line of the HTTP GET/POST request. ## Simple vs. complete Two versions of this code are available in this repository: * simple version: it does not handle URLs that span across multiple packets. For instance, if the URL is too long it shows only the portion contained in the first packet. * complete version: it is able to cope with URLs spanning across multiple packets; if such a situation is detected, the code reassembles packets belonging to the same session and prints the complete URL. ## How to execute this sample This sample can be executed by typing either one the two commands below: $ sudo python http-parse-simple.py $ sudo python http-parse-complete.py bpfcc-0.12.0/examples/networking/http_filter/http-parse-complete.c000066400000000000000000000104541357404205000252450ustar00rootroot00000000000000#include #include #include #define IP_TCP 6 #define ETH_HLEN 14 struct Key { u32 src_ip; //source ip u32 dst_ip; //destination ip unsigned short src_port; //source port unsigned short dst_port; //destination port }; struct Leaf { int timestamp; //timestamp in ns }; //BPF_TABLE(map_type, key_type, leaf_type, table_name, num_entry) //map //tracing sessions having same Key(dst_ip, src_ip, dst_port,src_port) BPF_HASH(sessions, struct Key, struct Leaf, 1024); /*eBPF program. Filter IP and TCP packets, having payload not empty and containing "HTTP", "GET", "POST" as first bytes of payload. AND ALL the other packets having same (src_ip,dst_ip,src_port,dst_port) this means belonging to the same "session" this additional check avoids url truncation, if url is too long userspace script, if necessary, reassembles urls splitted in 2 or more packets. if the program is loaded as PROG_TYPE_SOCKET_FILTER and attached to a socket return 0 -> DROP the packet return -1 -> KEEP the packet and return it to user space (userspace can read it from the socket_fd ) */ int http_filter(struct __sk_buff *skb) { u8 *cursor = 0; struct ethernet_t *ethernet = cursor_advance(cursor, sizeof(*ethernet)); //filter IP packets (ethernet type = 0x0800) if (!(ethernet->type == 0x0800)) { goto DROP; } struct ip_t *ip = cursor_advance(cursor, sizeof(*ip)); //filter TCP packets (ip next protocol = 0x06) if (ip->nextp != IP_TCP) { goto DROP; } u32 tcp_header_length = 0; u32 ip_header_length = 0; u32 payload_offset = 0; u32 payload_length = 0; struct Key key; struct Leaf zero = {0}; //calculate ip header length //value to multiply * 4 //e.g. ip->hlen = 5 ; IP Header Length = 5 x 4 byte = 20 byte ip_header_length = ip->hlen << 2; //SHL 2 -> *4 multiply //check ip header length against minimum if (ip_header_length < sizeof(*ip)) { goto DROP; } //shift cursor forward for dynamic ip header size void *_ = cursor_advance(cursor, (ip_header_length-sizeof(*ip))); struct tcp_t *tcp = cursor_advance(cursor, sizeof(*tcp)); //retrieve ip src/dest and port src/dest of current packet //and save it into struct Key key.dst_ip = ip->dst; key.src_ip = ip->src; key.dst_port = tcp->dst_port; key.src_port = tcp->src_port; //calculate tcp header length //value to multiply *4 //e.g. tcp->offset = 5 ; TCP Header Length = 5 x 4 byte = 20 byte tcp_header_length = tcp->offset << 2; //SHL 2 -> *4 multiply //calculate payload offset and length payload_offset = ETH_HLEN + ip_header_length + tcp_header_length; payload_length = ip->tlen - ip_header_length - tcp_header_length; //http://stackoverflow.com/questions/25047905/http-request-minimum-size-in-bytes //minimum length of http request is always geater than 7 bytes //avoid invalid access memory //include empty payload if(payload_length < 7) { goto DROP; } //load first 7 byte of payload into p (payload_array) //direct access to skb not allowed unsigned long p[7]; int i = 0; for (i = 0; i < 7; i++) { p[i] = load_byte(skb , payload_offset + i); } //find a match with an HTTP message //HTTP if ((p[0] == 'H') && (p[1] == 'T') && (p[2] == 'T') && (p[3] == 'P')) { goto HTTP_MATCH; } //GET if ((p[0] == 'G') && (p[1] == 'E') && (p[2] == 'T')) { goto HTTP_MATCH; } //POST if ((p[0] == 'P') && (p[1] == 'O') && (p[2] == 'S') && (p[3] == 'T')) { goto HTTP_MATCH; } //PUT if ((p[0] == 'P') && (p[1] == 'U') && (p[2] == 'T')) { goto HTTP_MATCH; } //DELETE if ((p[0] == 'D') && (p[1] == 'E') && (p[2] == 'L') && (p[3] == 'E') && (p[4] == 'T') && (p[5] == 'E')) { goto HTTP_MATCH; } //HEAD if ((p[0] == 'H') && (p[1] == 'E') && (p[2] == 'A') && (p[3] == 'D')) { goto HTTP_MATCH; } //no HTTP match //check if packet belong to an HTTP session struct Leaf * lookup_leaf = sessions.lookup(&key); if(lookup_leaf) { //send packet to userspace goto KEEP; } goto DROP; //keep the packet and send it to userspace retruning -1 HTTP_MATCH: //if not already present, insert into map sessions.lookup_or_try_init(&key,&zero); //send packet to userspace returning -1 KEEP: return -1; //drop the packet returning 0 DROP: return 0; } bpfcc-0.12.0/examples/networking/http_filter/http-parse-complete.py000066400000000000000000000252211357404205000254510ustar00rootroot00000000000000#!/usr/bin/python # #Bertrone Matteo - Polytechnic of Turin #November 2015 # #eBPF application that parses HTTP packets #and extracts (and prints on screen) the URL contained in the GET/POST request. # #eBPF program http_filter is used as SOCKET_FILTER attached to eth0 interface. #only packet of type ip and tcp containing HTTP GET/POST are returned to userspace, others dropped # #python script uses bcc BPF Compiler Collection by iovisor (https://github.com/iovisor/bcc) #and prints on stdout the first line of the HTTP GET/POST request containing the url from __future__ import print_function from bcc import BPF from ctypes import * from struct import * from sys import argv import sys import socket import os import struct import binascii import time CLEANUP_N_PACKETS = 50 #run cleanup every CLEANUP_N_PACKETS packets received MAX_URL_STRING_LEN = 8192 #max url string len (usually 8K) MAX_AGE_SECONDS = 30 #max age entry in bpf_sessions map #convert a bin string into a string of hex char #helper function to print raw packet in hex def toHex(s): lst = [] for ch in s: hv = hex(ord(ch)).replace('0x', '') if len(hv) == 1: hv = '0'+hv lst.append(hv) return reduce(lambda x,y:x+y, lst) #print str until CR+LF def printUntilCRLF(str): for k in range (0,len(str)-1): if (str[k] == '\n'): if (str[k-1] == '\r'): print ("") return print ("%c" % (str[k]), end = "") print("") return #cleanup function def cleanup(): #get current time in seconds current_time = int(time.time()) #looking for leaf having: #timestap == 0 --> update with current timestamp #AGE > MAX_AGE_SECONDS --> delete item for key,leaf in bpf_sessions.items(): try: current_leaf = bpf_sessions[key] #set timestamp if timestamp == 0 if (current_leaf.timestamp == 0): bpf_sessions[key] = bpf_sessions.Leaf(current_time) else: #delete older entries if (current_time - current_leaf.timestamp > MAX_AGE_SECONDS): del bpf_sessions[key] except: print("cleanup exception.") return #args def usage(): print("USAGE: %s [-i ]" % argv[0]) print("") print("Try '%s -h' for more options." % argv[0]) exit() #help def help(): print("USAGE: %s [-i ]" % argv[0]) print("") print("optional arguments:") print(" -h print this help") print(" -i if_name select interface if_name. Default is eth0") print("") print("examples:") print(" http-parse # bind socket to eth0") print(" http-parse -i wlan0 # bind socket to wlan0") exit() #arguments interface="eth0" if len(argv) == 2: if str(argv[1]) == '-h': help() else: usage() if len(argv) == 3: if str(argv[1]) == '-i': interface = argv[2] else: usage() if len(argv) > 3: usage() print ("binding socket to '%s'" % interface) # initialize BPF - load source code from http-parse-complete.c bpf = BPF(src_file = "http-parse-complete.c",debug = 0) #load eBPF program http_filter of type SOCKET_FILTER into the kernel eBPF vm #more info about eBPF program types #http://man7.org/linux/man-pages/man2/bpf.2.html function_http_filter = bpf.load_func("http_filter", BPF.SOCKET_FILTER) #create raw socket, bind it to interface #attach bpf program to socket created BPF.attach_raw_socket(function_http_filter, interface) #get file descriptor of the socket previously created inside BPF.attach_raw_socket socket_fd = function_http_filter.sock #create python socket object, from the file descriptor sock = socket.fromfd(socket_fd,socket.PF_PACKET,socket.SOCK_RAW,socket.IPPROTO_IP) #set it as blocking socket sock.setblocking(True) #get pointer to bpf map of type hash bpf_sessions = bpf.get_table("sessions") #packets counter packet_count = 0 #dictionary containing association #if url is not entirely contained in only one packet, save the firt part of it in this local dict #when I find \r\n in a next pkt, append and print all the url local_dictionary = {} while 1: #retrieve raw packet from socket packet_str = os.read(socket_fd,4096) #set packet length to max packet length on the interface packet_count += 1 #DEBUG - print raw packet in hex format #packet_hex = toHex(packet_str) #print ("%s" % packet_hex) #convert packet into bytearray packet_bytearray = bytearray(packet_str) #ethernet header length ETH_HLEN = 14 #IP HEADER #https://tools.ietf.org/html/rfc791 # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # |Version| IHL |Type of Service| Total Length | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # #IHL : Internet Header Length is the length of the internet header #value to multiply * 4 byte #e.g. IHL = 5 ; IP Header Length = 5 * 4 byte = 20 byte # #Total length: This 16-bit field defines the entire packet size, #including header and data, in bytes. #calculate packet total length total_length = packet_bytearray[ETH_HLEN + 2] #load MSB total_length = total_length << 8 #shift MSB total_length = total_length + packet_bytearray[ETH_HLEN+3] #add LSB #calculate ip header length ip_header_length = packet_bytearray[ETH_HLEN] #load Byte ip_header_length = ip_header_length & 0x0F #mask bits 0..3 ip_header_length = ip_header_length << 2 #shift to obtain length #retrieve ip source/dest ip_src_str = packet_str[ETH_HLEN+12:ETH_HLEN+16] #ip source offset 12..15 ip_dst_str = packet_str[ETH_HLEN+16:ETH_HLEN+20] #ip dest offset 16..19 ip_src = int(toHex(ip_src_str),16) ip_dst = int(toHex(ip_dst_str),16) #TCP HEADER #https://www.rfc-editor.org/rfc/rfc793.txt # 12 13 14 15 # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | Data | |U|A|P|R|S|F| | # | Offset| Reserved |R|C|S|S|Y|I| Window | # | | |G|K|H|T|N|N| | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # #Data Offset: This indicates where the data begins. #The TCP header is an integral number of 32 bits long. #value to multiply * 4 byte #e.g. DataOffset = 5 ; TCP Header Length = 5 * 4 byte = 20 byte #calculate tcp header length tcp_header_length = packet_bytearray[ETH_HLEN + ip_header_length + 12] #load Byte tcp_header_length = tcp_header_length & 0xF0 #mask bit 4..7 tcp_header_length = tcp_header_length >> 2 #SHR 4 ; SHL 2 -> SHR 2 #retrieve port source/dest port_src_str = packet_str[ETH_HLEN+ip_header_length:ETH_HLEN+ip_header_length+2] port_dst_str = packet_str[ETH_HLEN+ip_header_length+2:ETH_HLEN+ip_header_length+4] port_src = int(toHex(port_src_str),16) port_dst = int(toHex(port_dst_str),16) #calculate payload offset payload_offset = ETH_HLEN + ip_header_length + tcp_header_length #payload_string contains only packet payload payload_string = packet_str[(payload_offset):(len(packet_bytearray))] #CR + LF (substring to find) crlf = "\r\n" #current_Key contains ip source/dest and port source/map #useful for direct bpf_sessions map access current_Key = bpf_sessions.Key(ip_src,ip_dst,port_src,port_dst) #looking for HTTP GET/POST request if ((payload_string[:3] == "GET") or (payload_string[:4] == "POST") or (payload_string[:4] == "HTTP") \ or ( payload_string[:3] == "PUT") or (payload_string[:6] == "DELETE") or (payload_string[:4] == "HEAD") ): #match: HTTP GET/POST packet found if (crlf in payload_string): #url entirely contained in first packet -> print it all printUntilCRLF(payload_string) #delete current_Key from bpf_sessions, url already printed. current session not useful anymore try: del bpf_sessions[current_Key] except: print ("error during delete from bpf map ") else: #url NOT entirely contained in first packet #not found \r\n in payload. #save current part of the payload_string in dictionary local_dictionary[binascii.hexlify(current_Key)] = payload_string else: #NO match: HTTP GET/POST NOT found #check if the packet belong to a session saved in bpf_sessions if (current_Key in bpf_sessions): #check id the packet belong to a session saved in local_dictionary #(local_dictionary mantains HTTP GET/POST url not printed yet because splitted in N packets) if (binascii.hexlify(current_Key) in local_dictionary): #first part of the HTTP GET/POST url is already present in local dictionary (prev_payload_string) prev_payload_string = local_dictionary[binascii.hexlify(current_Key)] #looking for CR+LF in current packet. if (crlf in payload_string): #last packet. containing last part of HTTP GET/POST url splitted in N packets. #append current payload prev_payload_string += payload_string #print HTTP GET/POST url printUntilCRLF(prev_payload_string) #clean bpf_sessions & local_dictionary try: del bpf_sessions[current_Key] del local_dictionary[binascii.hexlify(current_Key)] except: print ("error deleting from map or dictionary") else: #NOT last packet. containing part of HTTP GET/POST url splitted in N packets. #append current payload prev_payload_string += payload_string #check if not size exceeding (usually HTTP GET/POST url < 8K ) if (len(prev_payload_string) > MAX_URL_STRING_LEN): print("url too long") try: del bpf_sessions[current_Key] del local_dictionary[binascii.hexlify(current_Key)] except: print ("error deleting from map or dict") #update dictionary local_dictionary[binascii.hexlify(current_Key)] = prev_payload_string else: #first part of the HTTP GET/POST url is NOT present in local dictionary #bpf_sessions contains invalid entry -> delete it try: del bpf_sessions[current_Key] except: print ("error del bpf_session") #check if dirty entry are present in bpf_sessions if (((packet_count) % CLEANUP_N_PACKETS) == 0): cleanup() bpfcc-0.12.0/examples/networking/http_filter/http-parse-simple.c000066400000000000000000000057231357404205000247310ustar00rootroot00000000000000#include #include #include #define IP_TCP 6 #define ETH_HLEN 14 /*eBPF program. Filter IP and TCP packets, having payload not empty and containing "HTTP", "GET", "POST" ... as first bytes of payload if the program is loaded as PROG_TYPE_SOCKET_FILTER and attached to a socket return 0 -> DROP the packet return -1 -> KEEP the packet and return it to user space (userspace can read it from the socket_fd ) */ int http_filter(struct __sk_buff *skb) { u8 *cursor = 0; struct ethernet_t *ethernet = cursor_advance(cursor, sizeof(*ethernet)); //filter IP packets (ethernet type = 0x0800) if (!(ethernet->type == 0x0800)) { goto DROP; } struct ip_t *ip = cursor_advance(cursor, sizeof(*ip)); //filter TCP packets (ip next protocol = 0x06) if (ip->nextp != IP_TCP) { goto DROP; } u32 tcp_header_length = 0; u32 ip_header_length = 0; u32 payload_offset = 0; u32 payload_length = 0; //calculate ip header length //value to multiply * 4 //e.g. ip->hlen = 5 ; IP Header Length = 5 x 4 byte = 20 byte ip_header_length = ip->hlen << 2; //SHL 2 -> *4 multiply //check ip header length against minimum if (ip_header_length < sizeof(*ip)) { goto DROP; } //shift cursor forward for dynamic ip header size void *_ = cursor_advance(cursor, (ip_header_length-sizeof(*ip))); struct tcp_t *tcp = cursor_advance(cursor, sizeof(*tcp)); //calculate tcp header length //value to multiply *4 //e.g. tcp->offset = 5 ; TCP Header Length = 5 x 4 byte = 20 byte tcp_header_length = tcp->offset << 2; //SHL 2 -> *4 multiply //calculate payload offset and length payload_offset = ETH_HLEN + ip_header_length + tcp_header_length; payload_length = ip->tlen - ip_header_length - tcp_header_length; //http://stackoverflow.com/questions/25047905/http-request-minimum-size-in-bytes //minimum length of http request is always geater than 7 bytes //avoid invalid access memory //include empty payload if(payload_length < 7) { goto DROP; } //load first 7 byte of payload into p (payload_array) //direct access to skb not allowed unsigned long p[7]; int i = 0; for (i = 0; i < 7; i++) { p[i] = load_byte(skb , payload_offset + i); } //find a match with an HTTP message //HTTP if ((p[0] == 'H') && (p[1] == 'T') && (p[2] == 'T') && (p[3] == 'P')) { goto KEEP; } //GET if ((p[0] == 'G') && (p[1] == 'E') && (p[2] == 'T')) { goto KEEP; } //POST if ((p[0] == 'P') && (p[1] == 'O') && (p[2] == 'S') && (p[3] == 'T')) { goto KEEP; } //PUT if ((p[0] == 'P') && (p[1] == 'U') && (p[2] == 'T')) { goto KEEP; } //DELETE if ((p[0] == 'D') && (p[1] == 'E') && (p[2] == 'L') && (p[3] == 'E') && (p[4] == 'T') && (p[5] == 'E')) { goto KEEP; } //HEAD if ((p[0] == 'H') && (p[1] == 'E') && (p[2] == 'A') && (p[3] == 'D')) { goto KEEP; } //no HTTP match goto DROP; //keep the packet and send it to userspace retruning -1 KEEP: return -1; //drop the packet returning 0 DROP: return 0; } bpfcc-0.12.0/examples/networking/http_filter/http-parse-simple.py000066400000000000000000000122161357404205000251320ustar00rootroot00000000000000#!/usr/bin/python # #Bertrone Matteo - Polytechnic of Turin #November 2015 # #eBPF application that parses HTTP packets #and extracts (and prints on screen) the URL contained in the GET/POST request. # #eBPF program http_filter is used as SOCKET_FILTER attached to eth0 interface. #only packet of type ip and tcp containing HTTP GET/POST are returned to userspace, others dropped # #python script uses bcc BPF Compiler Collection by iovisor (https://github.com/iovisor/bcc) #and prints on stdout the first line of the HTTP GET/POST request containing the url from __future__ import print_function from bcc import BPF from sys import argv import sys import socket import os #args def usage(): print("USAGE: %s [-i ]" % argv[0]) print("") print("Try '%s -h' for more options." % argv[0]) exit() #help def help(): print("USAGE: %s [-i ]" % argv[0]) print("") print("optional arguments:") print(" -h print this help") print(" -i if_name select interface if_name. Default is eth0") print("") print("examples:") print(" http-parse # bind socket to eth0") print(" http-parse -i wlan0 # bind socket to wlan0") exit() #arguments interface="eth0" if len(argv) == 2: if str(argv[1]) == '-h': help() else: usage() if len(argv) == 3: if str(argv[1]) == '-i': interface = argv[2] else: usage() if len(argv) > 3: usage() print ("binding socket to '%s'" % interface) # initialize BPF - load source code from http-parse-simple.c bpf = BPF(src_file = "http-parse-simple.c",debug = 0) #load eBPF program http_filter of type SOCKET_FILTER into the kernel eBPF vm #more info about eBPF program types #http://man7.org/linux/man-pages/man2/bpf.2.html function_http_filter = bpf.load_func("http_filter", BPF.SOCKET_FILTER) #create raw socket, bind it to interface #attach bpf program to socket created BPF.attach_raw_socket(function_http_filter, interface) #get file descriptor of the socket previously created inside BPF.attach_raw_socket socket_fd = function_http_filter.sock #create python socket object, from the file descriptor sock = socket.fromfd(socket_fd,socket.PF_PACKET,socket.SOCK_RAW,socket.IPPROTO_IP) #set it as blocking socket sock.setblocking(True) while 1: #retrieve raw packet from socket packet_str = os.read(socket_fd,2048) #DEBUG - print raw packet in hex format #packet_hex = toHex(packet_str) #print ("%s" % packet_hex) #convert packet into bytearray packet_bytearray = bytearray(packet_str) #ethernet header length ETH_HLEN = 14 #IP HEADER #https://tools.ietf.org/html/rfc791 # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # |Version| IHL |Type of Service| Total Length | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # #IHL : Internet Header Length is the length of the internet header #value to multiply * 4 byte #e.g. IHL = 5 ; IP Header Length = 5 * 4 byte = 20 byte # #Total length: This 16-bit field defines the entire packet size, #including header and data, in bytes. #calculate packet total length total_length = packet_bytearray[ETH_HLEN + 2] #load MSB total_length = total_length << 8 #shift MSB total_length = total_length + packet_bytearray[ETH_HLEN+3] #add LSB #calculate ip header length ip_header_length = packet_bytearray[ETH_HLEN] #load Byte ip_header_length = ip_header_length & 0x0F #mask bits 0..3 ip_header_length = ip_header_length << 2 #shift to obtain length #TCP HEADER #https://www.rfc-editor.org/rfc/rfc793.txt # 12 13 14 15 # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | Data | |U|A|P|R|S|F| | # | Offset| Reserved |R|C|S|S|Y|I| Window | # | | |G|K|H|T|N|N| | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # #Data Offset: This indicates where the data begins. #The TCP header is an integral number of 32 bits long. #value to multiply * 4 byte #e.g. DataOffset = 5 ; TCP Header Length = 5 * 4 byte = 20 byte #calculate tcp header length tcp_header_length = packet_bytearray[ETH_HLEN + ip_header_length + 12] #load Byte tcp_header_length = tcp_header_length & 0xF0 #mask bit 4..7 tcp_header_length = tcp_header_length >> 2 #SHR 4 ; SHL 2 -> SHR 2 #calculate payload offset payload_offset = ETH_HLEN + ip_header_length + tcp_header_length #print first line of the HTTP GET/POST request #line ends with 0xOD 0xOA (\r\n) #(if we want to print all the header print until \r\n\r\n) for i in range (payload_offset-1,len(packet_bytearray)-1): if (packet_bytearray[i]== 0x0A): if (packet_bytearray[i-1] == 0x0D): break print ("%c" % chr(packet_bytearray[i]), end = "") print("") bpfcc-0.12.0/examples/networking/neighbor_sharing/000077500000000000000000000000001357404205000221645ustar00rootroot00000000000000bpfcc-0.12.0/examples/networking/neighbor_sharing/CMakeLists.txt000066400000000000000000000004531357404205000247260ustar00rootroot00000000000000set(EXAMPLE_FILES README.txt simulation.py tc_neighbor_sharing.c) set(EXAMPLE_PROGRAMS tc_neighbor_sharing.py) install(FILES ${EXAMPLE_FILES} DESTINATION share/bcc/examples/networking/neighbor_sharing) install(PROGRAMS ${EXAMPLE_PROGRAMS} DESTINATION share/bcc/examples/networking/neighbor_sharing) bpfcc-0.12.0/examples/networking/neighbor_sharing/README.txt000066400000000000000000000052231357404205000236640ustar00rootroot00000000000000This example shows how a combination of BPF programs can be used to perform per-IP classification and rate limiting. The simulation in this example shows an example where N+M devices are combined and use 1 WAN. Traffic sent from/to the "neighbor" devices have their combined bandwidth capped at 128kbit, and the rest of the traffic can use an additional 1Mbit. This works by sharing a map between various tc ingress filters, each with a related set of bpf functions attached. The map stores a list of dynamically learned ip addresses that were seen on the neighbor devices and should be throttled. /------------\ | neigh1 --|->->->->->->->-| | | neigh2 --|->->->->->->->-| <-128kb-| /------\ | neigh3 --|->->->->->->->-| | wan0 | wan | | | ^ | br100 |-<-<-<--| sim | | | clsfy_neigh() | | ^ \------/ | lan1 ----|->->->->->->->-| <--1Mb--| | | lan2 ----|->->->->->->->-| | classify_wan() | ^ \------------/ | pass() | To run the example: $ sudo /path/to/neighbor_sharing/neighbor_sharing.py Starting netserver with host 'IN(6)ADDR_ANY' port '12865' and family AF_UNSPEC Starting netserver with host 'IN(6)ADDR_ANY' port '12865' and family AF_UNSPEC Starting netserver with host 'IN(6)ADDR_ANY' port '12865' and family AF_UNSPEC Starting netserver with host 'IN(6)ADDR_ANY' port '12865' and family AF_UNSPEC Starting netserver with host 'IN(6)ADDR_ANY' port '12865' and family AF_UNSPEC Network ready. Create a shell in the wan0 namespace and test with netperf (Neighbors are 172.16.1.100-102, and LAN clients are 172.16.1.150-151) e.g.: ip netns exec wan0 netperf -H 172.16.1.100 -l 2 Press enter when finished: In another shell: $ sudo ip netns exec wan0 netperf -H 172.16.1.100 -l 2 MIGRATED TCP STREAM TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 172.16.1.100 () port 0 AF_INET : demo Recv Send Send Socket Socket Message Elapsed Size Size Size Time Throughput bytes bytes bytes secs. 10^6bits/sec 87380 16384 16384 4.30 0.18 $ sudo ip netns exec wan0 netperf -H 172.16.1.150 -l 2 MIGRATED TCP STREAM TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 172.16.1.150 () port 0 AF_INET : demo Recv Send Send Socket Socket Message Elapsed Size Size Size Time Throughput bytes bytes bytes secs. 10^6bits/sec 87380 16384 16384 4.10 1.01 The bandwidth is throttled according to the IP. bpfcc-0.12.0/examples/networking/neighbor_sharing/simulation.py000077700000000000000000000000001357404205000276662../simulation.pyustar00rootroot00000000000000bpfcc-0.12.0/examples/networking/neighbor_sharing/tc_neighbor_sharing.c000066400000000000000000000031511357404205000263260ustar00rootroot00000000000000// Copyright (c) PLUMgrid, Inc. // Licensed under the Apache License, Version 2.0 (the "License") #include struct ipkey { u32 client_ip; }; BPF_HASH(learned_ips, struct ipkey, int, 1024); // trivial action int pass(struct __sk_buff *skb) { return 1; } // Process each wan packet, and determine if the packet is in the IP // table or not. Learned IPs are rate-limited and unclassified are not. // returns: > 0 when an IP is known // = 0 when an IP is not known, or non-IP traffic int classify_wan(struct __sk_buff *skb) { u8 *cursor = 0; ethernet: { struct ethernet_t *ethernet = cursor_advance(cursor, sizeof(*ethernet)); switch (ethernet->type) { case ETH_P_IP: goto ip; default: goto EOP; } } ip: { struct ip_t *ip = cursor_advance(cursor, sizeof(*ip)); u32 dip = ip->dst; struct ipkey key = {.client_ip=dip}; int *val = learned_ips.lookup(&key); if (val) return *val; goto EOP; } EOP: return 0; } // Process each neighbor packet, and store the source IP in the learned table. // Mark the inserted entry with a non-zero value to be used by the classify_wan // lookup. int classify_neighbor(struct __sk_buff *skb) { u8 *cursor = 0; ethernet: { struct ethernet_t *ethernet = cursor_advance(cursor, sizeof(*ethernet)); switch (ethernet->type) { case ETH_P_IP: goto ip; default: goto EOP; } } ip: { struct ip_t *ip = cursor_advance(cursor, sizeof(*ip)); u32 sip = ip->src; struct ipkey key = {.client_ip=sip}; int val = 1; learned_ips.insert(&key, &val); goto EOP; } EOP: return 1; } bpfcc-0.12.0/examples/networking/neighbor_sharing/tc_neighbor_sharing.py000077500000000000000000000056561357404205000265530ustar00rootroot00000000000000#!/usr/bin/python # Copyright (c) PLUMgrid, Inc. # Licensed under the Apache License, Version 2.0 (the "License") from bcc import BPF from pyroute2 import IPRoute, NetNS, IPDB, NSPopen from simulation import Simulation import sys from time import sleep from builtins import input ipr = IPRoute() ipdb = IPDB(nl=ipr) b = BPF(src_file="tc_neighbor_sharing.c", debug=0) wan_fn = b.load_func("classify_wan", BPF.SCHED_CLS) pass_fn = b.load_func("pass", BPF.SCHED_CLS) neighbor_fn = b.load_func("classify_neighbor", BPF.SCHED_CLS) num_neighbors = 3 num_locals = 2 # class to build the simulation network class SharedNetSimulation(Simulation): def __init__(self, ipdb): super(SharedNetSimulation, self).__init__(ipdb) # Create the wan namespace, and attach an ingress filter for throttling # inbound (download) traffic wan_if = self._create_ns("wan0", ipaddr="172.16.1.5/24")[1] ipr.tc("add", "ingress", wan_if["index"], "ffff:") ipr.tc("add-filter", "bpf", wan_if["index"], ":1", fd=wan_fn.fd, prio=1, name=wan_fn.name, parent="ffff:", action="drop", classid=1, rate="128kbit", burst=1024 * 32, mtu=16 * 1024) ipr.tc("add-filter", "bpf", wan_if["index"], ":2", fd=pass_fn.fd, prio=2, name=pass_fn.name, parent="ffff:", action="drop", classid=2, rate="1024kbit", burst=1024 * 32, mtu=16 * 1024) self.wan_if = wan_if # start the namespaces that compose the network, interconnect them with the # bridge, and attach the tc filters def start(self): neighbor_list = [] local_list = [] cmd = ["netserver", "-D"] for i in range(0, num_neighbors): ipaddr = "172.16.1.%d/24" % (i + 100) ret = self._create_ns("neighbor%d" % i, ipaddr=ipaddr, fn=neighbor_fn, cmd=cmd) neighbor_list.append(ret) for i in range(0, num_locals): ipaddr = "172.16.1.%d/24" % (i + 150) ret = self._create_ns("local%d" % i, ipaddr=ipaddr, fn=pass_fn, cmd=cmd) local_list.append(ret) with ipdb.create(ifname="br100", kind="bridge") as br100: for x in neighbor_list: br100.add_port(x[1]) for x in local_list: br100.add_port(x[1]) br100.add_port(self.wan_if) br100.up() try: sim = SharedNetSimulation(ipdb) sim.start() print("Network ready. Create a shell in the wan0 namespace and test with netperf") print(" (Neighbors are 172.16.1.100-%d, and LAN clients are 172.16.1.150-%d)" % (100 + num_neighbors - 1, 150 + num_locals - 1)) print(" e.g.: ip netns exec wan0 netperf -H 172.16.1.100 -l 2") input("Press enter when finished: ") finally: if "sim" in locals(): sim.release() if "br100" in ipdb.interfaces: ipdb.interfaces.br100.remove().commit() ipdb.release() bpfcc-0.12.0/examples/networking/simple_tc.py000077500000000000000000000015241357404205000212120ustar00rootroot00000000000000#!/usr/bin/python # Copyright (c) PLUMgrid, Inc. # Licensed under the Apache License, Version 2.0 (the "License") from bcc import BPF from pyroute2 import IPRoute ipr = IPRoute() text = """ int hello(struct __sk_buff *skb) { return 1; } """ try: b = BPF(text=text, debug=0) fn = b.load_func("hello", BPF.SCHED_CLS) ipr.link("add", ifname="t1a", kind="veth", peer="t1b") idx = ipr.link_lookup(ifname="t1a")[0] ipr.tc("add", "ingress", idx, "ffff:") ipr.tc("add-filter", "bpf", idx, ":1", fd=fn.fd, name=fn.name, parent="ffff:", action="ok", classid=1) ipr.tc("add", "sfq", idx, "1:") ipr.tc("add-filter", "bpf", idx, ":1", fd=fn.fd, name=fn.name, parent="1:", action="ok", classid=1) finally: if "idx" in locals(): ipr.link("del", index=idx) print("BPF tc functionality - SCHED_CLS: OK") bpfcc-0.12.0/examples/networking/simulation.py000066400000000000000000000105601357404205000214140ustar00rootroot00000000000000import os import subprocess import pyroute2 from pyroute2 import IPRoute, NetNS, IPDB, NSPopen class Simulation(object): """ Helper class for controlling multiple namespaces. Inherit from this class and setup your namespaces. """ def __init__(self, ipdb): self.ipdb = ipdb self.ipdbs = {} self.namespaces = [] self.processes = [] self.released = False # helper function to add additional ifc to namespace # if called directly outside Simulation class, "ifc_base_name" should be # different from "name", the "ifc_base_name" and "name" are the same for # the first ifc created by namespace def _ns_add_ifc(self, name, ns_ifc, ifc_base_name=None, in_ifc=None, out_ifc=None, ipaddr=None, macaddr=None, fn=None, cmd=None, action="ok", disable_ipv6=False): if name in self.ipdbs: ns_ipdb = self.ipdbs[name] else: try: nl=NetNS(name) self.namespaces.append(nl) except KeyboardInterrupt: # remove the namespace if it has been created pyroute2.netns.remove(name) raise ns_ipdb = IPDB(nl) self.ipdbs[nl.netns] = ns_ipdb if disable_ipv6: cmd1 = ["sysctl", "-q", "-w", "net.ipv6.conf.default.disable_ipv6=1"] nsp = NSPopen(ns_ipdb.nl.netns, cmd1) nsp.wait(); nsp.release() ns_ipdb.interfaces.lo.up().commit() if in_ifc: in_ifname = in_ifc.ifname with in_ifc as v: # move half of veth into namespace v.net_ns_fd = ns_ipdb.nl.netns else: # delete the potentially leaf-over veth interfaces ipr = IPRoute() for i in ipr.link_lookup(ifname='%sa' % ifc_base_name): ipr.link("del", index=i) ipr.close() try: out_ifc = self.ipdb.create(ifname="%sa" % ifc_base_name, kind="veth", peer="%sb" % ifc_base_name).commit() in_ifc = self.ipdb.interfaces[out_ifc.peer] in_ifname = in_ifc.ifname with in_ifc as v: v.net_ns_fd = ns_ipdb.nl.netns except KeyboardInterrupt: # explicitly remove the interface out_ifname = "%sa" % ifc_base_name if out_ifname in self.ipdb.interfaces: self.ipdb.interfaces[out_ifname].remove().commit() raise if out_ifc: out_ifc.up().commit() ns_ipdb.interfaces.lo.up().commit() ns_ipdb.initdb() in_ifc = ns_ipdb.interfaces[in_ifname] with in_ifc as v: v.ifname = ns_ifc if ipaddr: v.add_ip("%s" % ipaddr) if macaddr: v.address = macaddr v.up() if disable_ipv6: cmd1 = ["sysctl", "-q", "-w", "net.ipv6.conf.%s.disable_ipv6=1" % out_ifc.ifname] subprocess.call(cmd1) if fn and out_ifc: self.ipdb.nl.tc("add", "ingress", out_ifc["index"], "ffff:") self.ipdb.nl.tc("add-filter", "bpf", out_ifc["index"], ":1", fd=fn.fd, name=fn.name, parent="ffff:", action=action, classid=1) if cmd: self.processes.append(NSPopen(ns_ipdb.nl.netns, cmd)) return (ns_ipdb, out_ifc, in_ifc) # helper function to create a namespace and a veth connecting it def _create_ns(self, name, in_ifc=None, out_ifc=None, ipaddr=None, macaddr=None, fn=None, cmd=None, action="ok", disable_ipv6=False): (ns_ipdb, out_ifc, in_ifc) = self._ns_add_ifc(name, "eth0", name, in_ifc, out_ifc, ipaddr, macaddr, fn, cmd, action, disable_ipv6) return (ns_ipdb, out_ifc, in_ifc) def release(self): if self.released: return self.released = True for p in self.processes: if p.released: continue try: p.kill() p.wait() except: pass finally: p.release() for name, db in self.ipdbs.items(): db.release() for ns in self.namespaces: ns.remove() bpfcc-0.12.0/examples/networking/tc_perf_event.py000077500000000000000000000050271357404205000220600ustar00rootroot00000000000000#!/usr/bin/python # # tc_perf_event.py Output skb and meta data through perf event # # Copyright (c) 2016-present, Facebook, Inc. # Licensed under the Apache License, Version 2.0 (the "License") from bcc import BPF import ctypes as ct import pyroute2 import socket bpf_txt = """ #include #include #include #include #include BPF_PERF_OUTPUT(skb_events); struct eth_hdr { unsigned char h_dest[ETH_ALEN]; unsigned char h_source[ETH_ALEN]; unsigned short h_proto; }; int handle_egress(struct __sk_buff *skb) { void *data = (void *)(long)skb->data; void *data_end = (void *)(long)skb->data_end; struct eth_hdr *eth = data; struct ipv6hdr *ip6h = data + sizeof(*eth); u32 magic = 0xfaceb00c; /* single length check */ if (data + sizeof(*eth) + sizeof(*ip6h) > data_end) return TC_ACT_OK; if (eth->h_proto == htons(ETH_P_IPV6) && ip6h->nexthdr == IPPROTO_ICMPV6) skb_events.perf_submit_skb(skb, skb->len, &magic, sizeof(magic)); return TC_ACT_OK; }""" def print_skb_event(cpu, data, size): class SkbEvent(ct.Structure): _fields_ = [ ("magic", ct.c_uint32), ("raw", ct.c_ubyte * (size - ct.sizeof(ct.c_uint32))) ] skb_event = ct.cast(data, ct.POINTER(SkbEvent)).contents icmp_type = int(skb_event.raw[54]) # Only print for echo request if icmp_type == 128: src_ip = bytes(bytearray(skb_event.raw[22:38])) dst_ip = bytes(bytearray(skb_event.raw[38:54])) print("%-3s %-32s %-12s 0x%08x" % (cpu, socket.inet_ntop(socket.AF_INET6, src_ip), socket.inet_ntop(socket.AF_INET6, dst_ip), skb_event.magic)) try: b = BPF(text=bpf_txt) fn = b.load_func("handle_egress", BPF.SCHED_CLS) ipr = pyroute2.IPRoute() ipr.link("add", ifname="me", kind="veth", peer="you") me = ipr.link_lookup(ifname="me")[0] you = ipr.link_lookup(ifname="you")[0] for idx in (me, you): ipr.link('set', index=idx, state='up') ipr.tc("add", "clsact", me) ipr.tc("add-filter", "bpf", me, ":1", fd=fn.fd, name=fn.name, parent="ffff:fff3", classid=1, direct_action=True) b["skb_events"].open_perf_buffer(print_skb_event) print('Try: "ping6 ff02::1%me"\n') print("%-3s %-32s %-12s %-10s" % ("CPU", "SRC IP", "DST IP", "Magic")) try: while True: b.perf_buffer_poll() except KeyboardInterrupt: pass finally: if "me" in locals(): ipr.link("del", index=me) bpfcc-0.12.0/examples/networking/tunnel_monitor/000077500000000000000000000000001357404205000217305ustar00rootroot00000000000000bpfcc-0.12.0/examples/networking/tunnel_monitor/CMakeLists.txt000066400000000000000000000004351357404205000244720ustar00rootroot00000000000000set(FILES README.md chord.png monitor.c simulation.py vxlan.jpg) set(PROGRAMS main.py monitor.py setup.sh traffic.sh) install(FILES ${FILES} DESTINATION share/bcc/examples/networking/tunnel_monitor) install(PROGRAMS ${PROGRAMS} DESTINATION share/bcc/examples/networking/tunnel_monitor)bpfcc-0.12.0/examples/networking/tunnel_monitor/README.md000066400000000000000000000050631357404205000232130ustar00rootroot00000000000000## Tunnel Monitor Example This example shows how to use a BPF program to parse packets across an encapsulation boundary. It uses this ability to record inner+outer ip addresses as well as vxlan id into a hash table. The entries in that table store bytes and packets received/transmitted. One novel part of this program is its use of `bpf_tail_call` to parse two different IP headers (inner/outer) using the same state machine logic. Also part of this example is a simulation of a multi-host environment with an overlay network (using vxlan in this case), and each host contains multiple clients in different segments of the overlay network. The script `traffic.sh` can be used to simulate a subset of clients on host0 talking to various other clients+hosts at different traffic rates. ![Overlay Diagram](vxlan.jpg) Once the simulation is running, the statistics kept by the BPF program can be displayed to give a visual clue as to the nature of the traffic flowing over the physical interface, post-encapsulation. ![Chord Diagram](chord.png) To get the example running, change into the examples/tunnel_monitor directory. If this is the first time, run `setup.sh` to pull in the UI component and dependencies. You will need nodejs+npm installed on the system to run this, but the setup script will only install packages in the local directory. ``` [user@localhost tunnel_monitor]$ ./setup.sh Cloning into 'chord-transitions'... remote: Counting objects: 294, done. ... jquery#2.1.4 bower_components/jquery modernizr#2.8.3 bower_components/modernizr fastclick#1.0.6 bower_components/fastclick [user@localhost tunnel_monitor]$ ``` Then, start the simulation by running main.py: ``` [root@bcc-dev tunnel_monitor]# python main.py Launching host 1 of 9 Launching host 2 of 9 ... Starting tunnel 8 of 9 Starting tunnel 9 of 9 HTTPServer listening on 0.0.0.0:8080 Press enter to quit: ``` The prompt will remain until you choose to exit. In the background, the script has started a python SimpleHTTPServer on port 8080, which you may now try to connect to from your browser. There will likely be a blank canvas until traffic is sent through the tunnels. To simulate traffic, use the traffic.sh script to generate a distribution of pings between various clients and hosts. Check back on the chord diagram to see a visualization. Try clicking on a host IP address to see a breakdown of the inner IP addresses sent to/from that host. As an exercise, try modifying the traffic.sh script to cause one of the clients to send much more traffic than the others, and use the chord diagram to identify the culprit. bpfcc-0.12.0/examples/networking/tunnel_monitor/chord.png000066400000000000000000003017121357404205000235410ustar00rootroot00000000000000PNG  IHDR8gVsBITOtEXtSoftwaregnome-screenshot> IDATx{XeET`LX V2Ht)D۷[b]jY+VWb[ &]Ѵ@[C:嘧!Oc*y`"h 8{1sյ/n޷_ee-4@Y@dA 4@Y@dA 4@Y@dA 4@Y@dA 4@Y@dA 4@Y@dA 74ޫZVVv룣o~BJDDDHHӄNfsѣW^@h4c˖-| {ܭJݬeeeeddW>Ç7S۷|]ȑ#G-)ڵ/{'|7)=zO>o:x7'z@dA 4@F3g/_{w6lhM4KT~wЂ h, Ȃ _o@=v{qqqˮ]}Ç׮]cǎ={ n۷!C C^ܞ={\RO>J}~~]v9rܹs۷޽{xxaƎݻwGq8/_nӦMzywFFF^S Q\\}...>|``Rٳw=dȐo{lzZ{9srBԩS5w=\@ keddW>Ç{K/'WX7|s͚5/y䑿U_۷o[lLtnׯOMM5jԳ>{=x/gΜ~o7뜜 3^>`PP7{h4Koĉ=gO6m7+++ .L>L%ʖ-[6v܆>˩111uBϝ;766vƍ }}7O=TgǍu:J*6mr?#/]k.o^z5//Ͻ2jԨO?4&&7߬7}B߿ȑ#74TTT̚5kѢE^N|wRRR89eʔkz?䩧r?]/{?_^{5VVV6?ބ>?7ި-m۶ul޼kJyw;w:Nرc%c***%%%^nJIIIJJSO=ޣԍh~|獘'lذAS\裏l>299 ޷oɓ8ЧTx^ڸ˗}>+,^g^),,}BBB ťKN;;{ĉÆ {3@@͎{:|Qӎ;oB 888<<GGGKzr/޽{wwɒ%UY5{={g=s ?PT*3[.]:`ɓ'KV=z/^ټyiXm=ͯ\cǎ#F1رccƌ2y䔔[nŽtw>k֬5kS;x@s5jԨUVIgw~OF,Y"I=#$<:㏿=ܓ=|pI}[#Gb.]?v:]v&?q¼y<{#""½_VVVT6ОG0??߽2bĈTI,q}ےbq8uN@Thh[wp&;z˽XGW &̜9a'O^b%?~|㋋322<y}BxFݻgΜYd3>^@?~Mo]v$U~I}ر'/^tVPݛ*|&) En\d25I]XX#)&''{>W4hГO>ӽsυG-iӦ:͛7;vlnnn˽{… x7 %ݻ׶ @{vh4?zkll{}뭷W***Ο?zJrVTOY***,Kmsl"9@{^o6 z͛۷gr.4 >߿*\tQQQr.W2k׮… Ngmkk߾}rrrBB{U[l A 4?sOKN@ !$gEqN=oTWtt۶m ](++$ &4b)ǎPxկ;\.WKN׶mAO"// !  >|شiSG 4?(x=Nڞ={$U{^9qĹsT?J~R$7yO>ϗ/_>tdؐ!C$]kK{%7.^>h߾N5jȑ#ju@͏lrk߾L+~ݜJKKe]7m]=(((22ҽ"!<"iyĈo8qرcu?N7|o:t_|O1?O4#LTkTYY)xbb@@@#GjJbKN@Ѓ,+9-ͯ7n\paü&:uW_/,,r =[IO!D۶mZooZπѫyέ;ޛRb֭f;}twqG׮]_<=t޶m۔)StbqV7L0a„ gΜY~ lRcO2?ouB@hhLB}#SiTlq…F9++w.KpӦMtǟ1=vz˖-eee/FՀ s<qDqq{% G޽{ݻ/]T]j;v:t{ ?p̙b6mbbbIӶmѣGg=w!,00PR9wӋ tGҠA$-5o޼ |'6lGuرF׮]ۈ֬Y#hZ޴iBrg*#Fp}WJo <[oGYn]zzY:v+ѡC? N\B4 7nl4W#GC?nݺuذa Z̔7o,9o*zfW/_>iҤ䐲ѣGG/KKK % .z̘1uyp=x?։h.]W*++Ν{պ'͝;Wέ53111ZVR\pavv7ٖ744n3gΔTǴiӼie?'&&z՞kHrHjFVTTF(kwd…7#/&&FRܶmO?]RRR㔋/=XYYlN͛'^xW=;[n?~~ʬY8E !NV)Sv]o%zJ5?CiSɂ'M^)))yG$}=zѣǽ[ChƘ:uo޽… gΜٺu뫯^}fsРAU?{l~/^,9zů:11d2]pA2h47N)))d8sm޼ʕ+ 6DRɱ+99y֬Yʫ9rd޽ǎ-}3fG} 6[^z-_<""w\RXXo߾C?ѣGX&mL~gy뭷 ٭[7#[!22/\bEffT*{1N7Z``9sFcFFի$s?ԩSJe+PVw޽މ7nt/4UէXn7WvB~X֯?rzjǎ{u]wDEEI+۵k{7;SC7|7/Ͷyfzqҥ={w4hȑ#ož/+ '~WRRaÆof?~ܹseee:t 뮻F?ǥor%%%yyy{7RRRRRRR^^޾}Ν;u]Æ 2d??@Y@dA 4@Y@dA 4@Y@dA 4@Y@dA 4@Y@dA 4@Y@dA 4@Y@dA 4@Y@dA 4@Y@dA 4@Y@dA 4@Y@dA 4@Y@dA 4@Y@d ta_o۷4_eehh4@Y@dA 4@Y@dA 4li IDAT@Y@dA 4@Y@dA 4@Y@dA 4@Y@dA 4@Yzh | |ѽ{w_o]aaKȂ h, Ȃ h, Ȃ h,}J-&1oꄞB3-;-$~in@ٸnZf?z5"aRX| 8oymR6N'hs>Qaఞ|1pԨEul>L>G @K5ByIЂ hZ}s""+W !Dɚ¦mt{ʲtδaaaaaa v|+N߹0x4i6E&yJ hַ8~tB,{񵳦MۄӇ?{=%.J694'#L!DɉCkV?3~ƚ˴[b%<4tݦB۴iW !y˜˨Yoά,@صT+> B}$he%M"'̥̕;tǃ"ַ5eeT!=FhBQf\aٷoߡB3$(˟;>ᵵ\Jxy '^y{{N2E!Mxf/ wq˒3֕ !zLJ44G\BCXOus^'Dy%c^_ZYNgLq͔wYuҢ75s^37",џ#N@X!m!ʁa7mpC"z2466^:MkϺ?/BE_lZ:LUVVzhu w]+,, .|Y@9>):::::V9;e!DuΘ1I9-;cX~^Rj .han0BRO}v{.!пWvZG +hi:jb:!ı?5б>{Bt<_+eIkuIyN_oM-N@ !DѪ:gB5kDMM4EƤ"h5!F o^BQ}eWe !Ā*{"": !/gAP&)kadG 2h}@wori !f ! Or̀?u"9C>\ inbN9*qE-hNYt:NGWMC5օ$iE Ft누?9+d+{Q+M/X(dHvaz2ic+ġw^ޞ673WNTWtCSj{70[PRXhL$톋d[/h\2_fo"f\h!-;K@ hN4|n8<'=N})K8 RJNXh*BL'!ĕ ׾uv{Wa h/Ňz !ġKa:SQGcL˻ M\bbg0:VQTE*݄W~ ǖA />K!r&4&t,yd1NӨ~~10zZFU %lsxZ'ZpCbW1_i}!QR&ǖh~+k Vh4FQ].Pp*;h` %goq&Tw>SLJS맾sH[6罘;ajVQذك+p8ی4-4j@Kv됄8B[S$fHAޓ_|*L!Vyo87!9-5)hk}> !B"5 !FQAq,{OB}닅a~C/>!E+^~o߅psq9V@;RM&SRjZ9ji-R!9i5 hOMQ*秲 -b~C/%4q%jו 挌EgN?`J5[;PB8I\-] { !foT~/jZlCe+.>yd /Y8cJҞ1;~`'֘j B@S$D!r{+BtAu&€vM9Ҫ_oRRkĤ)عxJ)J_NAAA} @vY8-qυlkޑBnZVK^N9w9!DHԔ8kb|+GA}C$ 7h4Z hr̴6 !DȔ ,׻r9,9i !ߖQNSRth |4n^z @cz̓Ӟc2% 0vh3hnU%,IIv%S9NЙRR kqYSt9'ff&8 7N9#Zh:9˜e6-BSFAU݌.1bLu QV(\g[98[*C GVC #5>qU !ã`4 $vP(dRB3uxsŜ-!)͐lħZu;@m|B2N^vRMqNћ-UP듲lYGT 2[v.r3PBno_ Rl}ٖj̴ߠ=eIT+)tSEYf{EP)p:] mRe~sy.QA+u]17 P++RsӇtI9lDOʳY2ֵ29xXTr mrN G?R?ݜb>ժhaBh&3s|@4@2q©E4ϠYmL1ͱ4zJZԥl[%rgG߮>型LI47ש*ޘ F-9TL3L Rs:P8٠JnqZ٪N2[bP+iI7jm$Ъ9ԙ2L׹y-4@=8u|niFFAܒiq|@+Pjw B iDôkЩ-gƢnS3 Ь6&6 PSZR)rNfZVK΢+ !˴ z_4AVujnq# MbMgLINN[|"$eYiI])5]x>J)Y'-cW.%'+3-;! XN5dVhRq.ӥP*;5,8GmF4CI%})}A̙"|B6nDVg#h ڸDЧf|79s~~~*m\jgp٭y9y66RK8qڱr8)M,qT O]K7@xǞl2%%Չfsžq#Ôn'f$& %e\W;4w.KFƢޮ'epXIL1doPӒ7jSE%foK`ׯfĞ-4wTH!Bf̘,zhJlKRhJjGreg՞t:q3ϭ3Y8 4TzqΡ2eNQn\GcH6ؙuT7 FiN6ΐl`ܑgtZFV) ?????[:uOĸEBܙCu>iAf 4j1|{r.)f2^ 4rNtrVLLE:3 zW6rb RT4jBT*JRR~{ԨrM}Hά{M{/J}gr?_93MuLHמiѽnj))ǾT_]lƅ١->w?p"&..|{I-jCr-1/3%%-s|!DH%q*_Sp( ISӗ/VMMKOK6dqQ M,9unqU<}_q6H 3{j!t<)*eV<{ZTM>q]WonzShn22k9+mlфf}nFժ5FQ<VF2R(=3hj ǩAڬ;e)./gLVW_oq999weK5DuFNZ@U\UYY]+fG+P6{K$ 6(y#;V`˿jo<~u:mXOM6>A8eɍB`!@8 @.Kvhs玜nYVt PzC\^ݸKN6+[s 8NcL4sڸdNmP?eKR7nEd,|[݀9D'阉CG̅>S(GX/U(O^:es)B:'ߟ4@)BԾH\6sJRrZn>G fD7 ];$.%B£n1otťd4 Úb26a*Jp9%Go|مBO nF,KO[1k<!^XcX^극kWp-z@4!J˘>To2}#ږ`WeG̬Ͳm~=#Ɛgs?Is B&kKIPKp4ž\K͜ JZ-x!ݤž<;9=PMiڄZMELy7$* XPR/b 1)ҐiݹdDfXȉ/,oI)jS#NK*3jhN{^9++˼2H!DȀSFc\Pi5LkJ353-_L(˖7}FKML3'yPt6\vٜe2 !DHةFѠת|YOI @'J=Ϝ{TD.N+i8L]f-@/9-i,15ŤW^ 3BGMyh4|;QhSr->qafI-PlNEc7鲥ƴti6 e#^RZU biZѨm ڤz>Π bk iyTjsZuY!ʕ uJQdMɯ{@SPPн{w_]aaaxxw4953)nGG" ta9.Tk47[<}\ݥPUMÅf-=p|.[7-+ŔQ!BꤍYi5%uj-8pЁߢԙ2m)ê ZC k^q)D/dh} ݜhZH!DHZ)i:–cTݠu}G-+3ٻ4_-֌,k(bwKyx^B!zjP7~-}MVCO R8yH)\XrB.̗O+MV-HJ aˮ)F&WW!B!aB؍UJɦ_N~d7V+%OfvCR]kU>SKbzzYm_XK IDAT)ӨFj-n*tYU!Bg!eVI2^z;VV%n0|uJAWri)(u|UmdhbV'єb]jJ-A#B!~=B!4lH[IXUk7*蕫xm)DRɓAv; @^i8<{ݠ~^ct1}Thj)(bv BI3_B!Z_0Fn``(v%%%%''{i!r)X{O嬦FW=2`ȕ2VU] V{5U |P9i:xV$M5gĴRÎYrtIvRK!BX`0k@NsssffJ_%n$|>v)߼ysTvA+ ܫ@huЦr-::islYkJBL-}}Y1Mk!}~T+gj?Vi Q2k4A$I$ Bo L^_ Z QC@.333;;{===?#s6x<1} B'2M3Fd7|MByZmXo2"I3B!Bh~E˵i&>;}KOOt:xQK m6[8B !JrB&!5JŨ{UHiWbrp( g; Tp/!B! hj[^mg;t萬d# ~/_׆BK,W+d2`8--0 3eP}5d7j+ռ&B!BM44|>0ߧsc\.S '..n5?k;wT ]aZZ)r@ώT ;*XVYLg?|_k^ 4/B!Bhi&/O3K`I"3&t(&cK!,ڪ/WeRLTW2GnIi[br*boi E4XyuUB!z6`hiRSS۳?Ok4}C)ڼyZp)mlhmjp=Zk;+\πRR2nҕ;Tg2 ^Xl{+jXe~R{LeT!şJOOe@NEo?ik-"ƛ:=9Nہ""!ԝ>?ǘ@ ^|y<)#\.Ib8///** p:</::z!!0Fb%$$~?11ќ-eR/᳏/^h`'5?>ʫ6sGg)ҝh%@=BUƒ'm+HT"H$D,Billyf x<+++112Leeesl6j=t]wޭ70Fa"rfͳkfQCqST{z#"Z"܋@s $bB*W&l)l)R,JebF!4uuue$\lkkkjjtґ#Gf+INN<sVVVhnnnX,6w||֭[K:B(`B%JhUNR&]PiTJR*M*07**W=I[YQmN;-8VVqBBsZL\VVe˖{.33knn|v)=:b\.wXL>|\RR,*!"!Z*NVB?}#;l^|lŁR&Œȯ%b^OXhE$ǶjS\с'e2T,$I)bў;{b3WGcG!&&&nܸyyy3YYY(,>|xNgBBBz`Buffz `UUUUξ)}c s]#dզvXѝt4 +j*9#Zf%$$+mg܄p455\.Z\\<~CMLi hK[P1}BV5YiŋX,V|||ll}7oAGȍ7FFFZWW8Iccc%L&cofdd$::Z (_<O$vכNt2̿哒6n 6l $߷LݻwriP!B+՚;@Pvs]JX7-1Ӗ+R|8EQJ\&JB!Im5+oQV,nAqXjB= FGG ))it\W^/,,ܾ};fʕ+oƔ4 ^z;>>ȑ# %$$>|s`SSӍ7zzzRSS^fڽ{w\\\0sNCCCkkkqq13 KKKl6<|ڵkE 0HZZZFFF<<"sҥ{})|srr~޽{C߿fy&ns$$$,RBk;"B-Xd={^v;vS5ϏjS{eųG7E^YU.‘XM R.  LyC3oD)W,3,5JEoF)^GB(|>vq~VV֮]bm۶---mbbjN_WW>ϖ]B,011Sw˜f@}}}0ٱc~y瘳022"(x;vdgg?Q,'q\===.]bصkWؙf?sb8e\i'VZ[cTUW*W! X]Q_g>^vJR*UgI\V*j4S{PVA<{9l$9uVuuQ|F!FsIMM ma7q&JOmNjﯫ;rSn߾*vNKKs\M6wm/>GEE͸~̙C+`Z=flnȐdٳ'//ofc7L<@#BhY:)ȤSgʬДJLVץ-5*sEʠh;RJ$,eS[ĺc $hn[-fss@p*"~ ^.|8pZRboB󖝝m6GGG].l0ǟ'P(0'ČeB?6 %._lbbb$IJJ I **ŋ L;=fZfk׮]v ln.]T*v_O?51117B"!BL(>y2Ug/:ҏWa+5҃AQІ 4Xԛ/**>:M/QJJ9# /Ъ}}Ȼ:m"B-88 \nJJS'dXN(|[2GJᒒ;wܼysÆ LW l6WWS&x)z&ʸ!Pd())g;;;`֭ܘ.++ ڦokklLEEE]~}瘘)xg"2ԃ~LLLjj* /vuuM4*4^ ,kRRR<2bP(T@#BhkԪs͂#ԍPb\N|ɜ) 4ը+WɄMyٳgϞy Jo]StkL5z}M`0M&lXB#H(&j^Zy0CҚ<Bu'???;;; ^vn|@ 022b2._ BeOUXXfܹ`d#::z`ZD1^o{{{`˗C$**w MhZoܸLaa޽{i.˄ceW2z{{pƍf۷>|m۶U^$Bh` BkMW麚r٣B`R^Y}B{PWe˭U_y5uѶs׽ *;~ZPeRJ7UrI#^ JՇVUU+#hN湾'GNu4$4:JըOjOhujiVA!9edee8p`W[[[__Ԕt:^oTT޽{| mmm&)==} SRRkkkx|iBQTkk˗|~ll狊ڽ{w(:GE B!bh "ɀZ)Iy{BB 'UmgCw++UO d8X]cՔWhV׹'d$!-:nxF^M hϜX(ͣ%m7-v,V fmEE P4EQMQ!ooo3O!"l\n"U1FՆ/H=4B!VXY~ҔXi&Z~\UV2CiUl3Pj}3峝_:e*cT)v VB(B:be7y@trX0 4E٭R` IDATVl20}F!Bh!ZAB^oOCޭYVkA\rt3; NU~%I)ɑ0e{  L `5[i{reӤT$b,B!Bh&@#BhE zKZ1iR"WT+XV2zEEL|ihuOk;e^pB UzϿ:N(T&:JzB!B{@0dh]IZEWT*!i` Jk***TRhlez^)y+\*fW@psKͣkbr|v!D!v ."D. QChBOtzNP,rB& ʹI#}NQB,솪n*mr#P\E$|]@0: $ڢ/TQFrx2验JÔOQ%?...)))999KCKObZ iJ8hlZv HR,%RELeTq9Dl4_^saqi)4B!E@ vIvnwGG͛C!6vCZsbNRI"(~}̪m:U?9ABR(Ageҵx1>{f8&۲mg<9~>+ JB%'?/eI0B!Bh@]]]fffNN\.7))iÆ ?4BXQe'?Vd<ʪ( )J$I@El6?3_RRRZZZ$ɲ]]?POg1O %k9\_j!L rӓ7&m̍(( B!p@H?22RTTH鍍iiipm_7S49%-o/3Puv)[dR`tN6 K~QQ˷ B ֚S֋}Ӗ˅O|~j]N_ 6_kJo5~d/-Q@SmlҔIcDs9Wڮtv::82);J.u^msN6;2d[&AC[BKz!Ρ>&w{v޾M"6nךEH Ȣg{v1ǔEJ0!BaВd)*|Om <Ƥ08G %2dAu nf ƒ6GǀD0 -CFeF i:2gjv?u4?>qu9( ~PPwkIiXB"_^˂9@q644t͡`0H/ f<A;wZ;w,**ZK#"(|r9-. R <@MIg zGNBqxƐp{_7"DAJ!?kUYebf\1KOTVVϘwhl==t;y!ݥI˵h6pOLh&Id!VvZrrrYYrz[;`CCC}}=㉉&bKi:1q^n_~?p8)91L܉gGC|]!B,4PB[B WT*Hd*yd'у}PO煚@?.ZL p h~<檳F JDy)sý.Br;v숊 B?99yzGP$%%MLLj{@rrrzzܳ!d@Hޞ5Em޼yEę9eBc&/O91Mi{4$3 !D*;c+ UBv&e4[%L3S8V)%:;w6-S!'$"q;'ߌ!bu=x_rHG$݃I4B5(@tt`~ Bhi8Nnncccjy988߿k׮ %6' t0 @m1t: 5WC `o SD]!)vcF}\Wyreهp|rl.Ћ=Kv{Dchzp8޽k=6m*((:{ne˖F~j:NE͛n1}˗/wtt=zf񄄄Æ %%%SO{lll qJ "hdddtt4>>>)))%%%trݹsip8펮]\$IZA"txR@ff&Ùtgg+W9Nbba~FD }B hX,@ ؾ}{{{{cc@ x[v;۷ ї_W7vnax;^-ajɒWK84\ҡ:0p/; &衔>חhJ/>kЫ1B}IQ: ܂5g>{^9d>.3.x˹yqu>o˖-=SV<88l.Ѱ鬭dΝL[[[{Ĝ/_v:BpϞ=0::Z[[k~ݻw/ty YYY2,>>~bbl6߽{xrdܜP(̘~;..n޽0w\|^\ԔUXXzcnApc6vlvWWWkk+>x`4v1[r^:>>^XX}v6 fs}}+Wx) hlvRRn|>JOO]VVvrEVijso; ;y~ߒfBoi8[>E+nV:vx[:=rB!Ѧ ^(8\6I?m5jLf;MHdJJ.\/jzt޾غ}j>0?>i4Lُ_Gc#XXtp/ !VEQ_SRRJKKc]v]zl63tGGS{PSԴ-hyqqq/ $::zǎLs)5ˇ D999mmmLv[ZZ`iiḭ={ PeZG ]w /VOO*((͛7[,ñ 455]v1GX,ֶml6[jH$KB(X,VBBBlļ9?ZdON=\iz #ظJ'g+ KM;'5|Ͽ?n];ӷv!ZB**++Sgj./ p><}X![{shlwβ4<еy\aAn؋ rD#"X||2L;vHKKcb[NS+򗿌:@ ۸q<00ۛeggOI|$)y4|x  >3D"ݻw'ЙN >>~r 2cyK3 B hV9k)ݿr*s[~kλT5h;u;saB5k*>cVThT21Z);PXk'nuw[ma?9[>S$INjdYByx74Rf4+Be&J CLLLFFFVVƍC~?1ֆP[ N@ `)FGG`uLɘ1qhs鱱ki:)qB͸Qttnw8Nstttxxr`zOIgГ0F!ZF30!4)T*m؋Խo޺60eBb!t atZsI%,FEW_}utttttL͛7PȬz2c. UR<!OIl'ogV8d9ui&߿wsb%&&ζgjnzV!ZhV!;S eVL@i): 9hVW1aظh6gbܿiDg@ /F$ݳlIgz%&&8p`bbnwuu}2_}`fL&[톙@Y,,Ν;,+777==$I$9N}}P\!<:B5jf 3k}\Jg,JLO *r;ޱ'A;Forc3.1{~S_?'[ Afxܾ}c=p\z{{S~Ӈ 1ezb̸61004X!@^RVVw޼TGe z)X,V||^g/ { tHҧb؏X/_E}c? e<`=f.+pkYj3}}d c_3|,7x Nmmmߴi oeZKJJ&x<|ÇW=bFGG;;;7n: \244u{nf<OL4$==}q9*++bl6۔B!ZhJ̔IQ: '!^1kȣh{܌B5}Kmpl& 9QVƟ,k'ZV,.O1bx>S W`s /h7hϸ׳k 3p?w?D=_y+>%#+B6իc2hwmdz; ]vm׮]111w5cJsrrNgmm}ce4'&&H #MHHyf333߸qchh(::zqmC*All- |eeeLlr]x`9Zguww߹s4*** ޽{/q~Pd!BkX*B-jVTTGzݤ- Whhւ-XB5zgn,a t`|qw=Cqk&f#lep'@;]ky4ӗ%If4+Byy?l ,tڵC}\NPsNfXttC? |$IӠ WL&svoQQQriaPIII088SSS_~EEE[ xޡ!STTt=&^}755%$$8Nw^>?<B !Z$i7uB#Vk+Ki)}9L c랝k-O)H¤981{GYB;D\@AG MH; ^{PgKx::y%^y ch+ܿgttbegg~lM6I$x@䫯t:߰aCqqqllp8/_ob IDATR[[ۃFFFn7AEEEɋ3;;[*4=w֭[߿OQn+,,,**nll(rM["^w^oopLLX,...^CE; P'l^B:ʤ-v R(5j‘w*˕2sG!6&ÏGko'=~nEN.:$f<\YhςfHH*=rzGv)14B! !B=3HFg|yBhnf=cMuu'}w&YŽ6~Ec6%oc񞘨{JOZ~S&[p;PC7[`:0ᏊNް?[z].pgýB!~a4B!4 n5u@+2՚OPp]uL9ζE\.ܐ1HƺۋjX"t` ]7yd".4%>%,b`Es`5Egl.1yET Cc54B!  BG:  h{sCpU''~WE_Vѱt ³5ܘp oa_gL{A4;˄0s$Sz_Ln<Zy4"5ս4&Y;7YWZ(qJmoB! BGiyRj*L(ڢh>u@trxKCz[B7tqv{NH$1_]ONѥ,/Z{6_dSЮQHH&e*}>9\+-+ps,+[p/!B @#B Ty :"9F[k*՚k :]r3m3/0y9{ѲlnH$bl000\&|@S@<0w1֊δťnںc[eֆo>0d{kIB$ʡSNt2oݢ2[q:۩kݝu[k:]-i-N_Q;AI$O?1 <9߯?<ܹsSLbOB஫FkKVTT.˾gL~^[W]w|y٩Iq<"i]qW/]&I }N- [, 򃫍e9LfnMTWlrXh_Cc+T@{Kv~d^Ӻ2ڪgש߫=:z[8AEѣe~cW:GG@6k]wV5IL :|<6y&q^O4\9rĚMDLZnޯliEͭN'ƿûX3&muyKM-9]g7>[[S9#Y5>e٫}ؐd+ow3X^:J&JJEEY "c1'^ r*Wm@GS3-d?o]h~z;iЂXYkj*~XUz)8x~F]7st랿~h4ЫuH^eʵ v 3Nt{#cQsa ?b9V %l2yr;GZκPȓ?K DDڊ}_#GK3 WԴ׿䗡%t]i8__ N ~\?~gyiGƧDS$c։#>*jW>mloFl3|5v{t|o=b ݛg chڏI^MƺvʗiN,=-X_`5re)mL^-M7cڦ\㺙d02PHiL/;@K 8Ь&,N_u_ u_܏x3r6> J/!bP(d2 hnܸtR>oiڏ#}ʃGjN^C]QUfR($DTRRZf_=YAe7z#w"诙lU@ǥf #C^:mOfsh<1~;>a/{_Ym K嬹sW]Wmahw 7Lv^$^:===>>^,իW aq6]{c)kOorDD$/ȣ:m0DD4gꐧ|+j׿4]ne024>&-{Mv7G*K=UB9y8nvU*ŋgxbJ_ u]U􅞉sа,eŖж҄_ҬP_E K "izCK@f|f y{hmG{VwCX(Ђ"ХKcbbX2,666>>ٜmhݞC͹JX.|lsITlctM^]ܭgg䖔ږ7Ң9[mf9E?ܱ}c,o=s6gr # z9΅Cg `7 [Ʈ[gpvɌ8*yJT:$F{cRs,rz1<^+-DqiZpt]|B78K57>O1!,! tj)-55ehh()!Oâyglߧ{eRs lCߴu'׹f]Kϼ`Ĝ 4ܦn8ynLr_s4#wC\Q7>\;MMξ|vO Lƺ:u{WWD&dڮvuLYcD BzM+q=\6hÍAC8>bYLhSX!;;w`1{[&FCY ]w?Y~o"T*^N* ? ]xӷCm`᧋DDz[stA.~HoI凟z_}S~q"OFD~sSt/5U9A_|G[b\(ʬJ+ʋ?YH|eט+ylC,&/??B>E6kwkVw7CR ͚Ǟbs FKRϤR^ߚ{9Oi>W_IV(d)N9t 4$ƴI(mˮDb߿0DDd=O*+˻U,[}G.Cg׸~6t|O8A'+W2ŤjlQ?~=ǭƺ ρò֎v|҄Ȭ>b(y^4>ʺ5B e:7& C\f!G9x΄nk톜BG.~ !H3>>.q:T*_F_oϜ:yy(HyVc!"^CĨ '2'ɉuhIOZZژIdlSL-}O5 sJ~v~v5?/-5A_X-fD[{/ 4DLf4}o4fʼ̩tF"Rxe3 D㰑 ! u F=K=)"2o{V,[ eIe][dWyOMGKIkw~\QFe[Jnتgх#yI'acͮkheoZkl,a$Q!\_ Xq߃V #W/ @C76 2,KD̔3"%ZX""瓜CX 1"C,8p X$YeU_}Hrf_Ee8MgƑV=[SUf>/ qj+qݬjNH!#JY<?λXM~(8$KZ˂x/:d6h4III~*;9;n);gh E s ]3""b=,7p $5m*%DꙂh2$_RWsQSY g˿6YLهrJ30WL׿[9~Iϣ:5{ ρ{&دZ-51 )ShA#GYYa%fݨ`OةT4DR)J{{}:7&&FTtK2Fty%"iH$MkT!#3D$dѱD$"6Evt?qaI =M{K_?.  e;5A^WH4tjHkWLc_IHM^|ދ hAc61}@ );rd}3n 4DaT*VqÃ*jjw N$"uN>y2q*(%"yZCLҭ'$HPɉؾa#<GǶ8Z6+YښMMT0u[^ZRRZ ]h\?/;X1gnc' 7\0G=kFW+geb<{!2蘻nrchW;`. 4D \n:Fgڵ4 UA"pSM%ReHYIdlkwOۚD2"Jɑ;ݣ*CY *H {lӔڏHy"q$rQӪ,RChmOAnqݳ~uN \mu/NX}PGx`ilEBI=T/Vl^B`۩Կ>>6E  rڵkYmmm1fyddڵk---Vuڵr tӊfzVdԾ~H҂[sҟ~t߭ߙξ~j(g[Qhw6'UtX%C멷YoܪB vtu{J~y n<(,=x~SWZݮm{`IEA_if ڊ7{nݳW[fm马CY|_OwӣmkfB4px;4|8K̎#WKA[0P0V5 D$ɤRERRRHsϜlxy?-\j.;$-ص+Ovsbݮ]o~g ҨI6LL7J2.pܡ'4ˤƎn#%n޽}gpUUVr\#5M%9~)U:vH(>6iG,۬n4fخu}A[ѥbZF|5DBү`AhL%17sgqt0JBϫVD6ㅀB=!èֈgȚyf*VmapVC FEE,˲f#"@͈Ҷ?tw6;M3 ږ6>{X^Vf6"ijnIٮΆ-3On}ùn"&Qa;,MYBJS#goVl""]MYB_RqrOIL:D&;*׏5}g `]5ٝbkw!#J^ݬfv+y #DePhcRMDC8&!o oc{l*EpB .YhU'fPZюgvx}zR^yeF27]>%s+'a6R|v=o8ڣ{}'rt=+i=_] r|+ƮmiyA6Onhtpƃ<RE"ۚX;@W4ԅ[!ZGsS+{ŲܒdWvuՖgݾ9彠VW)-..)U^j$Z]sT:uv3}FIn uLDVzCFde6cutim9i9[F"!;p$Zb$1qYqwwg9ܓLjKSW!CogF6{ Ĕ`"<>u=_N*y- NH7㺖,㷓 ߟ8/ ?z:;s[dAhn_`{k2_>_ $@IG,vﲡ6j$;ﳓ"!n٪^E C;+CI94:p GtFp@G7-̄/w&YBF*OX1~,1gx/~u ! řUVS8wP 74̌t?OSZ#b}|G' Y\L#fch4;-Ʒ:vPp C (uF IDATp{n)/klT-+,%#U0i0N8qsE^NlļQ2;ø ߓ_wšo+G>0".5؈#2y@w2]b+6|OOş cAX 4@)K,YSNv=vO NWUSdeegggg)xQun+'Nxꩧm65^YZ-j!}ay&}fj'z.~EQ|}ϸy<^@w2-Ha+Y:ki;B a@$Q?XU?sM$#ϖf+4zJ^UYqx:xSO=EDcccw& ì 8iD3 f8Q?pmPZ"h:֟n;сݼGv{v2/$iYB^=@/A?.28{=ǎV(zjJ`'xСM][ZZmAGc.We-C_nJ'Aq?=8dѾ[],`VN&m6D`cN&dM?sd4@Hj˳˪glUUVr\#菵{K'xfKK=n*▭Z1Ud5==A Eo02dӻ&/7]RE"ۚL d|li1/eby ə @/A tKN4ȼ⧵Ue YmܸqjT]]o>A<Ӳׄd׵B,]zۭVGb5Er@ $Zk2ËȠ"hPPmM}t򒃗ě^=ZZ?tm۶xk8q押8?Z CV EikBR=օC9۹L/EHFY\"AsSۑ$d,AD!"VNSBhj,I}55YA|477oڴ{SO͕Yws!ݘʽGZv<ŏ):3泑@HlJH_vbW hp(?R{dA?777o۶ڵk%"ݻw777afEn_HvC8AG]Q2#zbц!GDyF0bB'Ś CD+޳98Z?{Lա@XB 6LJKԤlfOB:̙>>m۶i4 1w)Lje}Aݘ*@G@uÁَ'n|0@Ś fD[$1qRd hpSW8c{tփ5ťe{*J Ji!}v۱cNsH[jH}_kaɣ:e߃{XpX- }?pLc# |jyDv-3(>}YDȠ-XpUP~vRPN7?d|S9}vjiiٱc|~Sp>o1GJۍzTǥf8%1%+sm)F:mOf^`64hAފ!#\#q|; Ut|bwS} vԩ6"pKP F?)KV5vז VDN3}v:O/@[-,bYgIBUPO˽:poc~ ىq>;>|ĉwYF( TVB^}ή@U@2G#PLvo$O|A, T޺ ښ}MMGj+\wM]5 /SW5yq>;=SΝs#NǮAM^&nj`Q|yB/hb'"Hap8N)j©A,–,{~́{n\[SQxKW#B \SbN;vhnnv@nLί\\mǮ<1&!6u@K} >niagi Lt|rZv>ӺC X`tu{J~vƜBmUA]mEǛRz螊Uuuf6S>2LjUFnaV +}['X-f׵q'"ӸOO:G1׶[t$0J60I@4@C t-yOk\Ϧ?¼YzRd+8/Mر#pm02'x X~_I<Ge ҆!Ҋ!o}1+w/͇9gv-G(*:_@>_*|@ ፝OM"PGGŋ??ŋׯ_&Gޫ=Zxc/#5&VhRWh%Y^Um۶e3Mo珶>t_Хz [G">Z}]ӲZ?p$FKh~klOY,k9bv{p Iӏ{NW[=2hF`!ݬ6<.E7znj!`Ffc 3 B!Ƹnk톘dj?  !dE"իbX,OOO_zP(<<2hXjh񳶦UJ"=zQYT2?N&ܿ^8Gj;rj#w@]f~6Юx67m5s+IC9B!GYӖ4`n@CϟWTŋU*_| 4I#"KvO[,""RU7kkUkqɊZʠxl/۽4E}XGS3 6bKcjkHM㣮f@ q221Sj|<>GdBh髿 0Po{.]JNNaYoo|rvv6gk~Ps?=Ö '_>o$bU%w}gl]W6wYb%e)&Ϣ9[mf9E?ܱ}cYޯ>usHDDU"$P~\}NW\ ")-mx9$Q"TVj4{_>-eqI1 );J*@-L\(s^ekF{_jgY߸ICKc ߙkl@dx|6DQAk<-g=~ݧ_LPq>9*! t:RiAjjG-[4:}zHmn5# ZѷN r/"= #r6kV `<ሎON)xfC,h! HRgRt```ygcsϽ9mЧhcOVT'_}يg_͉4\)͞:;YF/<]kWų/Ķqޯ3`Qմ׿Z*iUk^[{MEWWW/Sy8'_PfYEĉ!}&"<5^Stw3kjXEo\š'׾u]+>uad xP?xлe#WB @CR3T71]S={TtZ}D[ˊn5e %cé6gk%G7,mUHl,gTeeyZnVnߑ t'|㣛\5h0Dm5  ( +5vif+U%첪/>ZFIvU^]esss9NVli}%ɋ8]1 pYYW?p(„wB,eIgtĔ?AAj f H3>>.n\T:66_oϜګRO$sEE*γ Y4"FUĂ<9QN 5R sܻ9T3m~ 1[wn^9s[ CDl k>)\Őd\OjhiLIN۶m|8N/?y#^YM4݅zF#k;1~3*fWV"P<]2N'gbfhyϿg);)ɢ""J>WdQR3o%C2D Ytz"&)M>iribHbHn˶m'f#QbYL&"eYT4:}v2 ydЌ8j j1w7]+¢ׯ]q]R=:xb:*Z}$@z # xhP0c97͠/ #id2Gh^%tF"Rxe3 D㰑 ! u F=K=)"2=Mdh}sF)/[OڮisW:"eKwQ̠83hH᦯uLBJ^[-f׊ɳ6̔%1о|l扑؉H`%['u .>.5bfh4A& lm0xVG3"X%" KD$|sk!"FFc!hCN4wi@*?l^Ӿc5uuu{K w1:P=SW]]NM #C2GiEA4:/R3f]Ղ8<)Q(V6|K}'QA/Aꯆ;13 4D$tٝ34v"9uˮCDSs';Y&gV#ɯ?R;h`W˔?qk.>; ^mȠcR"5\p^Kbikz"^׵XxY:/lj`$2?~P(2B ?(fMƳo6/B FTJه(31)DdD$MR"b Ct"*d"brjâcH&@[4|Pm7 w喴JQ}'ګ+oSOb x:Lt θ}Ӳ:g7 nP:58:>=g;]P ڣc>V›@e_BiGz}0@CaFRi7nx9<<<88RvHJ""]ۖ!TA)Ӓ"}en=%D"JNM6I?]=FQmɢ< |&Qff+gjhm_ܷ[ʷbfi3eEtѐ0_t^3⨻ 6zEQ g?Յd2[e0 +,_;8h@<O.]eޑl6GFF]bZ׮]+yYZLr hkQoM$-ؚ̈[sҟ~t߭z맆r9E˾9[%aSo5$߸U̟u/9=LŸ=dQoAK j1_0ПkW\M?g)6:/9gZ$ǃX#h@ pmF@B`z! jd2T*]hQJJJTTPI~ /+XuvݪQV۵+o?LCa^5ۆ[fn]r@COh I FJܼ{2fXͻ;y b"7'N|ڝ?ij$:63^ju4ČeD՗#\q~/7Hbl|!nY& g^pdu7mӲ&~c ^ bccXeYf@ `n fDiN:ֻgϝ&bvms6>{X^Vf6"ijnIٮa-[g|sDLjGwlYv3nh3{Xݔ[p'knn޿?և|Z_t7ŲXg`tGŌ82r Z- R3" #Lzi{2f_5$^A@pg"I]x呑>;1(-_|mm1xpN /ҥKiii@X"ԻN۴iӵk׸6˲3h"v~atʒ+q^+8edwoLB 5}trtp3b=4M绛3 c-fyy¹1nնp˟ŲXa4ԻfTs<l{i~H.޽`u qLDk;rTJY2T #7TM^\q^Ħr+T(翮Q I<s 4=o}9< TVV~ι4cs~ njˠ;N\8p Y!tb翮sD濊,&’@.?~Χw Ν;|0s*b{GNg` IDAT5߉ 4>:yaq3fQKu'ؚ 6>A="+qjlH`XKZNs&79h;N۱c>?D'\KX8ȺcBU0nyeȚ__(-̳):>9&yn0v47&sudǎccci ςV zQe$*qT>]8{a]Lm6K d 65aJҖJb⸝͠ 4W^9N"_ss??s;o\n Πq,5~ftһ 漈Orۭ|*񬖅| >;9춌Յɚ; ޽ V[3p";"@. hIu񣃽.'xdZ3}'Ba$QYSr;'ho߾'TFaֽϺvAF50 9묽w0iᥪyA 鳓H"M)vοsl94@$;wk??/?y'"3#5҅4>F{k=+C|sz€0"N1 )1ɋ웇@D,Ncn|KSx@\3 MAur0 8njKF5//oX-f/-y|>#ZͦPo$\8U@‘ޫ  b޽{ll |s=97 2gmӻ(UVmydgVu qG;h.s܅d2i? bĜalօtk2z#5V|;.5r;' 4@d:qć~ᄊt~)&!%2=ί}@,v=7Ӳ4Fq^(s[ne3g,6ꆝ^c$i[-f#g 0ϳ2dj'4 d"7Eԏ U2LD1 )Jw99 VcgL[}G,Y9MfvvnNnjGqtIL~VF$N8qy'2p,;g\A3~dAgІ83M^W[3Az(&!e#DdL}haOP Ͼs8q;'qp4@Dt6(X{s90t|/ Cijkoߜ+qWuabƲj #7K}l,_8Mhmz g96nA'@DΛolGiĴ~ֺ[I#f2 \Pjq+3\Y=S'h5Et|&p꿱o }!##4?ܹs~!F^\wXo}%L_}N A߸vyQiFΡjL@sc4@<Y+" <R t:ݻ9p7GdF|Y8TZV-,j1ym3聫-D$4z4?M 8mY6ۂh!Gx}yJRjՖGhv?s^,6Sf_t8/ʙ@$usLWlO[8;0p(bBa/tpB; hHpܹ^{ htbwz\wbR|9󆃾ίGlIn/G@K~M =nl\ynm|C$AH ӫpGA  l;5Mo g.P#_G8bLwwQLB W]4PjPOQ7<휬Ĉ=[Z&l|P ÍXk1!}PR2Ak;/}L"PGGŋ??ŋׯ_TYYlK3\f[rKȈܞ&3-YW< :yIδU^@[-itحS[m8l6`~¿Xk6zn/Y *N#p/3a0 EZZL&#"`04͍7.]5X4ͱc8p8o3 ]y}ՖGbRw~~Y\ gshg_5}T#_pcpjjRJv情Z-3#8j}%#CLhv~T-@߸ve7gQ$>cn9pq狤1q}7i/D|r@m{u~{L[lbŊPo6DovU*ŋMsѢE*JV`>Ν;r5[{Qzֹ$]pFo?2|&"IY CgKKC+cRVmyd>ujӘ^#-J%f,,:h)vlB ]rxQʚVv֑BF>_Źifl"h4{kD'H* 8ȗl N:LԴn lh>2Lٝ-gvBf;)N;xN/dNI68icql sK" !ӍVdn~?Ѕɓ駏l[>_.4 Lܓ{\ibie GߞQ`4vs*-;^"Zw9B]X)o< "呯?[;;@DyZA#$?d'nhbZ3Cjy"f7/NDSZ{}lrY?Q3qiv3Hv\5\He"dp䄜W[{CR 4`ʂOw/>*FܣD޵,PW% =;$V\R%"Q5&R ssӉk UI5@ ?cJ4_bU;PB?7zK'}uVJ(benUV۽y9zM0F$V,Db'&3ku52©2l|g >0АjjϷ| ߯*{eGD}ي\|D߂uk/) (sڵm;J L\F7o.~0G3ǒHkcbHa!4zjvDP.ITQ0$@HA%)kdKwJɯs=OMw+/dt?^RO}_6zS#g'YqCϢOwA?wL/;e1s/bj87$R*Su )BdYyrXX/yAeXG.9n#V5TRM^^ގ \܍6'&EŇ|T^ɮJ" w_}rMt:{gi/odC imwwٳBGg_OB#A(\ōn&4T8_^oгhW0iwPm{-U߉ pL5CN"^D")v3; >U+Œ/+EIW#^wq^$Аj Czz̽/%4 {z$.|BsӉD tZIt(\A"Ro=LwiC,W <裯Zwwwa.#oWLW&G7Uڪ'ϗ勇)BM\^-9^O}h&hLs/2u?G:ԆXnBdtJ|NG'ZΫɔ*QJycQJ*Аj EEEf[ZZʅQQQ~:ڊ<"r'D-SGw_2& -RW% I[XiKD~yMoНtD 4@ zGzK m*DХXK?Ḧ́#Uuesnh7<3tPq"z>M|ݞs-HBHp렜ISeY,Yer&57)n Xc4Al4 VLLLl=33355uiF5bU%Z &.*Ζ*HYz7\lH0&:tTMDG͙G]1WxGߞ RT9$"?~z Uh g/?3kwI2s?diU,Fͯ(,9y{h ?H`;W9z{%Ri(JXezf$W}Ti%Ιq;TirD)d}hB} D"jO7nܘq:qt:CӧZDLϖ{|yI~gO*W~9{'`/<""O}"h{]}?@}BIDT'JD4r]~啁 )|nQSn_6^Noog?^?6n\hWϽHr|.ws$ 9L#5T <!\Ȳ]5JD{M_ѳ0'V5#kmmY\\ӧJiiiron<|"}w/߸2:1z7?}=cğ駋<^ ~~f&46]2NOa```zzzj897xl#iҵ9DĤghA"9S\:+57tuiz]q<:NPH˘\BUfs$D2\*M^&3K*p+>H*.8,IݶP'[]1iޥ_)1zDR)F33<<\PP?#L&SܱO?_|}lM|{?z{`GЖkUiJ^ Q5x~tyhGȭ엿đ3(W|=={sqۮiEϜ;&'..:B}L*WID\R|\d{xtXKxs@*}0?$YG>oFڨɓ_VWW_,嫰H8윝pۧuLzedv?λl]qW;è3ppg+];h5rF )&$RifZgrK*jp|&=c93]#5dU.]@C 4^lq͂E9y>#.\`=;)lK4 erLb IDAT)z; g뜟\#Z՗;t4M.bd Uz$Zms6["!c7Rg&L$'GDI!8@ hk,xcH$O[CTGyD_v, :s6a$(-S/':m2|R_(!J`ip0m2=S&W+p($79Ty*`~uv ` 8@ ā Ykﯭ*aӉİ3l0_ojdfi~ZΨrJ*|%3pmvr2D3/x޼A)4.9YLːHeAַHQLSD h37J&M|A12ʃLd U1Z~ϛ.0.L R Mk` !qЖގ.Z^㤈77:*e>|f&;*S˿zܓ7{Qgiy`]v̸lS'0LT&P(l?.̬0òAeqY6xOuޮD)@C l_gK{Wov"":Oކa1U#6xW? CBdj?^|x &GƯ\JV+Xɳ:y_!PehF/W*HecveѦT+|w7ziȿ$=M~c|0?tRw|s_@ @ q@zLZ\om0<~MDk7i^!jّ]`>9`F x .Lx>HcԚ5wyrҔ@C\ Q|lΜs}i#"\}CCccc I^" ͗P24%'nC9ڬF ĝ\NN)4BoLZFw/7@q.wۉ9sc=4A8`[&,=-_u[}\ Nz+:y$+蛈b}&W2:z/\ɚ²]Yvf~szLx4ADI}H l_g댛]R=$J (:! āfMƳ/ul.yO{]Jϝ3lWUϾ^uk:̊„"gҴ"=YfD$jݎ+Iʒcvu }uhp@k6zQ!Kc.sKO~ZEDĚLg_}󚹅\eph.5\^':yk MEd*^ %Peuzܓ)UNJìgQh KN%[\:}T/RGD)@C l_ m]Z:꪿vIKŃҙ [l5.Xf> okhE N Z}xu=C3CW1z;4R[qY8uV32\JƁ/;>4 c8rWވ 9'}8>$?kO>`\wRK\wD2 w]jq=KVWo[sۧnC LC'}mқ_vtJ-Yxh?,N-N`~*a4lf@@42M}پK}iv~=73/4M=C-FQl:u}ޖj9nmZ, N%}V>SL7tֻX̏PgˏU9harm^K;3?01㤎hcZD'(J=bh8@^P~bUKYKgc݅jmi33+V DuvOPL={_Nw{IG_ov%c}c*J0+JXɁ%'nC;&'.c ^:ǯ\rIItD/L$K{yeFD43 tǒHei:%گTI#8 04cl]/g~QAΖgYګvi[g;֞;\u{ru&$߼)ȴ8%TcS &m <95==\hZ}aNQ*SKDAO{.TњŒ|D,MbΆ4%'w&Gn}p"F#!NɳGUxm&h?ͥ.e.M C7444lsl{JfZ/qlo u O~jP:[`W'̳kS|+3G񺒓gンkk2Kt]nnɑ*""ܤϵM-;'W2hO]RSvQy=;&>4qϛHe2*e\f!QN\ qh8 )jH[TKWWKes NϰW~ekGjB[1:6^d}]]?!upϭD}eJO]C1>43t5QgiE _YʒZطHZdhH"J()S氞XɝȬ=1pفb "P 0/9xAx@4l:`X\AJ""WOS=3]h'Wϝv"2[ m?7/XݰhKZܹ[FIdg"rp)8TY7NR0iGO=mnL!M(c7柈.)"W2Sa7XCa}7:QND@@0wot=ҞoV飬YUX}f7聶Y.cݳ u_t%N8ɢU#>P)9yV_~|x\\$1y+UF!"s>qIC:+tPq3g-0} Eeh" lyqmg4.KSm+:zꈈñ<ۧ5]\}ֈ(uJ‰+7}}rrT~12I+zɑˑb3'(}ٱcџQ@MruH`" 4$>C][?3~Pe5EK$z{9zzQ֞~n/vu67xImTjE_+V+W.Mcܒ#%GX{rR0OЗ. ESCh"{^ $3vS/44:j[q w&]Ulik46ޣi^T"^9XT(U9ج6zZSr&:mg.QeJU8 3#4$']]gKT|ҧQu󽍆y24<_|Uok\eSMCskcOy6qOh= 3Z}LZB4D\N!z޵P+^韉]Whɯ]vva=6Vtuڗ?i}zJEDdnĄh zYtm(uF.tzS]7mm/JZz;ngyhK{Wه5lRQkG55wjGHϹC4q9X*#G(GH0soeo:=}j-מ ]d\1L{tu'RqݘgwEGgWwKK?U}NK=U6sg{{FSm}SS+GX?&-/ \uV^Ee.*Tej?x_Xs$Z ):,<>_O="ʑt2j97t=~rKn7Ι;@bC Ggk0e\Sez:߻+u1O6ƔVTD.?k%N[dŗ[;lڸ; n9vmjPx:o\/4p.-9! vA&W2e M7yp7Y~59Ơh8(\.ƻgkOX[kZߧlDSK[;.,Vt:z~Iw;Fl]/>Yon2ntSVQ/ו)f-Wg-Ws+UVo6C=]x4?cqjb- B}qCʼn ]\CGBN_~|vu&/3'Y3Om5 r";:\<ﭦZ97拜}g ZQrkыU~a\H\XR;Cþ NȔO>|xNZF{ч 5"0&ﭦjo+-% :"g_}x'Y9d~)mgrX㱂lQ2jm~kluDD?_a:iJZ3VF-ه/o,NSQo{s9~$ǎߩuQe6Ŕm3ݍ \>>16t-=13knk|n׺]5]{B֨bښk/}vLDZ붬NDD7fߩ$Z?}S?}l0xk~4ǃNZj5@X2w9nѹz:&wVstjJUָoe#´`naWE'n6 #KSccXW)Qkrˎ.e̮GCc}R`Lဤ)fhia"[+R&oeu5@D.>:=T2UM6YFߴۉ{j[}V]͛ìec\^O3DO .ˇ N72%/m<;{oe%{,<-LDsO>P+G-Bu9.3uig򢏈Hkq裯Su?\ ݒHj: Q KekjsM%Ӭj'}I*7=TgpwM9Ъvvw644lcYQŊ35ol&EZ}C I4$΄9N !HV-?4_龵=lygu195*{6} rkОι¾`){;{w_&bVյ*vIdG"[veFANK}!)##"b=lq)S;nGuyH^_ t12) WnC翩i=jyan3|F!" $?IkƵ Uff[w۫surDZKIeimlxuS_[E i}zJovQy֩hHRr%STWvl _G//S8 I(;WGZ6$ػe*%pDJ%#B!6ѝl8BrF.Y2v֕g鯿oi$Lq흛m4 ]ȝ 'p{ftDСНh C:|+T9qmN;q#:NDD,IjSU[-6UL3Tҥ^+UrJm7#:̭]hصhC*CS`:_vLmq=D9>$;g/l!g-f-GpPy6|M`R}_P\ X"#'ʔ&(ΝYS)qV "&ԇc֕0iU^Q7L0$>]OxКDٻBakeGUݫ@}Eo>=|9oH~63ljn}5P{S5us148~}h\Gˏ^^go|u뉢-f~0Lfֿ͍t>lhd>uu9H6ZQQܦqL ׄxbm?:ZϡZc'O6^O[6]]֪v#hT§^&"b*ϿչAL/*)h^tT4qSi}Ӣav r~Y5vuN^ĻX-^ a!Z]*Ѱg""ca6^ US\JF_3k w:sSiLP /,e^ 1!?3K"{9BnrurU.(4( Θ3SE}N_O09e5cf?hHa۷q ,"_HB@'<}+\qǽt+K4G'/uVV_m,Mase p}׉r`L~ /yBI42Lmt̴snZ.9V^GQg ŚC% 1m#H#y]$*D9A4N-h?#sn.{+Kkq/LЪuyb9b=n>Щ*dy^Zܒ NP@1:~VѓhޚZ~^94uc~މ68F{rM -N@":T"0& T`?8@8$Hr(ۣ$f^i+K)һ#W2ܽnp^/'.c^df" 4~cF.?Ul݋.Ӈeif4D3&65=1p  /@)1od=‹p^9?khrc[E`6KA_l` 342JF'S0׼ ᾂjQP@Y"R[x̮pߐ+87ZSX(3:Hdubm~FxqHy&ty2su(D`o, NWCBx@8 ~Ct`V_`}z܋ӣ^?T)R,FBAzn#V>G@!6Bg+dNhuV^NqV_EkX-BA\PF( *D8)j@E"ژ,s'2:+O1;09l_(JT&$,ՠKDRڗ#H6^t C.E~@C,>tDux-8&7ZvHe2ЪT&H$Y] t^C_$0~( 4Z:^VMʱRRC"YM-Na lS^9&#K:<}_a&GsK*HЂ,)ai}htt]nf>G #Scw{  LX*:+OxYG 4^qV"(]YfWnD'u"uV^FV~*M .Na3l! AW*Bg,;j> (UOxP㛣7y*CѬǽ8=85}bqH(dP{ijeZP3 )8=.yhx`?@8/fptgxtFv^ўEԘ)t:p8⸀B6ıRLR+iZ~CR@д:`;`"nEk$Hz,؅Hļ[ Nh Gؕeve9vjP/W*bZn>OaV"p(H( /JeGP9w$Ԉ s7: @!~" А,^lho!W4J{˷7* )eYsTα^YB4{Bx&Hvgw\d}ou5"W(rL+8CGD~/m'kJ9]rrD`OL- #r7rp0e2B&WJ BTJϣ6Xj$Oj;#pcwW-BX4"}h=܂.|~;H*ɕ*ij"B<8 38v5;¥XKW1h=aT@C |sԅ }I@ 5=MD|5F&SrAD!Dh5 q;¡sUR85fEH W>ob͝Z3xs$ԛ+WIdRA'Dp5cO2MLP0:*<,0^YuB e 2 % 9ϝwk.m6q4T&RRv$qon ͤ l Hp7'&…ܒ u 4BghiU?A^BPeh W;[ths t(xt:$g{  J/B! q- tQX+4Y_\08Bϻ-+2e)n dž<yh FTX0q@|,"*_s۷-~/'2TitPH !!ΏA$,t?~;@j@ Ņ."bAMлB`  `B}$ T %.]a(}SsD&"m|P9a9j$𯄃p0 {*@AG4@B:TwiG{s-|("Nj`  9j$ B!/p_Ae:q@d6`$"ZdfųdN0+nλyez81Gpe!Ø !t@d[ &-@Di2MT~3 gD7Ԫ2orp:Cneyqb2kJeQk:'ADXO+tTl%IۥsKti?ܾؔ:x!::[$q ~U~4guV\V{EV_LD2!D4^?fxL2W)33+hZRc !gn\x5!otP>}6s ֨24wn7cXwtF"t{Y<Ω!VX$){kk ioi4[\dkjmk@R̮l7S:+_T}]od6f:C(DeHdSO$"tGbdF,Zkk;{ZLm{;|t/[wc顯65ގvrF6ErǴT@lDY; =."//-e *2]Rk:jem+M5QgeUZ㽢^kg1gn4JWossKkXNYWwc yO{Xwku-ՖknmPoIETTMM}ͦ- h1l+WE9QS[dred뉈zRwm u ij +,M6e],kXt靖5Ԝli&v>B?SP{w_ޖ3hWOs݅KU( "bcv?Vg "]ucggS==:N5ն4W\G 4@H}`ԙ*uff>3GS\S\n8rcf!uvRQҥ2Y {RQk2T*(Ur>=m59[WSCs룷kM&`T^ZZV%ccݡ? .<,c}e˿XKG}݋+6v4տwAqw M- Dk- ;q[NJM8lf OVPNLNj9Fu*WǸvnpe2Fs 36r|AcnFEMsyƨM*AIxF~~?;k`Ϗ[ǽIwk/^BgzZ|U^8.nz7$ -7:n҄ƅ.Ow44ŁA; hٚ'~ آ !`R$E¡P0 FmLX '`ji}\]}h\]!p*ukޖ3'1R@gKON5u46ڛ[^B10<}zJ]kwOx6^ړ/6t?t^Q_w8[:E@)ݞp4>Zu68kAhT^!G*z!&A LGp~Vu@8\^Z HH[\^^b aR:#*u;ܹS*极wiΆd_wCOـdOʘ GcGOgz\4޾O-:/۽mOycbDQsh(uUQyQ!TEUV@<ƫŵTpX*}*۳@\Q@ S$h2b|/bQ#PHӢŴ`4ƒkr5/ ݌]~t497^ٳg ֟\(z]+Z^š x(lz]-u}7xnp)Ώ !)gs{[4RQBU^~|7JjU}tEY (H.}";;',BRi~!tv,/-LH}}.1j*>I},8敹>/6m+[q\k^MOoKK4^/}]?yzp::*"sy˷:!}fEEBNUxb5M[1(H.0A;KKP@[#R$b9T(q&.u=*܊Ux7xWtgsvN>٬nϟBxnnV!\=- JCtjptlYe^j[89+jxWiT'^s x\! 0YF Q6 le;KUTSpV축T9J^ba\[~iEK**Ne:oZ@QWlXjC]]DukG&岻9q}jQ p:jC{]nAQjꚧ6[w0vKXƖZ1ZwEQظlO܀@&mf# .E! U,J~gAOC\=â:/*u IDAT ex56ۼcV>vWkޣ8qym};Ojc{W:::!t-z Χ^lnlqj-]: hx)atڲ>}+{?.[FjjxڷY*jMt]s+nQ_9k-6x{o^:[klV;OwbEQtΩO<`EQD@Q+ G^y֎F\=mmghPBloC]7Wn5 !<~rQgKSRUqSYhzwV~;aCmhyܚ⨫wb57 hL9m0ZL0(H9-QW.p Q״vu6:8򳁕YӼ^it(Bl==hj^wymo\KWSge+>Qtg] +OTEl*G7Cs4x{Z;{[7 \{ 9G\K`a#+t1R$-Bp( As,V[3J1vqƵ Bw>wӏ@*<}uu-zs5?ƍjs۹с:|Non hp]1bz%';bq¡@nO׀^'(**ա+B8lhuMuMr~* >c3vd0p[2y!3MP[p{NO=^נKso3@pi_3ŴH΀Z`6C 4G6ZLo0ZFCJg UP@dh%lb:+;;G)@  --y6K-ǚ+=@Q@-VźbZCJg P@@bzŴy!3(_ciZXQc/GBN|f)4b*,޵gUzU!Ăw*'Am;yTQ@ (7}i UP@@QPjӚ߷8 gss?0?+р!R4+V\^d]JARPt83<G~#B KR:oft(87= g&) U{eRWmX&kntGٞBىQ!3"̈3fX +*KntՇo̵bp`#Ϊb76+w*'AbS`0 hKVvR$b) +vŠhيEVt`nvqg8# c6 (pҪ}mVLk~/0?N{Y d:! e+./WFɱ}Lm8D;0 LH(8?3Q@ 90MF$/PWTtN6|l1->8;!n42YU h$h\hTU޽E2[SL?vU1;1*(Wi> 4%OgX +*UGUi>Jt-^_LGMOڂw* tLb7P@=^Аˣ)z-/魤r*݀[L_Ӊ4=r#^W_G8ZZ[ܽͭ/]X-i~n:P]$ZanQUXQpbE-Y1 f|y`iBiV&5C]MϝUתދ/za`ABh%/T[<ֹۤWй%ы Sيˋ%(f5oqG1m;j>. u8:Zxw.Q@k-ϝSOirkmx'-;3l};:{m(RD~(Btc=N)(px(87= g&D$L RP@58G]CCz7v?޶ . !І_(xgiGCo  {sLǬ65=CZscs`I=Qe1jګ6 $͊i!̵BىQ!3"2^X{ q ^:qoY{Z[Μsvv;V#: moT=[Ϝj>0YAnk9#=/x=Ͽsز :(u k- t+zuD86m0BXu -ŠJQUX{Hh7+3 sۧZm?o*|^/PBtՇo|~}12WL¼Kn]x<7=Z]{W{"w'g?wё:7oc=}Ϟ u{}]~QB8:{\}'=1G:nl׵Ợ6Eys(jCאGw5WwKK4olaMdQ@gŠe{S: ,L~y=VÉjshXmڊ+KulcfŴx8XNd8b1m? yzz> 46w+榟s|Ζz;왦:@siӭ-gi/\FֆzEMm7ag[@&T;9zT!\[:՛@U/VUU7Jii8uVyMζHһ 9VVU [>m~?{9 ` __oV\nԲ"me=b/|JvE |k  h:rNRF:Cqu4|ggW*\zEox蕮~wks<}g~jWcjct t{&պU{k9T!1:T*λ<7׊r5Rk m1ZeZ֟db+./W+LWWkN90K0~`uj[Eo^ ߷8 gssd(u&`8 h4WwKs!MmM1lo|e:EM:keG*׽PfGsǭuІ15&pu7; G+?9Jo v6l9Bu:Vj(lZi "EgO[C}ohdܼGBA!Dtlmޓ3EdMIm/,g(BlsN846 dF ii|'ގeGrV;׼> hY]2 #6V_:}uwwC u55=Woy-d-/rj+T핅J^si[2ˁYm'f^&LUFNIa2JjK84L&S8esPP,i <G As OM17m㜨}yA֎ ӍcPpV:B\;;>o:[;آVMN|3ESica][3ںZۻFOx'Zx}ڑ_\lߎ1@0 ZZ/Jؖyu>V G*+m%g,-/-EBA=OҪ}: G AiR!ס vo8R;;.tnR{ښNG~w*j[jq8⥘z$U mp *Z{ߴróݣv}Bx]"{á:Tg]PUPΆ uK~ЦeF@';ZXQ: +v0Zf@,T7R!DVvNN8hVr'[g[dZpXJ!nL >8ִ_@{C/]۷XշJu"pcrRPtաAhpfzB8ԭT*^o̵fm'9IqBx1j4xe]5u6o$ҹxrk^H8LF2o=EBbHs&$C ʘd~!njXWj/T|}{ћNpnjnVO>?pSu'[P+k5JGkuޜl~As~նo},O99= &'gp*Nc58>m_L72;љ`9($Ƣl(Z u \'׎ІN7?w^<@Jy=WSuE>wgn|Mksӆ}./;ڛ?V7ǡ8Ic91eg}bVK9:@7>v @>@Rlh!^[G]:ϸ/nPTgZŹsOޮV8;z;69 [KbP V+ 핪J)03`_ ~a)'04`cּnMձt+E:u p8p)Bwhj}cS~ F1˸hjXPv&ͪVzLeY) +vfW [,j 4`cMvc9: e/3*ح: +*wIsǣ-3 e2ϰ04`3glWncv؊ˣO6盟|l%Ye{j,z7%fEEa2At`Iվ"{e[__Nis>(lhqާ3l͓rQ@ Za9[0[ 핪*~WWMOLrv Upȗ[KEQV駤r_\w j~Dt|¬Wg% MP@ ٚk2[A=!YbIyR]\w F'8MOL-t&Vr h"1?qUO9$0huܱёE 1są%Xg:L,WVU:L % /uHą=&'[/y-ŠҪ}v Ѯ!3CHEnk Še{K%;U {栔$ h"drZ!V퓲Kd32{$ hZP?$;+ hYS^3df2S:CJ4bأsaRXaiuΚ=!ĉBV(ܒ]: "Ty*KPuTVq7>@(8? hEls!,ح:J*lbi(r@XML[mR (){svxusٞ;l 9HK1 zy T(R tH=cK` 5dɴ軮3dׁR$(#{suf $eaAN&$- hHiÝ~;l ,<(#{VE hCΫĨ67@JXL0[P@B @IC1;L 3j$ hHsΡ'l5- a!H?ė=ˋ^S'bwٞoksto33`k] P@_: ll÷Uѱ%wWQ:NzQ[? P@_t䘳?G:T+Wn33tRNT(/9{7\ZOWXggJgdb4(/5תVzB֏Xg>gG<Xy! -Q@w: lŪx+ܬc64;1?% h.rך {;=Y>>=z7>٣ۤj+rB @;nolY-BMz=#ӣWm#& ?tE @ms bOcoў;Jooo!@߫?dW-4HO$fcjEcoéVY'Ibڈ!IDp: !^f $EO-me{(@z { +kmuBxƯ_xy;FAH",@lD0[slZUϥW=_.B[|! | 7뿢? 9Q@D^W^A Il_n@4F .^z嫱74] 1B(i :C564Ƹe M#Xa-~l̄oF @B _vիə%,4oo4sK~t\5˕rEBp@me{j 9e}2H]Cf"o!BE!\iQՂrUN0P@IE! i_?d|`$SVn@G @B8 vOZb9hHf$^ZP?)` ol4V+?[`,yRo8(H=!caaVn3!Żҟ(HNESԥ?dG$? h p`)6`nOvv΢wJ7@GsYE ~M7@栀pLrd2s24Ɛ2czX`Rr24ܒ IDAT}w]\l'Cea/-(ӟ3HH?1? P@`p\Dq-x'T@ a/%g!Kħ:tjct FC[X^oLC XE)azlXJ7@H%"x2[9 i(0Ta!O`a^JB(0X=RrXJ d~slwH-˟L-e2IY?xr(0ޡ..sJb]`PJ΁HH-OJ-%h* .a Xa/-/WL͛"9 ̵a)9/%%gl1WJ ЦG? Q@=fE 0?+% Q@rI3TJd2SVJb d$ YS8.}6/%\LHa p$ U-ҟXd,4ɟD~d8 h5Rr> q v,8hsX?@ @=,(ӟ%h1:~E{xFJ@ꢀ |>9? E")QQSsloҟ(H.X$LFL\d(ӟ(H2_s !maw]ٚwԟ(H:3) 鼔?bHHi$U-}R  5)Q\ {)9\m2L.I9rl(TG @2RՂJ(.Av9ןz[Rr4IAyR ]LL,S#% P@ջd]h,$R -I|HH$/Y_Kcr{NJTwKH$/grUJHX)QfSYQw?MYQ/%+jҟd4?lՇdEi4,ͲF?u-ФDq` gJ`4*Iantfs/+[ e<(q'6B$3E1Hg×dEU:g-P@2ջKl\+# m-/EJ+~(D @*9&t8 $)n@#NL&ӧ^VZ͑ed% hRH24 L hṞXl#D2[FiȔ5۲.ןn_]F$ڍ>eYQ44F @ᄏ d}8aa!W8{'Q@zr#'J]\^.hHdʺzdkIV@z %pVUJQrMV$ဴ݃ (G @KBt4@?Qe&ߨ9rlO4G @rKk)ğ].+ #7Hroy\-8bH84f67+H w݃X| @α}<7l_IYiv侻UYiSbYiEFM !ıKL'?&1k"_0 h"wFաwJxKbOy"2FLWe% !AY^}@oD @PՂGbt؟].1ئI!٢}H~/1x^oD @Z9rUvC#HN@`2 ?8$f;citJ!xg4aܢoZg!Dc-S#1 3Q@n`4_lK`an{24iH u@`ksӓBkA XD>r3$}.,@` $_jIL&_xz$}!̊L`k!rG?{鿓(H[GJb`0bx&)+kw$yNXb (HgqL##  %ƞI+bƯf3e{jfd8 hYbo.r35 f@cCoͬ:to4iYK_SnHH Xd2]@A5( h#'%L`=bO]r !ژ/ 4Ǐ>PXH>d}z #8>pť+r3Us>!D~ qRd/,޽8)7(GJntx|Bn&+ǚk,"Koi}?@, h2?(}k܃F-V㙲d2ɍ=]r3 XB|tMsJfP =0LK 45GNIn&֠ 8_}!p3FIlN\ˍ-޽? (8}c96@n~Gf'Ǯ_lȍzd8"}t0bz{4G5#0s3-=?GF?$4(Wo> :1}'=NKO='=ŃG @F;rXHA20,/E.cY<` h2]< ! P0B) ` ` A(tyLBB!ĥk!:h̤G@x$x(`G24}ĝ)+ۿ1LғY<` h {_J<頡_v# s8IOf (Ç?̏<d. hU-wV h'&)_xMHP@9cq t-tE+q)~XBN"89q鿋S8vla/}k_StcJȱ}x>p 4CAO#{UaV┏T77=lxHaɵŵ}~p4xw?SslqGJ olS < ,}Sx} vЁ`h4"o) Q@m׏ŵð%S|"0L^8>$9 h]q燗k;ښ_hI3#q7[13@2!rEZG-E"W6;|5lOM 4=/] ]E9~@**4 K k8;B *m1!ćp0֕,Bqzf^>n@=)ќ[0sjb5$473I U`A#? [XKRG-.iwgC v.`-!*L+o?1'>" hK:KBXK"k` p%~+3@ʢz%GΏ3:L < 6d2|ׯm\B ( 蠣#rg*c4q}JS+ǑKDs}iR4 +~ʧ SEf VH[x/C3WR4 g0l A0o٢xDF|x? =P@:߰X>%282HZf2o^jh4a/wK@}ZTQ$ /473y=vCQsgS .ty..9lYOSs B0[+oȇV:z?0v sP@p׿&扳Ko쪰E$A7LO{cw=CGD>c0Rt$tMMB7]:?rT]}_dI҃)+71vl{>d n@*oxo^HC ȑ| 3L`ۿIsw=ve{j\$ h=x=&a#SȔ߼Ra-aϔu߆}^sD÷geZ^^6 +?ߎ'{r  +v~6?3iyR4~Ͼ?l;7%H*܀I$W~oK8!Kv=Uٖ`Bײf'>0;bIR)+5 ns {߿#/#9Ao" e(<X~_2b(1; 4&/yAL&Spq~,0[h$'n@$Xx? Ʀc;JʩA(c5YQ{WOcA.CD ڑ_?o&' 쪰̊1sӓŻD_-IBf5XsFUBǾo4HZ ٩jۧ LuZ=:eF!,/-}SF}4H ̈́ ->()`(G,x.>`k e8o FІϮ/2$ :szB؊ˏzG qΩ⍏h8W߿m:b h s hSV67G4psjXm/x:νjc-Gl8wm~É8Ʀo/̔-"#ɋoz7mm_{>>`;LFd2pOǿ3ݽM-3~б,_m)l)=߿{g& HZ)}ѧW?pى R٪L%à(5'9x[F)PChӦzϋ"g˯~“_}SU{_Œ_w/W|Kܸ-#gX~პWU:M c0Ttk퓭MOkϿ5ٰzz3 -xw?o%f.Da+.[p›W?4Z}ɲAoLCخ@dlǕ*HD- : ɽW5qʖ"%RH$Č1)8[V fzv64CCx`LI`x>|~;_}~_ H"HOO!d6Ԇ]yG!Ld, o^kyWwmE!-ύЉkȍYS=r.,zo9uyfsWz ;6\[h 7ܰ(=좼0PSu7Vv-3l@7xj~?7&VL- 9 ݘ|?O=Ҝ[-QfUwԔCOscO aig%k?u",xwY;z:ճ\ok(K C40nI?Zu~Fy_uգ/ >XOt5.s˗|7/J_ޗ><{rƻ0rj~E|7Ft t9~[I D,USi@!3E-~N6666UV奲>O7zT-0qx'lf?~w>o[?;qj)v=0@ԆVmdQx",dScmIp6o_:>ng2oX| $EG[hfL w-=0@FKnM|2T[YLpgcc]8|E浶w =)Ubz?q͢ם;>x"BqsڣߟHNĻ!3#:1\+ߺowIYN@K77īʦnvWW>75\z_o?oC_^N^}ñ#ۧ_ե\2R= |HDcMyՁcɕPrm50CMvCzl oC I XFz^e}WIy]]5vz ¯_x7^MD[y?5v)fݴtſVrk PvYTE^zU & |ߕ雩>*p z-%_)rʷAi44@o~TO4 _q}Ƽ<0o1\ߚ]UOO sԁ_ X%!B|k(K p5-:w\g恡ƒU[M5PPju~`bTrצ^D~p *oݷz7<]y h.* G;^zGʲ/qhsḿ=E%Uy=aM!xk'+qo4pud ]wV58Ը`9笲-<ݺy%894嵸ziέ}y;tgJҳCV[77=T_hGzAyuA&cJt~j_ݖ~g{6Xh* YY5M=e MMO65Ԗ\jLnECo|Np⋺>vc^omfgB*mWPDSmedpgcc]yނ{w6|CtLy࿛NH湤9o\C :4ֺG_IkCПcGOy?_T 3eiέn%0@Kj{ꫫ >r>.{ B'O>prHNX LnNO7\k:ݒ?'ܺ,7պnXj־]45iz~sx78hjTSwBHH+Vݸ\`^.1?=Vp\1rp虷xs~؍n_vuۭ_ |&M5U;΄VxD{!~o 5uZ_7o2yYu3X.r%;{ǓeyCC5EωDIys炼իBM$'Kt211pr(L^CnɿyY[pY @WϿ3Jˋ.z{OXdwOmArnH8"B~38yo%s[MМvrCm ! )!@WjXהCM%;p§xzu;{Kk U:0::>zd29>9->0YfѹsR=pmH4g}g_<= 5mml)mo|џ,POse*ɅS/R '>\鱴o.Yr%mZrsVp-+6XPOSezcҪC֚;ˮ`AYkGv6ֲD࣭!6֦"@W.'?Xyl%=}$gj8$C]iod7vlĉpꆞ &zk:IzJ&W?T5t'; /=zg^QMS"0 |vCuU4ֆO  '.l*J0UN@ ' @$h *򲳲*zCyi(;/Ht<НʦK5Fm+&@7XUӬ?oonm{ ?_X{buʿ΢}ܹsH}[YSYִwW4d5T\.򲧥ak涍F+&@M/Xۇvmw^rhXFGC%j>Iώg3hsuh>DWmɭ%!d7꼏>˚ CMUٸ$ҿ .I(^[Ա<BH/()ʞyVv^, %PcUOϬ{`m"VxQ+ҳya'|VΝ;4Z~S7Tv냣t},/'Z+|L,dᏻk R2*6'`am<0a֏?\0regg>'`!j,Y;^/ !6W}G:>~z*/0<8Z}SOc?C{M^*G``AI fdO;認ozM\u4,xOX/f.C`J\ǚ;TYy}/HrӪxUCrzAuS#ϼ`}tV``.)YNMy.Z_ydcjwhXFJ~a`8,Hcc]Y%/Ll{* =+;U6glGN^۞ghYyeul]_PYe3`!.)ZWBYE5M=_7 ZR=@$!}zDSmedpgcc]y\eN@>#0kɻ:/'`K ,h"a @$h"!@ HDB 4 @$h"!@ HDB 4 @$h"!@ HDB 4 @$h"!@ HDB 4 @$h"!@ HDB 4 @$h"!@ HDB 4 @$h"!@ HDB 4 @$h"!@ HDB 4 @$h"!@ HDB 4 @$h"!@ HDB 4 @$h"!@ HDB 4 @$h"!@ HDB 4 @$h"!@ HDB 4 @$h"!@ HDB 4 @$h"!@ HDB 4 @$O N4+>ڲ=7򁾈h 9Vً.'ۏTw ?9vէ]3V08 @$m :w&?z#y-?|tX+_9Ec78|W eƦHfο˜v p1+8 0o?/y#aݽeOwz"#'gyC_~VV! 6HGK_!LtwN87vhY&gyxq+{?#]'^h>d0>7o; @+}uK3B}^t 8񶆖d={UC-O:D|f3Gwwu;Ze=\B(O]mm]v<BXQ\&=T#kG԰#!0rdr>OBČ?E<4B}~8#е)h8OW?~poņenxax#Wo΍B,wǷ.! vtO"kzwi{LnspqZOڽgs?{Hv !x~ /\y-aes!Ҝ#z'{tN5kBl:}Hn !y7t[ъ _i;K KׄLiBܰmwuNt mŹ_g_<0vzf.ʹ!/ !d!${Vx?x鶖8=v:~ >,ޞB;v*xymtPT])pSm-!,/ݒ !bCntϵ^8˯k}OD[o񶽛3g|Bɤ ]V\Ncp;fnXqq iӯ!L(q];BݧBi~p%n,v_ !, iapbs$=7_Qqm†pe0[ քΖSb#GB(2s+vw/ / kKKKl)ݼy̙Ouwv 0 HZlf..url2N ]7}23?SOB,Rˈ0$KQ -}-]##}!lH/};:F†]!U\8LY?[oaб΃v=2o|}anoGg=hkڛv>-չvq2b!K&g$Br<gBZںtƎtQXb0ž#]#/tVtb=ɑ#-G'ZضsE-9 {p !mMqEEik7f[vuGWksZl~7 ]$sMnZǿex{ޑY3[>'ps:>BݴBB5sg/zڎ4c-C!mSiTn !SZ|QUm>|ه[&#m|otXxOȹ[Q~yx_WWWHf}=RkkQZm{BΝ; 4 @$h"!@ HDB 4 @$h"!@ HDB 4 @$h"!@ HDB 4 @$h"!@ HDB 4 @$h"!@ HDB 4 @$h"!@ HDB 4 @$h"!@sy@,IENDB`bpfcc-0.12.0/examples/networking/tunnel_monitor/main.py000077500000000000000000000065241357404205000232400ustar00rootroot00000000000000#!/usr/bin/python # Copyright (c) PLUMgrid, Inc. # Licensed under the Apache License, Version 2.0 (the "License") from builtins import input from http.server import HTTPServer, SimpleHTTPRequestHandler from netaddr import IPNetwork from os import chdir from pyroute2 import IPRoute, NetNS, IPDB, NSPopen from random import choice, randint from simulation import Simulation from socket import htons from threading import Thread import sys ipr = IPRoute() ipdb = IPDB(nl=ipr) num_hosts = 9 num_vnis = 4 null = open("/dev/null", "w") class TunnelSimulation(Simulation): def __init__(self, ipdb): super(TunnelSimulation, self).__init__(ipdb) self.available_ips = [list(IPNetwork("192.168.%d.0/24" % i)[1:-1]) for i in range(0, num_vnis)] def start(self): # each entry is tuple of ns_ipdb, out_ifc, in_ifc host_info = [] for i in range(0, num_hosts): print("Launching host %i of %i" % (i + 1, num_hosts)) ipaddr = "172.16.1.%d/24" % (100 + i) host_info.append(self._create_ns("host%d" % i, ipaddr=ipaddr)) with self.ipdb.create(ifname="br100", kind="bridge") as br100: for host in host_info: br100.add_port(host[1]) br100.up() # create a vxlan device inside each namespace for host in host_info: print("Starting tunnel %i of %i" % (len(self.processes) + 1, num_hosts)) cmd = ["netserver", "-D"] self.processes.append(NSPopen(host[0].nl.netns, cmd, stdout=null)) for i in range(0, num_vnis): with host[0].create(ifname="vxlan%d" % i, kind="vxlan", vxlan_id=10000 + i, vxlan_link=host[0].interfaces.eth0, vxlan_port=4789, vxlan_group="239.1.1.%d" % (1 + i)) as vx: vx.up() with host[0].create(ifname="br%d" % i, kind="bridge") as br: br.add_port(host[0].interfaces["vxlan%d" % i]) br.up() with host[0].create(ifname="c%da" % i, kind="veth", peer="c%db" % i) as c: c.up() c.add_ip("%s/24" % self.available_ips[i].pop(0)) c.mtu = 1450 br.add_port(host[0].interfaces["c%db" % i]) host[0].interfaces["c%db" % i].up().commit() # pick one host to start the monitor in host = host_info[0] cmd = ["python", "monitor.py"] p = NSPopen(host[0].nl.netns, cmd) self.processes.append(p) def serve_http(self): chdir("chord-transitions") # comment below line to see http server log messages SimpleHTTPRequestHandler.log_message = lambda self, format, *args: None self.srv = HTTPServer(("", 8080), SimpleHTTPRequestHandler) self.t = Thread(target=self.srv.serve_forever) self.t.setDaemon(True) self.t.start() print("HTTPServer listening on 0.0.0.0:8080") try: sim = TunnelSimulation(ipdb) sim.start() sim.serve_http() input("Press enter to quit:") finally: if "br100" in ipdb.interfaces: ipdb.interfaces.br100.remove().commit() sim.release() ipdb.release() null.close() bpfcc-0.12.0/examples/networking/tunnel_monitor/monitor.c000066400000000000000000000062251357404205000235700ustar00rootroot00000000000000// Copyright (c) PLUMgrid, Inc. // Licensed under the Apache License, Version 2.0 (the "License") #include struct ipkey { u32 inner_sip; u32 inner_dip; u32 outer_sip; u32 outer_dip; u32 vni; }; struct counters { u64 tx_pkts; u64 rx_pkts; u64 tx_bytes; u64 rx_bytes; }; BPF_HASH(stats, struct ipkey, struct counters, 1024); BPF_PROG_ARRAY(parser, 10); enum cb_index { CB_FLAGS = 0, CB_SIP, CB_DIP, CB_VNI, CB_OFFSET, }; // helper func to swap two memory locations static inline void swap32(u32 *a, u32 *b) { u32 t = *a; *a = *b; *b = t; } // helper to swap the fields in an ipkey to give consistent ordering static inline void swap_ipkey(struct ipkey *key) { swap32(&key->outer_sip, &key->outer_dip); swap32(&key->inner_sip, &key->inner_dip); } #define IS_INGRESS 0x1 // initial handler for each packet on an ingress tc filter int handle_ingress(struct __sk_buff *skb) { skb->cb[CB_FLAGS] = IS_INGRESS; parser.call(skb, 1); // jump to generic packet parser return 1; } // initial handler for each packet on an egress tc filter int handle_egress(struct __sk_buff *skb) { skb->cb[CB_FLAGS] = 0; parser.call(skb, 1); // jump to generic packet parser return 1; } // parse the outer vxlan frame int handle_outer(struct __sk_buff *skb) { u8 *cursor = 0; struct ethernet_t *ethernet = cursor_advance(cursor, sizeof(*ethernet)); // filter bcast/mcast from the stats if (ethernet->dst & (1ull << 40)) goto finish; switch (ethernet->type) { case 0x0800: goto ip; default: goto finish; } ip: ; struct ip_t *ip = cursor_advance(cursor, sizeof(*ip)); skb->cb[CB_SIP] = ip->src; skb->cb[CB_DIP] = ip->dst; switch (ip->nextp) { case 17: goto udp; default: goto finish; } udp: ; struct udp_t *udp = cursor_advance(cursor, sizeof(*udp)); switch (udp->dport) { case 4789: goto vxlan; default: goto finish; } vxlan: ; struct vxlan_t *vxlan = cursor_advance(cursor, sizeof(*vxlan)); skb->cb[CB_VNI] = vxlan->key; skb->cb[CB_OFFSET] = (u64)vxlan + sizeof(*vxlan); parser.call(skb, 2); finish: return 1; } // Parse the inner frame, whatever it may be. If it is ipv4, add the inner // source/dest ip to the key, for finer grained stats int handle_inner(struct __sk_buff *skb) { int is_ingress = skb->cb[CB_FLAGS] & IS_INGRESS; struct ipkey key = { .vni=skb->cb[CB_VNI], .outer_sip = skb->cb[CB_SIP], .outer_dip = skb->cb[CB_DIP] }; u8 *cursor = (u8 *)(u64)skb->cb[CB_OFFSET]; struct ethernet_t *ethernet = cursor_advance(cursor, sizeof(*ethernet)); switch (ethernet->type) { case 0x0800: goto ip; default: goto finish; } ip: ; struct ip_t *ip = cursor_advance(cursor, sizeof(*ip)); key.inner_sip = ip->src; key.inner_dip = ip->dst; finish: // consistent ordering if (key.outer_dip < key.outer_sip) swap_ipkey(&key); struct counters zleaf = {0}; struct counters *leaf = stats.lookup_or_try_init(&key, &zleaf); if (leaf) { if (is_ingress) { lock_xadd(&leaf->rx_pkts, 1); lock_xadd(&leaf->rx_bytes, skb->len); } else { lock_xadd(&leaf->tx_pkts, 1); lock_xadd(&leaf->tx_bytes, skb->len); } } return 1; } bpfcc-0.12.0/examples/networking/tunnel_monitor/monitor.py000066400000000000000000000055141357404205000237760ustar00rootroot00000000000000#!/usr/bin/python # Copyright (c) PLUMgrid, Inc. # Licensed under the Apache License, Version 2.0 (the "License") from bcc import BPF from ctypes import c_uint, c_int, c_ulonglong, Structure import json from netaddr import IPAddress from os import rename from pyroute2 import IPRoute, NetNS, IPDB, NSPopen import sys from time import sleep ipr = IPRoute() ipdb = IPDB(nl=ipr) b = BPF(src_file="monitor.c", debug=0) ingress_fn = b.load_func("handle_ingress", BPF.SCHED_CLS) egress_fn = b.load_func("handle_egress", BPF.SCHED_CLS) outer_fn = b.load_func("handle_outer", BPF.SCHED_CLS) inner_fn = b.load_func("handle_inner", BPF.SCHED_CLS) stats = b.get_table("stats") # using jump table for inner and outer packet split parser = b.get_table("parser") parser[c_int(1)] = c_int(outer_fn.fd) parser[c_int(2)] = c_int(inner_fn.fd) ifc = ipdb.interfaces.eth0 ipr.tc("add", "ingress", ifc.index, "ffff:") ipr.tc("add-filter", "bpf", ifc.index, ":1", fd=ingress_fn.fd, name=ingress_fn.name, parent="ffff:", action="ok", classid=1) ipr.tc("add", "sfq", ifc.index, "1:") ipr.tc("add-filter", "bpf", ifc.index, ":1", fd=egress_fn.fd, name=egress_fn.name, parent="1:", action="ok", classid=1) def stats2json(k, v): return { "vni": int(k.vni), "outer_sip": str(IPAddress(k.outer_sip)), "outer_dip": str(IPAddress(k.outer_dip)), "inner_sip": str(IPAddress(k.inner_sip)), "inner_dip": str(IPAddress(k.inner_dip)), "tx_pkts": v.tx_pkts, "tx_bytes": v.tx_bytes, "rx_pkts": v.rx_pkts, "rx_bytes": v.rx_bytes, } def delta_stats(v, oldv): return stats.Leaf(v.tx_pkts - oldv.tx_pkts, v.rx_pkts - oldv.rx_pkts, v.tx_bytes - oldv.tx_bytes, v.rx_bytes - oldv.rx_bytes) def key2str(k): return "%s,%s,%d,%s,%s" % (IPAddress(k.outer_sip), IPAddress(k.outer_dip), k.vni, IPAddress(k.inner_sip), IPAddress(k.inner_dip)) prev = {} while True: result_total = [] result_delta = [] tmp = {} # compute both the total and last-N-seconds statistics for k, v in stats.items(): # subtract the previous totals from the current, or 0 if none exists v2 = delta_stats(v, prev.get(key2str(k), stats.Leaf(0, 0, 0, 0))) if v2.tx_pkts != 0 or v2.rx_pkts != 0: result_delta.append(stats2json(k, v2)) tmp[key2str(k)] = v result_total.append(stats2json(k, v)) prev = tmp with open("./chord-transitions/data/tunnel.json.new", "w") as f: json.dump(result_total, f) rename("./chord-transitions/data/tunnel.json.new", "./chord-transitions/data/tunnel.json") with open("./chord-transitions/data/tunnel-delta.json.new", "w") as f: json.dump(result_delta, f) rename("./chord-transitions/data/tunnel-delta.json.new", "./chord-transitions/data/tunnel-delta.json") sleep(5) ipdb.release() bpfcc-0.12.0/examples/networking/tunnel_monitor/setup.sh000077500000000000000000000013141357404205000234260ustar00rootroot00000000000000#!/bin/bash # Copyright (c) PLUMgrid, Inc. # Licensed under the Apache License, Version 2.0 (the "License") # this script: # 1. checks for bower to be installed # 2. clones the chord-transitions UI from github # 3. installs locally the packages required by the UI function which_() { hash "$1" &>/dev/null; } if [[ ! -d chord-transitions ]]; then git clone https://github.com/iovisor/chord-transitions.git fi cd chord-transitions export PATH=node_modules/.bin:$PATH if ! which_ bower; then if ! which_ npm; then echo "Error: required binary 'npm' not found, please install nodejs" exit 1 fi npm install bower fi if [[ "$(id -u)" = "0" ]]; then args="--allow-root" fi bower install $args bpfcc-0.12.0/examples/networking/tunnel_monitor/simulation.py000077700000000000000000000000001357404205000274322../simulation.pyustar00rootroot00000000000000bpfcc-0.12.0/examples/networking/tunnel_monitor/traffic.sh000077500000000000000000000024321357404205000237060ustar00rootroot00000000000000#!/bin/bash cmd="ip netns exec host0" if [[ "$(id -u)" != "0" ]]; then cmd="sudo $cmd" fi B=/usr/bin/byobu S=tunnel1 tmux has-session -t $S &> /dev/null if [[ $? != 0 ]]; then $B new-session -s $S -n "c1" -d tmux send -t $S "$cmd ping 192.168.0.1 -s512" C-m tmux new-window -t $S -n "c2" tmux send -t $S "$cmd ping 192.168.0.2 -s128" C-m tmux new-window -t $S -n "c3" tmux send -t $S "$cmd ping 192.168.0.3 -s1024" C-m tmux new-window -t $S -n "c3" tmux send -t $S "$cmd ping 192.168.0.4 -s128" C-m tmux new-window -t $S -n "c3" tmux send -t $S "$cmd ping 192.168.0.5 -s128" C-m tmux new-window -t $S -n "c3" tmux send -t $S "$cmd ping 192.168.0.6 -s128" C-m tmux new-window -t $S -n "c4" tmux send -t $S "$cmd ping 192.168.1.2 -s128" C-m tmux new-window -t $S -n "c5" tmux send -t $S "$cmd ping 192.168.1.4 -s768" C-m tmux new-window -t $S -n "c2" tmux send -t $S "$cmd ping 192.168.2.2 -s128" C-m tmux new-window -t $S -n "c3" tmux send -t $S "$cmd ping 192.168.2.7 -s1024" C-m tmux new-window -t $S -n "c4" tmux send -t $S "$cmd ping 192.168.2.2 -s128" C-m tmux new-window -t $S -n "c5" tmux send -t $S "$cmd ping 192.168.3.8 -s768" C-m tmux new-window -t $S -n "c5" tmux send -t $S "$cmd ping 192.168.3.9 -s768" C-m fi exec tmux attach -t $S bpfcc-0.12.0/examples/networking/tunnel_monitor/vxlan.jpg000066400000000000000000005605751357404205000236040ustar00rootroot00000000000000JFIF,,0ExifMM*  , ,(12GF GI idarktable 1.6.72015:06:27 23:22:092015:06:27 23:14:19  #2015:06:27 23:14:192015:06:27 23:14:19chttp://ns.adobe.com/xap/1.0/ 2 5 1 1 flip clipping ffffffff 0000000016512d3e6b6ba03edcb66d3f9c10523f0000000000000000cdcc4c3ecdcc4c3ecdcc4c3fcdcc4c3ecdcc4c3fcdcc4c3fcdcc4c3ecdcc4c3f000000000000000000000000010000000000000000000000 gz12eJxjYGBgkGAAgRNODESDBnsIHll8ANNSGQM= gz12eJxjYGBgkGAAgRNODESDBnsIHll8ANNSGQM= 7 7 0 0 ICC_PROFILE lcmsmntrRGB XYZ  acspAPPL-lcms cprtdesc,tdmnddmdd twtptbkptrXYZgXYZbXYZrTRC gTRC bTRC textPublic DomaindescsRGB sRGBdesc Darktable DarktabledescsRGB sRGBXYZ QXYZ XYZ o8XYZ bXYZ $curv #(-27;@EJOTY^chmrw| %+28>ELRY`gnu| &/8AKT]gqz !-8COZfr~ -;HUcq~ +:IXgw'7HYj{+=Oat 2FZn  % : O d y  ' = T j " 9 Q i  * C \ u & @ Z t .Id %A^z &Ca~1Om&Ed#Cc'Ij4Vx&IlAe@e Ek*Qw;c*R{Gp@j>i  A l !!H!u!!!"'"U"""# #8#f###$$M$|$$% %8%h%%%&'&W&&&''I'z''( (?(q(())8)k))**5*h**++6+i++,,9,n,,- -A-v--..L.../$/Z///050l0011J1112*2c223 3F3334+4e4455M555676r667$7`7788P8899B999:6:t::;-;k;;<' >`>>?!?a??@#@d@@A)AjAAB0BrBBC:C}CDDGDDEEUEEF"FgFFG5G{GHHKHHIIcIIJ7J}JK KSKKL*LrLMMJMMN%NnNOOIOOP'PqPQQPQQR1R|RSS_SSTBTTU(UuUVV\VVWDWWX/X}XYYiYZZVZZ[E[[\5\\]']x]^^l^__a_``W``aOaabIbbcCccd@dde=eef=ffg=ggh?hhiCiijHjjkOkklWlmm`mnnknooxop+ppq:qqrKrss]sttptu(uuv>vvwVwxxnxy*yyzFz{{c{|!||}A}~~b~#G k͂0WGrׇ;iΉ3dʋ0cʍ1fΏ6n֑?zM _ɖ4 uL$h՛BdҞ@iءG&vVǥ8nRĩ7u\ЭD-u`ֲK³8%yhYѹJº;.! zpg_XQKFAǿ=ȼ:ɹ8ʷ6˶5̵5͵6ζ7ϸ9к<Ѿ?DINU\dlvۀ܊ݖޢ)߯6DScs 2F[p(@Xr4Pm8Ww)KmCC#   ~     !18Xw "7ATVW#25Qavx369BYq$4bst*HRZu%'Cr&FdDESUc  ] !1T"6AQSs257Raqr#34BUt$CDb8Ec%d ?;k5v{.Nz ѭ#- +=M2=żJN9i⨺{"&M~Gyk+OQOn dvGhX2<iʥ}RDžT记hXiʥ}QtWXui4y L,4R龨{DU:+: D: ߶m" m~10s0"6,I-¶jҙuzQ+;ɷCm[dIr4M4TuĒ5Vad'1һ;i$or{ʴT%VRQi]|{E$m'd(N6X*R.Q.k:MļlK,)88ɹ<j%!uQIQ&;-*qJw{7}%%uu(g{&ՄWsVvR^'RMx//qiů,ܻ*MrfZmlyw\kOtF#SٱiސhܮOrj%¼EJJ*RڳIOe5wf︺QԒڻbSn2]&ZW;*Jm C%ͨ:HDT>%H6IpIn_c}Q-m)_ge}{o-SjΛ[2NQj[\[RNM4ռj1,,T) Y:ślxr$-YeZhkۄ}te3YԌԥ(.pPm7k=^|D}.=F~CgT{2IӲi7wX[~eHVfqzU F ]]蛻r+kZTB%}LjVӥtzqqyɑr}GdlM;HIU|*&moḞHA(SfG]qkԞC: aT[2_(3s&ٷPتXڠBѽi&u SoƐ3$Өqq8qeS Vލj zFXˆ+亖.rEŹ%S *eӢo[&RFVgW=\0hVU*ƚJO#'[/yxҧ%%(1wrܬeݮ5ཹZr(w3 G6RIu->\A,fD%#2옩`iNRvnݹl.?NQRWW{f]-F#x`e+ k^@fTۏTdu-e u6ӿ/d3LiƤ!5ܥkZ*kzvouڼJ*T[N2dm/u|1ֻSvw7LǹR#deɣQ$4\֗%*ZB(1g*ܴ%daԩVOy}ɨi4Q.uU(ZUOa{^+}$ӌvqv|j]fYS.TBMr#߻W[w8V:q[M[zi٫qrfR-R6ߊIL[VbldYB[} KrTE<tx2SFr⨹sM7*i8ؓ(rRmFMrBNj8+x5(fY'$۸nQTX~߷V!\1`Y9)JTJXbFI()Idm'΍Z3j0K6ۓQI&&MV,ڽ|c Kb|'!GRݦ!q߃.LW+yJm[ĠԞ+^U,>өvZIJ1MfVn2:BPn/mij7M'i'k39k偔~CrMT.G)&qCCJJ&J.f\HGo}2vEe5fM_&`g]vK{Ju!M6Jqnf{]ڹ뽳߻- S>iz5Y ~q-x系itVx5u}E@tlE9/o|еj#Jvr-NyMQQLC+2%"1>j27WRT{m}+wR.D[Uob)6.˾\QN8]4(ݹ$\֕iShTݹF1In7eu}3A9|Ejw.ym!S=c/:˪@6@MNo twTXv _&}?귐 ;*sG;}/zPSu[9=c }n a~Kd݆=zRI\OLjZiJezq3$jݹNzdtFmn{`ۂԛX?S'])-/Jtۇ e[:&v2 .);Z2J0j9(%$Xž[(6TeVM96NnݖRVlg^7ڧLCZɵqH&q,6C>ez=옩MMW%*n%J &uB1lv5=Qj2vWՕ/.)osFUU,-qWF2Le-K-~;Pp4F3^Q[c%E'w-NcY1 sk(ɴ=i+9=n=6^!`cͱe>?jdLu,WU Dӭ(JXTkK7%-[Ci$+z\۷n[Jv'q'բjU0pmnSmޤ/-$i=UZǷn'1\VF2Eh8y9WwեhJД{^Xj(8u ӧFub>Ư 4I8q5<[J 8^8;E4&Ŵj3fz} >5re ԛ}vȌt82fd-4+`f u}jAPsْ(գ/}mɵkTnœՒrNRQoQ}/z~ܷy˱m˻!̇9jDuqTTԳ",>*/miBL]1)Rt\a;O)E줤y+=e.*X*x)EMr'4[eԝhk[7m+tFk ++p[`9|i>t,/=yO|~}ѝt46}^߿rWN.{Z[>r%xxۦ ?E䎃U<4~x][4x}ݵW鸱3{<yLл˶c06qok-?}%mhj">#uKV^S_wxQj0|6dϵ>53L$=8D_{sw"[ce~OűGg(^7dmxn:^~ B*JuJM~y+~6ڸ+ |{=|>{Zڿsό~7g$|޽=[Рp?~8b\i_ Dr?2 $E_o8*A.EݮoM9N5?DEOsZ!BU s.Sqd$)gm"F\ܻznӧTnINJR6dZwFfFD|dwXD&:96Q7ɀN_uM`co|dwXD&:96Q7ɀ/g.a6}ϩZw5 uۓ91R<(Afey3 1֒jRZRb8zDg '{RC d2_6̊Uq'IRM{q虍l3jr؁M-*K \WJ3?D;Zg_6zMՍ&Oө\*7D_CN\A|^dVvܭ83jfnOi!*{u6Î7VKtI뻊sxә/'q\Χ[mӓncIfK|yڻSQSєIm&댭Dmp \5[mMnn n7(Q6eXf䛻ڂ-})Ga890XL}޲j'_;˖2CqS|%*~1DOYCqFxiOs۩j{{0J)ٚZ\[>iвڊrSKm|AnϢShNq9adC!N:$>xԲ2FĬ2j- }IgxST~^&/V^ Ƹ b/U?o6_kdlq-&c}כ$-lɷ1LLfg!&\ۈ.2JۗoVKv|Kpz.J-ͻ=.~MՍ/4qr:Y-m)-)Rwq!DU›O`{6ˍ*m=q9E^.5bII]7g*Ŵ#4)e%*ڻGz_zҥm鷭Rĩvf"1UoSIITdpFiUTM6\RpjNZ8N=҂{.1^m}㻶tWK mktt_H4*z!T)^!q֢%+\u-DJ$\bNJ4ӾHI>5u-e]ZQZuRuJQv^2jy4S9vmK鋺-zU wE_0$I9D& \I{8$˲Gti? NW㳲x'.^n-5݌E"􈈈LE8%ci} .ũ[8[9KLT-G+(,$C\k5Ju&اFn7mF4'R *4jV#[J;MõmHaC|ŕ)R\mR sVa )K%{S_Ul+芩#!J9JTK9դTbߎMlXRo;{ʼ(kҳ-t=~IKKATdƌҔ|11w8GoEqn*[VkJ~ vIƒ[:Y"Vt{):qZ严)IgUu?6NM# w:]+TF9|ew2i,jFT©l'KE[uI%n"DJj+* ^A“TW*LDb7O,hO-I"/dwZt)91Woė!6kC#ڍ">uQKʭTd;j-ӎ5 MiJN=Qn3kkFq*Kh~}Y=Yz˪@)ۣB4za~K]VG|Nho%Xjw.ypۣB4z÷_u5;￁U8m!S=a/:˪@6>`&쿁sϫ ;*sG;}/za-.e]" -D&tgѫVHiD-YPd!IRFGF]+{7OsOimL.*.8{+ [4O*pbQ$JZƅ+wZӆњ[M_JFE,T_YtD6`u]XK6h5=`/M1~͋ Tر|E5Key+;APWǿxi>ϵmjṽJ=__tX}٧oo66~kXd|[3c򜧝/l[rEt&bvJv_._dUavk-vmXϚ8e㩞KNJoϪ:Cr[]Żvc &Ou4/ǵ;^>6\v>b=ﵫ~=v~{c0rU9=p'+x}N>C~֗-Z$_'c{gcy6n{;H_gYY'^A;;Z!֯rj/{ahONg|H5*ż;xsi?9Mx}H?)v!Qיj=Zhe.(eӹ)B_Q{DC-4wA?kVq<*-C5Pi\iжŮLDoZD9\*L4qڥN+uڻvo0Tu[_r+gnw ςjr_u\vۿ~_ujr_u\vۿ~_ujr_RS5_1w-d &K~]=uRˑ#4QmVjI"IzѼU`lTi{;OiYf9^R_Iڨu@RIDiQܤ(qi3m; a ha/sjw5`9L&:>tTn>6o n]I YrZfs aiҨq{[06ۼaڲ{7n2^I&q')ʤ^9NroEZ1_ZE'܏qf|r9<!i.e> tjBVԤl #J/9=-&{ZjMEKf6 0**-JOWm=ܦm& jnejdžqiSmSQ%oO!NR-km)A#d~tܔ[w{=ʿ[;/4qnME[)ݯn[[{*ROtt. 3UzMjTXY)D4LA!%m,JK2ݼ~;6uC FPۊunҔe,.+qn*ҭOcECmM%e%;75&㻻I].yn\IqrR DCt<ӨlȒ6NdVa*9qT!n7Wqvڜe%$V ΎiJ3M8&Yvqi&;ti9WSo E*_8Uk6b sKmӣQہ%\Ɲ⾤gdEGdԣ-{Q% FSR{=}g΋r$vq[|-]&.@’njW2NGRa[1('lgdKS59T-)ȜJRJkV *qiE2qVvڌ]rVQᚽyն_Z]͛n% DufeDk*ɡNAlRN&4Ŵe ݧQEېE<V5Voje=EnmݽVKjζ4,RSe(q;Qwd-p㽶wZx@5 NzTn4Z6Jҗ|&*O nSUZ؜dEuk߉;8J0m48ԓN2RVRM+Yэ ܻ)ёrVZ-dVlܓ:Qc>I'0!N! ]|FSK%dS:TO[m;'tc{Kf 1mEcoiƚ[4Uڳm6WKjVWm$FHeݶuA6eKa9[6J C/0NV:ҸRf$!Ice(ԣ(niSlQt(6J7M4rWO|e(qMjnƢܫ%VMl-0M6\(eidRj̧ Qi҅c9[jMNNRn1IQ9ׯ&Q\VSmE%༤mɶgL;^vHɸrcý$ci7ZaRX9 ͅ%--H)ɗ8SnB ,&dWB8եw%mnI6e-u{^ahɫk)6ҒNRwluG\m`ͪXT]PUj&|=r>ۯԧ Ƶ)K%X~#0znu%mhVM[QUkmRonMmی=k䵯ƧNr)ݐ{7m&)rEfšY(rp*ԧ)(+w;[b,RM)(l&u)ZiTO$k"N}bN'}owQ"-Z=yT- t.vnBwԯٯ"-j~_/R @c3(^Ĥm᫚}  ;t! RDX%rio#e{]EOWB۰(\q$Zy/;"C?!yRֵ)J>s33330}uBm&M:6cR Ka|+iJB~;Z[%h:È&UƒITe@p0vjo{V2mYFê>y۾[aLeLM%YulҦ"J[̭JgnS$('(T$jSexK*[Sj2վT]JSo~՛G.lLkDmbQhU*2j5##[y-H7)B̍ s ʤqota1I^ψBwR󜜪J+RnSi6-]$#Y"\xЯ1Ql*|;7-'"- t)4gKIn6LƦNrOe*-l'9ʡp F6Joٴbgru7Q1#÷\! #u u TtRd㭺ݽ49[.s*tcd7%j+|,'MU_kw4wjE;GZ&~5`aTo QUt Eeəb¶2gA $]h -اQM527[gW VI)mxl5P{iT]W*u&Zkgi;vwag]Ll嬍yZ$*vERU-yQJꔩ ̹"E_ mL;,':04Mٶ S[NJ _zQYJnkuem۳3U{{ө;W!L1!Q҂4'8Х7 'ƪT\doCm~uڵ՜o+/zVl6's=zmu0lrjMLRu7*XiL,꟧4)I;J5N_FGeݻziwum[SS1RZ0խ n΃wskٗUQrfý6v?VR\Kqsn:S첞Kew}new4; ۾};Ew5v V-CSW%eSI~(FF]FF'ñ*mӧJƝٽS5[up4Q񚊄)0M^6qihUSrnӅԣ&ޭ+it!Ė n͇uVo >~dK.ylj͖#FaR)f,&J %:vos')Io{1W{0Sm:R4j1IoQV{+ܧ'eyNVIY,^{&cN}tuyUeT[=jHyZ+v^[ i8{QΒ(B;Z1wNՅ,tPԤԜ%;vI(=+I&rxW,96c(.('~-g.Z3 OsReu ASi?cdE &O^w=[Wx;_hl3d72_ Sא4;حNB2Β$J6i%jҩG(8oӳنӔ#}ZFTpӖd\[Ӕ%+Skg, qj*vJjf_2Ҕ[5XH6MEpdn=ucRWIťmq{{Iܵn7{+*rڒneƬ՞C`XޕM]+y=ֳ%=*Gq=M9)RT RImܥ.⒥R1mʣj۠Gnm'7ĒJWSZJ4ԔU=7erܝhs.Bٕ_m;ꏽL̯B',?JhKwl *vG'}Fsyȋ[?y @ =X zs+|7[~4dD`E*"RUK-eQܽ,[z qq Z|S-'+A6}{XC}RH[lvn)x63h?#?ڽٯJ|kZ{\Խb0 @(˖7LgT\AXa8g*УYzΤHJaZteEVKR!s(J͖Ic0Q );KExj9?˱2ҩN)է%%7{Yw5{w_{4i33VR4H4F Q76j<&$m"#Y!23=^]f3j*I;wmݵiNQn}+F PJ;JrWrmMÕj}cVgMn<<ӗRuǘ | qǰSj0bc(J1J+i7w:RET{ܭ{+wm柘ؾ%?(튥Ety+}-5竜N޳=\3lYܩJ-1v IFIG_T6[ƻ}jFP}Օ'[ĵ+֍2T &JTU<Eȡ4|6սm{~v+|f*)bi乻nR-%iTr[ǿ}nVK}-ͦ{~RWNșR˴$ͭÏG> Vq $ށ>%+zԥ8NJPʹ+niJ;Mqնwݰk$ًxF)(gi%>6Χm[mtjoԩ<;{Оw\C[r|n]jөVNSmmnwܳJYlʃpͼ`kM V~2lc["D mDnT)-*{Me'r*,]"UsMJMNJ^u"Im6:!:8*tqRK{mEI&u֊K)|qoyyڬƨP. ~Bԩu(b\G=HZT-+B`u֤%uMI8-=J-Ҭkg5iE]5MI)&jI4GǸncүy^>٧ɾ2c4ƖkD8A+ r5e*q[,Ҟ^k*^NiSoiwR'dڊo-zuJ{ vׅld2Nky*'Q׃D-^ץڝMCkir/HYz ϛq$YC,NvږښZg8F}̱T冕.{Du[efBT1)5p޺ng%(̉@9H%Oq3ץ qQk'鷉"?^{o&"]ݞm O9KѨD:W+hKEݟ=H/³񬿋/BCf_-xW?ރ`54{'4>>z4M$Zk'՗ގAH~x< %|C̣WHBH뉷ޭ]-Kr"p՛ڟADOfHr- )kJK'^aVbIvFٱ#Q%ojCp2*7$ʸePq*9U8T7Pe4iRInQ)#L<(f=q9q[B)k'd]bY OLekbMSWҠvnBNir[Ȏ"B řg331!hc4o96K RUp)M(YA^VmCW"cMF6ēĵG}b4k!Y&cS jPw.tcip\8x3? X{A1.ʣXiͪ;5*SuJZ%s#".b!q j+RT].)I-wG(֩ɜq!sO"N}bN'}owQ"-Z=yT- t.vnBwԯٯ"-j~_/R @c3(fƋg7XaBz7]@--Daø?@aBz7]JJ>#%K!+/T>{_9IQ7ʜD\6"+& woѰх|Uy_VUD"/7\HQ$Em~xq[U.`{3Rt9X]WҨժJ Mj:RQ$0{)H􃟬{rNɹsOěLdM!GZͶn"\ Ofq֚timl%kM%L]zمJr}ʽ)M^źt#G^( ,vH[ Ip̽WO#qLF|$ D: ߶m"~hs?vQ G~sO3w_K{E>}Y};(׀OW;?U}čxt$@k/+f Q^WA qNƔ:#jf[{[n&ʳ:UU SgXF*6;ݷYt;b.zWj7^-1P3n6ˮPeI\'ɤ̷l-<µ:q%(;[ymBMIxnuםW JD*EF&eq2mĩ Z|+IsPl5)Tl` Ge g;q➢䪝vzz ڜ:,#ѣy<={e5U{j19}Wj(*n+%DBӍJFK+/7Wә}z:jaKDZݼ,[Zs^E wG(lui-pIJ i-5nO _hKyR7pӷޒIݟY mf Jv`:fYUbJxMgZ#tTtCbr:rS(pW2/fkKkK>OGiS\XXMRM\0NQ@\i_ Dr?2 $E_o8*A.EݮoM9N5?DEOsZ!Aݣw?~u lUR ?OX\ܦ Ȍ;w ?OX\ܦ {jfK6ch_WL?33O|? K/OcfHg;r8HS ޥ_`Z)B-N%-_Gr[O<.ZހF{*DgQv[M*~[{aP گ%.Z)K.ۙD!1Nm'|ۈ̷&lKQYJ'jq2f0=ƍ".ɭ]ݥV|+{<Avүwd~mwo9 - tڇ]Ǯo>2x9IG_y7FzPelO qjO4:]9~Vn}h4SMFo̵8K!c\|er/r+a8Ҿ1djv}W\DE:䃏Tx:)aS|)QK53.a4U0qqWa{u%F{Jq5 {J>nszŻFCA3?s͌BmS!ח+Ja2= uIפq{Z1t׸JKrKp]`$wU{ޔpiSxN||ZTmJ>ܪbΫIj#l%/ժn:i#R|(I$""t~<>kӂcrRrEG;^/o

D: ߶m"~hs?vQ G~sO3w_K{E>}Y};(׀OW;?U}čxt$@wO:,QA2~>ŐʿM?VODnϑ|Tr| ZTW:uG`4 'Fɗea&,ɷg{Ͼzs5?wўWݧx7̗V^n3# u%.*V*,gpe MNZ)`}KK 7˪qjQ(JM>̊JZ㴷=S{-MǺ+ʧeili1om4\ILŻHa=c9 9,ȹyBFيv2[R883-YlV4"I8ǒGI=,gwG3>ꚦreO&T{վ깇W-.ltwU:\L}3uWʾ7 ,QU/)Y=jGN:!x%PjL?KnpIR3)Cs kQ$fD.p<RF WY^?adrژf_GFw05*9TVzf,RzDLO1ę/&!(ѺE,, мO 9BRvݬ͆Ҽ҃~`nuWԥsIm3݀R5]qSRYW${d354!7y;'{qWx:*Yg.."钇ڽx˹*( S;(I۪$eK"RJޞiJ4X1j (xm5 ^%<\F 0"N}bN'}owQ"-Z=yT- t.vnBwԯٯ"-j~_/R @c3(fƋg7XaBz7]@--Daø?@aBz7]KW32_BJa xM|j_.=~?+5G?)'@@=UZuIH.M&N(uh.e(DgvݻyT8̸/4SNJuݏ3%{ k2V+ˆhL2xHޘm%J2.u9!J=y cipYγԎ)u9FW*?8픰*7tp؉:@ME/_Y:4d3Zi490ѵ^uU^^CeWT]. zmY'-Y#!8Qu,ZMoӬ놃NvmÀMĒ㒦j[eΖ/I+EiZE~з:ԦX-_dJH9&@ kfWwi~>32 C8*EA/Eݲ?h4@a"-lC D ]0`3qόECEl?WtMчf?[qGr\o3y~gS꿸Gh/GN_5;_Z?s6iv;f)dQ?3-s#RXaCg.7\?#닡Ot׿J`S-!ر R{>v_?$|!-W}"&>WT?51^+'3bɨ{?/5yv"ج+5 ‚x+L$DĜSdS([b5:f[l%\G4rP­NIxe0ImƒpCk~pw'Y+ eFG:G ]}~](S?f?VuCb}_MΟ'^g[ɾ*s9}+jvrj{Cv#d2v0][۳tL=9?dBӍJK+/7Wә}z:jYvi3QwX'<TW~.;)SҲ_&i2\D.I@FU6&{os8dؼ<0$D0ni4w<Ш&Vve6oZ^}(2W q79 mX^~ ɻףIRI?r.^z۬(bƥ/22D&oS-'zyN>iFgQUӗ)JkG_5faO\ݵmoUǶ'aʽō3nf ɥ2R-K)V&[i}P٭(0Xݪ oȰIJRS k-"56UoU;)] |g88=,\KyRg9VegapNs.k7YEx}V\5;;>ti%1M/[j1 JCRo/ũJZQz.7E:u)MזnVHזf{-~J>L9{'\!y;q*C q]ĞZ+;',VUumK*[=.~)q׀',Qݴgi)1*k̸RDamF}-P24T}p" HiS5j*8MYXc @\i_ Dr?2 $E_o8*A.EݮoM9N5?DEOsZ!Aݣw?~u lUR ?OX\ܦ ~vuǒLHQ]PR|F#ɞQ5ϼ-gLcGy8!oh^Ntk:d:2ζs+X"Q?f5["ỏRjRi~_B-Ih2Rw0/GW .m;XξPL>*ǜ.[a4n.L U߹aW/  [j P?I&>7ۋLk_يbNsdqN1yЦw}[l#*" es=@1rs ۳*kЮ?[pIij:\j+xl#[0~%3g̊6-#tjE0Mɪ>q\'[tk:d:2ζLcGy8 iڙou'QV葥KD1 Ң[q'y$}"\ vGm5IKߒe&Of&5u[S/M.tmU1M^mCr6Vg? I$mN'l5=7*j+O:JhADmU;V"Y񙒝'&ڍMy lxi&TjO3c(R9n2HqnN~d"]ESoLMh{45gqu(QFxL94u WVyTJU;+r*R_QI)řLJ08^^+Ը:dPj\5LM̆PfL: )R Q(zwp2ζLcGy8!oh^Ntk:d:2ζLcGy8!oh^NXvXM]5XڿVy蕲b駺_*#4!/AQ>`7vvMl/¶Gަ{WXuQHB%G{#h9lݥ> 5w|=e~^  =X zs+|7[~qt)# L֙jeD=9;_g_Ԕg䏚2ؽo_GjǕg@~sGy4YMDP~9?Cb|芛

%h]kΘrUsUʔs) DnKojQrHk4Eq~iMG%uֺsƋ/vI&yE6vkNzt:;,bOnܴEDMAe5JCij3J 5xRNz8NI;jM5{s14g*w-ʨ#cEݫ==@57.s旣PuW>{;H_gYY'^A;;Z!֯rj/{ahONI{gnRыڔ#Ww.&_{\ibM~ A?` s].\X%N!G[%pM ֵ($Dff}b!1䢕>۱U&r{3;BݼTi:{}E(dw.~ kBl4KK|/Zd 5_h$Mˁotmi\5!l;a\]6;.ɾqApX톉rIt&K֟_bö%%c|#'3"W bbV#'ZJWﺥ9燔jlF^Kk2ڗ1g=UM}{"zj#NLg qY?֐XZ4Bwn𳿃PS ڹu,(:lwSo܋Nx^Tzij3KwOe'K& aJ;0qVWg0N/ eblYYf:F>t*⋄FfB)tI)O\f׻R\bqY<ƃQNkkwS[a)O +;_iW+ j*ʹs][ԲUl3 |X\vԍm%yN4ڣSmv_Psg;åkO b톉rItٮv\|kO a p>M? /l4KKe7:_.k.I.93}|a[bYf]eٸi[Ѯ\L*l!N&MI߻y Kʳ|B_R{-]6Ӻ8z)7o5>f1nFjZWٶRƐ*f&S%p&FjZ F"n䘼neIOm ~e:UznAgV}nշSvӪ%ۗUp1z1-QM}qj'IRQы#%-Iޛ:F2/kį+ʽ QztowOc_tgXUaFO%*F%DP5^n7Jz:~Mދj;x.|)x2i6^!eWAj. g<}nNc.*z7IFY%:&%:8WWoe}giK{ROU'hqm!=۝i 0FtT%5%ʭЪN@jrloܦh\{~nRTdF\@MLl}AX!7u3d3s6z/=!J2mev(Zs*t*mrmU&[%!<- JBHRf{`Z(".ɭ]ݥV|+{<Avүwd~mwo9 - tڇ]Ǯo>2x9IG_yHJ}Ubե5G-*5pV#-ŨJRf;[Fpt%3z 4O?B1?ߚӟ̦1FTiVę,E3_;# U#f4[m9}O=&cJu=aa+F6NCUzQ7oF\1e|:uewvpgxv+K֟_bW p>M? /l4KKe7:_.k.I.8~ kBvD$lv\|7̘#j|,Cf\5v&7;^$8˄N^ԶI%ؙC)hJ*JNqn2'Z6ׄQڷl`j̴"ZNBu,VSKAGʘA1g-i4:kvf{Zݙ\,ʚY>uoF士"̼g?CsLLSQx祈^zOG$ψQ+*5tO+u.^]9RyEio>6k[#ZS,ejQ,(TeSԔ}fGɴIG}HZ>qM˸i콨qs T^ڻiޯlwu* x6'a'tBT#pļ\/`R'+ҧbRws[|*o6Kx ٽ6ܺ6ĵ/c1.赉Pk}/8[ĖI؜uU[7}medc*X$o/Zd 5_wl4KKˁotmi\5!l;a\]6;.ɾqApX톉rIt&ϣS[U\HR"U鎟4h㮺 (BRFfOժ(C7'%61TISwH#1V5٧\n;nEn[Ofݶ9*ť6iEKSQ!XM9KQΜ$zR7_LᨵPi;S`"޶f=o]WjX؏K?C jÜq(5<bs?G8 Ƅp9_fyn;8gNN{. x[Ns֊ [.ېܫz%NSW":nGS- P[;mY.aʭ,ړ㨩YNڷ^PՔ`fT~JĶzu!4FcIE+RL3NrsysqoIk4g:g:E*Zb \ZJD=DT$ՁMq*rS˵T`{;58qW9OC k=[.rJ A$Œڷ-r22޹s f5ib#(I]4Mnqѻ3^r,%`+c^661RȭSqLܕ[{d2JFъިNN+aw.Oi=eozv}g? /펼vD$l.ɾqApX톉rIt&K֟_bö%%c|/Zd 5_h$Mˁotmi\5!l;a\]6;.ɾqApX톉rIt&K֟_bö%%c|3>@dFiwn_D֞Mj~K(LTJȔRҜ3J\ۈYi_˳7PqRISQ֏o]ڹ뽳߻- S>iz5Y ~q-x系itVx5u}E@tlE9/b| k?X C%WMMpqGzZ34/Ȓi5vR8,} ҃TlO{xv u# ɭy,[\;~X\%ƑSr|ey#C%*ZT\IIDe3>Ya.8Ƽ+ʴiWѴT=|v^_khW$ygOՓ/p[u9և>{E95=bQ ; eC;gɭ?m:@^svtgwi %Dvi5@;r]淍ݘX4&&䙓\\Sjw^TVrIdu),ruﭛHm-4PXl5 H5(սg~z[)ζSO+َۛrW- {/sI74F{wRyp*:i>rb"s}o૪$T]&[0gty_%obu2R,+a8Ҿ l|sb>3&)lsed>n|'](~ͼAv}7wc!;WlI?/ΩhKܖ˟T[YvjE\] ꛏPemeRdb$9ϏOG?Z8N S)?b4R DFdJݼodޜmL{WE N6=ȢSUQ|HӍ}($Ǿ{_tjcj/pP%QݽzB[qSmZz Ķ+y\4\kםQ[vqGVM5QDu51;/Rimm+eVJBe.c##߼@aBz7]KW32_BJa xM|j_.=~?+5G?)'@DkPNZ*EYɍ"p}TuYi4(s16ojDy ,rZRd&9nhٴ{Pȹ[ߞ \%e֔[uح=3M1]a%-JK龯pL.jmoQQrڊT⻻qEG9s߼ G5++N ޵T$)r1C${z&sGζǯXկ|bWlu:e,xuue#[Ƃ23.d+L6ӫ֙ߵ6IT覾&i%DJ4}rf}hg[?B P&ewv[|NS=+ЬD:(ҤZ!]#Jݑ Qg"0^pB%_uS~cw\wl+.Лb)2[2Q/TR%Gg4ogaԓӻ~eJ+XXw 'EFi:MCQPi%䒟}D[֣?4 iRp3z피 0օrHq^;e//LhW$@R.օrH;WKZ#/X^;e//LhW$68/mq5ZYP>=yB <S|'Q2_|O(|;?"zO vUSOˉ>Lzc^LnLi*Fm*%XmI> U/Np [, p }?5BժuS5be0^ͻ-#5d2iRPFIRqs xTTU)T{jۼ)$-5hٿpv^~L n3:FItyb6o3=_w\D/\P E)7>d>&yJ7{406[:Ns\-vڬb{ΡN*+8)>g+#W(ӆP`Yb<*'ƧMsC޼ ts1VƨXwj~|'M "g|+B%"Ϟ}Yx_I!Noi@"61*+bTۇQW8 ([V+މJ)둓 ]5}OYbq) 9_7lr۟_qu¿YJ.+ʼ?*\Tjq\QJQVC B4FVIq$=(r) WƋn**ir+rUPqI"h̐RDJ"=ӭnBw2?ex7./r5,s#㛎vYwe92U[M!iQ) BIQȺc2dԋO[Ѧե:3qG6e0:<;ENڙ1C*o6K_çg n{ӴZk(:rȲSJZpߐf㫋i|$麱VMoI;,ivNN[[փjQ#p%++nRIS#4ަے%|U9GjV4{+0M٢+: B^4nMSPKo$d܅˛ȷudwNώuqӍٶWk^4(u@iJΌm:$hbqZt/m?ZFsv4%ЦQ)tj:7}qM(\"8 Q}e,8Ixi]i;;S^ʆ6qzU֦Zf4yusRW"oA2sʵO0V̰|*_nORE=z2Ԧ'вqB]^zEJML9Q6PT{Ƨ\^ZK?X\9a_fOi%{w-ɻ|ے#3cp؅ռЕR*4&F$!$C^KKBzܕk]i4iթj!s<`V)(7I3O KrR.kc|(F2inWwm>Liž7VE\5X=:]/Jj^>+a8Ҿ l|sb>3&)lsed>n|'](~ͼAv}7wc!;WlI?/ΩhKn*#UR:Ǽ((%Ke/ҟW9ԣ33=f|`'i'zv;uj_x膣J}n}tO[Md9 K-;TgImE+BI222220aBz7]MM8q% 4-Ң#}a< YuNfŏ^ٚvDӌ-:fn]}f pӕH6ORrEq=IhA4n_>r=˹wWwd~yG-'u?;8kW>K3Lr{Nサpc3mQ{<fc AW[')oHiDJuIIq[Eq s13q5%R|W"֭jydi_.B&M6V^:_iVevx\ic1T}4>ا93q˙0ӥX70ʏKK}?7<.441P^>Ѳ3RVR_:5<e?bS@ ”2 PۉP.&s:DD\m8(^#au\_fЩtb_3Oa=Oekuy2q&$4KY KE cHʭ-v2rO#^6sڏD)U5!-+\&D{8[%} ZpNppq~y=:lg4, kspI)6;/Ƨ#xLצ? C/˯ljvzlixϚ^B!w¹_| D r^.G!~'e]dQz"6hKI-K-#۳13NuqSHRR[^+މSSƓ%3_ep>'jWb/sko[-eܽt޹.J_WUfpܗ IbZJ)ufjRfff;YЍ*QQU[H#6\G@R={CoJF]E lT=GrNM%%R.$o$>Tdm :\`hbxٗ|i^ن_ lȾEѯ]D<Í3]V$aY>PkiJZ\n,*ITFFdrSYMUSt'{[汙Тܾ;{>tڤ:5n?>tM^yԥ(ϜBb]69k=8J3N_Rѣ QWAukW:dU%\یdd~1mWN2ֆ kgDp͕^nqUNɔ~K왛?`ŶJلiIY#61\ILMםKwJ%UZ"q*JOGxŤҤ %s̈;Gh;IZMY5Lba*mAk9-Uݿյ7w} [)e2]x3`3GǓ_2T3qiuzsAo4]t4#Ǚ eD*;mE*Jddded`0稯Mَ$d$blQwq:y (XXթP-uo%#';GZfKuz(TR).Heq$d^i%-]Bub]B*&FGN'ց?u/ kfWwi~>32 C8*EA/Eݲ?h4@a"-lC D ],Ŏ&:S6m:GqlS8Cij3 *gXک.%_/ *ѣSQB %u܅H[Xؔ1:LhTvc%FjfDrU+uio4K|c }lEEtVnxd֧%rɼ#5Man+IaShgIJX=ov obvVI_鱗a?tKNme[`Y3ҷƠ~v KisO;}+PJf6i/>+@}+{jGhp٤F4دuﱨiݡf\bJ>ҷƠ~v KisO;}+PJf6i/>+@}+{jGhp٤F4دuﱨiݡf\bJ>ҷƠ~v KisO;}+PJf6i/>+*ޚtccSK-]n3R'"D}o2ǻl$:xcݶOBL1'!^mS/ {A@=d |ev?>Hu2ǻl$:xcݶOBL1'!^mS/ A@-mm[/"l"hG[l#Ao?d@ 1)iIeMa%hQnRL23 흳C ʛ2L["SHا6"2m*TsQ${f{S=ddwY'D/&{z1Fj[Pih91:Q!DIBn2##2[Cs1X@:}PP]څ5+ wz^b!O|YG~" "U͠N0?Ց!A.ZҎ;>*o亝Eź"V)[6y͎ۭӈR $Ҥ"2شcIq){kӋRM;?O/ uۏdﱨiݢBI|.i̗o_%X[cP? ?C%4|}co}@3H4\vW>5#8l_#K}qW_:V4I|.io_%X[cP? ?C%4|}co}@3H4\vW>5#8l_#K}qW_:V4I|.io_%X[cP? ?C%4|}co}@3H4\vW>5#8l_#K}qW_:V4I|.io_%YPI4VEᕮ dl{ʳ PL]s DiQܤo# F6iV溮~+Brֵ NTz]YCRɤ$$v/auϤ|4iʝ98Ԯv&n*1I9'w[Jqny |+PLm+C"}glߘ3E:ny |~J>cqib {fUb[ѩJsd($_Qk>u-JQs~+4Twoj'Z΅L%&~o Hy}cu"P"]ݞm O9KѨD:W+hKEݟ=H/³񬿋/BCf_-x5X=:]/Jj^>+a8Ҿ l|sb>3&)lsed>n|'](~ͼAv}7wc!;WlI?/ΩhK@:}PP]څ5+ wz^b!O|YG~" "U͠N0?Ց!A.JM1pE[`Eݫ==@57.s旣PuW>{;H_gYY'^A;;Z!6+l ξ#[)$BRnQm8Y ej&JiF(i3-C}E%u/kƟ_:r ĺ2V85oM})7(ci%n9ť+RUfG*U#MMgv6];Jͯ N]|;}{ۊm %F+[^ܣ7(QZ#"5JEFE(UFv%ton}-/+SV`vov]$%sCl"eK5uܥ)U30shd+o* n{[߈B2ftӳ[) /beXމ1^"O^7y.yg~>Ww.\W{V|G/JumpPn56tޭDD=zRnd)l(_mFeRLJіOw5uηN:ZjnfOՍΪ򶟠`]*ƿu9'yF&-N!6V*14u)Mdi߻q4S%RZ}*U+Օ)쭘-qooCG2\vWsYԎv$Iԭ>Jcwܚi(Kh;Uٓ6W5Egc7TzNiT76cfXShZ_Fi7R^R5̸<(`|Sx\C ;pBJ5ԣ(Jmvzk]aa&RUI)[jP'mtQw[7[Y %^=!-)r$zT)T֔F{p6BExd{ѭY7mtJ+ KrCC.6 Mo%$GIFPMY:S^Oc*möվte<jZiPLF&3o>%n6i 45{f)ʚSQjQ^([_a_37|i4OzirlR}mjXZaͬ^W%y4CZZmGƥw\1{5^Q߻mEG95i-,eI[iKionVr&^fC-Hoi[RRLy MJniQZ"jSdM;\iJk'TiC#-CEl?Wc݁C_6.tGחs~u?-.lM/w{9~"կٷ^Bш @"o&'}Jm""֧9- tvl}`;o1}_V}"1LX*O--"]ZMַJ[oFj"" J mEomGe~ Q0ա9x%7ݙ3SUl.vS:ӟ!*n#o8GoqoݿøUjܹx&x3&^`fCN"j nJM6^MReV*NUD A_00jQEzV]4կn[KSr6muq6ݒ{^~ ke,e|+lK¿im]4;ZPMpSfLʸq;D|HQu2Oi-wW[zWk)sku׌zr%6-zueJ--.m"D/a.S 5N4TnѦRr|Kkbvĭ]()q+v_X33j NQVa$m#|*JLXgJi]v5;+~YI+oe@,mMjUӥP(&?HMq&}9' +q ݹDgJ^x7$ޛْ$ԗ{DS;FV{̷V㴒n/qG#@:}PP]څ5+ wz^b!O|YG~" "U͠N0?Ց!A..܍m/ϳ$])UI奶bKIiKm(DD\TaM/9J2U#/ķ&s;&c{ }Jr eJbէ]\Zs7Ҥ%MmJu4֒Kz 7;qqN5aJS7-ɿ7vs5}YhjcjTU)PI|0J} <;IoDwJ )[~jKޓ[Uc*>/썏iw+T2a:bdru$JLB/iN/5N377d/z(;MXY ǗMn;$/r 9!<.#I[BR(-n-w PT?{}~&s:6+Y0{f%cJEzyZU9hI@hG.%^%+hބdYܻVqs*jtT{7R2V8Ǻ]mq94վL5Zΰ]Z)ƒR?Kn4鷰osNHZ\BV%!i#J#I'R\L2*^q xd/8nV&K_o' JzyϘ?r.1ޕ]_4˕m+_NJ G't:4zTDRnC fJn;IM,y (1 ըQrk')NЕ ]/oo6XVE/ tk%Be5qM%2ˊ[*mY!E#(h*n֒uun޷_aا{=uuW^3B,\e>)ܶt*i1q6&ִW/τq(׌u ldҖKeYOjFP9(vSr)nmnOsrQؿ3^)7.U]ڹ뽳߻- S>iz5Y ~q-x系itVx5u}E@tlE9/5'Žjw"X7 VD@\5}x-{65gin9O,EDf ^hZڟdR{JQ4{)vgJyN۷r 'fHܿ j.Ę#Y,אkWMca\v[3+i]`!K1֙Ӓ)T[K[V:xLMjVWۋzM:vQ9^pӣ EJN8CoC 5vub-Ҝ8*sZ{dOȹ+;W= ̨OrHT 1U*A*bXSK5h7,mYr9 ֚J>u*8B7W{#En;-Nr*i(q|wuye.?Bxؙo"UsDi *G48]~4&̿]SiB=2췌xxRke&^sS[ Ji_~c)(K$RSӶ3l9gcLmT{c 56"+*6T'YI}?qPp*tԮըniӭ-_bfK}w*vR9+bUf%*V\/S'.LwЂYBuy48nW &R}\aZqS]~JP¿5g$Sr]*Њ[8S]:vg0(ڝEKnO|'qiխAmlaR*#F3. U~9)9c1ʃMJXvM^)rZ\LY0RP$-NJuOf)]ŒaEEҽI.*6[uwZmRWԦ$lTi0R̶6`\|T]1Z qt9rݍPXhu:k9Y,} i׾B"<,;w"JqۄRߤ8-9&Yɚts1ȳ*΅ gaWSCj1%y)lg^KodkS⋉6y:~oսmҺMU|[fm{>),˴' exkWi4ں]WE,N#Blom%QFVi=ZJ6nLGɷDR)!Αn?%\I"DnJ+$э.mp*f(9*{sqqg:J׍GvqU f"fQԦ:aҒnINi^61X_ONṠ%Q&e veJϡ6tUL霛|*3[ [ ~I:mE VBqT`pKkB*1^8%e5.joiʵF+WRKII`{ijѲp!vl}n/[n%vlҊ5E_/AqFOljqXUHՊV^U"gyR}˖Ͳ'?fZs9F\bB}ˆ6h{b79\mr38}'rE/:w;םznrq{'gEx̸;Gp؜AYZ)-nRkskY5X=:]/Jj^>+a8Ҿ l|sb>3&)lsed>n|'](~ͼAv}7wc!;WlI?/ΩhKkNjGqmᔪB|}WCTXt\qu:Db–Եqo8ӛaEIVqݭ׼ʜ0|צTRZM'vW-n0լl7-oRm鴘0j$(w>ۊ52DRē;jOZ*ضoqT8N uG:]Ԕ f:x|UJ{S)K{q55)59ACj?=Q}RD OIBef˖MNj_ټ۱RuPJYZnQ7ם\zXⴍZTV Ӛe-NKQsV#jh ֌i=u-}lZ)2ó)X\n;r*ˑcmy" \'dpkϕRxѾփu5S!FOz'=A%.1ܦ&師50]8[2%-۳[J4bܯ-lQ{;KS؎JWc[ KdZen6$B50QBOk6(qp`Z"plm5-7{[.#F{ju,!;ۘR5*BƋ\UsUΤ{QJrWIE.'dR;Ǻ m6NҼr̶ҋvd]G6mSFļ#\0nj%6kf%KLҕnkrKff^Gg1b (vo} ۤ-ֵ%qN cNJq}Qo t>v_n)|Nҳk=1p*Շ_y4ɑэ32 C8*EA/Eݲ?h4@a"-lC D ]ZuPPT=o V%ZSæW͒㋩" D[~Ɯ3 -JMwemnT[F4ʥRi;tnq햭e3`ٹn},UTjhMQ$qCyQNѐ$.$UxԩU؍NŵS{Qp[9%>NU{7tqSڝ M7J]cۍtI P9ꏓ bzN8(s6\jvU&݌^#zRrDobƜ)lݪt]ʋFaU`0،GpeWn`x-SIsmegmNɖJĵqەyW^\tm͋t\R.Y;#8]|ƍa2{I9 -qv57-YRٔx )mݚQym[e6b܂ZuzV ߰n]{"+t$UdɩZ|YF[ԱdinٻUh/u!4OSei)e{ԃMzsNjfR/lu >r=E))}sۚIť ktd)P!S SR^]G-֫Rbçф/5c)Rn5]R8ꚍҥkvKF5Fu%Uzk fXz\WFk.F3VؐOHg$?UWqRnaN\mሴ`0Mϱׂ옵](llRu/4(nrqr+O~B4v흝v+&s7u|\vPavm-|Tlׄ'abnj߈_q4ѭ)*(_-U*w5Ej1iBҼmQ6kMEUq m-9mSܭ-ݙVЦ*NhSZ^bܪm.E"hs/t:ES.6a>w(pΌ%N5hӌm^ϰϱYJY2wMNJ 9RQNQ{n5v'W'%VE#km]9~-Mb2' P?2B哯kϕR8Ѿ'WU{FQުNSIEn[ Ma'(I&ake6`׽oFbK_mئ䫕N->EQNW MEoI)1iѿ-L+WY:r-pqi RnJ=N,BQw$9scKfH)z-jצM*}€8aof՚ަCŊڔ[8 q 6o:%rʵvmlT9F0*6K؝?k9ZTݾbA;SJHRTIB2IJ3Ӳ1F.,(i.Y\]mT[t$Z)TT%HDb0JJ~enYU:n|vMw%)IUQR>ce*Sig)>s[Mɻnj _|V]uݎ '-Ӭxun5U%oQK #,ZmZJWiaԪnVV"UjlsWnW{f+ulqcp)o*MxiƧcF-Pi8GjIɛ*S&ck!Њ5BW=w{v{崁ztGWFdL6k}+Q64ȋ~7\}X+jgBxܝRnKgqyn&,iYTHZ2߸{?DhA˲R_Ҥ\tHcqۖݭr\&GiGQSrVgJIqȌwed>n|'](~ͼAv}7wc!;WlI?/ΩhK@:}G4ѳJݩ,[rFTUwUR1=. >UTh+Q6DĦͶdDAtzĻr*kvA떌5=+\8tGiǣ^Ze m%M:ٰ8I:NuMR-CZxvؓQU%5Ʃ5&9ɺqd&j½&ZwXV-ZUeGL(!N7EMrTI796̌Sq[ǯf ضª鎈M ~ɗQi=++ W$i6c%[ mͻGXXU]C7L5L[j49$ :uN&\MWf,I᪻J«ٿM;xlFqX-M^4-᳝5{xVot~$G{#h91VƨXwj~|'M "g|+B%"Ϟ}Yx_I!Noi@ĝQ. OIG_yvsv~>Dع?^_y lm& S:Sf\w+̛_[g ^~5ɷhIMAP֥?;!V3[bA=-{4 0vlk.5fTm}M'U޷ UA--e%" mg) L\_<,jKQF0)jGISL$6rN4u!|;#2y3lz9#=k :dx%6-[xj>d4%DEM<ɻMKKxCN O \{0Ga?S찌qڊ|v;Irgh֘2u TjMr~V?= #g. 6u}iaʝٗvֳuUUdK%_) B%#."ZOeIQO.y2NQ:.&}FWPN:!In"JH1whnNZ=1E.h[S=oS-E%<힅%-(Z[fЧTT"1=x]5ҊPr x?Aglړ*t n>G'Ӑځ%_%D=>U@{sYNCj(V#drz=9mZUTCX5znq250w[:S]ҥ̏9,Ȉ7 tWB<eS6E$="ҜkPZ[)M+$ "o&'}Jm""֧9- tmm"8[1orvՌ-pjkd6&˸ulf8tTZF(*\$\It=k;A.+ IՓy;@:ӜNGZ:M'N7'wN߸r>q";Ӭ=LCB))eiJ6ZHĭjNM}rWlJ4nSj;ea̐Y{g"mKsiڬ>SԵHZ۔J{\H:GDeQă}{iiyGev3l)SUfĎf7vCL #1T -T V*xzyu';5'yYG5:9^x7coKs+ L7KJG$To Ӯn nk4ڋ$%IN"RffDEƃTL95rna'I8`̰Eݲ?h4@a"-lC D ]S&ck!Њ5BUjNz%×{U0 -\Rޙ-Df81J)J,0J•WThg]C\($iYN̷ GL6 |2LH4"yN8ӫl}l> [P˦ O1[g/VvfC![)#G& V7I\jiUK V25|F˕5Fօ I}&hmf>m{S.ݯbk{Nӡ*}8ה܄j#.IeLq'%jLp/PE=5xic3F$OiWv~5o1`ŹZ9 ֶO% ̫u>teA%D- ܴ-*B*ISy2@>{;H_gYY'^A;;Z!aFv=~WG_6-sok*f́pczCPӋXyD+A]统k} =F|NdR׏sM_MAzǕ \RZ:d7 \IG ۍ)#N2d%R+5w&{Yr^hjh]"ÚfyL,+L/mb*7!͢a jJij5n{4ŷ-a537Na[ *"KSM8<fr,'k㛞KlŽe尺LiaIBKq" luwM9`;c F#puWX*PRJQDdNpphvEMy)?%JOE3Iy)لBĤ:vw9PF&]Dݏ_ͳ_3$DZ_:A.-#S)} GQ[1ןbwUa=٧᨟\)0=wirʝsi ,r+92޵ykpKpܗ˲> 4ַ}$M[dJ:Qs(_Qy:j2N^*m2)gxVgNdP%8r򦆖ʒnѻSZV= \yFmڸ+Eq(iSJPQ(JTd%Re?ŸouTHC&6j&=:>5OB謙"њ2I~p-@P1Ej#Is:J #]릘!_SDD}TpZZLjXjrl}oVj:QȫaE^y[QRMChKm(m "BD]b"E6(>EgG;zHg_WYt e3~pQz4jG{#h91K5+fǖFyܕc0#D.u-81I,|kiaUznU:n.WJ[Z40f,ņaW1l99[ym2䷮ZBv;îg?y8Zu)uF4ɬw,īvnhJ}ŐqM0W^nQ5Ѫ=J[hٲO3~Y)lmN4X$Ci=D#1w,n#ݯv^ؼCrޕ+lWkhV'jcNUZXXѭK6X6uOr D: ߶m" j3;<ѵk~_S7Xd?lN רO|Ay۰0Bֻrtב%e)+DE Np;[ɽ%E.H䘲:kų^':mUNc_IIus%%o%Iܱp޷mJY1^'jkaʍf]>kqDi58{̒"@g;_Xb>j۾-'MznTz:Y2=2 rnû-;!wu:G_DkmPb% ׭__4̯Ht̯οD17"o&'}Jm""֧9- tioKeJ?ҍٍ=| 2; ? Dީ ̦TŨj1\bO/0$n62R24w:k̑l՝bSEknΌV~JVC"D1YIo>H&n|n Y.6|kwH3ոKNWb:q>K qUO q)_ Jk;aڽtZnkZ:2c sYΫKI.ʏKFi24q$%n&!IB15hKZ!?ָhɶ6N$ܤ2W7 Ja8GJPhVglZMn[t~NnP٩e6Cm JR%$DDDD'}QV"dŔyR- xgXܢ{3|=$c3Iz,:2ӏs?dwh?z8OI]#Jݑ Qg"0^pB% GtA7'WpI5D61NEĹ4KFqĭK%)*RτHy~s4] 56wmNFh;UMt{cxacv-Krj왥ifi᱘L& ZUԥk6rj\O+B6ne=Yhi_..u6Mɦ,i"HaC "7 $dzJf|魡ZEAwnմ)izLgy(C5: $HJT$Ȉp(C6F0%wI]ej{bGen6}5w{E^snI){rjGXɗZy9W SN%IKI\9ZVW}'lW9ΆmS7$kSW"Tv8nQBH҂R aevOaa S|{$^fexWӍj\[7`hwggv> '2_٩zk9͗ L(Z68ʵv!t=2vwTK q#>.Hȸ}`yXO.z|;Gfd[N3GВNm2ީ =_xǤ|IZ5sJ4tMF- "TiIq< E#6vlj:Y dȊ^=p]!iSYd2ۅM0>oPDDD!E͖ItNg#/ТDZ{6ҨZ1ևϯ_ I sWh:_W_QAwkwv?B6|kSꖈAW N2Fl?_^}WkW|foQp.Bٕ_m;ꏽL̯B',?JhKVZImHZ IRL 5+Lo- L5FHJ"v}Rj5#{{҄R?QgK{&-h*-ӫ.rK45h[OV\.ӊ`ȖmRBA**v"})6:q:|܃mBfM!&V44kڽ7QM]';_˘{:=vk GÛљ iW)ͻkRWVH=)C$ܣi[#s?o>ԠvcZ[enS=#)f;Dg%Пr :kc?Ww$j/^,5> Ŀzz( "Ϟ}Yx_I!Noi@kQy߸ƍ\Zʙj')`pv(i=F|NdR׏sM_M.%R+Ӳi7wX[~eHVfqzU F ]zK;A;|KJ(#~.vnBwԯٯ"-j~_/R @Ja)TX(-݃Gϱ;*McM:Eݨ][2K'}QV"dŔyR- xgXܢ{3|=$c3Iz,:2ӏs?dwh?z8OI]#Jݑ Qg"0^pB% _g ׌p<ϖFo,صN }B(Ur(<6R}MhE`ƉO0n4>PeVwu Ft|Yi/-Qiʫu=繵E%GYnQ2}$%v*^6lLKM:6櫋0J.u]ԗ"Gj$w4vbA m)Kq?AH8;ӓIal[<)QKmDrBy%2D^|S=/ Pů.{e%y2ߕIqךm#J[[]ɭ1Ɲ[ÐhoxҋvQI7\?r>ƫҩ-ۺ_+~ 60idF$[rVwG,ͨ;|H R 91ҵjܷ\uIA^3E٧&f{u[~-Eq:¯ WW~@.G!~'e]dQz"6hK] |hͿeƬ~2rh6 ѧb;PLgK5/Axqg?ٲU"-;!wu:G_DkmPb% ׭__4̯Ht̯οD17"o&'}Jm""֧9- tioKeJ?ҍٍ=| 2; ? DAأ]څ5+ wz^b!O|YG~" 1Fy./3:9CF3:ԗ3N-80aGX8FѤPEݲ?h4@a"-lC D ][<KZvrx >0pSP)󣭩'GKҋr(#222dѫe,Rv{mZYircW\׼Q?8} ltiSwp>ltiSwp>ltiSwp>ltiSwp>ltiSwp>lv{b[jAE>`Hpe Q?)xdl>G (T-ъevbQkGGcr|f+&ݑG@ _?/:ߒʔ+%{|?Py'|@e_v]qG] kfWwi~>32 C8*EA/b\^xfts/fu5/Eg@Zqa;qG I6d~iW?6;7VDZ AǞ_W5t^Z-@B32KZA{L>=.#dYWÑuOz˫:GeS#޲r?@Yu|9 Tڅ;- nHM\DX!9Jէ^vE] &BM4~ͧ[>t823[<KZvrx W=w{v{崁8Rqۏcpj2j;K&Y;qNlkQy߸ƍ\Zʙj')`pv(i=F|NdR׏sM_M.%R+Ӳi7wX[~eHVfqzU F ]zK;A;|KJ(#~.vnBwԯٯ"-j~_/R @9xܰFjIR֝:=(=leN bֆE1}No\FW"kK֜)rQ_ŝ2NqdKV릦h84w%).b"!ᾗi^uC[3*9֪~$F+tWvz 2QIx^9I lـ!IZ*#J{F>)+>#N|pf4Q*7dkȯ˷ՉߖQiqGΥXIQǧ9ꮕ\d*2岣(/bqM{";NQK&M%#{Ga}*F >S*m.j_VaZ$&gB+aZa&JL.# 1r6n/`m#U*1"U9S*r";":TdۋgɚJ߻yE,2qtj2u\7-]ۊ Ux«I*c{+9Jy~S]~xQӿa՜v?)9/?wI9H5$Pj.5'&̋][}^\ҷ_'{ݝFb惓lkO F]KO Hq$gi34$wsF/ҭXGz5v& 3Oi',+V9BV]iMf-#S)} GQ[1ןbwUa=٧ᨛ;uP&ewv[|NS=+ЬD:(ҤZ!(>EgG;zHg_WYt e3~pQz4jG{#h9yKvzlixϚ^B!w¹_| D r^{1w\PWWOGoQ;[;Q~ũPF&wggv> jv-z-RSGl5^7-$Cm4L2^.2#F0-ѽ f9>kNc58(4s̓ٵzM,.b'F~*qrQiI{7k"b{{ ,w܆*?g痻-/վ}aU.x!|ê=i.B_oawŇ T~QKuoXzb{{ ,=h"pvZ_˫}Ы]aCDT{][gX>'r'Ȩ:ݖk>/m;,lKYk\e.\!)ki[lEv!(!gK5!Pۧ{ӆQM5d{n֜JY%Pqw"8IDjm(O2Ld31R_kh] )v5S)r+٬^zjU-5=MGOM+]_{? 2mR 5pgl]26w^aB٭)W so"?#+Vi.'+%*yqqvm]'o"D-$iasZqjEJII]'fvlvrUɒp55Өg+eřTF:{s&df|$FdKA\nY.4Ҝ*JRvM_zM7i9޸O+L6xe*ӊi]ˏ3+ĕ>آX6PۛUreDܜI.7mVO^gV"rnOvtMf JP[YKbR6uC׃ j3;<ѵk~_S7Xd?lN i 1'h'{yϙ^Ù_~EboEݮoM9N5?DEOsZ!5V QX%%sIROW9jʑjYYO4?id-`:E咨t"} *kM;e>q4&}ׯJdz:lX+F1h~R #~-SޑJŘ :-Cś$~gg|ڼ&N 4pc$Cr<:iVW۫=}B_oawŋr'Ȩ:-/վ}aU.x!|ê=i.B_oawŇ T~QKuoXzb{{ ,=h"pvZ_˫}Ы]aCDT{][g>## #s0zM~cG LVYT0\4^AWQb&iibpiVg6-F.O{jVmfFzQ2"72J3O`ðf7Ưrs'n ,X'J0.L40z從1zJ:%gjˑ$jr:ɣ%tT?<]zN%CՋyϐIn%()拜^3*EnAII%䗩5sxOgڿl6V:xةACkj)3&ѣ(Щ 8gfMNPm&r׭j[ܤ;>Ŕx|2d%FfCN(rFh{evc:ĥ%7F2~{δ T٭Ix"ėR=`iԝB[WlSmSTZMR (q\ZδsVM)^qJ kmm){~~=ѽvdKLfR5NS{;e;Yo\k[;]qa58ՒJQ>ե%DDEc4SQ[sQsV/g;8X#@0n48v=O m DDDn6sصUڧMG$Фԑ)H[1}sr/#+lii2pTɶJUtQh6FCuV˝ 6$iFFD`2=&Uoʿ984Eyη뻢i-"VVm.D\EME.'?֩^0"]ݞm O9KѨD:W+hKf>.t9/U6` ?[}RMWT(#~系itVx5u}E@tlE9/m~?Pr+9t\ީW̭c#KnP{<[NحS-`y :L_6 wq"wҒ֑#?ILTg #8hUJ֣EKʋ*B[X}Qd񴥧q!*Ot)0:ъNM4g%kwń1ڞҚ8 iF M*mZ{N7Q~ 4w-CơNɫ\(J\:)S\;m-{BBJWES&>ի)NckqNޥkn# IO Q,$kMJ*{ dPbk#1J`Ϊ[QI˂MLȓ 3Li Z I%=v瞵jeuߙO: 78R$ gVNY +$%%xNs&);6'2_٩zk9͗\i_ Dr?2 $E_o8*A.Nhzaĝ>ezFe~u%v}7wc!;WlI?/ΩhK!&oCr9k+5oiPtVI-Y?*&*Pôeb[oku> OTJ:yU-LvQRcriqI"%(Ȉ"]l;$Βj{ݶkZY7n;$f>1`jNcҲojm+& BtaOυ#raR||Z)'uS)O1O%#QB+ &ue_ / QI}-smuDFMlcK~\KE6KloZniBcf fb1 tnb)I/Զߍ#ܣE6]c1ֲڒa;1m/rm~6db,lKzpZ݆w0[JФ4#O L?d֖k M)63p4&i;Z?{d'sDus?()£䢜ZZit5ȎioKeJ?ҍٍ=| 2; ? DAأ]څ5+ wz^b!O|YG~" 1Fy./3:9CF3:ԗ3N-80aGX8FѤPEݲ?h4@a"-lC D ]gS%@gOl@N^uh~혾'n֝ L2y 4 JR"". b8,vsSJodEKqJe56$3ē-͚\A#v:-'P ѳQ4tRxH1֢N3=qGk#h|Y˦u=NZ*i i[&L'qTn+D-LjI%ND5m^#3[W9p2"33E3x+2JmJ>R<'?֩^0"]ݞm O9KѨD:W+hKec2fGO}Rj*zL07>{;H_gYY'^A;;Z! u?'gnz! lh)*ds|jgY?ԭS-Pq0:Kd_RBǃ:I+OgF;y~;c{GQY}, 0G:ϬH؁mQ*?Ūw*bK/]e~5ڿuVaf%a?O?J=IAE^u+C VW6cdkR0|I7VE0Wmq/dvhYET`#6{Q[JhK5g/oD[u;2g|+[ |U9nDHmdY(nObsg'ld6߉Fܓf[ { dY>+9K6vQK{i:Sqmۖ3l—rA}4n}cZ'JCe^uӞΖQf)^ eNsPiɫ7ۙiƮ®qeZ|4Tl9);7iZTլW>Lw(P[*R&:Pn>zӜvTѪtrlcuˠx ^Ҋ(b[M8Kzk쫢|qiqSTW*u9ԥƎ&P∍|)m;ո̋ym\z<R3*ٻ\ܦ{$ەߓ eez5LNPqڲo4kZ-1 kQy߸ƍ\Zʙj')`pv(i=F|NdR׏sM_M.%R+Ӳi7wX[~eHVfqzU F ]zK;A;|KJ(#~.vnBwԯٯ"-j~_/R @ C/XL܆sJWk5H5cC:i?{Omϗ<{ƾOUDo]?9# S*(ly+u=({;ͼ?BFvɽK_h˦EΏL ;t_aè>W)٫.Dܗ̉Hgi,hO::y49?^sϴGDD=-e?bvx4Wπ~O+U*{%~?tGLJ6~оMPzGz5=T+=:%e?k>;7cϪukIj_ȤnH C=[seK5Fu&mIl"LKL#"ONIo>}ۏJZ*22:Vc)5}Įcx΁f 6 QJc)7m;%/흦,sQBp_çssaUN&n?$ Ct,)x6{mFe=rޕz5ETR]>R\(IJorȋq~mTfcK U;:R'-8+;q77[F,&qtdQjJR\v[WMYqY;NlN٣3OiБ_DyWak:Z9b[Gf?Orf){q%WIs.}wN[%#+jWݳN?z='4G!L7a2>[IJRSs|Q߱C7FgUqƔ\`h.j̟V+Zȹ3V7vz$i JnC-yE[Ʀn>;uoe©*Q~)A)q.48x{7̿@eT2̮X[xc5')*{T[:;~(7P)JT)F!0eQTH-#-Kz:9mF4U&([-&~51h5s[וJԢ F.1bJVvbl^PhV{MV65Q3&T ҍ-KH&ω\\D󙧴~}AiivI3 xuMRR,[]Vꏲ IvbEqq;Dڻ&++Zk;=<ioKeJ?ҍٍ=| 2; ? DAأ]څ5+ wz^b!O|YG~" 1Fy./3:9CF3:ԗ3N-80aGX8FѤPEݲ?h4@a"-lC D ]gS%@gOl@N^uh~혾'F*.è3:D p IQ=Ud}z-wskwzeKj>M嗲K}M=gMM~I}ȳZ;{ۨER_|7z9w)*eǷlV]%:Z5NSh_1ٗ]&vm[TQ$u,q$]FͣJ%whq$D-$٨B $Iq'FyY)pд(ƕ9 e]-\OضpԥV+m*symն-gΥp;|egkT@A@wj~|'M "g|+B% ײR/uk¯ ?[?7KFnߩ2߀.G!~'e]dQz"6hK3Cߏ<tܟ臰'E?_2^ρoGѨb? dRLC@-O}K  '|X9?IMk a荏aK,ȲnTY7Dd8ۉRTsy(Ijq<]%%R W}%ᄪͦN/u+i}=cܞr*g?POƾv7}|`{^[K!U9~5ñf_iX'\=l_4WHzǹ=*U~~5m/zs\-AEV:]a ^xB=) 2O2Ls{*|0NxN1e.6;K3lWZI ' J QoUxJf; (55V7FM>V'9}#}]b"RN[qOj+HGrlWQؖ[47CỶPxTiХ*ˑfC}.j%!i3%CClڥ9)BX4{[^(Vj'R.2i4VaK:FtΏv[O¢"LfšjE$%oyqndckuu#N5mҧm{+ gtM凚IPNVUJ2Ap[?@NRUqjHE2 -\+rM*#=G@]e=1GglRڽ;hv[JokËڻVf׻n;'_|;ؾ0z?Eym/;ܞr*g?POƾv7}|`{^[K!U9~5ñf_iX'\=l_4WHzǹ=*U~~Ӽ}q]ռeN:YfnƑ.!R1D#%yѼ8 p*84A^\nbܗ->;oL}SdyN=>&g+U٧I(}կW[F#J@+UP΋M8-ĢR[D#ݿy-C 6)J1m++q3*xD)Vnɻ"3- fԜ0(28gYwn\BHˌ(GuELGfu5z}`'ի:UcJ{/*kv՞zн]fك4qU:]6gU҂{n_7kmdWNǾiȦ,Ȅ&PN[sυŞcffgΦ4"q[+Q^ѕ);U)2$BQφהnWU&c̟55Q?<+N6K=f 2M\s 6ifkJ}ʥF^*)ueRWDQ' _djf*Nt6/ [ |nb!`a} "5 -Ţ[ƭ▽؋j>#ܥ)JEkQs+[ƦAa)0 BnFw{n&t; ]vKOu/)nrD䒿E Ԕܥ.s TR0qn"i>mF=uVҚ~ҫrӄ7kƟ} 6=MJIVs+xeur]ILhFja5PyT(2FF~泴Jzx:>-(5FMI4 |yΪ`18b(&)4sDkcw 崾OJ_z'_|;ؾ0=h-rzUȪC?Eym/{ҮEW/u+i}=cܞr*g?POƾv7}|`{^[K!U9AL%<%fJ+\ƍ茷qT)JIffDD\cI^hm\氆.WI*mJVI_{fmZ:ʧ<%UtJn۶䏝v(iu9KGxqg?ٲU"-;!wu:G_DkmPb% ׭__4̯Ht̯οD17"o&'}Jm""֧9- t:n_<4e|]fWTV?9O4?6'ݖ{CǽO|kPdOfC~_r;nO e?OKBǜ7S҉gC$f]}n<-f=bW@F.$j#2F[ edfa rxҔc6 ٵ903PH:M2OTtW}l_;Eym/釹=*U~~蟍}nbYC=W"sDkcw崾OJ_z'_|;ؾ0=h-rzUȪF.Dמe \ќuJPYfdDEb;h'U:xRՒJ[mӕIdL62z0ch69$܉-n;z>|oȯQJTZ#F-]8W{M74i{3\4EVu]䶟wC7~m53*̧RtU%y(W㕬ׇy\cJZ1kZe5(TOEG'P˜Odvicu=֌ pQm~"ޓ߻yOϕx-sMZeN\җ=ͫ= v'G٦kjmFfVLP4tI)"i Qd[G{?cg!U':U6SN_&*{#2mFU[Qe~rۓ6_օE\_4jNKhy(+;y{$.e.t$仚qvW_gxєiJm'cBx+NJi|̣֞bzӼѼ-SEӜmFrjM=GqJ5s=be[\uW)1>ueQKyaaa\$𨷚TeR@t򌪬գSrٵ6 {2{J+56{5^i=L7TavQ9T5wE7t4N־%fcX7yۗ}qM5*8ϩFۜ*N"Yw|jIr\.UOر4(ӋUR#lɽgຕۭ:[;fpݗ ^I)R{n;su᳍^_r}l_'u+i}=dOJ_z'_|;ؾ0=h-rzUȪC?Eym/{ҮEW/u+i}=cܞr*g?POƾv7}|`{^[K!U9~5ñf_iX'\x$k?c~:Y݉l!*R=)^?'?Q{1  2&Oewij&w".ɭ]ݥV|+{<A3ϬnfQqx=pr1aviǹ ;b4'Fލ$ڀ.A^s3Ykg/8Z!ug>/g;h˔}tzzʿtQ峳O@ sT/,k-OgFpm rڶ=u$֥(u6.gWIvSKB(ǖV٭djoyu#I0(%%9D #\*zBƭHS@CuW5C|V>夕ňwԲo5 [PĵtcIgj3ɐq1VȨ''֓분snWѸɸg®&]y5rt OKO\C*k3Rp.c3v;']ins5SjL!ب-&JބA#wXmKZdt[JاK[R4Q6 zԣ3Q}l-j((.\񩿁s4Cr8@;6Z#VH g)uk޴~OILEwggv> Ck'*e:Il} _(XgI;yLh{XO5OGkZ4s*W-{u:Pzuj!#ާ4:&:hq>ڔK֬wU:MPSb6D51;VZkarhү&F;Vmn3k/NA3Vij"9J׋u%IҖ)AqV#-޽ej!K>SKzuY}Y~KO/J[.jU~>z^'ŘU1xxӍNiAƋq]ͭK$&,h[nȵXt:LSQ+O-#3DD\c2܇Y MSJH+1Ri%}=Rfَ{^ rn۷d,it+iquT)bƍo+qj..FjV~tSGF6iJ6q%n'0զ+c*IQTeޞYup.m*jeK^ȶ7lƔBiTnHoyr?UQ֓&}iab߳:t%}Mmm:o6y\LT1컶Vb$k@4q g+~ ڎQO}c?]sC:@4q g+ݨ~[Tth6WQߪ)Ϭ?wǼ%lvTSX~oSSe2ĴKl|GdTtGn]]"Cl)Ȕi#2#"3"> 'MuOeܾO0a'l)ͩI)ktZ^ɽ5̢V'Nۄ5 +nkcC]zHԺq.zWQґ }ix˔'JDF{uZ%* qqcT%%~j&ܮI ,tIWxiRb{#57nf췜^8W9N[ u3]5yp(RuK&R#4oM͉_螹Q\at.jJpM9(ƒN27:M-u56,-8uW'&mSV:Oъuum[=wFГxdN4wKSz.inS*Jۓ9ZύJ+m^ҊlikXy:Ќt)Tͻju#{'=kOÝ. ~E4;|jU(gSZpx\ad 2/cNkRXlEjA?SKe +ۧ6wS)6i~^BYҍcv桇HU^enfdy9 8nw|?]sC:@4q g+ݨ~[TZe2EBR ĦRP>:ZeP[!%)""""> aF)$K$!^/ʵiԛmɻm}p[\i_ Dr?2 $E_o8*A.Nhzaĝ>ezFe~u%v}7wc!;WlI?/ΩhK!&oCr9k+5oiQ{=^S?T?ٽt+:wܞ6~pW 9}=e~n`{YH2tՂ3v(WЯJ#!J}BmE6M-jYbwgW 1Gb.ۚyc֞ M0X|:Ci;;&?KGwp'ݨ:GNhuGH=,ex<zꎐ {Y7j;E>y9 8nw|?]sCaEJvemZ=mV3 ؆u;2ޕ$0ѵ-OdzL+RVpse65 YYVT0^ͥTi>M i=kf^[&7'S跋sR`$%}I讯7`̳XJQj+jn)K;m8enYJ8 ӕ--Vw{|Z7c=7iJ,Mt_7 8nutΩNq,yEc})XbiԒIv׾m 2ҭli0J 5Iqv8-<:8#icVdn+ڵImrj'5H} ȷ\|8jKW,\NGJnTg$՚[ޙ^:1MRbjSU`i^i7-9Gky~߱t֩ \tu2upFO6[>b/!sмՔxl$a:ju}ְ7P.')h«7m*I[u)?ݽhL7K1 rݕEUs|}MT٫.fByΡu!,._f9l***S{WvR*[Kuj覸s.̧KMقٲ*rv[|~j@4q g+ nw|9 8nw|?]sC:@4q g+ݨ~[TM 薇MXa\HS"RQ~#׵_cgWN]j Uٵ-n߃gD+g}{043kBŕy S\êq&|\d3I-{"ts+?1MaQ78ݮ=Z{(5YG;ƼHYnޣFrQ}T'd岕 i%æ+K-+'JJK ("%e22CRRʉVcAnܳ">x2>iJLHq$Uj)(+o )4v+Q8ܯF5؈/JV\ĝ͗PGҪ=.  rҤ[rJdi :ҏ#70k鯰Z94V_' ʤb_Ѐjٙ<¥:)QH/J4%N11ч:v>t^Eo(~2YijSF-yk`i=`qѽ Z?*/޲}8ZoKJRkrW1 ZF_NJS2#ܤ8TF]cU`1^2x|M7NRM5ƚ{;o NJiJ2N2O4nNϼ5DZ[kZ,\8l\Le<pI%y"17/gQc5%Q;JM5nf@:hb08k':q[Owp}ۗ/H=,exݨ<#ާ4:Gwp~ ڎQO}aGNhuGH=,ex<zꎐ {Y7j;E>y9 8nw|?]sC:@4q g+ݨ~[Tth6WQߪ)Ϭ?wVmضд(-fނj-1mOX^ə33j333331.d6U],BQb_vۻwl34 Ү7UկUJRwm\IY+$r1]څ5+ wz^b!O|YG~" 1Fy./3:9CF3:ԗ3N-80aGX8FѤPEݲ?h4@a"-lC D ]gS%@gOl@N^uh~՝#&CVi4USE7E6{˟w8}]Ym^cl=ڽud{{cj` 鴚8NbrP'kfaTI *3ݼ˟<6ups2ig|*y(|^` ߭:nujd˦rR#OCVlb=#|d7uk\"GG5Lpy˟wX7]o/iLZkeSMQgqQ#>>-F]~]Ym^cl=ڽud{{cj` c3y;ۙ~IRU(He%wx Ԛ 5i1نƠեZ)qĶkuMTI3%jn/[uI֮h{`oZ^v%{ l=ɑ8Rz鴣Sbu')ebfzcrkXIK YJ-䤶lj"= ؒ#y%J*f^˪g~-$P\ޡ-#2߽@OM:Y._p&:MjQoSI'*5oH=uW} IA2r;vBY! q&5]c.`9XξjfPz}˅>J48V$(>- ud{{cj`Ց?#ݍ WVGW>v6|1}]Ym^cl;j4-PDR"S˿5[$I#m+tEwj~|'M "g|+B% ղhYS>yIbAէzt6QQEݟ=H/³񬿋/BCf_-x{hΏ3=߿_3}5JC8'7_g_'Vw%/|c$'3 a99U wʛ;ݕweEwפV!J򻥱Ysnsy9Pi> CտRj*8 -:]1d"`pttc(I|rj'RοYfL*Jܸg&TfOip$__DO(ۈIx^kScUedu( I{ߧM)qmQM6XڦE!(3ticO|[n<{FXoTED5yWSD[6w,?Ao"Ɣߠ<<* mx|#{:mEiި1Ʉq}ӗ-J'([o`̴(7WkJFxHyceU=Jcĭo}4̳Z~5UWkVw9nu}I}>@sed>n|'](~ͼA:vw9PF&]Dݏ_ͳ_3$DZ_:A.P^ 熕jʑj')`u?ҿd֞Z`x?\?`{A?LLGwޞ6~pW 9}=e~n`{YHK|8}ch=&W3A@O~_r$Kn俵PdH?RC<~He0tBg;ɧ?+}=# o7S);EC>½s|$S:|Di_S+>vgԗUijևr?KѬYYԭ(Ξ,)YٿcnWU}S^ORE#v@hӗ%ЮHm Г3ܦN_7soqWʴu%m-W,xླྀ~#FIֽYFr-7Q^ÔԣJ)l6sp=ah`x=0SMwx~sy4SjDv[rܯľc' @ kfWwi~>32 C8*EA/b\^xfts/fu5/Eg@Zqa;qG I6d~iW?6;7VDZ AYϮKYcNRL[Ѝ*I#.E B'>5$Qq*D49@_ i,~+1-F)>d:s};pv@SDvõ/*::'~yP?KʀK i{fؖmV'd8NUPH2qSM1o %02KFMm;**L37ӹO:~_Vi9!6O[y0M6B9Kp{ϜϮEas \IFf&MSUeQRi\#Yq^FҮ>(imcԦl(67Z^T>߇j^Tu8tOn;Râx;wڗNۿԼ4uCfhWd@f̑ߥ)qtwU˫*>qTlȸҵTFJޗddeDe@if>S%W~<|s\ ii0(A 6e6f[۷e  Uj &TZIHτ̈ȏxu8tOn;Râx;wڗNۿԼpv@SDvõ/*::'~yP?KʀFz,tSw<~f2y}fګKql/Gv 9xq#Vo.\񩿁s4Cr8@:Vsfv)}y,+/i.&N)}ir( "Ϟ}Yx_I!Noi@CSrqYvmLjʚiI2mf_:Qgn{a_}s= Ugt]\ Ufułj6d?*f&iKOKD [Fn#b7k(gSؕ 9FPڶԝ4pM39WTqXg>mپwkqVPE4pᅩߕ[:gCU({c"^8? -Ne0 u={/~Uo~:P_GUwJȽ旎 S*?s}ld^Kᅩߕ[T>Q]҇2/yʭ_\*k)oZwV]2.SOQ3'r+qF̝YᲚB\E\bmOktokq+lSNoJiaܖܣ7)(~vwW::FWq哳Do[^^b/eJW[1g,Vcr}JRճG `TWsNrWH G5Q[y${BG纼Jϲ*"~?_ˏxFoG>m#bŶ}E]vWUu?h6TWm'U[f٨RiޔݼMc},e(^ vw^Z46!:0^vZ;V}0[Ҳwi?;JuQ[` :Im3yNS徵uDɚ²##4o_6ekw3XlgR; ɸfuҵ[߱;3Neo =J)t${rEp\RSt;:ι*7#A@gRJI:)>3#YoҌ25pڍ u=lñҎ՛MEn~V7Jypl:GKĤ+xm{_y薱AQF]~7x཯m9[k^F3U| W0u'*$5xw6n<%.Jᦹ)a[q&GK2̷6úXQT3M~_fyF!UV)҄_:i|I7kWzyjuB*nYm)%NNڊvmъhTi2RLҤ'\Sii5fvمw5ʍ~MSM盄&7ꔔ$3N䤈j'gYwrՕISRrwj+ewwk+%$x\ucQJ䢬rܛnnm0vX6ZvC&u;{̿Bj/JhKZ>i 1'h'{yϙ^Ù_~EboEݮoM9N5?DEOsZ!w3K~LOqӽKZREL̈iťR8A^OY%nnN2+AJntf_Rnu^ ~kc: =;? 3W BT{;8}=UwJȽ旎?~Uo u={/~Uo~:P_GUwJȽ旎 S*?s}ld^Kᅩߕ[T>Q]҇2/yʭ_\*k(CajwV/c-'TR]):Uh[O4ZS$8D%I?RL%d0zx% -lCjMܤm+$E7f]9w Ԍu=bQSi7evݖ4nly)EDWT[f{qpvM ^jN^lMO7^K}o:Lڰaq\_"LԿKUD223)͡W"*4\} %o;:fII\2"뙑j _>(Wlt i/ bb`=]zЅ ݒor{ 615HB ]ADT+ŌMJ^II߲sEȜ[ڐ{:Ty=s,^A_^[ru%FѓۊRm֊i['cnWU}RR||Z)'W(TKQ:E6BSZ5b$Œʾ44' a3lتqJjҌd˳(C*uE-q4՚AhOH˫Y--B'I?@\륳GTeUK60-|Bk (`gB[i63uqu[EEtN\Ԛ $ޤ-*I[0A]2Xؕ1 [(A$ IJVF| nXl{F$bBJx/`4C3VMg[Fo^HhIc\5O*ѻP[/Z\ȵFm`<}U(O_A•FhR~4Є,̵uByˎk8ȷ+y-.rqjHݸ׽;jw|2ŻS*%- D: ߶m"8}ZxҮ_7jkې"UB .uJ2JP5)J$̈aH2mɪUKI^R~x[orJM5;ifuG.˩:ъ$ݒIGVgk&U"nbZcmw &֢Tj/yi$[Ik_׆m1],? |[skmx-څFQR.aY.S}pO ^юہ>m.t*6\t߁:#۬ډHq .t*"22#![F)8-4wM?Oz(bp|ftjJMI5tVi ksGe ڑ |,kBlNϟ 16[N4 Gu|F{O6beS{* {/ MnRcgR!ժfҕnǦLP|R 5:W H%&{``4GvMe{ x@E^Eq]ʝTN5z9W*n82#!<NCR٭MOf}-3Eh_ ʪʶ 8uBQ[NI4[q& _GJoF41ĺfTE6䒕ID9&2M.W UʼǙ iҏs1gIm<)tJqY-6ܚ1W̑ LAS aAE>-R<ܟTsY+H\.G1 6"*) [pvNfG !^GnјiNfywU8֎xRݨxVjI':q_cOE4+h*K(J;N>RjҔnkkz#2|YuJjۧ<|),q5ȓ5/C 8F&y| TdFW=%NL%-I1=Y^ƬGkWkZ!6ڤq&jMsOd]h'Ѧ94*l9m_c}Mk66&FBr׼#FI8gNI]yf,tfUWe iwU$djPN6)YDE/t](ߌ{+{mDb\WMT~w^̦7B%2z i!ZEysS:Fex]:iʌ-GiwMxW]]2fu<69U#Z{%)w)?nl,Q]EmUQ$! -D>4}WyfSF}2j\)۰ɽafաچtS\(Կg5[6T"l2Ÿ5 {#rr{)>>s_K>߮5>˻Vn 6irV$ѝ\-;͍TGG/֔ t(ҝf$.[mXcURۥ9R^Ȣ{.;*m6PBm\mJ}r2Hqc89FRNREzf}Z*E+taj=斒gb5!Ռy,:u6aR;*NM/ٟ< T+8N[N8Tof?h?i~c]PyVm;t6hJEk4MI#"R}[uyԩY)8Dzݛvn.}֮LTiEI-)IںV90cX6 Bgټ*{ժ@xd[CK&K\fUl?XSڧ:Hv5%Nt$&XS\M/Cq8=`!=.Ȥۊt׽w[{IqG?OJ6R*}:+Ι%Km2ٳ,qxQ)I(]܍_ PF.U&bvI|ҍiJ,RSp\QpkB>XZЅ))*VFaiGRb0ҭ~ Tً؜'*sٌs+mbXJ)bhם aNRrۼ# &)Jn]vݴ:BMJE":c6oyȔffd*R)Ies!hnpJeNshVe)JRcܤ0YC \Rqc)NЄޒI6Qri?]jqnJF2pZ7-*3R%ۗCQAtԖm:˭L(f[ZI*բN":*QSį Nd9+IһfOZMgj6n-NdN҂wƷmur^w, nYd]$-F%,Ү[s}-f~k4;6\Rt⶜e'9lɨ9-[4KZ<>Kcn;_f**NM+95/ %A] סÊUgF HBm*n-f#<H|~C(å''.caQMJ.4Oʹ2_iJS{../iMM' A|i3iKj-^M}Sl6yKab4SLԝκ"2Q{iԥ*J0e­HEATZ{mNIٴCFx&NWRR!VRi&of]څ5+ wz^b!O|YG~" 1Fy./3:9CF3:ԗ3N-80aGX8FѤPEݲ?h4@a"-lC D ]$^u{b?Uq[ޞÑ![%]~tz 6VUs˚xYi4ZA9 leRUFdd -RZt3h7}K-rbfeٙ%[ҥ@;Lc4ˮ.40u4Y4N:>K0qəʬ_ϼҩz>NK *k H`|[tυ;ԗ?9(5*v?hiVeBeS>KTqq3lY+r|;܉KIfʁ\*b&OD.L&M!sE)[tυ;ԗ>0|.u˳ٕOj.PjY$ӅvsڕjUZ!ȑiRfDzǻ` jJ)jQRfg" eM*QjqU&OKǶ@ѡV8|3B{2#l.\񩿁s4Cr8@:Q a>YŻBN%x?ucB?"߀.G!~'e]dQz"6hKq;\/;vַa)Vr $[kR$JQRFfD0A6s ª)?m+I6֎u,.*F+$M:;Y^n۸ݘof>3ܝ斒fkK^Z5lBOt-vJޠ۔u jw&-W2Sc} m](:B S`i*I.TKnE6B}Q)6T2N  fR 86i4ޚ-X\.; :NiQM4՚ii3^O8)ΉL̴ؼ4ڂ,r0wY2J"5q{maᡔfP໙nJKxD+wQu)=>N1S2x9s}xmeUviޘxϋN\%Ro}?q2|8DI#IΙ2#e~4XyFQeғM_en& [/+vQMNdڌZKF^{ãֺ?Twԇz|ꎐ^{=kMO:ԇz|ꎐ^{=kMO:ԇz|[ }yd{U- ڝ^K^}C0Pᚔٟ6BT{#5>YMJ7Kׂv3Y܂tSUw] W*mBf]9-3-m1QFIJzw#25$OM.=iUAQ۴JmAݢQ6X4k[J6*^RcR+{[i67FzШ°;E'o?L܉DS.(-I%6D_eȍil\ iW̩;=rBiF*[;=bJ?T&hэ.ȫ{(IlQ)$jW&5c"3C-&msSnyu"!R])M)EJJVi[KiOS R.e(U}Rʼ_dKSڵnƜ<tkSC[Tٴl]4Ȍmjµx29 %PJvG1-8ԥnpЦh-|i%4{KicO Rԗct(5M¤ܭV˼g4Ɖr<66&xK*;)STnR \bU`ߧUn\iޣ6l)nϭxM&"=0l>BZؙ3Z6[ aeRil(dfif5jTFt%44Bﲯ)]^M66baSO omG 0,*9I.?qa0%%kɨ||> ʭ*_o1ծF`,< ZZ3-岵% '.FDzK>eK J7S.n)ҫSf[=]FՊZ\eCnctJM9Z{{QiKrJy/ڀa`q>qM <%U.(M>}p&jkvrzlu 8[д(dc̳3󜾞/ QTQ)FQwROyeyIbuFJ-qr/ϏaKtƽ%.M2㭓^gw)IJzKXnRM\os=]|&eyaӨbJV~-^Sj E:63$(ˍąnGCȌW]\Lrm^..={%!¯ma>Hv=cxCzGꚟu?RO:Cyw5??RO|ux$ w-"X}^ǝtMNxu>ٛL8vX\iűmW 8L.D!_J]}Ē"?dzmhpMgzzۓsO”~y)1 ފjj=6 )ny!9V|Ϫ@ -3TQͶ\gPkigTrZ. fuu#֫Kmũ:rt =NPqnМ :PBMF*u{Rjjm6 Yxi%jUQt%SΆw5|26CJ2i+&Хmh߼ +9%\.*NR^jS*קJjt&7Fx*`3|s8(v:TdEI ZO}yFVL JUnr6c%fߪʛD(+|.XlUL<刌ͮqpcj=˜'qBզ'C6,Nz]*E;Ri[nWt5~<ǜ%Qw\+Vi>L>qdÉr"K<4Dih՝,OeVJՔܸԝuz?x=(Kԧ*Tk鸷N1ۋF&RuQ*=vwƋɋ~9xkg}l[ڵsř/a,jyq~-u/ׂ-^mv[_G~KЛHÇ'ҵмgW%;f<$|^lmo;%;;^ 4{ovkdzFS?f?z |?etM;qJ*m.r ^{};Zboc+=ts.pn' Km{lSv=wk[<mW6߳}{ bE52$Gq[+qTh$T=Ȕ^>KChaqO7FX'[Sm/}*EZrliQPm>&ix?jɪcK`2%w=gi+N-EXJ~8P(xi&X 6&=_$I]jtźNKAh\ig_wBѭSO|SUf$)-ĒEnr# kfWwi~>32 C8*EA/b\^xfts/fu5/Eg@Zqa;qG I6d~iW?6;7VDZ A$u/=p"W($d` *EiݗqRN.pQe5Z\F]e%Df%DiRL̀4[sP6aNT:&JE/m.nK-r4망M=iR :J&iFVd]m&)j20}FmhV0q)Tz5K)nE"Rm?E;J' Xnێ.j'Rgd$D^br^k<UͧVhlf+ rmx |23N̿lOwebyW6Cvl' -ĸHL8m둱5FkͰ)3fxuKJIC)RR#߹&~ҍEӻKҵ_5W^3RPrKr~+0ͷn..c u}tV1i"ݿ|Y+t{(JOtںOsz/u! 춷7~L깣К߭iGbeدUoQZmfB"7ZmqT3>˻-Kgmpui 붻;n'+&7h{$ MSpMYIBռe&}b1%Opm^3ow*\Mݷ_zkwwE͕ǒߐiIv}3Ϧ! ǑTMPKky$33. y(Tުv[5jUWw >-{On:Nֵ^z;-3|]s1T׆JljV[U;KRSg/UMI-"MD{ˇr&.To³XfNKr^v]4{DS.J5c:]ZyWU6?8)FU{}՜َ;x\eNoޫ& %3Z+_UJ*Zo:6кT%Cܮ6ˊS~spWqsj5Jcx7Gk޹l{V鍍)=苄SmMT$i afȶY9Lup1:-/j)ꋩ5V%j߻=4ԥ]hwjT[Ӥ^WOxs yݓv_uHvh'C 6ZH2q5jҦvI1%F 48j1YFRQRdoF$AUc#BIrm_fnɫ9 ]wSM.G :M['dgk+NьM?Af^S˖mb[dVlĠɤÚWjtWȕ%LʊQ> Ph2;."RMN{%%[fQm5'ңYTɴ6q[[/~8oy4{dFUٴZkū׭AVu0؃Te: %jY'Ըk"+z !UTCTS⊄%RdKsjgJKSKi+oy6ڲOky@m.IG}jR#J2i1$z5)j4qi8V½ﶖ{8IQm-j2%lZtqrQ2M/} +Z5(5wBVmY _6_(U<v $aOTJ\[,Jy%9㆔z."E84^nY9(Emvo*ԃQPR5նK{6 w(XS)T촔kwj~|'M "g|+B%?VM6Fd FE}JO-4h+*ТIw6/@b"@.G!~'e]dQz"6hK@]s`evHa@9fg&>tq-nIVM)DF)~r't鮛OC^YGeTpq[SiQIԬrح?AFaBi_iUujFRj0ME6wm)|(ul~NڎuC å7Sw;O QN]a~ޟXt1Nxw[?aS8i/Of?c>'*uG :uz}aқ;⃁l~NT?oO:Su?'sPp;4Jn_3wu:P>MkcA?S~§TpӪX_)|(8|OTuC å7Sw;O QN]a~ޟXt1Nxw[?aS8i/Of?c>'*uG :uz}cSEB Zu2L܊uFPemD8T2ڦ  rT*A*5SM;Ozk1iѭ'NiQu4Vi+4Iq[(y/3Zb}Z#ܛi(Rd[yNs65w4aL:sN2jWvi6Sbv4奔]Tu\%/F%:{pMW)T배[ ~9pٓi0l"YHIdy$b>*UiRU'_UIJ=aBc^U\{I; F Ҕx‡f;-է:[騷S•;=]b|?*r=ڊzĝQԢzJK',ִ)H|ƓRFC@hTj[1UJ#8B1_7R[PX y+S{=SlMϱ5#(SKbJ2z,cVX*RZ0dۏ94D]\\#"e1aTӬ65Paoxӝ\,/4k-6W˴G184J(na7_鬭;=ora3VtFLVlR䔵LJ%q/\fIS|{RpaT*,hpu]8KƋob;,`Ȣk1=' ӓg8ʋSԶoe)(ݭ~NbխksQ4[.\l8dyMkcA?S~§TpӪX_)|(8|OTuC å7Sw;O QN]a~ޟXt1Nxw[?aS8i/Of?c>'*uG :uz}aқ;⃁l~NT?oO:Su?'sPp;4Jn_3wu:P>W6baj9@pĪ Eo =>KGIJ[HGfDdf}(`â\1֥8J_RI{Q[L}XK^rmantﶢ߽ݳ'fVw wl: s7L uyk!M!ħ%IJA>U{EjZ9^qJRu_j۩SbYQaΘyOCV1Qk)TqFp؅l{1wkWee1Nr%B)kRg~#Rȏq(FGPh|s}$SUIv'J5*Rӏs9IyK q8XaiM׋K/bV +iũqOj=ȗ#"E]]2kÓ@)&o:D5$5+JQFO8**K*RIZ;V$(rSTۃQR9eҜ]HaN7[i$ԜS^uK#.d-+xQi4"b䕡$ٹKRԔդկϜcY,LlJ)tҎr_ J%ݷ$_e:o0R -lNR'jq[*)cI-+n}RnzVeԫ4m}di2ޕ%>}dC?Y@ӭ{>-ڌ$Bc(IE*)ZC<.yO ,թqe&t!NA[diqsHm&{e3YVcV,;RN 4=3W۠;CWOFje:0ӌѕ]9NK4rsTڪ R9ۗ [*TZ##sfnp̈I43\ Joی#ض$q9Oq{3]o4r|.Ө1nN%4V+t^JOI9'jXS¹tj-3HLhN+2Mh%RO;l5(=}fqMjfƼ١ Og %%oj|ݦwpueSa^N %YR-;v$AE0Ԏ 䶄7dg01V3gŮNppջե}I&%> bIKj*.VNe]ρ7{l|MN(7]tWyKN߹j݉NthC6CRIq'~2W8ײ ibq8ؙQbUeElBSqn;u)%.Wqs+SBuU8JOjri9Fik5m/-:eMfAО=t&O#2<DdRT)*%%FGϴ Ѧ9SJ[tA8J7ԢJ2(2jQi,=dV8I^㌖mɦi(g8F]\.뚩K f0qi%)J2m{j2--ujug[WgnG(_b6a;Rj0U)4&w%<-:0ܕ:{[;Rrns&WݒlcnurY$h nUf.E椰.$fA\*O'O1̽WqP8wpn*QN^˔d%+6y>U2VOrةhڌΜڲRْKi;+|ǴuV5*%y>S+y3j%LȈDEЩR)FKne')nJW)Iۓ|l[J#W ?مi&{{g V+4e:M<#"n622>M Zgƕ(aؘъQQpJ++c mV\V6NpX+8jrԱM54շZیܒJSJIlHݑwg>.Bٕ_m;ꏽL̯B',?JhK#In#Y̅e]EdE:碿 =βkIlZjZU%$غuQlՋRLԡ/fI;xl\akBd;EJ2V_(3S)S71qx;[~vᲝmVܔL}ԓ 9.uJJ8Q٣*ZP6.]Pj`r)jʒQxFT%ǎ&\ss}6ZY_=ikjBLz깙6jSMIV2v+IbJiuQ<7ByG7)-*6iK4),uQ__hZ\I:Q]z2ۨ4ooBy46ё2UlZ Fnio+]ݶw{5sf5aA8m4zw.XqkjLgn()q$̌3!kF: I])W n{?dO*J-?o^ϕhz-zU:7+HSܦC5MNEG  *S (/S#.xz'97w:U' +$3ZT䳌U%Ix6Wlr =~x|٧# q> M?j:TଥN~UU%4vS:L/"LP*:Щ71nBXSiURR߁8_f zVb-DldeirJH1;&̏q!xztEuIx)wnWٕb)+# ؚ[tjSIjua*Q\{JSTdrL>5}8KfgSwHbt:ϙ(Ta#}t[;աV~yJ~ 5-iEoh-:?REJOrgQqj{(7:F:ƷqKL<2(Җ9ی2*5Qp; sLJRԅ*EGdz+{oQojc%QNAF\ =f{Mo%EZ\ >/ieؚ (Ч}t*'oƫbFTЩRm}.5SfvXzJ\ڐɱ2"u4ˁ )M&4yQomwn8QO ƭGΥ]=Eᄣ.&TN'մ# $PEݟ=H/³񬿋/BCf_-x"N}bN'}owQ"-Z=yT- tuCg /$o9+4/s+(M! RfgH|uKD ] kfWwi~>32 C8*EA/b\^xfts/fu5/Eg@Zqa;qG I6d~iW?6;7VDZ AiFWzEnҮ[nm'Tp1O$4.: U̇P4rږ2ܮ~Q͈绋zߺ![F^ޒOeqSĸx@ug(:eS!Xmo)lGQn͸(e)e+ݭlx.kwaSxޕҲ|]kzkӝW<. ^Z߫Vx.\{8k3Ffc:Tf %[:*U{V\r}iX%Drڱ,Jʷ-+r=6pYRo=żU9)(T{QJ=&jvݼmT5&unOܹOx)?Srv=ĘҜTsF\{Mbkv^-8q]44ފRu%;՞=i#K~ߴ,EMmV2ęn2ƍ z.aʣu +&n~;n[e8MN[{}gq6+\? %gpٰ檕h4ڹ ܓ{> 9#x\m>6oST{.ų6Żq`ʎ4W6V U*rܑQTq-)qFqG5-jQ:TP%%KsRYMݾ60Ѩ{vNm H5 "Ċ re%I%$DETZ{SmQN($o{{-AŸָׅlKrqytPm3xĮ^KmoW9(8G=On\qίNKsU)t:m"NWԣ).#eEHqi22221¥*u4=:u*R]ΰl\wK]vErJrg[TfJMG´ԜT[m./(t7$7IXȴɴo;2Ii7n%OI%(tE6+~jTS׏9 6NSiT]:2iRvAnJm$IJHVRiMݿ QN(lYx!CN~V7}--w;UcZG1-y>.q‡X8Sii̐.\񩿁s4Cr8@(UVܔS_5"A7׏Ҋu#/ F0!(Ƹzy}b?\ 'V{ӑ@jo@v|wm# Ʋ.Ow}HB%$wN}|%=|~\RzoYRj5E(fQ%(/d̷o2t?tw+UX9[blԥ6wkuã>[f/ -+65)-r~8ooJv]rgw+#>K~z; v>mjmZW F: 쪖w}F~򷾷2ۍ(J%$IQo%$< ˋ!L1&;Ax/ˆ#ޓ4- |G25sN/s>.lM/w{9~"կٷ^Bш @'^=}~0@bNN2#A2:߀]蛻r+kZTB%))-2Io"ޣ>@Ē2IFd绯 ٙseڞ/c*KFE^U֙vD2(JK23pKqN5m-exXIRnq^0r{VnV-\y.ΰٌq;UcJQ$:ng&%z\Xˊ܉16G% $)xTW Ϲ&}b0[ݏ{-{KgEݨ][2K'}QV"dŔyR- xgXܢ{3|=$c3Iz,:2ӏs?dwh?z8OI]#Jݑ Qg"0^pB%>8FI3"R̓uRLDdfܢ#~qۺ.ص Yf($Ķ-йm-II""d[[jԡGjrwI%߁+ݽI>" pUFR^?\fc=R ]kGw>o&@Q1DHDFBUBeJJ$2Z0ŬDB1ٔRoikb+Kgq{ҋ񥲼{I;n+f\Ya1z Yx\4%dhVܢ>qZq{ݽ_5]ڹ뽳߻- S>iz5Y ~q-xMq&ޮFDEFff|\|/0WR } ݗdeFFGut{[H:}0\ 3Vazr7Fwggv> MHםOu\_ lpRbIRPE'M>('*RVfrٵT}I-%ܫ.тSۋ#%^ѼX"E@}&]E4")2 B!hҦDCUPd#zu5y%R=mYmFUfMڥ)*i-jPuvSik#N-錯J-ёk֬&4BD?;"b ^3T~RT_s8e'VU(FPxvt)[3دkmJ]>(ó{N`&X$Ҵ\NcTi_QVN8.dUɩVtv}f۱FEJ܏OE9$iqn,9f|&M(b)"F9mNZکҎ•8Fkb5VYvq]w؟g:ӫ5'n*uv:$)n !iQ3?^uTWӅ^rLf Jf۱7gZjC] K[2L 6MR%jlE)fcj;o9Em(]⚦r)w7M~ -Hk|g:hQbءٯ\i2PJJ_Z [JȍFF[pմ9z|=\St톇fw6F_BvXPjxe8.£=m+{u0NuXړ+I+6sKxYSqa7qLӸsoj buvjR67/|M]nL82aq|E *UcRkfbܣ$]ِ!k.,Q1vHeeR躱S UPN0-8\FX"Q8ū ɨ9%+I'i;-FufuBV&䝣$=)fNe XstLs|q_ZWմ,4|u+ja2tuN8aB8 322,u[-ӵJv#仪لTؽGiJvQ]w%OW<(O؉lU*TI-.MY+8BOe9^.Aș kͶJHwmUq$hm, Q: UZV4 *)4}ʜ:Ob$[NsRnpN.̣RQځ4EOfV엱JAz~:Zcoi:l(ICi܅oqn#O 沩0ssaMvmmZRN;l%c;TiN%+bX7Q\ZqvVfӋr\i_ Dr?2 $E_o8*A.Nhzaĝ>ezFe~u%v}7wc!;WlI?/ΩhKq8v:.p3-R0JR᧿2[%"pizR#>㇩  t}Otׁ=׍NRYpeUS_RvrޛWٱ$h7-J+y*cEyƙ {j=TCd6diF?Վj5Rkf:ITMb!:aNkF*.XxJnPd6*;vh8BU'98uKҸ+v<=rߏ㘫u FwD!'Wn^u[f& ?!K&FT_ Ni_sٞ)m;%M'jrPJN+jNn' OGEhQŵVFjjI8'&fiLam' ut^\'uӿsKO}I+BlOْa>*øv3TSdjI{bTɴtKK$%b:\M"0ڔ蓞m-Tc&v9-Qdw팹bVR/tM ^m[HYo`{'u6Qێ8FJS{g[mcEGPN-i9S'yb>C=&G8F/)G,48՜nWvV;7Ck{kǶ={ó۾~i+Y6D6+z5 o*0.?.צlةLJ!.A?;GcxoUS[(Fmr6`>PwM[yLCS{$,%=%a쩽n1fYV~P1D >+~ԯfIJIM_b!KLdMթӒP}IՄIkՔmN06 |v爄%J. sJٌ)ҩ=Q54B<뷋q#7&f&jne%bse:Η6eZN6j91vצÎXjsq2.xXE'}ߌ[bGf5:;kr}8 oYTj7P#lw\IjD8h~[}b`cW TQ[]&1\rj_\a)$?6myoqKsrk[=m)w6be`g*W),ܗ&̸2&>=j d$XD8RAFfKGuiS<)Yf"$[U*u&ڔ"̔)*TWϜj9úڏdҌRm9wu%Vqb)PP&ewv[|NS=+ЬD:(ҤZ!(>EgG;zHg_WYt e3~pQz4jG{#h9Í{؄rJ;\[qټ(#V07˳X$W62_f?W5N6itLjD{eQ)'5k[@C.j5 _sJ.1V_ߛS.a'YǺrqb5t6wޚf"ҝ{1T*zWpD Y).9 &t:PE>m8dsy=-d8zk ,;WBWrd+MJNsR97biia(O%8lΖˌJgݸJ;4ӥ /bҹl)9VUb܇"WԹ()ͦP[˨ z҉(r+)m ^S70\DkVۇceJRnMҕ^_8iG2N.;_`qSu#^)nQq%(2[?0En)qfU4JMA9qF% QHA$x~II,3c{Ȧq𤣵}Ķ]գR[ĩ*.2R6(}׽V2Aܸ**͡uv粲pfgEoT)nYyDw-QՍ VU5׌f{$,9QRq[3*`*WNWezjobwJi[eڱ4>~Grd>nʼnUvJb1@D(MN`PT^# E%Q: UZV4 *)4}ʜ:Ob$[NsRnpN.̣RQځ4EOfV엱JAz~:Zcoi:l(ICi܅oqn#O 沩0ssaMvmmZRN;l%c;TiN%+bX7Q\ZqvVfӋrvzlixϚ^B!w¹_| D r^ݿ[~oT.p_S(b6Ja `"ባ}3i+ Kӑ97p>{;H_gYY'^A;;Z!E͖ItNg#/ТDZ{6ҨZ1ևϯ_ I sWh:_W_QAwkwv?B6|kSꖈAwjd̮+oT}gezY=eqTD ^73w(|8_9IjR^0΁; wYc|1ގ#oFm@vүwd~mwo9 - t"]ݞm O9KѨD:W+hKqw~ O]/}Wu7gO߃/ Kƶ>0L}ґ;7P>{;H_gYY'^A;;Z!E͖ItNg#/ТDZ{6ҨZ1ևϯ_ I sWh:_W_QAwkwv?B6|kSꖈAwjd̮+oT}gezY=eqTD ^73w(|8_9IjR^0΁; wYc|1ގ#oFm@vүwd~mwo9 - t"]ݞm O9KѨD:W+hKqfVdfFVKq:Y/j}W3Qj43;(~01Yz5}^w_M1# \{@f Qo=SIx)si{={inO8 N5fۍ (432”ݮJ^$G-lODWNy$3.iuGTC}v?% uC9RTuKt7mSPX<\3.iuGTC}v?% uC9RTuKt7mSPX<\3.iuGTC}v?% uC9RTuKt7mSPX<\3.iuGTC}v?% uC9RTuKt7mSPX<\3.iuGTC}v?% uC9RTuKt7mSPX<\3.iuGTC}v?% uC9RTuKt7mSPX<\3.iuGTC}v?% uC9RTuKt7mSPX<\3.iuGTC}v?% uC9RTuKt7mSPX<\3.iuGTC}v?% uC9RTuKt7mSPX<\3.iuGTC}v?% uC9RTuKt7mSPX<\3.iuGTC}v?% uC9RTuKt7mSPX<\3.iuGTC}v?% uC9RTuKt7mSPX<\3.iuGTC}v?% uC9RTuKt7mSPX<\3.iuGTC}v?% uC9RTuKt7mSPX<\3.iuGTC}v?% uC9RTuKt7mSPX<\3.iuGTC}v?% uC9RTuKt7mSPX<\3.iuGTC}v?% uC9RTuKt7mSPX<\3.iuGTC}v?% uC9RTuKt7mSPX<\3.iuGTC}v?% uC9RTuKt7mSPX<\3.iuGTC}v?% uC9RTuKt7mSPX<\3.iuGTC}v?% uC9RTuKt7mSPX<\3.iuGTC}v?% uC9RTuKt7mSPX<\3.iuGTC}v?% uC9RTuKt7mSPX<\3.iuGTC}v?% uC9RTuKt7mSPX<\3.iuGTC}v?% uC9RTeәvݔEQ.Mo[eHnTGir"2Dc{>#ɨV)5j4\棪9iVD*2'^=}~0@bNN2#A2:߀btw]дf~]ZrRv KZ"3{})׆gN $Cة 2N[2[&Kt7mSQpSG.xu= K]Q-lOC`9sì8CTR T8)#<:Ä= K]Q-lOC`9sì8CTR T8)#<:Ä= K]Q-lOC`9sì8CTR T8)#<:Ä= K]Q-lOC`9sì8CTR T8)#<:Ä= K]Q-lOC`9sì8CTR T8)#<:Ä= K]Q-lOC`9sì8CTR T8)#<:Ä= K]Q-lOC`9sì8CTR T8)#<:Ä= K]Q-lOC`9sì8CTR T8)#<:Ä= K]Q-lOC`9sì8CTR T8)#<:Ä= K]Q-lOC`9sì8CTR T8)#<:Ä= K]Q-lOC`9sì8CTR T8)#<:Ä= K]Q-lOC`9sì8CTR T8)#<:Ä= K]Q-lOC`9sì8CTR T8)#<:Ä= K]Q-lOC`9sì8CTR T8)#<:Ä= K]Q-lOC`9sì8CTR T8)#<:Ä= K]Q-lOC`9sì8CTR T8)#<:Ä= K]Q-lOC`9sì8CTR T8)#<:Ä= K]Q-lOC`9sì8CTR T8)#<:Ä= K]Q-lOC`9sì8CTR T8)#<:Ä= K]Q-lOC`9sì8CTR T8)#<:Ä= K]Q-lOC`9sì8CTR T8)#<:Ä= K]Q-lOC`9sì8CTR T8)#<:Ä= K]Q-lOC`9sì8CTR T8)#<:Ä= K]Q-lOC`9sì8CTR T8)#<:Ä= K]Q-lOC`9sì8CT&%u%/ٙϊ*e6S=UZ' kFWC?;p]\ES9ORnӽVM]siMENS}yS^&vh'p Q}cs7r ΎwÅξ% N= Xgu73>6i&Wl)*rl殈I5%PMKV Q0\n>t%x\wnTC}v?%G:rXCTR T8)#<:Ä= K]Q-lOC`9sì8CTR T8)#<:Ä= K]Q-lOC`9sì8CTR T8)#<:Ä= K]Q-lOC`9sì8CTR T8)#<:Ä= K]Q-lOC`9sì8CTR T8)#<:Ä= K]Q-lOC`9sì8CTR T8)#<:Ä= K]Q-lOC`9sì8CTR T8)#<:Ä= K]Q-lOC`9sì8CTR T8)#<:Ä= K]Q-lOC`9sì8CTR T8)#<:Ä= K]Q-lOC`9sì8CTR T8)#<:Ä= K]Q-lOC`9sì8CTR T8)#<:Ä= K]Q-lOC`9sì8CTR T8)#<:Ä= K]Q-lOC`9sì8CTR T8)#<:Ä= K]Q-lOC`9sì8CTR T8)#<:Ä= K]Q-lOC`9sì8CTR T8)#<:Ä= K]Q-lOC`9sì8CTR T8)#<:Ä= K]Q-lOC`9sì8CTR T8)#<:Ä= K]Q-lOC`9sì8CTR T8)#<:Ä= K]Q-lOC`9sì8CTR T8)#<:Ä= K]Q-lOC`9sì8CTR T8)#<:Ä= K]Q-lOC`9sì8CTR T8)#<:Ä= K]Q-lOC`9sì8CTR T8)#<:Ä= K]Q-lOC`9sì8CTR T8)#<:Ä= K]Q-lOC`9sì8CTR T8)#<:Ä= K]Q-lOC`9sì8CTR T8)#<:Ä= K]RgS83QyC1pA-,ܗn&LVߛL䔮Yo%r.=gnӽRM鶑do,U7K JR1&ɪL( S40¯՗WٺDz$;FI/s߭=щjv~@7+EU+kqW/ˬUTnQ^qN:տj?14L0hѣaII+%btCFqV2mOC> ]|m::Ep#!:Agዻ/8FӮ]S=$0џk.|pNuN<äF}Äm::C>Hs}bTp#!:Agዻ/8FӮ]S=$0џk.|pNuN<äF}Äm::C>Hs}bTp#!:Agዻ/8FӮ]S=$0џk.|pNuN<äF}Äm::C>Hs}bTp#!:Agዻ/8FӮ]S=$0џk.|pNuN<äF}Äm::C>Hs}bTp#!:Agዻ/8FӮ]S=$0џk.|pNuN<äF}Äm::C>Hs}bTp#!:Agዻ/8FӮ]S=$0џk.|pNuN<äF}Äm::C>Hs}bTp#!:Agዻ/8FӮ]S=$0џk.|pNuN<äF}Äm::C>Hs}bTp#!:Agዻ/8FӮ]S=$0џk.|pNuN<äF}Äm::C>Hs}bTp#!:Agዻ/8FӮ]S=$0џk.|pNuN<äF}Äm::C>Hs}bTp#!:Agዻ/8FӮ]S=$0џk.|pNuN<äF}Äm::C>Hs}bTp#!:Agዻ/8FӮ]S=$0џk.|pNuN<äF}Äm::C>Hs}bTp#!:Agዻ/8FӮ]S=$0џk.|pNuN<äF}Äm::C>Hs}bTp#!:Agዻ/8FӮ]S=$11~ 8U8[vU㢮ݻ )aNW?Y ︯0yyVMi׵칑r\'RXZJVo\aևϯ_ I sWh:_W_QA)`GbR`em/5Dw$Ao5f2 β X*ҦDz{q&F8JjHs}bTp#!:Agዻ/8FӮ]S=$0џk.|pNuN<äF}Äm::C>Hs}bTp#!:Agዻ/8FӮ]S=$0џk.|pNuN<äF}Äm::C>Hs}bTp#!:Agዻ/8FӮ]S=$0џk.|pNuN<äF}Äm::C>Hs}bTp#!:Agዻ/8FӮ]S=$0џk.|pNuN<äF}Äm::C>Hs}bTp#!:Agዻ/8FӮ]S=$0џk.|pNuN<äF}Äm::C>Hs}bTp#!:Agዻ/8FӮ]S=$0џk.|pNuN<äF}Äm::C>Hs}bTp#!:Agዻ/8FӮ]S=$0џk.|pNuN<äF}Äm::C>Hs}bTp#!:Agዻ/8FӮ]S=$0џk.|pNuN<äF}Äm::C>Hs}bTp#!:Agዻ/8FӮ]S=$0џk.|pNuN<äF}Äm::C>Hs}bTp#!:Agዻ/8FӮ]S=$0џk.|pNuN<äF}Äm::C>Hs}bTp#!:Agዻ/8FӮ]S=$0џk.|pNuN<äF}Äm::C>Hs}bTp#!:Agዻ/8FӮ]S=$0џk.|pNuN<äF}Äm::C>Hs}bTp#!{{EZPZύ*RE$:̈!\I֚Y} 1:{XW9FIM4M ь-U:xh)&ixSi3hFlHs}bTp#!:Agዻ/8FӮ]S=$0џk.|pNuN<äF}Äm::C>Hs}bTp#!:Agዻ/8FӮ]S=$0џk.|pNuN<äF}Äm::C>Hs}bTp#!:Agዻ/8FӮ]S=$0џk.|pNuN<äF}Äm::C>Hs}bTp#!:Agዻ/8FӮ]S=$0џk.|pNuN<äF}Äm::C>Hs}bTp#!:Agዻ/8FӮ]S=$0џk.|pNuN<äF}Äm::C>Hs}bTp#!:Agዻ/8FӮ]S=$0џk.|pNuN<äF}Äm::C>Hs}bTp#!:Agዻ/8FӮ]S=$0џk.|pNuN<äF}Äm::C>Hs}bTp#!:Agዻ/8FӮ]S=$0џk.|pNuN<äF}Äm::C>Hs}bTp#!:Agዻ/8FӮ]S=$0џk.|pNuN<äF}Äm::C>Hs}bTp#!:Agዻ/8FӮ]S=$0џk.|pNuN<äF}Äm::C>Hs}bTp#!:Agዻ/8FӮ]S=$0џk.|pNuN<äF}Äm::C>Hs}bTp#!:Agዻ/8FӮ]S=$0џk.|pNuN<ǵIvNfq*NĈ3 4.֖GmEG 5L4*c&M7jϝ;hna) %g{x6jʋSx#e"¬w҈I9[WfBQM<%=c?9oۭ}>o]WuƂn:vw9PF&(>EgG;zHg_WYt e3~pQz4j1^YDf;7FO$WpzH]}=&QddH55V/xNhzaĝ>ezFe~u%3ϬnfQqx=pr1aviǹ ;b4'Fލ$ڀ iXww'+߻~.B:ćvnKf':'~o 6Me7뿹=V /ܑhNhzaĝ>ezFe~u%3ϬnfQqx=pr1aviǹ ;b4'Fލ$ڀ [MaL̛ŷ<3<$=$cs=/E?g i&dfx"┳/d7 #!XNhzaĝ>ezFe~u%3ϬnfQqx=pr1aviǹ ;b4'Fލ$ڀ MViqG({Oxfm_Hpr1y۶KYt#FzpJ狩Fec73>6iFGMCK!S=Iouo^o&{ǘj1m< rR[3=]s0[䗅DI6/qG) oRɸr#Q [on{;}|ӏ\im*ZԔ! 3Z{f8Q[nRrv\gemm+aVJBТJI1!RQ$VhFQޏ[.BVPgV0rWA˨ܹAd6i#%8Ep[ׅE8%Jp|I+Jgjr%q$h{aևϯ_ I sWh:_W_QAG) oRɸr#Q [on{;}|ӏ\G) oRɸr#Q [on{;}|ӏ\dGo%}Q6%IFۄDfn+r{qq?S_Jhxmo<0*NEV36"¨ }qTjhْK"I->Ar8_J3JQ|RWOuT%N*ix\\+|(N)9BJ@3ϬnfQqx=pr1aviǹ ;b4'Fލ$ڀ ?epqRrScQ^w>32L'#nn~1^ҤMĢ23pC1]s1zDQh4i \;k635böN4Zw^U DGkȀK%3dDE%^w.TVS5o-{[gtC ae?ŵm\i6fm8=EGg?5*Ʈ=S9:bLPv3#%lEFfbᣍmBr9%~+ʕ6ڴIJYaPRm'(E^i)sNQkNtmR,OjRu-[puviބe-ў GbTGeV ۺ'=Ԋm=S҃xX7V1qw.3sJUQ[*TT.iS\ԓ x4pT;GXXKYc7X&}^G.'C0I"2ЕIĶޘu12Mz}jvP0I(n8*T !56 …U劧-rvwu*T{Mʊx^%R~kWͧ XSu=)֝WUBd 87RqL)%fqdzw]˫6 E=Kg%+m+BXYE>%'-qm[rMvĘֲ?9gk~ɨCC܇5/5D&QD7'иlvCjK!Eg*S,œjʫq#%KZ5vU*FRJH)< n/eMޮ'"Rjϰ5[ʣTԙtgF8p'JoJYfjT~5JpjHUY5SȞ.MlfC msXT&µJn*XŒSEQQ[1tjڤo9ӌܜ(N;3 FNIN2S`^ø;Űjɺaj9SRUJܲfKV_&gIFKd:9'8Xn5ŵ.۩I );VWnȶ?e羣67|{+Ex"۵(\=V휷b[yئP*5-œ&3 ios{O8DR*"JLbk6c%ጻ(+w2VEb9JZG~ui/x_{=ˡ <[ ѡUvtA{nr20NʔiH4jc\FޣIBK|]B*7IqeIԪ nZ"ն):nJ;=vNq݆ݝhť`X/ d ʥ*=m8Q%Ijt%E9)ADn6<jGC-C',D\M:q 2ۦۻTMʕrv8ui*"^+u*tK񝇳ͽe?-;q[ڶ.u5%T+mtouotJii*4HO0㣒x|.sQcQ\[RJ1RevlY^{8cwDz_hW*]jyYY:_#j ˢ೽x"_N[ڭ%˞j M91Q!ޞoFf/uIU#VgmjVR'YZjy|SP,ULJj7k+Wi…%:1Q>̍{\X&ȗ+ ٴ)EtVPMLxElO1SpkI(󹄪Vtk:*ңR{U)Btn-SŶ|GRt' #%zJQv[Q&d(Y7ekעmHXN0nyQ6;ң-Qo2(i8TWvw޼+ƞ;#F\5UR[]{a5Q`;6E:(vCfNh9Gi)BMn8d[ԵGM|Ve+诚1s)x#)$HJ*U7oam{۹B|Oa])̚]Q}Mj#tלvCnקL:3\)xHuטkN:tj񵛔f8F1fuF'z{S5{U1j ;>R{ӧ-kYK:c':yҥ3PWu*±eUU5S`q}R"vO Z_ל"q5)FC)ٝ<;uKAFUd QVRe5ȲȲLpؕJ؄uJ:r:Jmf8u$䉷mu]"uezV*}~"4J)[TzAo%BGT̲X % Hᬪ8Vm-tkVXzp`3_of7FNwŒcnkTN؝VԚxsNx PAStuU]3}Վ(RhWrt3 i*)= QDKm鋊]q3[[/^NZ %mקeJᰒ&cBSf(ҭGg,U9mT۔7NKԓRNnTU?JYu=ۮ }\Zƴ}pn'J.7Lĸ 2#S%lBiM %Vz)S0 0ihEU×N~اXI=u?9]XzT+' K*rB@ Q}cs7r ΎwÅξ% N= Xgu73>6i&aGsəayh|?;OF#H>,Z#-y~09/_zxO78jn\5NӔި (uFSmAlI5Zģ[ZK+C^gIB6"Jw}ӺI};Ӵ[J1t[N{]Rm949MFRn~~ꆰh:^Yϱzc7$Dza:>KԉGOEg[Ƹ~ؾunom|s۞deK w>֜wmaƧʊ[ي_KQz\jioTZk:V)%nrK1&fۨH7 lT\Qu *WjnɊb۲$miMl\ )$`>\۴徤on_knܷIr\b}W0}̪BiRM)F> J۾E$;~|v!`@NaEcڠɞǩEi*a5aA>e)eR;7a_;?cŭ튷ٽ8fte[(8eTM)$Ӳh庨ҝ.(\Yg>F&l&*ٕWJRͺmUiΈn*VIä8lwRhK}9m+=(R^qy{cÜwT{T2gn4k$a\˶T22n:֚ϊin2nEQV5mD(ԆR+elS ]R⬔5MŮ=kW-F4$\eܸJ|V{I|knH}b{o|wP,ESp(R]O(嬨EK֥޿V|,ޓcqO٧9:':]*qc)Z2N#FRU;g$&dQl)lUA*| ڏ ue%;NȐ}ş:uj>ssF}F)BN"Jo~8(A5:T[*ujIά-+nFcWNjkX1^Y5wUNOǏ](ulN*9&qKm e.pgxum)TڝI^ueԓn\ehgW_u%xqi1I);}m8YXYŐr "Y1%ܦҟ62yVJ$fBV]O4jI[M5M]i 1'h'{yϙ^Ù_~Ebo[?HwtPRC2‹@oA=RTÎk9EJ9D=|RʥUvn6Ov~}[o{S˱MTPqʩ)}RIefѱyC۹nFȣQz8(hKXCiRDĎMD"#=hC1[IͤŹRI6p읒 4VJU0uheV; =Ӝ|V4NsJͩ,E.D8͙ٛ0bf}웂oKT4S[#JLW7H`rRƚR(ҥ:$%$h5%**ygkww[_&=W̗l`.Ӝ8l8Q^+wt{1K]To|QUI]ߥl]U魫B)[SےM e=e +6<{^ъ`J]-Jqm**Io_RԠaM+^RmvG$'.WzbE&dZh/z ɒX;RR 6bٓS*IJH(;sۼ6%F~1Ww(ГrmN;woRt] )whӫ?A$* N[xѣ_x[-i/KXO鬻rN8m(Ȕfܢ4 ڄԔd5$ԩ"u(L,dS{2J\⦬եJ`-dYϹm'(ֶklCii, PmzsL!oi' 3#,\m\Ejk-ZtJwhb!4n1rizrݝ^k㹦—S-cK!3s@KytAe G'$k#s>.4Xa 1RJޭT'FɥhqS'&]ʫ%Vh%{lԥ {۽.Ďg1Fy./3:9CF3:ԗ3N-80aGX8FѤP55ݨ>g Dy~0G+ga~JYozO7a(Nhzaĝ>ezFe~u%3ϬnfQqx=pr1aviǹ ;b4'Fލ$ڀ >]E  lZ!f SatW/=4wBлv~-C\'huu=k[y5jm§өS$ɐ⒆Ym-kqfIJRffDF` (LُC*gBz^UuyjG}$ޕ$ys @hb!uե61=J9TZt5&ŽM)iJ;aGo/ś =4vŽ_6A>ԽDymo)V5D[x7+]Rꘂܒ\e!/r0\.q!f?3XU*m''ļo4(9]:pr*7&V~$mQf?GxnGl(dQf?GM͐~;aGo/ś =4vŽ_6A>zhl| ;y~,飶vYGl(dQf?GM͐~;aGo/ś =4vŽ_6A>zhl| ;y~,飶vYGl(dQf?GM͐~;aGo/ś =4vŽ_6A>zhl| ;y~,飶vYGl(dQf?GM͐~;aGo/ś =4vŽ_6A>zhl| ;y~,飶vYGl(dQf?GM͐~;aGo/ś =4vŽ_6A>zhl| ;y~,飶vYGl(dQf?GM͐~;aGo/ś =4vŽ_6A>zhl| ;y~,飶vYGl(dQf?GM͐~;aGo/ś =4vŽ_6A>zhl| ;y~,飶vYGl(dQf?G'75vu ƫS(Qfi˓%EqRАI5$n:${Ԥ-էC _d>^f-LDI6rZdJ!KhH>Q6Or]J]$W6g+$3H>7FSCf4986)3Ki5䑘iz:[Ԣf'&$Fɡ|/N&I=6H}3HcE9⾝=&Qn6\j~Fjk!5 h k.bMF)XjyJYۆrd)#3yTuˍ ,Ӌ=>οnoG]B7S~~W 8@\vv|frҸ(qQ5?HJ㰣k3~aGhg =+ŽѯA@zW_; ;F39\vv|frҸ(qQ5?HJ㰣k3~aGhg =+ŽѯA@zW_; ;F39\vv|frҸ(qQ5?HJ㰣k3~aGhg =+ŽѯA@zW_; ;F39\vv|frҸ(qQ5?HJ㰣k3~aGhg =+ŽѯA@zW_; ;F39\vv|frҸ(qQ5?HJ㰣k3~aGhg =+ŽѯA@zW_; ;F39\vv|frҸ(qQ5?HJ㰣k3~aGhg =+ŽѯA@zW_; ;F39\vv|frҸ(qQ5?HJ㰣k3~aGhg =+ŽѯA@zW_; ;F39\vv|frҸ(qQ5?HJ㰣k3~aGhg =+ŽѯA@zW_; ;F39\vv|frҸ(qQ5?HJ㰣k3~aGhg =+ŽѯA@zW_bؘ[V9[:Ցmy%99Ðפ8U,qn-DDk]UEMs\THk/T'mRmg)I1.^p#JZZOr|F͡jcԽ8)vьg3)FI-x=|l=8m?TDhCF XG~ϭ|O[>~Ҡ`e%f25o =PT+Әan1N"ki! !/4T7B J :e[S1mwZQ|͎= S !Tk4܅i!I!dqa[ϑ~BPAUе8`ZԺ]5jʓպiKJ[j\Y)%$p[I]{3k[ZEb2.Zͽ8hn1!6kR[%$l֐(>-f+/j ~c_ᦪ}tMRMqmrUߚ*mrPlA[؍n*DEo׮-Utn:qUZ),(\r .9†kZ7_ŲK4- Ԫ*Z>L\HaU {>c#/`>Ԏg ?_2AvN7^@op\lqPh=Z#&CLUև.K|ȥ0{AoslHzM&ӲQp¤qIq>=6Vm/{uwcdZqd'Z&VHh&EBs+vFK<$D&9fVQJ[Tt 0I^RXJRmPI(+ 69^rmEEY$Óokwϵ5cyiM[4=b媽CQpJCCJU5NE&ʥ"j;Jqg Ɔ8ͲFן`haTE(SKR0MmR۫+9MZe'RMGl77[0Hl,T@̹#+۔T" E~v5Sf^iTjP8(9׵JqY)7(6MNkSRxcUl՛[Wi 3N㴗zͻ6ڎET i䬯vz8]VGzE* Azit-ipF$ɏxY%pRսgwO J'vܻa ;tʌn>KH1e+{& VIxm95-rf Eᤚ̬xΨڐ>m :6D5SZNa eӆ}fV3JͦI֔dK'INXuexZzۡK0Q{*=]ьv-qK0c}PQSt jxtkΥkJTq]F8X^UUlf)Z^*ڎa,,)T)km7SN<p2lX=KӉҷmy?!!-;LINǧ1\t4Lz!/TlH / gۺT_E6}J:6hՉ꽵jrۣ"T!ˌyePj"R̢#ikA[0 VG`캕=1‡PQRۋu 'e['4ӄqۋJFڔKc͈WJgTDtE>1.GKqWj4$e\Q4>2כk\<(d{@\yk=oe_)NM*B tCmM6 6C73eeٺ`.+.\ Rq}k- 7ix0qơGȺh>{OؽVSr }sL9!Ph)#8Fgɵ_s(%G y[iڕ>۠Uk9A~|h<,Flqk)n:Z5mv2JQȝ[7PuN='wp ޸k~uڕűtj巪nBSKƐҒ^m!hqJJFFFD` ]vw=@V.j"Z\u""D'>uj$-+B($Wk1USnmBcHMpbZoP[Üm&DX2ܐouQ< _Cocj~r uNT~A DO>ɛm:rfKv^>K XFۡtWc6e\ThmT"ZZk/dNvmI-FelC[QEkZI[j56( ڑۜ{?<?\PC_h7'Rֳڹ8sKn-V[+Us*He)K0U jAd$Wx^Ծuonڷ99VXnX)w;NY6T]i6lVmw5c&}&J/ u9p*b&҄+rR%ps3Qn|`PۢWׁ4ڽ%;]:ۖYե:JI*˱T(ҝISk*%5f 6Cf?Uيbe[Eԗ6e6(Qelab aTd۝=6u#6zneyJ0|8bo lKnJ*w)5x<:FYy'|)CLgȴ6#6ץP|8 A6fEb%jr);I%hjIlM / >N)RQ[ۺka9JRsMdSZ^lqP*Y4iPq=rȒLlq2YQgt{Nj?jca>rm1e9c6U 7 [ QM'\v[ض|ݓQ#[{W4|imYE֋s%j.e^<0 f :-H5$>9 kڗ=[~V|wK9*K : e.iXk&ʋ&ԓkSSTZ}0⊅wl2Dv-r.uǔQML[je:XGŪilb ۼguRXqr%iwÎaI5,4%g*{̯5dAn|ynlnebظ\vdڟ#g MTj JPt$=xǕQeWGBچ:;MGiFS7$a&uL𽵕|/bZ';9Ke7RPus!kRt}: f Up_aTQlԆ>Qd1-"N2֔>fK;xK/شloz۲p]Ӷrn141Xq-PSޓڲ0~[YWi79vU _jBtzB\)9թF\II4u 09OngKt>7߽8ƾoKw,DqΫb`gJ鶜U,5*=)RN͔ۍ5`jۛ#B9 \UD:3fCtSJ/)Dp(ygr}kdVٽ{.smJڛ>ϵჇ;qIN[I-|REε++N9 ̨QB"LZTUv%"EM5 Mj#qPhSSQM3qISvږԮJ-Cf..˰EBJ}+j{n:r0a/i*ԗXbEr%Av =UAK󹃉XIS䞓5o;\/ӣ */n)ӧSr剕:TkF)>VHF&'0hs rԩZ6B*Ӻ:(WܯKʔl+uPO026,G'J։) Kq0aCm+Д3QQkfR3Ze4-kl98mEogm8L.QS Q׌5%6҅o$KUod|O [0е?XopCs&"Х0IRnCfZ3 Ko; cN6]99JRru)ͩNFc(eZmN;FRi(aBQ\ܦۖ[<ݏx߮3lz{"eЬD\R-z:ɪH(ᴮ2{ڐY^m66vtjьmS =6䶢+}ȱq]zn7Ƌ{Ž{P pdNE~U1A֚`vwKh/gҫ|џ,uRc}.?j2EOF{? _5/>w=lwOJ;֌Ţj"U*v46"k9Q.ga;09asbmapӸm nte3O5Io5]Q1%^)\<ΙAÈnq> SGm M3vQ^w~ JvʏeY^>Lu~yeMq+qŒ8'Rv6\BMZ9KN,'#+P PMvNyEoB>YɷI) FyMpJSA%.8eݺ@ut])"#YdQ+ٵrC(XbLb`1ꬡqȓ%7)˥ڍ :(ɷ2Jco|zg-çGN%k?W|mDjUF%M9i9>(ڑۜ{?<?k?|[+DFʺ#FHw;1e!oMRȔ\~ز| pMbǼc!q#MxI.;[%O,/ AjN%S7&^l+Ru/FDD9Fi#Q?"{g4T>{WsuA4]>_> tOP<3EDfGؐσ-=SQ>빴[DͫZm_LV4OzϏO9GGD-CYǿš`rZK>/Q^m6%j2MAGsZSnu^%>sj[2Jff[7B]' ګ'8_yiI&*qS8LY' ڣZ)N&C&hK.U7Gv#spANi/E>\+ڏU<Yhm,TZd׽^@l*h(ᪧ!=/?p^ =g2;5]GA&.9R_8HĪ?> iͷd948$ҏ b8G*8;Bջ x%O+Gb8F_5>yzk.Mk}>SM T822ެ8('8D_ fgm,k5r8;V3~t8)/ 8C?YJq–HcOĒ|;?cYWx2zY ]ta{ID4խ'ԣIkOo\×a~S b>[H-4̴u=y ZƷ>XNѺ JNनIӾ2XU؄}C/F)ΑZ0#joN2p=W\y>-?uDMŚ#hhcauw{W&$΅Q>WOjAIե/moCӺ _*_$BzRV_ V{ͰkոN$Z)#|0I/OWG(׭h5m=\GŦv:~Hqft3IS3-Kio07e+|]PԔM[(|_aO0'3?Y_zϕ0L֥t/??;>3ͿVVCk7?p:?Cݎs֞~ZIi#pR|-_iuZ\\h#S,dA4]q4z2ŕZÊ$6(/zsEi=Ωt$Ǻ"|Y]NOyK3'rM1 q:!Ωc^28XfQRX{T~J?UOQ3k6`Ù?,Zٖ!1-4ND<{x~ Ə: g18+v)v1qS[ GEVҼ;•+A kcmkϨ)QRq4*xIIL*`kiN*JsjOj{7o7M.Az{#ӶxgѭQ߫_|@1Sϟt s]6 lWC^WSidM%NUSmũ[nFgVԄ- KR D`uBݹy-۫ y_'̵k_RPɍ)l˨ZB(頑M& 5m s^/5T;7R\ʃLȡaMW\ qZRuWS& dۋs{I'T'S]"CTr,2Sęip舗Ye).$Y$Sͧ+ZƱy-kh+Ҙ nr{tnM.,iכ{ZcZF@ 7iJ2Iso.a3/b*B(Is&N2jn1o=3eW\w:M.5:rMytc>נ"CosQS/Y˵9WEz9blV$c%)+6cs})z]`IS*Rܔ:Hq 棿W_\egb?nP͘ΕX^BtaIEߑTJgR WGĵɖ'㎬Jd|^j7aOcY2/Jœ R*!q9InvCM%j"qȔ82G]Ekb4k!Hm.[zz!)u* V&Ha$?֥(BR'F[pNJLL5nRdRTtzuHq 55S~r]??6#5A8U.KQ B˼Q)DSuO!%\*[n6'v5=eBӴqOuaQc`#d+K6ܩ&@<ΗDօIoVw SKs!j[^Sb2Vm_ Ԗkt )"w]^4U.,)Dݏ92ZmHT7놓m$(RO1J z֦2Mf)_eO%Q< kpϔMo}_!Ҳh£wڨoP%ytKMe1\C+Q.2#-p8Nkk\_Q5Rߙݻ|n\RcW $$$dCnSv^ +[X$0ɮTAI(vC+V̋LK*J & $,ȗK-i+$(qIJ">n#_榿fu7?_F0 S_QH\:~jkj?˟P/M~G#ys٨o.s@555`榿fu7?_F0ߛ|,kNg&ݧȗp^7HA%ȤmqKZ)FdI3 'Za[Nex'hy>Z%o겸mT*DɎzgʸPN8{ۊHVP;)ymwO ]>أW3ռB'm6(Jg 37>\f(0%ٌ;cj-VvչwVf0T5Q\&0jK&ÿE'YU^6v28n^PiLTa6_Ar8.6J@_6hc:< okXwT£\%KFa7n!A{1.qspJ@ipT%Ky%Jm+)ǏrT㎫<ÅOk>xUo_^nRK&Vl7"9["hBCm`Ojk*ͽw+T`ɋjX95j#+Qa'W7^mW;4Vk¶&P| -S$RikAz6PŅdTmSpkVY3CjiJ%jmiH^{Jq~5,Z/ag"I<^&GӜ2jQ$La*vY{kh=%N/TeʝT;,5S~e*vPTN*zJ^?E@IS_;G*vY{kh=%N/TeʝT]wl:>:f+UFnӝ:tJ)CQ٨Nq8.'LamBd{ǫA2]}vWS]1njc>8q xRJ3 :jeXVFƻrzV>az2cy4QrdF9̸yJR-E%I5.FzTHCi晝o:8JZHrռOFk)EGylv-b*Շ'='(aA/-4:ܩ Ci*5JqjP^Ƕ*V/oby8 ٍG[r 7θRyjGgnsיqB o Q߫_|@1Sϟt=rXrugk&ȉpYJC+edDn2뭩 I$FF[6CNex};b\K'.NBL9Gxn6{P,y݊ݩQ,'-Jtxw}@Ta qn2ƧPe\ I64pIS_;G*vY{khT};d,-sGYoR rCIRaPJʠԒm9Y)Kڑۜ{?<?\PC_h7y3 evV$Hfk|uP2RC1Sl!n4YI?`/3ـs.v@i.WU[oZȼRretbpfcc-0.12.0/examples/networking/vlan_filter/000077500000000000000000000000001357404205000211615ustar00rootroot00000000000000bpfcc-0.12.0/examples/networking/vlan_filter/README.md000066400000000000000000000045311357404205000224430ustar00rootroot00000000000000# VLAN Filter # This is eBPF application to parse VXLAN packets and then extracts encapsulated VLAN packets to monitor traffic from each VLAN. Extracted packet header fields can be stored in a file or sent to remote server via Apache Kafka messaging system. Also part of this example is a simulation of a multi-host environment. Simulation environment can be setup by using test_setup.sh script. Then a sample script (traffic.sh) can be used to send traffic from one client (VLAN=100) from host1 talking to another client at host2 and one client (VLAN=200) from host2 talking to another client at host1 while running vlan_filter application in parallel by using command 'python data-plane-tracing -i veth7'. ![picture](scenario.jpg) ### Usage Example ### * $ sudo python data-plane-tracing.py Timestamp | Host Name | Host IP | IP Version | Source Host IP | Dest Host IP | Source Host Port | Dest Host Port | VNI | Source VM MAC | Dest VM MAC | VLAN ID | Source VM IP | Dest VM IP | Protocol | Source VM Port | Dest VM Port | Packet Length | ---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|--- 2018-05-24 18:43:30.386228 | Box1 | x.x.x.x | 4 | 10.1.1.11 | 10.1.1.12 | 54836 | 4789 | 10 | fa:16:3e:ec:22:99 | fa:16:3e:1c:6f:2d | 100 | 192.168.100.11 | 192.168.100.12 | 6 | 1285 | 20302 | 1200 # Implementation overview # Example application implementation is split into two parts: the former that exploits eBPF code, the latter that performs some additional processing in user space (python wrapper). ### First part: eBPF Filter ### This component filters VXLAN packets. The program is loaded as PROG_TYPE_SOCKET_FILTER and attached to a socket, bind to eth0. Packets matching VXLAN filter are forwarded to the user space, while other packets are dropped. ### Python code in user space ### The Python script reads filtered raw packets from the socket, extracts all the useful header fields and stores extracted packet into a file by default or can be sent to a remote server via Apache Kafka messaging system. # How to execute this example application # VLAN Filter application can be executed by using one of the below commands: * $ sudo python data-plane-tracing.py * $ sudo python data-plane-tracing -i eth2 -k vc.manage.overcloud:9092 # How to install Required Dependencies * $ pip install kafka-python * $ pip install netifaces bpfcc-0.12.0/examples/networking/vlan_filter/data-plane-tracing.c000066400000000000000000000024711357404205000247640ustar00rootroot00000000000000#include #include #include #define IP_TCP 6 #define IP_UDP 17 #define IP_ICMP 1 /* In 802.3, both the source and destination addresses are 48 bits (4 bytes) MAC address. 6 bytes (src) + 6 bytes (dst) + 2 bytes (type) = 14 bytes */ #define ETH_HLEN 14 /*eBPF program. Filter TCP/UDP/ICMP packets, having payload not empty if the program is loaded as PROG_TYPE_SOCKET_FILTER and attached to a socket return 0 -> DROP the packet return -1 -> KEEP the packet and return it to user space (userspace can read it from the socket_fd ) */ int vlan_filter(struct __sk_buff *skb) { u8 *cursor = 0; struct ethernet_t *ethernet = cursor_advance(cursor, sizeof(*ethernet)); //filter IP packets (ethernet type = 0x0800) 0x0800 is IPv4 packet switch(ethernet->type){ case 0x0800: goto IP; default: goto DROP; } IP: ; struct ip_t *ip = cursor_advance(cursor, sizeof(*ip)); // IP header (datagram) switch (ip->nextp){ case 17: goto UDP; default: goto DROP; } UDP: ; struct udp_t *udp = cursor_advance(cursor, sizeof(*udp)); switch (udp->dport) { case 4789: goto KEEP; default: goto DROP; } //keep the packet and send it to userspace retruning -1 KEEP: return -1; //drop the packet returning 0 DROP: return 0; }bpfcc-0.12.0/examples/networking/vlan_filter/data-plane-tracing.py000077500000000000000000000170251357404205000251760ustar00rootroot00000000000000#!/usr/bin/python from __future__ import print_function from bcc import BPF import sys import socket import os import argparse import time import netifaces as ni from sys import argv from kafka import KafkaProducer from kafka.errors import KafkaError from datetime import datetime #args def usage(): print("USAGE: %s [-i ]" % argv[0]) print("") print("Try '%s -h' for more options." % argv[0]) exit() #help def help(): print("USAGE: %s [-i ][-k ]" % argv[0]) print("") print("optional arguments:") print(" -h print this help") print(" -i if_name select interface if_name. Default is eth0") print(" -k kafka_server_name select kafka server name. Default is save to file") print(" If -k option is not specified data will be saved to file.") print("") print("examples:") print(" data-plane-tracing # bind socket to eth0") print(" data-plane-tracing -i eno2 -k vc.manage.overcloud:9092 # bind socket to eno2 and send data to kafka server in iovisor-topic.") exit() #arguments interface="eth0" kafkaserver='' #check provided arguments if len(argv) == 2: if str(argv[1]) == '-h': help() else: usage() if len(argv) == 3: if str(argv[1]) == '-i': interface = argv[2] elif str(argv[1]) == '-k': kafkaserver = argv[2] else: usage() if len(argv) == 5: if str(argv[1]) == '-i': interface = argv[2] kafkaserver = argv[4] elif str(argv[1]) == '-k': kafkaserver = argv[2] interface = argv[4] else: usage() if len(argv) > 5: usage() print ("binding socket to '%s'" % interface) #initialize BPF - load source code from http-parse-simple.c bpf = BPF(src_file = "data-plane-tracing.c", debug = 0) #load eBPF program http_filter of type SOCKET_FILTER into the kernel eBPF vm #more info about eBPF program types http://man7.org/linux/man-pages/man2/bpf.2.html function_vlan_filter = bpf.load_func("vlan_filter", BPF.SOCKET_FILTER) #create raw socket, bind it to eth0 #attach bpf program to socket created BPF.attach_raw_socket(function_vlan_filter, interface) #get file descriptor of the socket previously created inside BPF.attach_raw_socket socket_fd = function_vlan_filter.sock #create python socket object, from the file descriptor sock = socket.fromfd(socket_fd,socket.PF_PACKET,socket.SOCK_RAW,socket.IPPROTO_IP) #set it as blocking socket sock.setblocking(True) #get interface ip address. In case ip is not set then just add 127.0.0.1. ni.ifaddresses(interface) try: ip = ni.ifaddresses(interface)[ni.AF_INET][0]['addr'] except: ip = '127.0.0.1' print("| Timestamp | Host Name | Host IP | IP Version | Source Host IP | Dest Host IP | Source Host Port | Dest Host Port | VNI | Source VM MAC | Dest VM MAC | VLAN ID | Source VM IP | Dest VM IP | Protocol | Source VM Port | Dest VM Port | Packet Length |") while 1: #retrieve raw packet from socket packet_str = os.read(socket_fd, 2048) #convert packet into bytearray packet_bytearray = bytearray(packet_str) #ethernet header length ETH_HLEN = 14 #VXLAN header length VXLAN_HLEN = 8 #VLAN header length VLAN_HLEN = 4 #Inner TCP/UDP header length TCP_HLEN = 20 UDP_HLEN = 8 #calculate packet total length total_length = packet_bytearray[ETH_HLEN + 2] #load MSB total_length = total_length << 8 #shift MSB total_length = total_length + packet_bytearray[ETH_HLEN+3] #add LSB #calculate ip header length ip_header_length = packet_bytearray[ETH_HLEN] #load Byte ip_header_length = ip_header_length & 0x0F #mask bits 0..3 ip_header_length = ip_header_length << 2 #shift to obtain length #calculate payload offset payload_offset = ETH_HLEN + ip_header_length + UDP_HLEN + VXLAN_HLEN #parsing ip version from ip packet header ipversion = str(bin(packet_bytearray[14])[2:5]) #parsing source ip address, destination ip address from ip packet header src_host_ip = str(packet_bytearray[26]) + "." + str(packet_bytearray[27]) + "." + str(packet_bytearray[28]) + "." + str(packet_bytearray[29]) dest_host_ip = str(packet_bytearray[30]) + "." + str(packet_bytearray[31]) + "." + str(packet_bytearray[32]) + "." + str(packet_bytearray[33]) #parsing source port and destination port src_host_port = packet_bytearray[34] << 8 | packet_bytearray[35] dest_host_port = packet_bytearray[36] << 8 | packet_bytearray[37] #parsing VNI from VXLAN header VNI = str((packet_bytearray[46])+(packet_bytearray[47])+(packet_bytearray[48])) #parsing source mac address and destination mac address mac_add = [packet_bytearray[50], packet_bytearray[51], packet_bytearray[52], packet_bytearray[53], packet_bytearray[54], packet_bytearray[55]] src_vm_mac = ":".join(map(lambda b: format(b, "02x"), mac_add)) mac_add = [packet_bytearray[56], packet_bytearray[57], packet_bytearray[58], packet_bytearray[59], packet_bytearray[60], packet_bytearray[61]] dest_vm_mac = ":".join(map(lambda b: format(b, "02x"), mac_add)) #parsing VLANID from VLAN header VLANID="" VLANID = str((packet_bytearray[64])+(packet_bytearray[65])) #parsing source vm ip address, destination vm ip address from encapsulated ip packet header src_vm_ip = str(packet_bytearray[80]) + "." + str(packet_bytearray[81]) + "." + str(packet_bytearray[82]) + "." + str(packet_bytearray[83]) dest_vm_ip = str(packet_bytearray[84]) + "." + str(packet_bytearray[85]) + "." + str(packet_bytearray[86]) + "." + str(packet_bytearray[87]) #parsing source port and destination port if (packet_bytearray[77]==6 or packet_bytearray[77]==17): src_vm_port = packet_bytearray[88] << 8 | packet_bytearray[88] dest_vm_port = packet_bytearray[90] << 8 | packet_bytearray[91] elif (packet_bytearray[77]==1): src_vm_port = -1 dest_vm_port = -1 type = str(packet_bytearray[88]) else: continue timestamp = str(datetime.fromtimestamp(time.time()).strftime('%Y-%m-%d %H:%M:%S.%f')) #send data to remote server via Kafka Messaging Bus if kafkaserver: MESSAGE = (timestamp, socket.gethostname(),ip, str(int(ipversion, 2)), str(src_host_ip), str(dest_host_ip), str(src_host_port), str(dest_host_port), str(int(VNI)), str(src_vm_mac), str(dest_vm_mac), str(int(VLANID)), src_vm_ip, dest_vm_ip, str(packet_bytearray[77]), str(src_vm_port), str(dest_vm_port), str(total_length)) print (MESSAGE) MESSAGE = ','.join(MESSAGE) MESSAGE = MESSAGE.encode() producer = KafkaProducer(bootstrap_servers=[kafkaserver]) producer.send('iovisor-topic', key=b'iovisor', value=MESSAGE) #save data to files else: MESSAGE = timestamp+","+socket.gethostname()+","+ip+","+str(int(ipversion, 2))+","+src_host_ip+","+dest_host_ip+","+str(src_host_port)+","+str(dest_host_port)+","+str(int(VNI))+","+str(src_vm_mac)+","+str(dest_vm_mac)+","+str(int(VLANID))+","+src_vm_ip+","+dest_vm_ip+","+str(packet_bytearray[77])+","+str(src_vm_port)+","+str(dest_vm_port)+","+str(total_length) print (MESSAGE) #save data to a file on hour basis filename = "./vlan-data-"+time.strftime("%Y-%m-%d-%H")+"-00" with open(filename, "a") as f: f.write("%s\n" % MESSAGE) bpfcc-0.12.0/examples/networking/vlan_filter/scenario.jpg000066400000000000000000005547031357404205000235040ustar00rootroot00000000000000JFIF,,ExifMM* $b1$2i VWindows Photo Editor 6.3.9600.17418Windows Photo Editor 6.3.9600.174182018:05:25 10:49:45 1http://ns.adobe.com/xap/1.0/ Windows Photo Editor 6.3.9600.17418 C     C   J B" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?S((((((((((((((((((u/_[^߬]YCޗhZk,$UYm+žu }⯈|[[NEA6oL󟆿?h? ^ߨk:q5ռ=yC)#UpO. We~o |>OjO,7r4PW,Bm3_^=5j5i<6WnA?0fEQEQEQEy?ß|E Ӽ'ڢ+ {+wc#(h(xHnwZ/qNA gb r{gZ}#ᶍq}knWRWS#\$H9К+ [|R?j7|I5ޗ-[AX[P 8 v z((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((^|McS0eԴmeq;na2ɟB+w?i7\ qaew74dgMmq8*`?wY?n=A{<ʈJ9,A9Ӵ7◉4MR6n+*iIgX44W7 s~_~4x[hwobyѕW%r2psɯ?~:Y7>/ƽ[Ll5z&LG,-(Y$1^78KU ɴ3>U(((|;Ꮉb4w,*Gs)8c`NIEsT:/]k?>^R,>#Ӥd7b3 nVpy5I__ ^ W-C_)whx|b/<%EhņWk砠7OxC$z+_ ?'ZWT4-:}Bh[bPc4iMŧ^]){].6 $.Ajcvk?}2_h8^auy⳹B9e~L9V~|s7&MA/M'@~D<7ݪ]mfL2/O,áy🋴oxv]kh3,J^A<?xF_x]Dyq湓坽dM~D&imSKO4)o3Ho}Og -gR} YF%'4eՊ*PZާ%Ʃk rd3d[&g'tW:]槩CsjXp-9߁u?{JOyKJXX"d}k !㯆''tOOnɪiܴja$.=~*ߍҼe;/?,kq,/ pWkXlb3&KFH g$3%>k*x˯.ښV*x˯.ښ(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((+3 ;']إgWug_Y:+?*S|b8HǮ(σtI4vT]rqgvd\m+Z/o:@_R=_oWWMg?9 ~`~ڗSt 2u;MͷP ?xM~|Sc| 3w5CN<⅑m/zy$($rA y{ |LJ4[Cu  M!*d<u -C_|Tu{Ųm->v\]B!?~k7߈<7}gFT F}dubj G9gk6onn_V.%@eDc :Axi׾]k>/ҡ3hXo-n@lnPH2cqW%xgoYjD %l.UYPI$ ?koCgC6EΗa/Fu8WH<xZVXDeų8`H9濜2L'YY[gimRm$q"G@8P_xWw|Mx)KW! (y68ݜw+z~|2c/W] L]Atqp'8Iv6<ր q-8x⿈<xȶetK(P 7y40(mx-_KmL/t[QqkOxC$WvZaV+0*+Z<;w|WGA.g&ȎDFswּڭi0ޡ}w"I23с@L /j3UHz[~ȿ\×}FWO>%՞u ^"Դj(Ɏ9Ô0qd8 2Yɞ"'}ߋ-yR'ZWW&,F0N g8l./ Vd>f 8G v~Z/?u+]&69$ArV*1u374i{h? FcWpAB~À/aO]?PkOmuKQ4r^,nૺn~S|L]_~;+o|5->E7&E8ut$duȯ/%큯~ּ%K|WdV[Y *Oyio Ah쟩[xAG/!N f"!L x-R:Z|'^"LoG mqI wbzdFO!p?~Y`zWI_į U೟2~-3MЭ5 '[24 ndJo!oIЯ+z??Ǿ>ҼcM? Yٛ;RuH^\؛Hzl־1x>ofԮH" ˑkh C'Km *ɬ;!_S&c܋P kt :GE.db+#gI+ÚQij%@C@Pr? }gtߠa}WL*X~'!i/lhjnRi7H:$d;ynp+1g>S#j:67wX*zޠ?oO&yj º3jZlx6o,r3 WO^3~ ZxVj.mϓwo rt< G?/wE; ak$"W-o3ąo;v |n7i9lanտ+qowt_l2&̙j~Jo? N&#FO}snk+۫d~ o?yV{M+Zቒm~_Gs:|i^ +P|'·B_cq 4gLXk_>8xᏀ=L]x~! V:&dr"6 B_uPU/`짼;k[xY¤h,ǀ}*~LT?n?`+l#:Q?Cn7OokqD#hʅ0Gg XTP"?%UU7Zgš=|P񞍥Z OBH@=־3S> xP^JǧR;2$P(=w;w/ qiL2(db8ʐ~WMX^ ^\}H06Eƿݠ((((((((((((((((((((N?sitpygS5?\jzYT+G!9sQ,s毷N?s(ygS5?\jzYT+G!9sQ,s毷N?s(ygS5?\jzYT+G!9sQ,s毷N?s(ygS5?\jzYT+G!9sQ,s毷N?s(ğy'NI e"ޔPVP6d[6 d}-ZC??E(?# Zcch>Ck!}p-PZ7L6>|(մlwڪ ё#+?zZ5x7Gb9O PK G_TP??\jo(f[ 7:b"fSq'nqN+({OyQiϱnס;j~}BE* ) M׆o& i:Mc.WN?s(ˏ)734IqMh26՝ e"ޔP?Q-^[iV "9B.}p-j(ygS5?\jzYT+G!9sQ,s毷N?s(ygS5?\jzYT+G!9sQ,s毷N?s(ygS5?\jzYT+G!9sQ,s毷N?s(ygS5?\jzYT+G!9sQ,s毷N?s(ygS5?\jzYT+G!9sQ,s毷icD9/i7y^|Cu_oPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPE|?cx|YuqZS[gܜp;s.ƨ+eG\s.ƨ+eG\s.ƨ+eG\s.ƨ+eG\s.ƨ+eG\s.ƨ+eG\s.ƨ+eG\s.ƨ+eG\s.ƨ+eG\s.ƨ+eG\s.ƨ+eG\s.ƨ+eG\s.ƨ+eG\s.ƨ+eG\s.ƨ+eG\s.ƨ+eG\s.ƨ+eG\s.ƨ[mB ͼW1>\sT ,_.~?W?Կ˟u/5@c)OX§Cn-⷗N)T `~2#Es.]KQ H\˗RT?"t(SC.|q H\˗RT.~?W?Կ}#P0+_Jh G[Ҡ1#c_(.~?W?Կ˟u/5@Z3>,^hDtlDj溚s.ƨ\˩ᯄo|.?T.. ZYY9,Ozmq<?<+=]IiIZOs.ƨ\˩z +6:ޕeYgq-x3 ?i+r_jeG\OYiv6mdbrrp5_.~?W?Կ˟u/5@oVLѤ&bij3[!$W˟u/5G2#Es.]KPh?,r+b*l8?i+r_jeG\?i+r_jeG\?i+r_jeG\?i+r_jeG\?i+r_jeG\?i+r_jeG\?i+r_jeG\?i+r_jeG\?i+r_jeG\?i+r_jeG\?i+r_jeG\?i+r_jeG\?i+r_jeG\?i+r_jeG\?i+r_jeG\?i+r_jeG\?i+r_jeG\?i+r_jeG\?i+r_jeG\> _hw]{qZ]|'!+/((&O ۪z!itp((((ZZ)x1HL/ޠQE(Z):LpE4`:m=RRRSzҚ`-QM(QESq覂hQMϥ:(E3) @:(EPE6Z)J~)w/ދ):t:oE:xe}Ҍ{pE7IzSS(')h*=٥c袊(QHQMuOjuQILg":(((]>#7n 7ޠ(((((((((((((((((((((((((((((((((((((((((((((((((((((j?=Ӕ|CQ|#G(((((((((((((((((((((((((((((((((((((((((((((ɗ5}_L(( 7ޯ]>#7n(((*սwV I<S׆y~)xľ*oDmyki$#8Pa?ypG=j˅RIM|– \q"4ŷ9bO<8+~|xO`Jz|zUpգ5BWڥOMճRNx'>ĚeϢ\WjO)ՓObl@ge >s]6~$ŷ[u xB0_/oKz..0FPg tVXx$N|_+jiQ׍Y)k?g}b\+;kHrܕQzOjMҜ;0I54K׻M%Щԛw۱׆>*YxnRѕgWhW!pzg[ď|7z:dJƮ I!B"۠<ֽi73[OsΒ]۸2qZPҭ|Ui"{%fѭnhpI xbnǞ:ib*M{uZ)B+G&zoio~;բ4ycOs$\zoWX$̊0G$qng7Z)g3bRųܜ"/cEVzo*M^GQX>-]mS]aӬb3`v{[k㏊߳~0nUkx -*)۞X`pQUQ;jV"Ϭ1+OxOl q vFY5k=NP ;DQܓҸE`WғMω^_ GcMm `gc)NT:WWܙpg[qMSc,=z׮AZuk2\[LH捃# ;/|)<5.dKDbXW!7\OYҼaFfo jM lǢ3:sgFj2pGdLjUE |ǭxׅ>[/5xt/BAl{&jD֕ [;03’x'y7ZfO C Ӵ}YSWki Я![(Fh9B 7ҰuV8I7ozKt>5''k(6g _Mkj\x=oH|!!%=:iQZʊݻӪMKړῄi4ۭϺJ,d$|%Mǚ?Ӵҭd[PhiA1#:y747Y^x_M(7pYrwLv a^գR4o[Y;9=Zn.V}I\ω"xjf'= ϕeZkos|D}6 tۄ;7Lϥp說s/QÖ1ݳ;n}ډ # ''q^Ybx7Hc?^`ťU+r~W#߯K ZQmto˵*ε4祗O'4FV~{v1usaѫ7MyKӬ[USʹ''o@?kU tάavv^ךuUԥc X\3( ܩ#q]}|u<#o$r鷖# K|W[cC]ӤmfxjIN[EW % zS9[<ǩIc`ŝc uzzҿ5IN\ҳN}K[γ]D'!F@zg$q[-N/UaPIȌO&jP~Y?\W9|F+NSMG[rnvpЧZ4;%oGZ扠L` R>Vi$OY}VPyᎃkatYo7I, rnjsY,~25k$XEԦ _X Q^GiC K5&x4潛6RXjwWףk}}o=2Hps Wm79[:߇Ew `Y /t+ =洉͠+7Zs&ܴVN?VKY[Իoq),2,ʲ=Aן!Q6H-!f5Քf4ŵg%gzҥ{؍Xб`i6S ͹FO`zV'wW}ƥ$:Re9vOoG‹X56 E\ qoC(QoYZ-.:ӨyF[kzN?׼]xp(XFf#{cRGӮ/&W l+ xTn5 . ȍs?,W~gb))0VM-եމСM֭~Uoh><8kSV]Rѣ:՚Vnݎk-fn }:3E: 5֚՚\YN}fsv:&@%fPKrsך/"/x[xLJ*cyDL\q_S̩bp6\P:ΏTgk:Ѽ;0 *5.뎟X|YxK;'B .cKmjVFc,q =2A?P~VXMܪ8Ww |okK ެa$׻g̣'e+Vp,$[WףkYƱkYL A=Nk@r52?xIF~t;mGW8_iE'RMF)#QUZ[$G]{ ?M`eZL9/\c5kC÷َOG+WT͆}guyUq)$w_z ]<3#mA\84pQm]n|fV3MkЫPA<)kYy-ׂtVc@c˧Z* ^qO]s`kj̪{qR aէa5셹=qWSM~$X/yq\M+y93z5j!7ɫ멾; 4nc ( (>!itp&O ۪z ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (>!>#NRG'rޠ(((`kN9$@&Zi(u{ʣQf-y֍"O`^aKpG=+оZr S\$_mROUf\uMcV݌w?e;ec#?ޛUu}/v1[rvu;=?lQ//Kt}B-kk#ZK%R2@~&fct|:֙n+ޣZ"kRwLe 8YW xP֥|;d4u?j7[%ܝ@Nzs~5Rlj7~^~W{[mGrVqoMA/t;A4obp_Eі"b*pIGvn'{/@&kR%9sd\k7Ins[euv f:$d[z:Sfդz[IKnuFA=دwex]3%NNVh}Y#-QuY[9 z8wƸp92Xg.W6AEVWC袊`u?ZMƧcan $H 9;-DJҢ[oqigXV48$[}B3+1hۛc uY%_>8xŖ v$ l╲0q/|P[Wk/ kR) c>]|D5_ kCxw:Hޞ[rd"@YxA 05EMkNm%)\~&xs᎖#OBV zaW #*劻gL#rsM|Tkicik9GOpgOaPaadUYs:UY77OZƒ5[RrpT˒#$aH:+/|PЭ4kHZ{o+SnXF kyksS^sZw-7ujcMeQGvf xu/>2W~Vk7|VS5šTbEvnFOW], jR6~*%LT4NXmu %VPE.[heޡw*ik4Af' ;꺵Wx}U [iM+YZXiX"XGf5Qi~ t'Z~pЌu qقڰu9ҳ-NoBɤ''|Ru-5yWlsqGn99}\թN:Sh~ ͤU^+O,WETH9f?oj ՍF@*aӧ ^ МN/Uj(c`((ɗ5}_L(( 7ޯ]>#7n(((on֟Ht)[Q3؋EvO>/ů7`ܷv,ɝ>^eȣҽ> qj+gH$W:yOtտF2¨c |l.#]k: h 8;Ep #.q`H=p+𝿊:+m^O[ sl0pHXW5V.?wiI,}6O|Y䲷ԐYh[PF_yI~N%*]% W3T jHSStﯮ"BNu$mG5ڪ18~:jxd*o6|TP?tf0|]I/+Mou Au)]Gl71zޓ>{++toQG`jxyiƴ`6.Fҫ DNRVEt[O :V1(=`~9мEu3TH9{ء_$ />CB9-ep G x=(+E?@$GW_%c z!_9&7WKל~?A4Ou[_ޏYljS[ʶ^XcM}V~7.]W;(y+vESqi~ x5}*6QjIb=C]?ho°k/=;Mo(5ťE4 TO9vvƵeui{}4'5& ğ |[~ޑbI$6$p+|~<-oec1#PaǪ6m|i~(xĻkwßg^4T##$秘cY>%o Ǻo$encq ~VO=s]\R`;_O+bztԭ+O? FHFn#GEC|ca /1cr q^cc꺕v" IFN> ~Ϻ\)g@X$. ݆Q Ȯ B[ Uh^}N\J4ލk{%yOI$/w~fk-ؽn1LpH A\'I$ſ?pa"㋦κ?r~ x+6)4b?ۋ†^7hasW|3PW~V^~)[A-Ŷ7ص,Nrx(^VY%c93jD-7 j>F)&Ե_y5}>;[{k7 `d6><]Bj,mAa"${ٖe֝Ek+oue`ko}yC1]Kƒo|Q/%M'No.G|xAFp*%V5ekwfm) a18=u(yԯ5pU࿂ğ{.>?LWܑ@2Dzqnb^ìÿe.{ic3 *I+Ϣ?EXA>zIp+ SC.[޴otTz[d"_'-.?*ўgtSZ~CC"ʠ`O P:\Wt~њ+T3ZfiR)_:BbyO3 FxY՝Uez$MjFz?Z޺-p3wc }_wJF]RB8ou]}s.Ǐ|?F?5a }&fچ~Gq?yZU~7fIl`RO`95E~־ }nsY ;/Q-?ww5ԬCCiܹ]\3ם:tai qC.8PlBv,=\TʣWyG3CjRJ)՛$i?sjmc!ZR>Hӧ' ýRK5_3PHϵ?v%SI[=Gд[˻ۛ6OdIʜa߇ZuAӔg/Ku4Ҫ16#=xc>!i_IɩژLc ȮJՎ# %Ov|viS< u!9EBvIv֮1W=L=vKRM'w]A 2֮$<6n<ɤIcic7م п }jօ/ {OPT|͞XM}&eGTƌ ?MMy?__CLFw3_H#E}D'j~E 6Z smh!Tp nI»oxvN$3@k>2M:xolx!ϰv4jq eJԧJ[E2m=I)]ISjuq}-Ӝm=~Zv;B_1XTg$snv:촳Kpw9Wϥ?Œ7k6,~CɌo" xU+JFuc'Zoz_Srm[m%aޒXs8wѼOx+ R1Gy#[z$ko1ƃz\s|i*G|oIn{v`7)T-tdohN-z V͝}forߋIUE ý? j {W[zdГWᥒbjӓM6iEuѫ8ʬy#[YLEyΩJdVޅ'Ra i6)d 'zV/ƺe~mcw?#}{WViC59Jq1߶OQY(zjm|#ZdsWk7 OtJ/.I1 wt]u pGnspa1Xy,(bf:sݦVtQJ5u(_MQ)ScJ>! vOJjG-4iR~ciya_ 9kxK #g5ߗ<lUIu]f5sceJRAo!^ޞW?\|j; [IF&Ζwr2 =y'C^Eizu+'Er/jqXӫ%rOMgl)Ja*^[wnyIgf#7 \o[y5)5?\%&"-?"6Ch֧/SBɑ9MuzVZB11(qo 4n%G<+}fnbY#F 2Yk~>$|{sQL֖}sOЮ.>ǧ2\/:=+Ow(mo`S^V;3beQ)컾#xzq)j/7kO@|{rI''k^޾BeˑҺ[hlCc8V)eja_J50W?*r[twܜ=JT7ZN5%}:Z =z7ҼV@U#dcn݆=q+״6-'O&("98 VN68J WeFcGW|]QE~xaEPEP?M.U|Cu_oPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP??Zz!>#NRQEQEQEYڶay?n[E t?jRV5hAӫ(b)AEٞqu{MDUD["]"]+ɧבq_Ԗ>t^KH2__<Ѧu8%=ыl[h}rW5?O%ɤ\Js"nG$dcSǾM"PVnXbgBIs_.mL>Vj4wSՠWNo9͜w"OQi8ٝ*J\H _<kiu_ɦx{w'I.<}zqyԩӣגVK<*Xm'6`OՆP.,$gXBKKk-"¨g]9.yn 4*JovW'YWJڙӵ+#! p^sCêLj%z%y+(QJRe-μ$*Uۑ귣M.Ȃ&'W':_ԵK/u9)g 1^A9>x&zY1|vuEVe%XqƸgauNRNZvV[ὔpU%RV[tzt1dU_5CA4x%O:[9DH5?=CHl[{\c< PԴ} /3J  fSeR9btRw֍ԫaץ NN.Z$y*|}1ҡe_]D`mBs:AQy#>|>B.?XIṦkm1%9U8I/Zӎ>e`ui55З%-7~=<ҍKmSž2:Di|Wz({jv;dVxhδ ̟MS1׎3?>Mr^CEA?/mobNU27__fc=Tb6=3x;ݶV:/ Ǧ~GT2O|[g^G7%n9BbxW2W~y t|?ѩR`ۖ*7U+nUeo!v WF2zןdYdM˞^jԪ,sY%oRۑO\ LSH^6d'Y#Q}nkP;gcrײo{}Ď q~)^zgA]N"h~{ؖ_* *~Yur?5)K(?€ Җ9a2)6󎅨LB^2:T:}"5,[h:t׷==ɮ"?S4w:}?AS+5$<ޅy5Þ[/9>sEjAnEZAo"跷E<.HV|Lt=!nv|hX?ʵo'4{8YqQʼA机ƹo@V#O_)S&uy*MYeeS:LR{w̻q߈7>o 1.4^o4덢kvfPX02lBn.M]Y-~^qZEz NJ 3RU//mVś}ObHj_6 {m0WЋ”@tvyǝ=͘}rz^uo@3rFH `4sX?ߊ,fkȷ,!wuP֧[rYN^ҍ9^3eVM{m7?,r*6ky(tYtu$Ivr1+ȧe?M_[]>ݼKŲgˇX=[rza|]^Gh%E:9nKejii Hu?(r& H3\_Jup >~Ou;y3]I=ŽORI=ϽrB{E$ͨS"-ަxO^n ( ?}I| u0<*>5o:WЍu峗qoNY,>/C_#xQF&dO.V,Wս|-<5ouQuGHy$6^S *_DHROew9z]_!J{fr{rs~ |3g"y{lVͅ2) O`JOp?^MC"Z""ԩ`x&KB$Țmof/e[|:> U<4fV+7+Y{k 3K0gi GH1Bi[y[Պv] L-s^*y\M n@h[nPGLWr^QRVjPBCF `( +_|OګTW h|KC,ʼ~Ƀ{WFBơ#QUFcOeL#4`RX~ 'k鮕nX-hVV)}s } NrzYSo~HxOv3]ߴoe$].ÄP[~ֽ&~xOC,tg]ʧ!v?4irI-q<~?:zW^Jʩs!fLk\dqo~PJXysu_.#CY=Gª^ ?ϋoR?ل2 طIit%E|£@'񬏇v?h{V?}0VJIҷcujo}BoK {7e %`YXA+g>֮1;{}.{6Yd`%=sw?iۭ'!)Y@ᣅx z}3ThO+EnvNh$?%+Hqܳ OFQ'#{W_k`$J~y?v4''i6mXÖbtݎ?@@woxq+ə4"O: e<ϧ +1M>H%V_7RW<7=[+jV\ǕIpA4fM'+?;o'CbRqMuDڻs#}[>kMM /i s 6~+|}97Da|*!|&g)-_:f6%ͲQG`*(_]rOp=:.9;F3L[vH" {~f}5 MVnAvX: =:7oγu&MJ傳Ы}ii=K= # oxwLX[1d0$=:7oΧۗVC^s~-Y+tP1JG=:7oήYӿ#jē*iuc VvH"(x?D wKmA#8Sq[F)J2[-r? Au #6wG9C#}T; WYh^ ntJ;+1o2WS؊{~tnߝ\Ԝ&ؔ!d{Nki6XV+xW OII4oD6a8[{^@?hߝQ.nkhۗKMm4m>;K+tC*#P0 }i5[L::G[FһÍ7T4ԹV޻.}B1*?^qSo΍'VO1!~Tx߆ [tk]U#ϖӧzq\~SwBQIaL˜uFy=Û7<'xHK,!Ԭ$!# *cogi [ۢ1(TaTZ=:7oγ*݊os5π_G>xRkm"H,@b{kt=MMvmkƊ:T` ܽȣ"jINMDi.I;#ι[*d oO_1$(ՠ`HF=kz`S (VU MR,N#R/=ZZEč,h;'MW +(t{m_С:k qI\m+xPԯt2RK{4{g#7_RmݻI+!)hŠ( Xuo5vFʯuiVP iE+맘ܛd:TAUAnZZAcnģ U@*ˇY)F-K4j&ff;Zd{Hn`P63G]9:Eb!wTE,a(ڜ9v rSbag+&W7BS)ɍZ݌?Y7vqps$9f( G|QPQp\mb1kER,'8Ƞly?e#( G7V`%)N7[j!Q]suOh{>)f& #jX^=AH S.$;?̨SSwi]7K-vŜ@>xxXaE  G7Q o|)*=ʻ7+x~9t *@0E`#( G7V8wa/~H{Y~ET&ƛ7Aң;KXIs.7z| ?mGX^=AH& PjEBJ -/ ,RmkQa6V@tX^=AH ք-{Y\S&Ƥn^nĺd-#}H۷aQ?ax{M#*heg*9"Y\%MDy}jow \@REVnH|Q9} ? )G,/R?Šu*y%5q&1q\?SnC뵔QQiUj-[xT=Md#[( G7WC`\G(-/u{vu i:eW06=29:feۘlm'f#( G|W4WNGM/hqP|x~Ǔj׈ikf7I?y:unһwiş[+bR!Z_ލxL?#7n 7ޠ(((((((((((((((((((((((((((((((((((((((((((((((((((((j?=Ӕ|CQ|#G((((Ѯ⍕- 4݃|~=iWu$ߴFZyhtU(X|qSۿ+#,RK#~RHS:|A¶[qS@>XW,"eʤ"Jk&t\aSQmYoCa 6}_1cV*pCH]6+(Y c9I%V^21ΚW}z3߈5V4F`XˉoOBORëk )O ,{%\F13]/G^<"4xIWoݼgdF)5M$խ{%q:.wĭne )H ]'9|195E*ҤVSeRR0Gc^mbûH4umI `$Ys߅sfYWק :ua{2=pyznzSm7J1 ItU k+e}8S̋q@]G\1E5וWOO%$'\J *[v4=[X5K{?p-}9ӥq{xCF.-Dr;(^QVx̂242R'$޷մOMK9ZVקϩi⎿?WKuM&bbǹVG]l#R/*њRQ^$eRd*pQk1u6t}N8H.-A^=1Zu\xZm yvOR}qJ ? 9'IU*;.V+t6Rq]{ \YjQvMF$_N}׋ jRj{i=Tܼ0О_x+ 77׵ogȹ[L jRl `,,ӬvE U`Vsx^OH+1's[;OQ^RF3B;}V.)Ce?ajwZ:IkiSpӗ^(U|&6WяjdeMԵQ{+-gD1TJto3Ʊ?i#۳sߜlκ ?* 1Zn_i59F<8o跗0ͽIoneu+ֻU.(|[K^z{i-.iU ʷp+9Š(lڏuQ1RDJ8S{FiS]O"kžD5/hm47Q/ c+t8kϵM-'[.e)pUX~ r_N]jN4ki)>^ [OtY 7.Q⯉ e/+n]7Ay.)O j~aF?t1'JpRM[L?>qw7(A0s1^+)W)I.q5s#ǂukkXZ~GT '(kV.5QY~cҾs񷄼aơz>lH1$yϑ6Uaz]5fT*TQ5/ߴe}9q"Ij:++; 9 d}W7?ƟOm[1Zz|2"x=ǽz^(좢߾k>|ߊ~!Qm1+P3VV=,/ǖ1kaAh&˹c b+|QZK5Kv+ ita'.{[[ΰ{ϗthK}BY>̠Os1^mliz½WW NÁ#Pz9,_%5JwWuC|<{[6o~+x񇈧M&IXpI't;ru|;/5 QxBIgm`elP]ebYsmƊbO*xŞ r@-6םQN!0$VGI+VNx5=OX%HYYT@>PI(tM1&OO_2t5ibv!#>Mh|~-tc٨Iwϓ`}a?> |+%_~}^ѮVp'co `8fc:{5FU\YMOZOln5 ١Xp~(^sω?]+B5q[ tX]ݴr,}ɯ+Z8N5%{Ѣgr{9xO׾.Es&^`B:^z kHԕU_7ٻ)YxGX“hZ|ۡɄ@lWq:mn6ĊBKNU*1M%J1-QElQEQE|C92?ɗ5}@Q@Q@4|FoWKo}@Q@Q@Q@Q@Q@ n/;_zG'40][H򏼑 r38oHxYrX;}|v*LBJv~{|:O4wÓ I 3M[[kp_? ?}h߉:jG4o5o o7܃G??~&Kѿ?tտ%Ƽފ?p_?rgHC-4FLVz(Ϙ?q_{=#7g 3M[[k7>c }h߉:jG4o5o o7܃G??~&Kѿ?tտ%Ƽފ?p_?rgHC-4FLVz(Ϙ?q_{=#7g 3M[[k7>c }h߉:jG4o5o o7܃G??~&Kѿ?tտ%Ƽފ?p_?rgHC-5oL'7G|F[ זQICY5;3zO^m55+#X&{;}/'k%*\2GlzzWAjQo+2?.ZůŢ+珤 ( ( ( ( ( (<pu)n$QlOכWisF?*,c=(ЍQEEPEPEPEPEPEPEPEPEPEP[׮4=~٣vK"4}IOZzO-?1]Z2RR>)iZl~~QE0 ( ( ( (>!itp&O ۪z ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (>!>#NRG'rޠ(((((N5ïMckm۹AqWL n.8,<ϡNE |Q o𯞨j$_ϡbx{q-[+(Z_&3_X<=A8 ?֬WG0ɥ''G,ON?¾z?i3> |Q o𯞨j$_ϡbx{q-[+(Z_&3_X;A8 ?֬WG0ɥjT!Z\Gq`:5ςuۍY9N:z_kk2k5b ;^ZZ(t ( ( ( ( ( ((jӥm4Hp2X@|w(Gٵi)bbF\Wu)߀z@~q_$Uw 8\} bK[%V_.o<;ş.i?]йrumS>/6>x'4O.Mg) .m?}e_ O'G<;.i?]|ES>?\?4O.xw\9?6?՜|/<;.i?]йrum9O_{0Yso/xw\9??+sI(Vra_йrtüWBɴQ?}eͿ+sIx'hY˛YüWB_ O'_&Gυ6>x'4O.Mg) .m?}e_ O'G<;ş.i?]|ES>?\?CG']^֠|_ÄMBBIoV 'nʪSq.W7))S^e٥c9Xу+  Zv&> :tбBGz: k?Ud:(((((ɗ5}_L(( 7ޯ]>#7n(((((c~:xGM~ſJtMK_?W *(a? ( ( )yQE+ ( ( ( )G'=E+(B((8xcѕz?8xcѕz-OE# SObEمQ@Q@Q@Q@Q@Q@sF?*#t?qunmW՟uw ((((뚕OoL8ʱz _ _qRWTݽ =-stWI  _ψ'O/}7Et|G@??\K4}C>1b讓ψ'G+2OƏbԾX9+s?I}/  _/):n߸k K&.;p+ zlBR>p+(*ޓ!KOUJR/N/E-@Gd~zQE0 ( ( ( (>!itp&O ۪z ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (>!>#NRG'rޠ(((((!!M? v_4*"|WzYwO6+Š(((~EPES(()QE((R>6!KOWkҿI?GfQEυQ@Q@Q@Q@Q@Q@<Bnoku__W_xFK+bQEEPEPE;hۜI%Q^(^(^(^7{Rɧ; (QEQE?Vf? !ּcOZ]k_?*?QE瞐QEQEQEQEޯ&_QEQE|Cu_oW?M.UQEQEQEQEQE1շW㧎to [oO#qz¢+((?ew ;Ҭ{tb` 9=:s|O h06>Q<(__$>?k o^1_Z,+ڞ#1V5#S)79su>/ ߸/ ߸oΏ1:T/8/GOR[R[+?''KRlH?7GOR[R[+'$}hT?/ @Q?G7~7~1:<X م( _ ~qW?~H5M.tYĕF^  sGD_1㿽~TixX+#a2<ߺ}og\L^0KyQE~ʶ?{QL(8xcѕz?8xcѕz-OE# SObEمQ@Q@Q@Q@Q@Q@sF?*#t?qunmW՟uw ((* \kQ7#LQTC1[`r*| -z iMy,O$jUvnj\.3|/&]ׅ9mb*-Zݏ`b)TZ/]Zg]G.3|c2f L+whզϕ;5㵩xcTխXc,ziO?̫6dD,]Zg]G.3|thثVS0E%C,;6Zpݏbզϕ;4?)x_rnWQ,d9"T,pox!KjiQ3aGAzo.+^kOUe(V<,TEWBQEoIu_*[)i]WއcꈗϧZE#?= (Q@Q@Q@Q@4|FoWKo}@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@%%y xNg5 j{w(H}WZR5;&r*I.I\JZ+7R]j:5]9wheˡ$~o0}=+lVXZң-ѝ5QuIǃmVO.5ΓR=%5~tjWWwzX+9X(nu |u_ԵI4^LR0R;6' kUb.˩bqV]\ȥ?"׵O (idU$NJ9_7*;/34FG|e~֩]^=Lv9 @v`H | gŗزݜPdd c9緸jj%QF7듷T'tW wEo EiTun>Lïӡ+?iڷ<^ Thbթ_{+sBpW07ŊyBхvs]/Rxjn*Y[_sb%V֊JZOD((((((((((((((((((((Gp9K_oW??Zz ( ( ( ( (^EBr?zyƿ iU#dwhզϕG~KA1ψ'G+2Oƻ]Zg]~K4??U1?8W>#d|G@?uiu/ M+Ə1ψ'Q[>6;pN>w_td,x"%&2xjގQbWn_/33MsN #玜 |@-_,`*7`z\|EzNYS{ ((R4 ZUb^O >k7EWߟ>QEQEQEQEQEQE|s klO۟HM[3_g~_-? O/͉EWQ@Q@Kο ]oTJҦm s8$k5迱Gc篊ڣ❦{ ~.XG@ܯ#[:ͱxZv}%C&ʰ8z.n]Ϥ>X_EǬ/"bC_"$ۧ;1 WcCY|WQğtڼ7@Oü|!C?>X_E| e_ No7_"$ۢsQ B+/k8]ߘ?~,u~a(O _ڼ7@M8&[ρ.Lj}~uM8*ف<k޿d_:c+>!S,LFpUAEwoO-cbsL&q _] .W'`r8~^oQ_Q@QI skcOZُH?F=kw1}9~lܨ?EWzAEPEPEPEP?/@jz!WQEQEKo}_4|FoWQEQEQEQEQEVO_:_3_oҿo^gY<=Xkzxٓ៍&n> x}j$oRVH~ھwQ<=)|DZԾXog!5#G-K\!0s存)|)'?d ,:/nAhNzc3'goO{nu$jVhIuO#޿L ZI;XW| DE,x|/)xT- KywwF|nGfj>G_,'ʪIIZW 56U u[qtaI*F=Ҽȃֿn!R?a冫*SѢKo?~|Ti~Z[__?ڵ!_ſ/~>_EWcQEQE}wץ|!ץ>qoz/\#"~(>((((((𿋟7I\W]Iso꾬'=QEaEPZ]ϭ{OtaU=Duu?M' yYW_Rƾd8WW Kos O dqkp&(;Z%7#%Pv80#!cFW75o L>/opSzc;OXMR94GrOx<6Ms$;AN])H?zβq?^v M2oۯ+0爣Nܪ_֞j{<(Ϧ=w_5ڧvj-V_7j٫/}aQEQEoIu_*[)i]WއcꈗϧZE#?= (Q@Q@Q@Q@4|FoWKo}@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@ AZu%|[E~z:6 cM}OŽu+6qk,I \7⾆/-|a'' ? hŭ~a?sRw^_.sx7ᮘYy %o.2>_UZ=t; *<;#=*W߳mhFFkK7agU8 5 NdiaW{^S菏~χ?|>'k1FqJ_`> P]\io,: J]cI>ةk?k}t[O"6~_e1ݳEJ>Ͱ{׌>$]՚+|R0= 5S '< +  W|1_.-?諸Ph^'u}"SFH"/qyF8oϗÞ|ȷ@Z2iI&7ͥ1 +?ly_1nacFGК}@_?WϿj;| JuK}<"⥏ߩ) +v;٧xw!09-12d  5Ϗo76"~9*Wȿl3+IvWC+?ߡg1H4a6$ƠYElQE!Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@{)kGp9K_oPEPEPEPEPEPkȾ5SOGB]"!M? g_?U[ͨt0((|"Mk-xx:ȓ5Ж!t/Q#($@3^C J.}yٗ'ou/WKGKsUu?3z +:( zO-?1_MJIu_+_1QE}EPEPEPEPEPEP??f6z$5{|"'#4 ؔQE~|QEQE _ ?z?j#?M\/F7[;Z8FGT^Y~d~p:ZE$gv8UQO؏ľ9hu/ћ eܫg| "JoxFͿoG-}@ō嶥mդ[ʡ pE~[Mÿe,^ᶇ|?x\J56SGIOƥgĺ2卺\K XZFDdu8ea;yuoqu*[Aye`:OWߴE׃>:S<+?Wƽ`Ijߪ>8+󀢊()=n~~A^CB5^a1wyק.?ﵿ/͟yoCH((((re _oW?/@jz ( (>!itp&O ۪z ( ( ( ( (+_:k7k-շW㧎to |ZOʸt=_aQEQEQEJ`ѱ(gFT?W5C?xF_J&?KK_>Q+c U:{_zTwA\h}#Wȿ_ =o<-ҧ7?QWk7S=E}ky Oo*O $+)AE/NB˼d9{bφtqi5m# Q}&7??A)}mO]Ԡ,b4tdYlEjk.˻mumdp(O$撻ծ$o,V\c{IkI(j"N,-VeBv{3Ҽ{.k쎌iVimG: مj%|EuSM[io?~Tj|`wg}Q_^ESQEG׌2^G׌2^ſ6$ApiZ(<0((((((.'rO\]v?n'. ͿߪAEWuQ@hxCwO+>Zo߲u׋4ǡM3:5)XTP3|]u^.8lc5YFKT_#ˡ*9[? м4>M@% ;Ucykܾ 3Øu_O/}y[rM6?O'ux5[RB,1/&X!z{6tp3QM9%hm^51lc.4Ȭ4ˋ;!uJ9 Ab {~nxvȾ؇37<O+ΞX!ǫ5O~ȣ_Ea/i_C5k[;9XIn}`JfXPicp3#0`W]bTi-];YƯ%EV?Er|^ގ"cSqSs =H\ҴkZTk{ZoTz>Imc^w|!$> g8vKF}Z2vGIW^]M_,.oM&em_z>~д[8^h T {WIٞfZuߴOQE'4W~;bk}Lmu(4װIYF`۔ }+CJ_g2Pc0=pA'f]z X۫5[FKyMٿ0K;x Ɏ5B}pC)d)tkFz;ΟdO[xdn#*&#B zWok҈]/;>$ +:jkL}drrUYU茄 uZ˾3Y|Tllri{6!I#N {UgN߉FxTy4bM|/.c1ˬݙc0L1Њ~~Ϣ?hn cz L@s_^i6]%,QCTP0\Əz?ƟGcZ_[{-B4 1 er3#5<2ԫ[ਚ~K]L,ƞw=tmYyT 'h㎵e7㾏b5ě4PyQK')1\?zWkrLjoF.9i#9؃'j,FO@2*Ѩ+4~]ɩqJ4UzRExQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE|CQ|#GڏOc?-}@Q@Q@Q@Q@Q@ "!M? v_4*"|WzYoO6+Š((?}:s_ k]׾DG+?<<ԽT|EWĞ|?ŖP 2k.}^7M<͘gu r#p%ͤ]O̪.U~d2i7r)*u{ƭa;~M.m tjU;Ov@Q+ݧO<=Zm8$*<Ş PWS}sgL++(J˔ɺ)kW|-v {]e0̿>#?j_8_+0{'~AEWtQ@ ZUb^&!KOWkҿI?Gf>cŠ((((((n!7_o~mIs kl+EOG#+(!L9nOݯƗvep0ۮ3=XMb~E<?]'>oE1?Z`.?w_Qo x;Oֿ9%U/$_/QEIQEQE?Vf? !0;kֿs<ʏc!QEyQEQEQEQE|C92?ɗ5}@Q@Q@4|FoWKo}@Q@Q@Q@Q@Q@ m5C5+_:k7k>-H\y:0|(( %sWK3JF`dPŨA<:[ v@ }I_>9kZ^iZcT9~.aq'1Gհ:>Ysw?0诽?Sq ϩ߸¾n,G5EGFe^̚]k/wAw_mSɈ\MҮG^%ERk+c;NBs>ce\1l_z|K/ ajqP(J3&?Mx_mOOJaiVJ;+YiM|1Eݻ]nwQNzW ϩ߸wg^ߕ;_g"|EuSM[vFWҾvFWҿ8F=.M?WEWǟfQEQEQEQEQEQEx_I$Uṷ_V}(N({OgF 7_9o=DuO,qV PnTikl/U^+DO*a`|6C_Aoxg~??3?Z;^OKoSLi5j=V q0B7ݱ=:W ٲ-#)?»#µ#ַ`X[ZIYYIm>̳X? :U76:cWJˎh=CFkO Շ;IְWhL0~ <7uyn?W/}j9 Z5bT+:_)iZ(`QEQEQEQE|Cu_oW?M.UQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQES1/SKsHSh?j1NPKL'ޓ4m3E --7? `1z袊`ߥQM(i~44E'=E((((((((((((((((((((Gp9K_oW??Zz ( ( ( ( (^EBr?zyƿ iUl2evabI@7Y)#AxN'LeaN]=Jb.%E q°cPOy3;4r3R;JV;5K3<]֪Ȝ}Yq vCFkW՞:tF ^[@Wm>ˣJxq5FpfM}]C#B~#?jlgn0~H(΀(ޓ!KOWkҾd)i]WmzW<':uQ_~|QEQEQEQEQEQE$&ͳ޿I?n!7_o~mȵH'?d¿6%Q_QEQE~n~rG=~ixF GB5[g_ rvUaωwڥ&YYYs_dx6 2%O>jk}V[<ڱ~GjT/|Qv~$e5CRy·^m %3WZa/ǗWZuNԍZbON练S'ufzm퓠1=CwpC%b=ď^(`dASk~&i_?aKrN\wvz#7n(((((coOطVO_:_3_kG?*EWEPEP/Tu+{>k+wCqxt Jkۂc-lOԕ漊㭃u?5s3VQko6M~chCd?&? SVw2=N޽'<_BO$ɳW^9~β.K;TJ/r;o2xH%xD `oZ/px(m/[<+Fb ;1_YzI__nT Q?wv8(k_ ~^hbYM@#ƽ޼/ז[8manF,B2?ziNLX៾ևl m[ɿMt3CMUh5_?W-my/amYz_S>eOjSߛm[g15%C#׮hrHX{hϽqTWO/Ғ:1MuILV.*ɧѶQEz'QEQE}wץ|!ץ>qoz/\#"~(>((((((𿋟7I\W]Iso꾬'=QEaEPEP0ŨvӱHeQ0 95"J4Dʛ} 4.w u`_gPji> 24r4}q~{).i/SɖcJEGEm5hֳ~ ӌTy>U7{ ՞D>dX8;zgRͺCk|mVfO&X3Ҿg<(J}`q u8O }cj]+Yͥh# '* WbFm58udQEy;|((?TzO-?1[,}QYKHtRvGES((((&O ۪z!itp((((((((((((((((8kzL \$dnD# dVzJ3Ѳ7ЌW5VûT~} irH/ i1@fe1%H8[ɾ_IpvbS?zZxg4mS[ݗf?Bѕ 8215''Kx`sc$ zq'MkKlIz? vƷ6v̻e)O]ǒ?r,uJM> x/o?Nn>8ȲŜHpqy6%"kNq;TWYaSh=_cyg;?x>Iif* ϧ_}BU%nA|JB/_­|$KԵO\K"pv? تRTN/IurQs/⤤OSk^2I-T c=_ 5SeElVq;. F.lλ^,m4l|nS"y~m&5-H}!%6z|i j"]ﴗ40T%F?QKڄ'<*.Ʀ58V23~4ȩ|5xE 'V3DOq%u957ho6Ӽ'F{H#c=r$Oɠ%6?Z쮼+a{V!=DF2 p7Z r)V@TFF+X,Zr/(_M=Jut:NoS>,MBYp6t'Qnv5]gح>K[s(_ 8Ymqٕv!f;A3±2E|zJxw(>^ԬN F!Gi A 4pSOVvjzt[71? wOkjLٰ2ʠ ~+sǾ_J0I `3}![xzUhM5NW;pk}VPo[9_G*F*Zx~%F$@;>/mdmfZQ[m4RvPxHשfj,SY1Onfq tj?vm//Ie.eRs360>Hk4@j\_q[Vۤ1ıăj=$׭ǁ#X^(xapN~M f+:K*e%U[Mt6O F$H* -~ϟ (Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@{)kGp9K_oPEPEPEPEPEPkȾ5SOGB]"!M? g_?U[ͨt0((Q2kthMu .P !稯4r$ԷL!KПv Շ;_=^?.?ϡ?3?Z浩k$ve/OֆYrV22_=QKhJ).=fOjˮ뗗 J)뷠eE|}J7R[sڌTcʶ (((R4 ZUb^O >k7EWߟ>QEQEQEQEQEQE|s klO۟HM[3_g~_-? O/͉EWQ@Q@/m"Ml`ִf9Gñ:k׽? kJ+q=bh'~Ga2TiUj+mT}2 ?Mk!X\͇KyOB?jX P%cBgù%5ySKgs5/?Mgw4ľgg? ZO.k mKRKZ b@(lp#^׭~COV8Wx'vU= }/2|T|MZga!WA> t嶗vCLF7 7澶WS^Й&x ikI=JMQ8 ? UiYǞԏ_džimK~J9U -|Up{f kG[#0I$I$ Ez8']/ݣ+XMGV%)>3֡cp:5\b&ϡ>'7K ;^vue^?r7̿}WK?ݟ綗zJ9_iDv|OK?Rֿmo{\m];]J5P<;wӯ/Q^QL((8xcѕz?8xcѕz-OE# SObEمQ@Q@Q@Q@Q@Q@sF?*#t?qunmW՟uw ((((Gn>n>QUޗZv ֍֊)KrѸE.iwU7Z(ܒZaERQEQEU'BoIu_+zŏ"_ >_)iZ(`QEQEQEQE|Cu_oW?M.UQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE0bIJx>1C9H]GTߊ/gf>z}|U|+_He=@ ?W˰Ux5GJ}}~AZ/R:/0u/>*Ekv' ~B׮`ty%W--ܛUPy?Wv5p S2D߲IU;SVO%dBky.%PWV\VmJ_vk w,tQ+TߌaLFEy/s x2cZX ?ֱӯܷoIuӯ5i^HBu*,OY~=%;]b6~4]Ǐ4}> Km$ OֳX2״ye9#5# |?GC UVԽ:wnӦ厕Yԏ$gEu!CfkgNxvևo+?fk8 τ<aHҖɝ.|Qlj'(v>ƹ~$ÚRܰ\'{}+4|dB◇eĚoqng;CJ3-\a%_wDajF> O}E8?(i?ʸox|;$Lƻ/R:IdTMZ_',nK⋯ݧ[qm=ɭ/_Pp$U?]0k E*FT`;\GK_?B@3#y_\(U.iJRy=[:k$K[b𖐠c6Jl61?Q|\`7UnM*\W~UZg*h +OyGcSBGfyGkGqKי.K vTmުSS#]RMS}QEj (((((((((((((((((((ڏOc?-}_{)k(((((myƿ iU_)#W3byQ_fQEQEQEOWcEVl_᝼_ n>t0ɪTPY 7dYGq"q0at+Ӳ^m̻}$8äΞXk:NkYƏk?/{t_?ڿ/LM3")'Y- PxF^eL Q,,oy1Պͯi-hTf y<ŢӍJpVMǫN.IQEbXQEQEoIu_+_2i?}6+_T|oC:(>|((((((u__?f6zςZ$~Ɵ2__( ( ( (>Ylxk> _\\[,/4mV#<]hx`̫ƾB 啧*wz3iqFeF +%e#xz/G4xk}PHƾ T2fvk>?Q/ ЯƓ!smPQe?afξ}P 7G5ῴ|s`k-饒F(woU ׀]NU^Zv՜X#1eBDQE}A!EPEPER{Z;kּcOZ]k_?*?QE瞐QEQEQEQEޯ&_QEQE|Cu_oW?M.UQEQEQEQEQE1շW㧎to [oO#qz¢+(((@h|MvֺFy\y8g PN93^-ZԲyvW}-/ {߅pkTZ*nvB"&ҿk[uk>u52[9!J08 ЃWI_> ~m2ch׽{gE6Oyo]vjAR˻+?i T>BZ+I/ˉ$V2W:1$/LLkJʗ۲ϕ.<[= j::iA {Wm=v1Xo %75 tĺ$# )(~U?^QxYԙgb62\++/F\]m?2ʒ.nwg<EכAb >"z auӏ,/Vb?˦cHPkwyL֜V߅ +W8F EWӟ6QEQEQE}wץ|!ץ>qoz/\#"~(>((((((𿋟7I\W]Iso꾬'=QEaEPEPEPEhћb[=k񏂤_+umkTLs_kbզ׾2npb*ʔ/ݙ;xk.u 2Zr'[(1.ۨ%[qv~[Ac3[6֥BC+#Bc ©RZ7n.OW8*iEtCú3w:`FA !itp&O ۪z ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( JZ( [7Pz{j0&@X ?﬩}UIm(!@[? . T sQrne}KMIL9?ZiWQ FbG)9[å >'w)$)dEu_tM.W,bRbϜ¿:psʬpxNR/noUa1&IobSV^._a/窏?5xKw%]j[?,WCcmoG qP im젵 9x]++)?K|sSӡ}m̬ud0)k\/x35m(C>8/Mף=,KVSzrVm׾=|fd!NQBZJZ+#<3͕V%ʙk٣cM.G,ǫғ|¿1s^d vWcpզme 'PKKyXL5Lzuc/ :?*dl`k 9 #PGWq_Yc sJzͫRe|}\HG*@_>!'ΏE5)><^}j:0~evɍS?f԰,K }NKaQb%K߁/{L7/+/Bǯ+^iZͧ2eO49xԟ"Z.;36,C=[oD{?2S#NRQEQEQEQEQE/ЫkȾ5SOGB/~gj(>((((dzEt*;oMo-:2ݹr3UoN__N-87w dW{uC=0hß"P Z嬛3u7gƪs0"ONF8GNsid]O).ȵ>wG_Xqb L-1*Dh7N/TgEnQEQEQE[)i]WmzW̚O-?1_MJ'W5P+ϟ ( ( ( ( ( (>y5{'$&ͳ޿s?WĢ+(((w=;Wg>=xKwۯڛ6/dL s9u(֩Oe f%J[^v|EzW5"[1q] Lq}_*$=&{x-!uR̀`f+Lnq YRr僛eg^(IsIA_+fw<ii:vR#DK(u t_^x*@Э!\AlaTXRT?ަC^9`yn# +OۓItH]U l0@#'wS^_a&*/0/ 'wޗEWyEPEPER{Z;kּcOZ]k_?*?QE瞐QEQEQEQEޯ&_QEQE|Cu_oW?M.UQEQEQEQEQE1շW㧎to T2;NIkK)4Q!xIV~|v]1E/4s_ E/4sEJ)y.QKp f pC)уKFƝ?F$?>x&x_A%l?6vP3/W;IhpX4*5JQH, yW9INIr *Zt⤠s;}ga#Jjrs}U9ˏfۜnPװIu_|jH3hH@,WJֿ<^IB)PBQ/_J||]]H%CR߰.\3 I%-sӏaZ[M')%kG|ֿd_R4WWwK#fH'󯋿Wsό)_ױEExZf FFBJ< JVukj|e2{J)y^h拀RG4\98xcѕx?|^?fYng$M}9>qk_-#SYM?W+ ( ( ( ( ( ( #t?qut9QTDF3G7ijr̷wGथQEEPEPEP^X.T71Œ#PJ+ ^wk}vc2kk%`=8j _[[IVi 8 ~E+ΨIL%KS^S.fA$~{ϡ -Gt̑EFpa^{EmWy$#c$(Hw!Ʊ袼J=IMq䊏`+Š((?Ts Hd¨9<^Wb2%Nr>_)iAK_-Š(EPEPEPEP?M.U|Cu_oPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPMiR)ZuZ)QEN})9QJQE0PihESn.@J0PAbp=ɫ_zZ)X NQ@(QL((((((((((((((((((((j?=Ӕ|CQ|#G(((((_)#WzWj\_3_/K.xQE~:}QEQEQEQEZcYٹ)nO|~#?MY\z'τZnqiu*msr:dl7P4N9m˾ 2G~X5M9~1q[ϟ9{WKF,9v Äbni|yk 7t}n>9p99`: kx9)*[g=j½zʹ6XcMBk\}2\xP7Y#,0A޸LD+`agʽNT_袊 ( ( (-?}6+M'BWQxOu}QYJ(((((((۟HM[3_~KrF"[rd+hds?WĢ&D&J)rhɠ&^rz3#?FelX|?SQ|x=kKÚυC6maq#ʼ:w~=?˱O]:>[0+Nվ½S-?+c=^tڕ܍-WY$ߎZ|Vw?tͤ5nfH ) ޼ň~ @W:|Ko$1zWarmL&x~qJdV}'2Sa`vt>Wmp_1j7Vۆv尮K'0h V W׊8@"_׆_ľyOsouyaNU%v`Uly//G;_?~/#;_?~2}KWQf}? π~2}KQ__"6Ae_|__"w?Rٷ_rC*Gw?Sý|d07+7>MKY,Ї;̒)+ Ĺ.N#>oVx9z?0hԿEORK\ c?Rj|MT>y//JO_7M+nͿe_|__"w?Rٷ_rC*Gw?RüeAnͿW?üeA/E}Evm?܃Pʿ/4__7Vt'du$L{+}E'ŹV@G*NgNpz$LkdC! hRuTw4aBMZ+`+3`((((((qG*+ 2`9vc9;K騮zz5U#Rp]gχG+.?ƺj+aXg3 ÿ χ]5}C >܃\wqo4¹ka}b?+.?ƏW>\tQ /_rUgs} ÿ 񮚒a}b?+.?ƏW>\RK V%I'1r0QO|GuXݼMŷه?P+KpkRmh{1B~W}χG+.?ƺj+~KGs?|;@?\wqo5QG0}>WgχG+.?ƺj(}/*3¹hs}MEPϥ _{9W>\|;@騣_Xg/ ß kkOlצ=~3|\OLzN[ζgʐ}%_wڋ U?YxW-}ܑG">zTJ탞ן_t QMPhZtvl(a_G߆&@kuԃٜ\^H34Ϭn(P۱Xf@ F M_ڳğ+4Ϡx kRiS JL?vGiG5}Yx75axdO Zt[y#aݥ$}~`>hNpj?k2O;sŴM#!pO\_o vZcܮ) ڿ[R2_opPZ5wΣy%fF@f$ s@ZΡ[\y~t6RDs^%~?W-|cl(V(J?u{o MKľ1ӗRE%2I`wD*t3_Z|!VGŖvek[,D*X4w.yo>:\ _n7j kyyI%'v\‘_uk4뵇~P||ͣ~Ͽ ۮ@~ "?xV1 Z>ke e%VcXOrM~^b7{ h~?|8;['P,)#,]T́YOJK+I~5Ф/vQB>h`}ם~^ _LA @cmW1t+^vd eeveмCyc ր>袊(((((((((((((((((((((((ڏOc?-}_{)k(((((C^iCP FN3ν2H# s\qyPSz]k-ǀt G"vQ3χ_>q/jϟh\si} ß OƏWԿ>_A¹χ?+kRV|E} ß OƏW>Zo4AKY+-??\si}⿞?/gϴW_|9@jbCcjN<Ӗ.-ʢIyn,:+ݠo}[碬O庭º#j)"h)Eş?Q_A¹hsZ/+-??\si}⿞?Z+/W>Zo4¹U<j_ϟh\si} ß OƏW?+>}sG+-??\_ ֥hs HИd¨9<vT&Iɳ>c\gW_ie-xϚBE!EPEPEPEPEPEP/?<oR K_eaW?d եA7 朦UuJOI_Ggج4|o᳇UmIit~L>|H+Z' ?$Е~m7}G3\'~L=H+Z' ?$Е~m׌oa?cG Z?Q #k+hhxϨ?.~?&?$ЕϟJֿ ¿YJ6J?׌oa?cG ZIϟJֿ OmQzQc? ?e{Z?O?Ď?ֳ^OTz O=8ͽ__7 '!Z?R>I|c+Ѵwɖ$Гr(}BVOϴz 6AGG0Q_1 'k({BVOϴzQzR^1?ɏg%k_G3ď}ҍҟQ?\'~L=H+Z' ?>$Е~m8E~~$7:I/ymjisxwEWlh<đ_z 1Xl}H8F}MN ԧ94m|7i6)Y@sB+7&-dQE"((((&_|C92?((&O ۪z!itp(((((((((((q%*Qa`tduQ~Ɵ{K?_KwkWߪ|$kScd((((((((((((((((((-\->7ȷe_k=y-?~Ȗ#ZZ(Т(((((((((((&O ۪z!itp((((((((((((((((((((+a[Am֗ZKcwtE~a-A|,~ IqjXdv_UYnf3WhSG2KoR{md?Pb.͡!%m[Z=#s) ^v[ g?_ ,AM-K̗={b?t]j$4`wF~LuҿT+a `?3O ʿf?6U?7iNF'o>*x].ZClyv cj`ivxH~#octmWI@} A潿 ŸQUN >hOo|x$W[_7)76sb#hxg*>>\ OɒCu+j|\L\WNZ( (?-ট|vӘԪ~NbR Ād"WD3%I,l7PЃ5]ߎ?f?|-<QrEbxs >1=+on?ltlu(ղ‹öXnꮧ~җZΟ+CuWpQg)9ϵ|}SgNY?/xO,#%eu ]؜+}wE NNhڽ:A%ݤ9u*ø eo'j hzHץ!%b'+5`2NI%??7]㏉oi?f6ʐPyyđ(c QPAN ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (>!>#NRG'rޠ(((((((((((9"ݟ}?['AWK>do_B_ z路k'-_G+bEQAEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP[~?W }֯/]C;D%v3$ ]zzė>腢+P((((((((((ɗ5}_L(( 7ޯ]>#7n(((((((((((Ja`t|\JT_:j?3ο:oiq_4~Ɵ{sYg?>_5?ܡ(c ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (:"ݗ} {Ҿ跮gv?Y?[pe&/(Т(((((((((((&O ۪z!itp(((((((((((((((((((((((((((((((((((((((((((((((((((((ڏOc?-}_{)k(((((((((((J-^:W|mn_ 2^G/'zy_N޾|W:db{%K.b8@rCN?gCI1?AӊE }Q@Q@Q@Q@Q@Q@Q@Q@Q@4|FoWKo}@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@{)kGp9K_oPEPEPEPEPEPEPEPEPEPE2I5,GRkμe$zW]qXMt?]ޭgn.!7gy5=WÞבͰU0UQӇ,5XՎ->S, 5מ_Ώkz^W K BnI%8t ՝5=WÞi5=WÞTWϿמ_Ώkz@AQ_>^xs:?<9꿝}E| yUt5=WÞTWϿמ_Ώkz@AQ_>^xs:?<9꿝}E| yUt5=WÞTWϿמ_Ώkz@AQ_>^xs:?<9꿝}E| yUt5=WÞTWϿמ_Ώkz@AQ_>^xs:?<9꿝}E| yUt5=WÞTWϿמ_Ώkz@@7*k+P_z^xs:Ok ׽,9_q%c G*UDHN+_?^xs:?<9꿝x<,T{AQ_>^xs:,+ #%lgn(((((((((ɗ5}_L(( 7ޯ]>#7n((((((((+I.xa+8"PoIA+.C֩S>]VM(e⫨om lwo21ד׵ӈ8R?h'CO8R?h/C/8€>T'C{O8—( _?&ʴW}?s/C{O8€>U/BNyн@*_Uwq/PʔWh}?G!Zo R;i xN?PʔW_/iG!z v?&>U/CN8B.ӇʴW/QVw_!ZoQNBq|E}WVwп@*Q_UNyп@*Q_U?? MH€>T7? MH€>T7? MH€>T7? MH€>T7? MH€>T|[[Y88$ AY>]n!ʀO.| T*un4]ʗkRZoM< W.:zF>[Ygʏ iuD};((((((((((&O ۪z!itp(((((((((((((((((((((((((((((((((((((((((((((((((((((ڏOc?-}_{)k(((((((((b0ߴ[)qt_%;ȇ}T}![6}![6 (=+{yp}q$`+J_FG_~h)uVᾝsH|e.cD-x=kuL}h+=?V׀,PzG _hsP\gր9o|/zm6Y>Qu=W~܏L??c_|C]:7حn!6݉N;!1~~:,7A|_F7:1q t`tR(Jὄֱ"J?k?_n)?I_ AQN_Ư6 g?|@WM仱lL}G8MYZeӭl,^r$>Ӛvg// ɥ&owf}_wOOW|1"_?ViOʜ+? Zk6/@qû3 f1~ڷͭx/žK9|O%}.TWq/cj}knRκ[~#g1?OqeXݨNH#d!Ym\?('u?kO|̋3ҿ2Rb}a(4ү'Ď~^C8x=hſQo i0k%؈)J .7cJdQk|]\%-մîXve H_ X~7 {8ջ׾xFf0:軇u,8k&?u+I[MŖ>p_i B~3m'ok]\}L]+> ;CsyUp7j5&ԣƍq:#y 3.ȳLR4?i*<]ׇt x\K}/R7)r3Wk^C:WȲ~*r?Jhß ln=O!$;3rI( $χ6v (_~&?kߌ_EׇB"66j1ơZ]% aC&_s 5^2?>'[I#M;Vih^ b$. wX16e"|1g #t|O+xgÞt g8O!mx=an~1xcR+/^Ӯ5 }-O$·` /h@4,I՟TSj'C s6uZ _bWßۡ~hRx8Z5kyZog9σۏJ4#ᝮ,?kT~4 2}iAt~o$2s7 @^?=(+ݚRo?h3AaxCUԼ-mI$0#v>{G!B音ǂT}!NӼ͞kv<VQ4Wo/>>-!鍮;7̸|aZ+::wts0yA\s ( ( (>yAgWmJqZf L72xBgJ~Wuk5mLcxε2v4?&24|Q5pi!5\V"4,#㷅mP8қjptڂ;Jc`q@K_,k7OwW叄f}~|)Ns 袊(((((((( 7ޯ]>#7n(((((((((((((((((((((((((((((((((((((((((((((((((((((Gp9K_oW??Zz ( ( ( ( ( ( ( ( =etzOsJ ^NA^V((c8h_ZJ8 ~Gnпv @~<[7D~/]_ʰu.]qP+]EDM6O jL? rG,SǥS״UD(Ρ'"&*17_H=[1)?JL6EionzQc:]2"pBG,x? M#ڜ~UtI+"࡞.~:O^{/ [\<0Z`{}@~7oM6"oʾGh1|1 Aྱ&'?a8]ʙGFy$;@F/]&Ҿ״[5U"%|+ptͮ4-ܫ#SǭzxkPel}(wr6#_?_ woqxGw1-6{{YRCţaF/tVG@"ڊ*bzh)ޡY۞8ӯ.V;{y\ coPzZ KZ LJC: F]罵\n69;]GMTg÷|Ҽ ,͢~ΞI,oB9BWwG^ٶ]W6isvqq'%%]} Euߕs<.Ś݇ndQaY Nkl_ʿ8Y"| Bހ=~OMoAVnh/,I ´16>o cb?bO"4UgLOj.m/MX䉗r =Ahxى d{Tw)*ú%M6b@"RӼ&\4e..#uwVv2ead\?[cFt>`k)-Zh^6rbmaAF~eOqyu(M2M[ˑQ,{Нl<?Jm3 dS 2HpJϱ/t6Q'$F@.@wY"BPNz?AoaJu#l}+?hqƤDV7K9K, PM]6ҨIBXamKA[HFҾjBxPEG5V!itp&O ۪z ( ( ( ( ( ( ( ( !z=yo?ʀ?0+y]ӯ+y]EP~7.l_FG_y?iHrZ3y7xF< .{ /nm:Ƶj|z<<\_o:_DnwW+_ZֹXa8|#u_X/ 9|;{KQȵ Oʱ]M~FG/#͟&mQh|O'+u/.||#޴vl8([5 ~_klmSP0 6H8sοLW|HuHu9Xҁs"Ʃž~]mKV PNjsbY2WxFiH?~cAbhI#xQ/M܍JE.t+ r,$s(ؾj6qkwgয়X=ˠG;8ك#e}tɩjYKL">\s463+RҮ,6h%P"0+>~ԟi3T#M5Iudbc9P ӜτmƋ]OA 妝mo4{m"UaH8 (S h F3\#~h/ o vRimaq;4Or9ր>g{?>G4cҭVa"ٴq_ q?$x WW`z];v| q]?iwi .<0*Xxm>A?뗟>%U|н2r<٤IS+ H ͒Ka@>kX+Wÿ ,aҭ1 (?߱#uIv3iYl\9ML~Y s׭~XIg}:q Q0@RZ<T4->$?ww΅s(~I^-(J|wC:fo)|)6A _w[y{ .BʬAo¶|7Z,tAgcg͸qҿ%|+kg7qnjut#ipo#T=🇭hrI*ToWO>]™% G_;_3!@V> 4ڍkӘ|(ĝK a#9I9<Q ?οU>>D1.f,y~O6m|;a7-loXqvtU AbG>@<#oLxW 5+>e<*L<;Zi'.1pi_S⇄q;z-ޭm??իnq[H/n@qKF&s֯ҿ t;1݊.-woƛῆZթ"^⭭b~`*?-pMT[Yw_CU']IJG,F9jeNjh|+ϊ,k7OwW叄f}~|)Ns 袊(((((((( 7ޯ]>#7n(((((((((((((((((((((((((((((((((((((((((((((((((((((Gp9K_oW??Zz ( ( ( ( ( ( ( ( =etzOsJ ^NA^V(([#>.пb-57nп2wPZZּUm+WHoeּ;g@,|]jsxHl|7bֱU-{öQۂ!|[k Ws;#f-AɉKnO}d]n1^qU|=0^]lj`A&<;C8ּSm0ߏ6kHlү8޸lѿ3㿂|>8ψmzmd?ٗ]*umqN=5hᗋmuJ݌Ӽ9-v8 䏾y] _Q}J-evw OLl{A_? x(#4G61,2?>?i7v}4/ɫI-JLDYxo_e1L7aQp,+Ml=E >}5੶i{Ç*Ɠ76<\͵X|?Zsprp l(6踋{ÃX'ᎹUVO2n泺P|МY|{S߱oxjZN{$ngtvelHej~ "nR5#2_m@79I&8fE㘥Yج>5cr:K?2ԁ__ZM^-O/V?d?.: ?w_Z2 []zOb|$_í9|=50FH\a(8o 6jFι&ᕸFaii6(f U՗,]^5<5ZZkæU2( I"Hc# ;r?zF6~\v^̇rpʭ1R>Bzs@K]|Mq Ϲwk߄woހ<+s|efi|mH[۹dRCH#WF F(-|!]>' ntI $ĩD#fܐ&'+owZ76ڝHH\I*K9"7|@ᯉ_%xZSN4}"KW $0S6Gt?Z;?i&[G/M3Ig@$i# ~u~?lw>"]hp\Hw6w{*A$j(9(5?s|#G#Y. #ϫ޽ g Omyn55ͶbdQ,A$%ۜd~lcpawaCwH|xmMJRWOixX]$w0PkOz,MWPʳoa*6:_Q>%xkOMw:vD !tydc fH_@T?uuHIOUuWoAϫހ(((柋.{J uJ8'v@v&G7VIq@q{J_+~(i =kg$"t;K{6P:U3x7A=kChq_D=X7jiN[ŵ>ի?؟A鯇n:8(((((((((&_|C92?((&O ۪z!itp((((((((9ꥼ|@z5aC_xvɆL@kTWcwem Yݎ+IՆsoy@iMqhѣWϖ.PO@*+L5ۈWVl<܇(<:G,ETsy?7_~ڗ1P^,EToM7ucwK:q@gj7U?k ?o"9TnJ6/#vcUl/:Q'ɐf+,Fcg-l o"?gcUo"0ܔf?*k ,wzUlU"/~f?*άr?o"Eexnڭ:W[P7'ӫ7R؞]U? 7_?j3}'ɐf*_vyƪ~ou3?>#U"&\jv3?>s 7Uk _c:Y1Ql A 5SӬE2o#w 7_nʌ@fZj_܊WTNvcg1Pv'Xo"io3ʀ=OogxwWA6i~_zMo埕U}Ϙv"#ğQڛ=g٤mOMѧqUI琼gWI(T|5Zƾ4!u 4>|wن˕$[?nKE|չ;`R@JZo"[KMW~b/}) {_i?oxK-7_5G- [%(/ E|/bҟ%Ao-_y@+xtjzœyIy,ѶܥWX10+_ơƉ퉬iQclWf?*3?}߶2y- WxS \cgy58 *UυJWx#g70m8_ZfPQ@Q@Q@Q@Q@Q@Q@Q@Q@4|FoWKo}@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@{)kGp9K_oPEPEPEPEPEPEPEPEPP^Ǭ_2M~i~ӟPexוJڛM"yTҀ( oݡ1kk禍d7_YX633i+^`?~oYu\׬e*}kj=[ߴ'-=eXǜZkmnIWk{E?Z[P ֭^k_f)ֿ=5TtR?ᨵfctO$$|G!u꺌ِii"9v\88ぎ8ѝoV"m㵶>8B`~{ShkX9@j~:?q;{^[;Ws_7Õ-E᧵3K Ik Vz/So?5=RTa gx"8=Ҿ{W;o/WmZFQ& @N6ƑS37)?ᨵetct/tOGm#hWFe#Ђk? ~͟ &⸵HME[yT6KoPjp1#] OjpH@ |Xb7 6;:"=9$Bm 7w}eeZ->j>o5z] 5h?Կl/'k/i~*>'/.-l_Fbޥ 0ݓ@?tW~? t/~[kmn,"ιs oS55&@s{S?7)_?Cƹe?n٧JxqtCjBc)뎹xG5&@cI?_<=˭Ė6x E$7 Bg9S \5m1jWo * ! 2s?Z|ۃkmem+𖍠^xO ~q ܙ i{9zM xkutHoB@ߎ|p1g]Ԛ1@b~:/~7IcnDvLJo>>Y. FVlgHDeTA/ANӿ᩵Egܞ,-YBd<(LN8ݰc+q≴ߎ/<+ciK{0IY<˷p(ڇUnNwk</㖋|?<;Gu(PlfCm8" h].t6rc{]/&E'x Gw½{w6ye%IٵsuWhěжԮ;pxio¯=#O[-kH[֕gu;.@ X _7x5]XQo;dSێAf aTVʏaH,X2sk~)&|EX=m][{/4jO*$Y#$q޽_qt^xYK4Ѭ0l#3n-\;4'@?s PtWB'?]П@Ex4'@?s PtWB'?]П@GCP?)+oُĚn{}|]JGu}߿nU끟굿55vY޼IǍ4%c<xWHMqGz7Odt Ǿ*^Ѩ.!Wޠ((]>#7n 7ޠ(((((((((2SKfHn1bNO샪X:DΙ?9?C>~T'}nOQ Ѻ(??'G2OʿFOdϼ!y?*?9?C>~T'}nOQ Ѻ(??'G2OʿFOdϼ!y?*?9?C>~T'}nOQ Ѻ(??'G2OʿFOdϼ!y?*WƏ;񕦛n% w$SϦW=_ &|=eTKO8 i|h㱫K}'}OW? w^hVMpeȚ\ 3 ~W1xZ:M ??'_tWq!y?*?C>~U7E~r'}OW ??'_tP'2Oʏdϼ~@!y?*?C>~U7E~r'}OW ??'_tP'2Oʏdϼ~@!y?*?C>~U7E~r'}OWdϻU/?oq眊ڊ>|!Ҿؤv+K[EQEQEQEQEQEQEQEQEQE|Cu_oW?M.UQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE|CQ|#GڏOc?-}@Q@Q@Q@Q@Q@Q@Q@Q@Q@;~ύ4oʂ~Q|xwSܖ6,$Wԑєu Ɵ|?gkxᑺ(y#7n(((((((((((((((((((())hk)ZT_:j>&_=uEC_MS?HȞG/%_?O +zkþ 7z?5 }+Š(((((((((((((((((((((]>#7n 7ޠ(((((((((((((((((((((((((((((((((((((((((((((((((((((j?=Ӕ|CQ|#G((((((((((((((((((((Q8m藮rxW_%J_ďo~kk_[y^axo}nۏ|υt@X OGH[+_eਿƷ%:(P(((((((((((((((((((((re _oW?/@jz ( (>!itp&O ۪z ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (>??X*/5r jga`t|+/F8#䨇{_&GҽƼ;ףi^_g"gq(Y>h((((((((((((((((((((( 7ޯ]>#7n(((((((((((((((((((((((((((((((((((((((((((((((((((((Gp9K_oW??Zz ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (rxW_%\&+Uz֗#k^}},axm1}-o@o@WC+F5/V:(P(((((((((((((((((((((re _oW?/@jz ( (>!itp&O ۪z ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (>??X*/5r jga`t|+/F8#䨇{_&GҽƼ;ףi^_g"gq(Y>h((((((((((((((((((((( 7ޯ]>#7n(((((((((((((((((((((((((((((((((((((((((((((((((((((Gp9K_oW??Zz ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (rxW_%\&+Uz֗#k^}},axm1}-o@o@WC+F5/V:(P(((((((((((((((((((((re _oW?/@jz ( (>!itp&O ۪z ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (>??X*/5r jga`t|)mx*!/%[Fɿ4q/#dW|3ϻz!QE~|QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEKo}_4|FoWQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEG'rޯj?=ӔQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE'z(4_KQ\/&+? /ZR$}QoџuUߍ~Iez^_J7E="lcx7E="lv䌫^uQ^QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEޯ&_QEQE|Cu_oW?M.UQEQEQEQEQEQEQEQEQQ2Hj4Hg`:\ Ad)5jè|I~h3ɤ~K> k4Rn1ӯZ#+mS~j7h#(TG$ڧ}F?kk1b.&?o 6?sF?Qkk1bZ˟IO4MѠoZ#+mS~j7h#(TG$ڧ}F?kk1b.&?o 6?sF?Qkk1bZ˟IO4MѠoZ#+mS~j7h#(TG$ڧ}F?kk1b.&?o 6?sF?H|I|6e^kYj1!Y&zeU[d/39&?o 6?sFxUS{&: 7^n{)-eٽ:8Z5A1__j7hT\|5,=($YNU4oF?kk1b.&?o 6?sFH?Qkk1bZ˟IO4MѠoZ#+mS~j7h#(TG$ڧ}F?kk1b.&?o 6?sF?Qkk1bZ˟IO4MѠoZ#+mS~j7h#(TG$ڧ}F?kk1b.&?o 6?sF?Qkk1bZ˟IO4MѠoZ#+mS~j7h#*Bd oTW ISn4̬ASzNfH#ӵI1?A(E }Q@Q@Q@Q@Q@Q@Q@Q@Q@4|FoWKo}@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@{)kGp9K_oPEPEPEPEPEPEPEPEPEPECuu-,#FK~Ӛx!'x89mPoZ1!S-?+mO?*?-?+mO?*?-?+mO?*?-?+mO?*?-?+mO?*?-?+mO?*?-?+mO?*?-?+mO?*?-?+mO?*?-? >!XϪxvv4bzLG$W_~TlZ!UE24\O:#o5xTƹm{O?*??$ƾp\kmTyLZK? pL%4u=(~u޾PشCbyʽ=ƌvK''Ɗ;byʏmO?* Ɗ;byʏmO?*Ɗ;byʏmO?*Ɗ;byʏmO?*Ɗ;byʏmO?*Ɗ;byʏmO?*Ɗ;byʏmO?*Ɗ;byʏmO?*Ɗ;byʏmO?*Ɗ;byʏmO?*Ɗ;byʏmO?*Ɗ;byʧ,dU!x?D \Ƴ0vFG"(((((((((&_|C92?((&O ۪z!itp((((((((#&8}޼w@$׼AwspHy5Ny귟:mQEQ^=[-|J+O"ߕ/7l(+_:0<ɛn|+O*S64y3&+O._sG?oʀ>;~y3&+O&n(f€>;f۟-*!itp&O ۪z ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (>!>#NRG'rޠ((((((((*+o!>a>7?ldl%;|9=ީp73ƽ_Y~"H]Ƽy~y~iz ^}Թ,y$/ޏ/޻OT~,WL ſ ɘz-OL ſ ɘz-OL ſ ɘzkju]8[ x]2ϫ\Z}RpIR`nd/WH$I:ӼQ}¢EI\+/#C?|5`=~@ ۞;| :7ZzB{mPǯ> xu5^l툺%/ҿ |Z{ÿ{ !&g5TPy#Y W/? K'ٴM6 1y6>kg )^U/"IA𖚜nh'" @AH?iIg_ hUxYIt51#t I׳~?~!_xⶽs_B(kT"s2 <{52nS+GpX\WޭZxE@&?h|9?Mi+ i-A,lf޿V\*b@L~֓'_v:k6PM~0h>*gOi ഷxLq9fɰ$ƿN#?7RkqV YBW+oH75N&HI菰 'DTO&;`%GǶ+__KulV88#vOzOm"bʏ@`=]}6ooDZv_/;~_ emq1g%~G'cgUhfg܋_e~"ho!Ը%idԴL"N_L? >" F\3fK_< ls>s<|Cվ'PzݺioHPܨؽs_{/b~Q~3U vFl'ψ vk? V3@oqq$Z/nF?>!|AkJeHXf#_ۨ[*t 4[Hxÿ tx+H/gbӡp^P qю~7䳍FHJf8r~FZjZ/U˽WH jt\(Po~P|z$il4i;qo lÅ'\@9Xg|a/GÍEtۨJt ֿ,gۣק|#_V񿎼AV*ܬRgfOA,w,|EE5RVwVh]I-$ 7q'> $?ofynm3KV[EUo5\«lcjWIߎ>2O_x!kĆ"H^R|}+m+m.4X澍krۀ@}h+w^MBf)I EnY.'#tXE'_~՗¯7 <1OoMqnEZ\geڮK(n$ʷuIȨ%ĠW_~ſڛ߀> ]ɥjyfV[坆c*n byO~߁:]jrFz-w4fM 0V1E| x> j'է5ծ!Mj: μK3ǁ> G&uT!Lѡ JUE6ԐޔW緈~'|v|ewO EfYUY|:8C֭Ljק|75uVFkeȑz$!,=@b2+=r~Q[ja\[,<1Ciu}LF:O14cR;)f9I!ldy8Գ*2I=5`&еV\SZʹw ~Menђ"R">`>ٱ;TQֽcKM[@Ny dvu2/v sҴԚ^Gk,zgD6DFH4ÿj:/<[k Ooi!,68##>&?c|:/-i4V1j7;$`v~o+RG%sg3%if _!ֺnm%RPI‚p>>9.l ?s=+׿h^>|R_ x% jUѵ8.x68 s_ao|Z>,g%Gc`{V% m!a_PH4;NMr7RQ՘+ω^6h*5k[UygQd3b +A⧙4]sM aw0s pzVk%Vь8DQIPW7L/Va@GQX((#x'2J]p^ .+a=.ׁ\_C_gu̖r€>/o#?gj~ 6 uqK>rZ^`uHϥ~|/_>'YZ_0:/=<ˤ|ZifFPX5O t÷b>(((((((((ɗ5}_L(( 7ޯ]>#7n((((((((+>;ȇ}UCp*¾t?ΛNt?Λ@Q@i _>~'~{i?|:74G8$*jۗmX-l_Z9$O-㧭s֒GZl}k…uq"OιFՇ:tc?)~OxvYvF4 $c2IU?g/ѻizmξ|HBşUX_dB#g_xUt^Ds5}1C&Xジ]G7?/m~ķNsQY.y$o=FHfˏ{PQxn+M%Ye(,I9ϋ,?߶& ;x6,$`vd5и\pp"^b-{ᖯm7k7S0C&|yǟ6]KwV}aIy$pV$is>YP>T@?<3jN_RKMtnUrjf|:G8&f5 ONR60r:A޾lbIk hh`kGYhY~R8Mw_> Fn-==Ff&|cF?)Vۀd #`u/ڮxCmz_'=ȟ &h/jZCkQi ޖq+<#_hچ`+Ȭ뵭9/+)Aß hvqs_3=$ I$zkt' u ikkiw3LY%[F/-S~Hc :z[WZx~ZUDž_{lhY78ӿ)?KDqs}|ESx-Vzژ֦,k& QI l{w?g`~` K II[974Qw9=[[THWBg=j=O-3Vy%b=;#W1_߰?j:>^ˢO, gvX'A>X<+ݻA1Nܥ8c@Fi(js,5Krݤ]_[m|54&~Ϳ6l-&KeR%.2?7OZb!ހ9OhO< _$H+Yg-J] M:,A6g$Q]eՔ/;2)b ep j7K⿀uXz4]@*Cmё7&*A{7T_ZݤVP|Ő;X~>uRkW6[UoO5ô?h]?vRm5䚔 r_R0H~n>xJ8t}m\?,e$nf$8#/`>2R( cZܿQ c2s)m5N?{_<]WmZ@-ɸAҿm[/hW66zoVR:[YdG`0q@JT~86u-g?˥|t\x|0x̪Oj{ (OZ&s_uh**eHc$Qs | ]a?QvxzdIJ[ȊU)ۆ@;*Xh]ͥbV#{v*=['zO[ڗ{6R/WME[?\Wx _/ Sg/0GH ʸ G9,N17Y?֥se$gclPn@8GjB|wqme"Ȧ8<!EFaӞQEo(Z)_IWͿjDTWloWom }#G 8|\Mrp< rF/ހ<ğ嶵/\4 &ԓxPT5ufY.s@c;Ys|h- ~$3!_3Gy>u޿:j]U7 #:MS">c?__ Gӿ:( ( ( ( ( ( ( ( (>!itp&O ۪z ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (>!>#NRG'rޠ((((((((Y5=AyhS/ƼzW~ӟPexוJ(dr⾝QCm7}mi7K$+GƁ~UxLL? KnX:l."QϥAc MVEoisډn#hEʺ0)+Z?ܯOJ .mF!QP쟭OCHˋ5+Hpe![,Ƨ&K2!^y,tߏ|Юs oF/Mjp.؇BѶ| i&ߡY5}$6p06i_}eݕ[ ~{`++|'⯂7/Lf|zieߏ2x}Z%ъ xUYHTE%<M~2Fl}^ wp]66R+e;^9eBHuNLL|;%F;{[n"vB2DUU_S_%׃>!:LѬ/ 2mnKw:0ڠ+C^ Hn3En]]Lq1台ۋq(x69̏_|akdXnw.#vV#M|5twW=ڛr@iFf'=s7 H6K-œۜʥ,>LσِOŸ$_|YmVk᩵ (oeƝ+glD .pN:^)^xrVk}Q@HHctU6O}k[ |v3hWz|a~p_[_ @/o ts&z:8VL &~Қ/I wo$H oxT*Bqv]|R%@hw5To>/x;ax /K ~Iۏxq+Ğ(B?vE~x.SUxoqj%r$C3PORA'B>+ӭ O=>5ǵ~C4͠xUfgz;pV0F8 ﶋڶbS_ - h+k]ʨ_Ɏy.̅G$'' p =Þ/O:ޡ6jW&Լ6"lgw?$o#ϊ|M ]̆I`` WK kWf0мGIJ,tcqDe"2v"+3nlT/Ry[M^BnIm8^d |]"V<]c;Q[-onYwWdRۇ#nFH3/v_.[ij絗M9|a+pXo~JOݿ4{MxR+#*_#g?mO*>}%mYYDX]^H0'ȻUC _? {0F^<:eYsXVN'ig??M+]ko?I.[h&1tEVg {GVoP=8qY"?g?=ڷ43Munw6e@ʼnlo W͟DKXQhJq(m,?l>Yh:NMoȀ60 9Z|-֟[Ҵ[wm尸Ec-;qwXּ_m5eBy$t,E#`#Y8;H_S~ֿ;GIf^:t6]w#Lr.:(O% ׵5somG? x7MY.n;p3Iu@OetK|@k/j35+>[{KA<҆pH2 'K]cV3@7L $׏\~߳%5I|OnyMp|G~d#=1@|eF`>xo1sEh,wRGJVq_=~Z .>Vb??؟A袊((((((((re _oW?/@jz ( (>!itp&O ۪z ( ( ( ( ( ( ( ( qz-dxEM{Cq"@7yԼC(to3ݎ+͒[5#h,L`e_"Aq{;]Bɣ`*19Q^G#[Գe j?9U,#ZrbyZ|ocX4is\z>բ,uo?loոQϏQ(E@c`(lZJME|~>β[e?ᶭc:W_h>?Q@O/`yU\cr|uo|E|~>İmeG*(X?9_-?Q()m++VrQel[[G+>?QG$-&RVrTpٶVycsLxPۖߨVu]G+/>?QG#-:VQʆ2uoE|~>ڳ1հ?9Mֳp:W~|~>?Q@R[vտrovE*l5l|(_:2Tߘ|(6EU\o V1[xQ(7kFw 啥K3"x©j^}|;Ŗwm>ehcRbG@NbϏQ@ 7tFM/Nmb;$ܒk_ׇRQG>߿}x|/ׇRQG>߿}x|/ׇRQG>߿}x|/$ֽo_jv$pOja5S:޹ϴG>CEեS^mWA^EGY|U}ߊeMWcڀ4' ?~q+\mta r_Ay%݊tt-Wj_(((((((((&O ۪z!itp(((((((((((((((((((((((((((((((((((((((((((((((((((((ڏOc?-}_{)k((((((((* Ss/ "?4i(skץ{'Y>wB",~lW#PGJZ(0_S~Ǟ)RG O++VhwmI(w)S=~muZ6ʧϯh/-7(Y픿r?F,i2TeUO__>-/mOP؟>j<G[ex@21V*keo߲濨&mJX;yծvHљǵ)-i_9@jz fj:^{wZ0[ mn9RGBkd߲5}sϩ\꺕ٽ֯tP\`^S@x}شl@xwP? |`Ʒzl(<ëe8oٷG[+Qx>l-n B9PۑK)+gxl@xi)z_|A:ޣR, Ԣ0{8"DC %5>pҪ~|ŇG\㔧Ee/hυ?i2#[ )(ebUO#G>5S޴n ]ED"`*<54픿rŝs\((ugVU-֭lUI",kijJ|+޿><]7K(iR(|uc&jɬcg}Y2%ww*!&Sϭ~|@xoP?hGrP讓 :=>%i=kY? gɪZsuw7e#](a<*CeX)"OŽMK(wtHR ]xŒiw 7 . ߷گ|o5=Bm,qSI$$I$kŋcGR@oP5K]*}BQ)q{ZK*ۉwu5i:ͤ#ʸ}?:g_/MrQ _9@A~?_o~'e.w /c?%B@X soJ=W}őyQk, ُ"r"r)#'i9Žs\㔟^/~ ?hgkg7sqE465_3k6`{//$/{IqE WcTI+*.>gjōOQ h_9@@g_>U/TXnDtua$DFP[?h ɾ E|C 4{}RxX3Xd13|uC'OO\麜 dXEڤ:ƹ z.}o>ϬCn{T(ŀЊ&Gn 1xk|_xKO!.+گf'ڵ.@4<4O Eiu)|Q[3F\_Hw$эT3wb!>j7f=ԅe#ڀ>(((((((((ɗ5}_L(( 7ޯ]>#7n((((((((( -~ X7FbeG_311 :WUmQ [6(_bWyG1n<򯺨u_ߕźoʾꢀ>-~TͿ*WUmQ [6(_bWyG1n<򯺨u_ߕ/GX~>q|q׌/X?i֮^J"ʈ~^>ʊq`i6۲Kv{'ge rP-RC1h1kڴ_wV1|WѼ nێ??S 8L|0(Ibon/:M:M{3 PQ T> ^>g|_cOcO|@(a*?x='}~(> ct ct׽0 0WGJ1h1k޿_"fb/Y:M:M{3 PQ T> {lOP}_A+<SSza*?x?_"m_#%gT=_bWy]'>i^ jOŭ`;]_f=fFcaF&]_i&G1n<UmW4.|+ [6-~UU1n<UmWTP¿źoʏbWy_uQ@ Ϳ*?u_ߕ}E|+ [6-~UU1n<UmWTP‡-Fߕt ZmM199=x#ޕ[Tخ((((((((((&O ۪z!itp(((((((((((((((((((((((((((((((((((((((((((((((((((((ڏOc?-}_{)k(((((((((?#9fO9y>WW3?8(g~j}ߕ~3Oxわf pP?ݿ*>y>WW3?8(g~j}ߕ~3Oxわf pP?ݿ*>y>WW3?8(h~j}ߕ~4Oxカ_~^ 庼]g$Zg?ݿ*>y>W'm'fF*Z=: a{&]޿oK>y>Q{oʾzWuzWu_y%Gǟgv=_a[=+t[=+th~?Q{oʿJ'<G3?8(Wߕgv3Oxむ?5~y>Q{oʿJ'<G3?8(Wߕgv3Oxむ?5~y>Q{oʿJ'<G3?8(Wߕ,vw0TbǧS ? o"~ּe[qm$v9W藀o@$V?AXl Ԡ(((((((((&_|C92?((&O ۪z!itp((((((((((((((( ;mğu 6%=~wU*o2|S'|ag#V[K>AN1F)hb VxJ|^36G-V~I,'bUܜqF+o+lcA+*hVHث)޼:W%/5N[o3lo^2hQZ(1F+??wE /|#=[lh_)O+ʘ-\I1~)?6~N=_Fj6;$hES48‚apr~bW?N"g\Wc0o3j}EWҟQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE|Cu_oW?M.UQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE|CQ|#GڏOc?-}@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@ W߶׍;|>_c \pd@&\1?uij߆gy(<_!RQG*g܏5gy*Juiw:}m֨ebwbM9rk >_U]'V״-SOO.m"9Icu > k3xwu _TQʃ#<_!Gk o{WY-t.3- 0@QM|Tg-/Z:x+{M&ԏ= Ӛ9Ps>gy(<_!\ÿ[o;^pHM˸uVUu`+3GxB5EKE9sŸ H4.e@ cu Oǽoml.,bTp;nP|qs)(QV:k{~8aח5%FSI-fy>ɢ(((((((((((((((((re _oW?/@jz ( (>|Þ >=V#N+7owmv7.qpy7_[u?W=W={w/?P|+o.^ WV]x;UvG;Uv@  (>շ^#oA]#oA]#oE¿:um׈ú~o_mú~o_mA?y7_[u?W=W={w/?P|+o.^ WV]x;UvG;Uv@  (>շ^#oA]#oA]#oE¿:um׈ú~o_mú~o_mA?y7_[u?W=W={w/?P|+o.^ WV]x;UvG;Uv@  (>շ^#oA]#oA]#oE¿:um׈ú~o_mú~o_mA?y7_[u?W=W={w/?P|+o.^ WV]x;UvG;Uv@  (>շ^#oA]#oA]#oE¿:um׈ú~o_mú~o_mA?y7_[u?W=W={Tdj /lMwdv8X݆✁z}!}M$kp}EWҟCX &Tn#{N(K;';Zb6N&c

$`}|fh? #?^о |4kuu(ĈY| En3JUa~z$t7=(Un. 7vÁ`H?9_i^.5䚵#r]FQ6s^~ҟGt=CH?gc[Ik<W4r)VM}u.M;]ZsbJԄX| "r2~&C~ "ھ(>4y2/ˌSz _o+g-xfM>N[UY$f+yǓ9ß.FY%,:QFΡW7&ocyMc(d蓮QG@1hk]ZYnFP\DspIEpRFJ|n;4q7jfv"#c$D]sUHROL˹Yt??S4A m`7j?0(?z(⻯J>lby$d'_2E*ȥ]d0#_D R9WN }FᗸY"AGӊBI㇁c6PuL3kw$ o_5Kkm~34wd퉯˧#?ժ(o2|GÞ v|A /B j :Vn֟v7cr7Zro2~v?>>j*_cJxL?ْ4˻v8S־k#<^oN}  (>շ^#oA]#oA]#ҟ{w/?P|+o.^ WV]x;UvG;Uv@  (>շ^#oA]#oA]#oE¿:um׈ú~o_mú~o_mA?y7_[u?W=W={w/?P|+o.^ WV]x;UvG;Uv@  (>շ^#oA]#oA]#oE¿:um׈ú~o_mú~o_mA?y7_[u?W=W={w/?P|+o.^ WV]x;UvG;Uv@  (>շ^#oA]#oA]#oE¿:um׈ú~o_mú~o_mA?y7_[u?W=W={w/?P|+o.^ WV]x;UvG;Uv@  (>շ^#oA]#oA]#oE¿:um׈ú~o_mú~o_mA?y7_[u?W=W={w/?P|+o.^ WV]x;UvG;Uv@?Ko}^S?u/Gu[yI6c>kg9:WPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP??ZzM_CO:u|fی= n?s+ygS5?\jzYT+Gޢ!9sQ,sN?s+ygS5?\jzYT+Gޢ!9sQ,sN?s+ygS5?\jzYT+Gޢ!9sQ,sN?s+ygS5?\jzYT+Gޢ!9sQ,sN?s־4]?KZNǯ?ci}v.ݱ1v_9GqϺ跏S~?kScVJu}gQEexĚG[_]lm*w}\$D@k#~βjb|Tgaq4dznǽ|?BjZO6Ɨ7SGx6wr B`!A3&/q#MlzȽodlH)ޏi"-*SӮY̲2|ZίAVx$4;[g?HT9,dwğ٫XeN٭93[ygdny' 3Eh^ j,ZI*XV>>[ xo> /LIg Q+Sﭴ))ywqxIn'pƃ[%&f x?ƞG Y.E<ӉbY2F]CpJC 1g^h uld$j-#EwJۊH ; ?mܬQx}D eݏz[ӼIE]F%fd=I 5w?kKmmrP)@F!G窕8#4i|9ԮEbRVyc Fq d {?w/֧^&$I$YQ*x;Q_Yx[ C!Dխڎhj25$@+DY@5cۡyˏkj?&?g{? 7yR.+T|^B% n[!E$ߢC>jKG!11ґ?5~EP}+㋟HE毱+/QךN"?c3}1=qzaSWy_W>N?sϷN?s+ygS5?\jzYT+Gޢ!9sQ,sN?s+ygS5?\jzYT+Gޢ!9sQ,sN?s+ygS5?\jzYT+Gޢ!9sQ,sN?s+ygS5?\jzYT+Gޢ!9sQ,sN?sW__.ۓFp?|??2Hvgq_QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEdt*O+eLK{aa6#U~=_3;fX6'.b'L:u'^#? |W}FCdg $+mh'7S/'|iuisJ@ V88!_i_*OŸ|5^xx٣!C 0pݳbOaׄ4*V | ʸtV7߱վz-nd77Mvd؎Gj`qOK7~ O\ubeH`#H Txgᎀ'+i #J:l AۖmM|Sv޵c ?>6?#xn(Ѭ衟OK :S*? оrmt=0bCf.Tp2M~O'/mi)WgYPEje2A*?x`)|ܖ੟ R~%OPht{;y-]ԁ<"(P/ aO~_&?RI&ٌ{EV |9`#t,(>((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((h?io:O֯ӊkkWg_ޏ揸O\KLLJ=:$|CܒJZb? ~6?o?AKgT֮mg/wy1V`W׿ '?ۃ֭q{MY/b|,A?k4g?I|vѯp߰xo@ ~ўGCx@|9}wh/dm-h  qRla֏'¿OP2g`Ҥn)O3'i(ڊO_N~>&?g4t=V=^+pw,rEdTӚX9YI<_*ӷ|2_"xE zi|>OU}G3?=BGȿ?'(YЙ?_]}/ȣ4EQ/$_=BGȿ?'(YЙ?_]}/ȣ4EQ/$_=BGȿ?'(YЙ?^iB3ȄP`߁7ƿHt)s? ~땿/>YЙ?G<"=4Egy)G3?=BGȿ?'(YЙ?_]}/ȣ4EQ/$_=BGȿ?'(YЙ?_]}/ȣ4EQ/$_=BGȿ?'(YЙ7_]}/ȣ4E//~[#ohPxcžm/F`M IʪOO8>|(?[M&c^*Ι{(%S^"GRK/%30`**mOVQE{gȅQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@ o|if[\xO׀hдVv7n{]ҾˮKOý'⇄ ^6ky)"DD5xY0hת>*YVc Exޛd%g\׈,QYj|qiurXd}>V־)#&XTVs(N}kT`}i]y**9ɣޫzs7,gtWǟnatՇZm/2Tj#*+߷~տw[@.@!?GTWǿnaڷV]K?CR~V:?Կlϊ>ucwլ&J\zQkdo¾8SԽ>]&̓(fR1=.?o/ZO_ //P3uG8\dI&%*z_ph[Y)ghlEhiS|ь_ܶdzeQ_^~bQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEx&/s+oχ0S~~ڟl2?-:l}c1%Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@? 1zԔn(J`RHJ)hQOҘ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((($DGnsW~3|H ?M5?d7דׯ[(((((((((((((((((((((((((((((((((((((((((((((((?|en[~u ?JcOSM_W_-a:(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((oQOҘSel7Kgt)?a((]ԫokoK,*" arjK,(8ߵ6_AՇ6+ba;kQ_p|RkD =G7/OSW 4WZ?Q K.8\/x_p|RkD =G7/Od?p}E| K.Z?QP1w7/Op|RkD =GN/CW 4WZ?Q K.8\/x_p|RkD =G7/Od?p}E| K.Z?QP1w7/Op|RkD =GN/CW 4WZ?Q K.8\/x_p|RkD =G7/Od?p}E| K.Z?QP1w7/Op|RkD =GN/CW 4WZ?Q K.8\/x_p|RkD =G7/Od?p}E| K.Z?QP1w7/Op|RkD =GN/CW 4WZ?Q K.8\/x_p|RkD =G7/Od?p}E| K.Z?QP1w7/Op|RkD =GN/CW 4WZ?Q K.8\/x_p|RkD =G7/Od?p}E| K.Z?QP1w7/Op|RkD =GN/CW 4WZ?Q K.8\/x_p|RkD =G7/Od?p}E| K.Z?QP1w7/Op|RkD =GN/CW 4WZ?Q K.8\/x_p|RkD =G7/Od?p}E| K.Z?QP1w7/Op|RkD =GN/CW 4WZ?Q K.8\/x_p|RkD =G7/Od?p}E| K.Z?QP1w7/Op|RkD =GN/CW 4WZ?Q K.8\/x_p|RkD =G7/Od?p}E| K.Z?QP1w7/Op|RkD =GN/CW 4WZ?Q K.8\/x_p|RkD =G7/Od?p}E| K.Z?QP1w7/Op|RkD =GN/CW 4WZ?Q K.8\/x_p|RkD =G7/Od?p}E| K.Z?QP1w7/Op|RkD =GN/CW 4WZ?Q K.8\/x_p|RkD =G7/Od?p}E| K.Z?QP1w7/Op|RkD =GN/CW 4WZ?Q K.8\/x_p|RkD =G7/Od?p}E| K.Z?QP1w7/Op|RkD =GN/CW 4WZ?Q K.8\/x_p|RkD =G7/Od?p}E| K.Z?QP1w7/Op|RkD =GN/CW 4WZ?Q K.8\/x_p|RkD =G7/Od?p}E| K.Z?QP1w7/Op|RkD =GN/CW 4WZ?Q K.8\/x_p|RkD =G7/Od?p}E| K.Z?QP1w7/Op|RkD =GN/CW 4WZ?Q K.8\/x_p|RkD =G7/Od?p}E| K.Z?QP1w7/Op|RkD =GN/CW 4WZ?Q K.8\/x_p|RkD =G7/Od?p}E| K.Z?QP1w7/Op|RkD =GN/CW 4WZ?Q K.8\/x_p|RkD =G7/Od?p}E| K.Z?QP1w7/Op|RkD =GN/CW 4WZ?Q K.8\/x_p|RkD =G7/Od?p}E| K.Z?QP1w7/Op|RkD =GN/CW 4WZ?Q K.8\/x_p|RkD =G7/Od?p}E| K.Z?QP1w7/Op|RkD =GN/CW 4WZ?Q K.8\/x_p|RkD =G7/Od?p}E| K.Z?QP1w7/Op|RkD =GN/CW 4WZ?Q K.8h>)еRp|RPIֆ =O#1^zx2{C 2^M^nOw?y7z(((((((((((((((((((((((((((((((((((((((((((((((SM_W_-a:?|en[~u ?JcJ(>u+xc@V^iHov45U.GEC՟g5tAEW=p( Q@\(EP (QEŠ(.QEp( Q@\(EP (QEŠ(.QEp( Q@\(EP (QEŠ(.QEp(QEQE EP (QEŠ(.QEp)_P (_QEŠ(.QEQE. (!QGQEp((QE+QEQEŠ((.QEQEQEp( B(.QEp)Š(QEQEQEŠ(.QEp( Q@\(atQE-v Q@\(ER QL.QEp( Q@\(EP (QEŠ(.QEp( Q@\(EP (QEŠ(.QEp( Q@\(EP (QEŠ(*?D(-|_?x·_9f$DGnsP&ou䟲ɿk׭EPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPڟl2?-:l}c1&/s+cᧈ-|%ši%\s#ё8?e诙 ?熿Q? ?熿Q?: vL+ ljj׼ai'1`CQW{7­:?g=RA-Q\v <@: ~7X̷ks1:*5L*WRz~B˱-ۖޭjxE|@Ri:Gl "?,dzr#S|sC6}ԟ1$#QQ2N+U~Kq:5Og˯ck>$++=\@@ٷ>oOsC4GiG ˓t=qK1֚^kuTץ9-mNZl:֐^ºdD(o6!y?y#ڲ.<.HxW~R5)OzYI`I-nοmjM3Ok.&G1vVl|5>lU7l@7zԞ 7qo-YfxNi$lm6FSn-s*+ׯ(8.+~x{J)mX: XBL$[5X';k住VѮ$wq¬A!YrzpA i RjeGgɞ8:hhy5P[DLqF2;kmg=7l^6m4˹m73|dd(#ָe5PT]mE{p֯,g׉o&%7:gFG'񖡠}cG5]_h'&(ť4yopWzsrRCe֥%ӈF2,z >*OSiZƥ J+9 O κ/kJ\k_6XŇO}b 𮿲ԭF/Eq5j@~#n7Jᨯ^un4_ ^M$s4O : eZVM_sɍ:ʥ8+$|-<د4ΐknc%2Ka\\sV&-cv 4w@< ɂG;@ؓS^^1JIjME{~Κ7+]fed3e9\㌮zOz-SV>+C=mw:rEiGBw&em=_߆O|=! -f@h FK 5|J?ī+',g#:#rijމ{X6:S+m?rE|Yi]*K)%qg~Ҿ կRVܓmhH8zӣN4ճN'Co*cZԥ#\@ s2rk7_xAoefP 77#E{獾h? h>2Ӵ9#huiyWDF̠r5?Zo[k6׳AQYOʮMߕ< \>"U*ToCNœeV?][K7->ɧ}V@#1Kz mQ׈P>BMy>Ww7I.:E-a O}>|?E{СNgx(֭p-*f!_\Jv("pA9Z#ŗ(]Am(VEx' uD ZwlZ睭9(} {(՘kvr%I{I'UD))<8gֿ>93z}xN2Q1d;2c@~?JY7r-m E+xi>6go]^@Y5.=+*NL,NU{EN4!IZoWO+§~;4ar`xOto_G̵I~rOH vX?:Z爨I=ocCy@fFjq_rxGpxη6}e(B^k/$泉Zi)E!;~?~):V Os;O^"X%Iy{.X;K>y~?xgM5s8bsI]ͅBzUo|_6z7p"\xO5mNZ)X-Cep=|wmSNKY>v1XXΦ!|:%p;/Bt˨l/.v4QT7^=jO x'۬%h?Rixg5Jb݃D%o&J-HE[<Š(dYOy]~bhYdr>o@2x >L,V{Vt|0e+ӼM?5H'8SoDH~̃_x yfW{;?Ic|,e_{vw&GgTKJO_#J' z w$EG9@>`\ھƗ)Wv6XNs}UƓrK92ӕY:E>'ѯdL`K\{r;V }+k+ QDJ0.wƿR:_5WV*j|qќj_E x9Rgfʊ +_뭯K_:9eIԧ77w3FH+{(O,%Ȫ~_Ax ~隥2[$.K09ʚ /Bv?!^YL\_2Z1jU1t)8ͷ4*8շ#]j)ĺo~m? h_cIƋ;Unjq^w+§~:4ar`xOtxZ._ L!<5:VbG 95-Jpo9K߄5jǔ1ؐ]'1I$OgS싃ǙCv)*Nxt`엽/B!i_vFx^mRr/1#4]k?:_Ru%[&[)$Q#I$UFI$LR| WīI]zZL )1A)EhJ#I*Fz olLxO|ZG}=~(K euk[d'*H!_0XW|@ 񞭣8!mg"&n*Ex]Zќb۴_1I5-5Od t _xFү.Qml`=8~o9+fo_00jV<3_C{߈|3CDm|?Ÿs ؊xOh5S\ 3µUjݓϓѾLitY${U`"@5KÙ~{% lVtdT&tubVW8091UlpZvO4R;,+wH2h'^so>%׼9m> n oG_QUak?|Gsi.'P^3w}s Nz=~%|B%߯gy | t!Ǹs0|G{_.-_n)\{AxdI#b! Њ?Q>?jW1B*T߻==FI/zh4((((((((((((((((((((((((+EK E*? Ҷ?yb?լ߂?q❩UoBFJIa$!^^MGF17 cjZE$(?֯Wɞ z&ol1YO1#Umd|0G+&d0|Lj$DGnsW< ?9_/!xBl_Qծ#*u ,@U^ WK Q W%~MxGEߊ/4Jݭu {{cXRA#J( Yў6/}p߃0a+V;J (((((((((((((((((((((((((+oSDTU}JȃLgGv3KA=E_?W? l}|{tyvcp=IVԊY9=-R+w|ϝ+?4TFg'q}+{^3 U؅+eR7V_ 5Ug/,O&Ҟ$־)j6HN }VsRT?KT;\/|ir)fz'O].4PZF")ڥ7ŚXL41/m` [k!7OΜc˙-/o[IGV%և9>F6pJ1 p̹}2+hf{yRX2#A8 q^J+YD#i4lyod#ojx Xn͌g|qow EhP33{V/$ ?{=); im-I l*!с==kœdRۛ^G_ 7.qM0xgTY]XI&-hxaIshRA&lC狼[]Kmu611I!UdaOm{w=ڞqK%ĭ ,I8$xR_Τlj˷mO>#GM;=3H&_ yO?n߲AčC2k|a#v_ƭ²0KGvBZKᯀ~0 XFoGqY߶ƵNKMS&DC^7*YE=zqj.%yifr{CZ錆SLy ,Ƞ>W5ܟH /y9}_Y^ZwZy$,1\ďxƺs+:쐩bDq)!{c$Dutw6SBѣ?"_?^{+񖥦Dȋ3=HYb'*_BSMF|~Cng?g]tx%eYcϸ`PѿYEI?ٟ7[=UaaibR 8?h>3xoTFPj_(enm>h;/U<3_:i+ڧrEsxccu~|^?-kė{s,~)xm$fԬ-dNEpڠ_/Wп?8{o%cMOxPo͟=Wqῆ4gm-݌+5ġ!A!r8-x8"IhO-<2F捑W'%@khRWfRS3ץgn#cʹ ?4ӟzWϵA??+UrK o?&ZQ+ak_|]2א5_ ZloYlU/HU%P׌?ͫ}/߁>46`M!T<>ګJWk;K kLfME1.`Ip<2\{,? C{\W'өM)bzIROo-\^v7kϊ^@N;q|,ԓIứdi}f='5)7BvVokBFܜ#q>VΓ }8~.,;gcPO? :<#ѥ T?Ҡ֬|3kmBF}\EK=* [-$(U>>%~Ϟ"PMj2X$HB*/ ?~[4(,KT=llx ԷS\YvGnӴRb$gbpj}'6~ ğ&maa q~EL^"8J;]v|U}5o=Mk_*]q1]N68ls|Cz&wa}Cyo+E4m08?_z[v~"ccRG'hNW>_zǟ xºď%[|~fi+S[Z*:oCTY=/%c??SK!+# Fi/,Z脯BVY~uC+GN> ic_7Ϲ`~a\u}Oh:mfHAu^e 1 5s{jϦy.[hԘ@@UAF6|b'[Zߵ6 dyqOÙ?e~Ϟ!~PMj2X$HB+*Sv~T> b?cw?R_wR]Ms;gI3^<ž`/}'>#;+ʬ{gSh~S/g._q|_B?h|(z_ly|_PW_ 7C7zAdȱƍ ɟq_>xKfo%3IO_Br5ִ xZxⱴA0hLd| 4J\0 +l^ÿY3-Ť<N+j Go$"%E/?\W4Ǐ+EרxB9>xYQSpNߛ= fnSi .`6 {W+,gZ ѡ+ qV=A6o|=fhÜ c?y~Dھ߄5 fE 1NCz2aR^O=W?WoZG=z/ ?ux_9>ӯGK8pu=%3ݴfuoj+={ 8xM{_=#k8 5e4L0;gQEPQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEUYUtOƨP ,_/hl"*ZJo:@<#GZ6>ׇKfyMK6>5g #pyI?~'6 nFfzbw~gS#~R!_& !_& EfhyO)6cژv Q8$t#I(((((((((((((((((((((((((((((((((((((((((((((((((,Qkə:LƯCs\޼o,U zRy~ϟ ~gC$QE{'QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE|@wCдˋJM$nIU=>\-s䵎ѫ8FP|92d9!~e{2pGUR+A¢bRTfqK?Er22HFNykxz=h^|W\0pigdDteo]Mim dfPچ4+o-4$?JŢ==k|?k?g:?25{x BTY--l,}ZF>z9WԾpCu|9| 1Km+H,2'tEkR:)h'MXu)8d`r?Zipw古2}NLj]hv>,Ėunb@KѲ O?Rk~IU{>Q7Im+hx޹dt]ď='NҾYrb{(מU:N5$㷐Zp^pCoP{:OؠմZU$m|c =(xUWBVtefnx_ž.--U9+ipֽ~Ogd~*J+Hz8#NV_#uڊP>cDіW9\28?]w|7lt6^bv-6ܓ9s*!ag*XDyo(N|Oſ j|2?o#*p:ݏ--kq'nO\%ưt#x\ի|ڽց62ndYb{09GzEu9]|P|Pw٤7+sDI!q?o|1#D>KIM>cxj\xT%6o)+md9d y01TcBne w^d-1 H#p@Fޥ8փ5tiԕ)FǍcxO9]M`f#=qMVˋxe lJ\ӄDJ"Zލ >; 6 X)$cwa6>mv _|=-l# \]FЃԦzu9.Fy$r{kiQWM{2XSøGyo؊(H ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( xʈ?Jc\}/[58 T`.!^Նv+i+ $ 2#"Ŗ_>nmU .r}[E^ad:[_ʁ*ޮ-QE!Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@%-wiadਮ '?~)ˢ}g Sb_2uh!C._f'N'N ~)ˢ} '?p88*+!C._fb_2uh0dਮ '?~)ˢ}g Sb_2uh!C._f'N'N ~)ˢ} '?p88*+!C._fb_2uh0dਮ '?~)ˢ}g Sb_2uh!C._f&Sb_2uh!C._f'N'N ~)ˢ} '?p88*+!C._fb_2uh0dਮ '?~)ˢ}g Sb_2uh!C._f'N'N ~)ˢ} '?p88*+!C._fb_2uh0dਮ '?~)ˢ}g Sb_2uh!C._f'N+N ~)ˢ} '?p88*+!C._fb_2uh0d⿔ਮ '?~)ˢ}g Sb_2uh!C._f'N'N ~)ˢ} '?p88*+!C._fb_2uh0dਮ '?~)ˢ}g Sb_2uh!C._f'N'N ~)ˢ} '?p88*+!C._fb_2uh0d⿔ਮ '?~)ˢ}g ߔਮ '?~)ˢ}g Sb_2uh!C._f'N'N ~)ˢ} '?p88*+!C._fb_2uh0dਮ '?~)ˢ}g Sb_2uh!C._f'N'N ~)ˢ} '?p88*+!C._fb_2uh0dਮ '?~)ˢ}g Sb_2uh!C._f'N'N ~)ˢ} '?p88*+!C._fb_2uh0dਮ '?~)ˢ}g Sb_2uh!C._f'N'N ~)ˢ} '?p88*+!C._fb_2uh0dਮ '?~)ˢ}g Sb_2uh!C._f'N'N ~)ˢ} '?p88*+!C._fb_2uh0dਮ '?~)ˢ}g Sb_2uh!C._f'N'N ~)ˢ} '?p88*+!C._fb_2uh0dਮ '?~)ˢ}g S/؛FUD?z1/tO4(|Kx gI+s(~0վ%d~EsUhVd]O_5ag$_>}8_O5AkpU.7w2W־?L=mM3]f r;$;GJ4]=+$wQE|EPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPQKE (Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@ E-(`QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEbpfcc-0.12.0/examples/networking/vlan_filter/test_setup.sh000077500000000000000000000122271357404205000237230ustar00rootroot00000000000000#!/bin/bash # This script must be executed by root user if [ "$(id -u)" != "0" ]; then echo "This script must be run as root" 1>&2 exit 1 fi # add namespaces ip netns add netns11 ip netns add netns12 ip netns add netns21 ip netns add netns22 ip netns add netns3 ip netns add netns4 # set up veth devices in netns11 to netns21 with connection to netns3 ip link add veth11 type veth peer name veth13 ip link add veth21 type veth peer name veth23 ip link set veth11 netns netns11 ip link set veth21 netns netns21 ip link set veth13 netns netns3 ip link set veth23 netns netns3 # set up veth devices in netns12 and netns22 with connection to netns4 ip link add veth12 type veth peer name veth14 ip link add veth22 type veth peer name veth24 ip link set veth12 netns netns12 ip link set veth22 netns netns22 ip link set veth14 netns netns4 ip link set veth24 netns netns4 # assign IP addresses and set the devices up ip netns exec netns11 ifconfig veth11 192.168.100.11/24 up ip netns exec netns11 ip link set lo up ip netns exec netns12 ifconfig veth12 192.168.100.12/24 up ip netns exec netns12 ip link set lo up ip netns exec netns21 ifconfig veth21 192.168.200.21/24 up ip netns exec netns21 ip link set lo up ip netns exec netns22 ifconfig veth22 192.168.200.22/24 up ip netns exec netns22 ip link set lo up # set up bridge brx and its ports ip netns exec netns3 brctl addbr brx ip netns exec netns3 ip link set brx up ip netns exec netns3 ip link set veth13 up ip netns exec netns3 ip link set veth23 up ip netns exec netns3 brctl addif brx veth13 ip netns exec netns3 brctl addif brx veth23 # set up bridge bry and its ports ip netns exec netns4 brctl addbr bry ip netns exec netns4 ip link set bry up ip netns exec netns4 ip link set veth14 up ip netns exec netns4 ip link set veth24 up ip netns exec netns4 brctl addif bry veth14 ip netns exec netns4 brctl addif bry veth24 # create veth devices to connect the bridges ip link add vethx type veth peer name vethx11 ip link add vethy type veth peer name vethy11 ip link set vethx netns netns3 ip link set vethx11 netns netns3 ip link set vethy netns netns4 ip link set vethy11 netns netns4 ip netns exec netns3 brctl addif brx vethx ip netns exec netns3 ip link set vethx up ip netns exec netns3 bridge vlan add vid 100 tagged dev vethx ip netns exec netns3 bridge vlan add vid 200 tagged dev vethx ip netns exec netns3 bridge vlan del vid 1 dev vethx ip netns exec netns3 bridge vlan show ip netns exec netns4 brctl addif bry vethy ip netns exec netns4 ip link set vethy up ip netns exec netns4 bridge vlan add vid 100 tagged dev vethy ip netns exec netns4 bridge vlan add vid 200 tagged dev vethy ip netns exec netns4 bridge vlan del vid 1 dev vethy ip netns exec netns4 bridge vlan show ip netns exec netns3 ip link set dev brx type bridge vlan_filtering 1 ip netns exec netns4 ip link set dev bry type bridge vlan_filtering 1 ip netns exec netns3 bridge vlan del vid 1 dev brx self ip netns exec netns4 bridge vlan del vid 1 dev bry self ip netns exec netns3 bridge vlan show ip netns exec netns4 bridge vlan show ip netns exec netns3 bridge vlan add vid 100 pvid untagged dev veth13 ip netns exec netns3 bridge vlan add vid 200 pvid untagged dev veth23 ip netns exec netns4 bridge vlan add vid 100 pvid untagged dev veth14 ip netns exec netns4 bridge vlan add vid 200 pvid untagged dev veth24 ip netns exec netns3 bridge vlan del vid 1 dev veth13 ip netns exec netns3 bridge vlan del vid 1 dev veth23 ip netns exec netns4 bridge vlan del vid 1 dev veth14 ip netns exec netns4 bridge vlan del vid 1 dev veth24 # set up bridge brvx and its ports ip netns exec netns3 brctl addbr brvx ip netns exec netns3 ip link set brvx up ip netns exec netns3 ip link set vethx11 up ip netns exec netns3 brctl addif brvx vethx11 # set up bridge brvy and its ports ip netns exec netns4 brctl addbr brvy ip netns exec netns4 ip link set brvy up ip netns exec netns4 ip link set vethy11 up ip netns exec netns4 brctl addif brvy vethy11 # create veth devices to connect the vxlan bridges ip link add veth3 type veth peer name veth4 ip link add veth5 type veth peer name veth6 ip link set veth3 netns netns3 ip link set veth5 netns netns4 ip netns exec netns3 ip link set veth3 up ip netns exec netns4 ip link set veth5 up ip link set veth4 up ip link set veth6 up ip netns exec netns3 ifconfig veth3 10.1.1.11/24 up ip netns exec netns4 ifconfig veth5 10.1.1.12/24 up # add vxlan ports ip netns exec netns3 ip link add vxlan-10 type vxlan id 10 remote 10.1.1.12 dstport 4789 dev veth3 ip netns exec netns4 ip link add vxlan-10 type vxlan id 10 remote 10.1.1.11 dstport 4789 dev veth5 ip netns exec netns3 ip link set vxlan-10 up ip netns exec netns4 ip link set vxlan-10 up ip netns exec netns3 brctl addif brvx vxlan-10 ip netns exec netns4 brctl addif brvy vxlan-10 # create veth devices to connect the vxlan bridges ip link add veth7 type veth peer name veth8 ip link set veth7 up ip link set veth8 up # set up bridge brjx and its ports brctl addbr brjx ip link set brjx up ip link set veth4 up brctl addif brjx veth4 brctl addif brjx veth7 # set up bridge brjy and its ports brctl addbr brjy ip link set brjy up ip link set veth6 up brctl addif brjy veth6 brctl addif brjy veth8 bpfcc-0.12.0/examples/networking/vlan_filter/test_traffic.sh000077500000000000000000000001551357404205000241760ustar00rootroot00000000000000#!/bin/bash ip netns exec netns11 ping 192.168.100.12 -c 10 ip netns exec netns22 ping 192.168.200.21 -c 10 bpfcc-0.12.0/examples/networking/vlan_learning/000077500000000000000000000000001357404205000214735ustar00rootroot00000000000000bpfcc-0.12.0/examples/networking/vlan_learning/CMakeLists.txt000066400000000000000000000004311357404205000242310ustar00rootroot00000000000000set(EXAMPLE_FILES README.txt simulation.py vlan_learning.c) set(EXAMPLE_PROGRAMS vlan_learning.py) install(FILES ${EXAMPLE_FILES} DESTINATION share/bcc/examples/networking/vlan_learning) install(PROGRAMS ${EXAMPLE_PROGRAMS} DESTINATION share/bcc/examples/networking/vlan_learning) bpfcc-0.12.0/examples/networking/vlan_learning/README.txt000066400000000000000000000037561357404205000232040ustar00rootroot00000000000000This example shows a unique way to use a BPF program to demux any ethernet traffic into a pool of worker veth+namespaces (or any ifindex-based destination) depending on a configurable mapping of src-mac to ifindex. As part of the ingress processing, the program will dynamically learn the source ifindex of the matched source mac. Simulate a physical network with a vlan aware switch and clients that may connect to any vlan. The program will detect the known clients and pass the traffic through to a dedicated namespace for processing. Clients may have overlapping IP spaces and the traffic will still work. | bpf program | cli0 --| | /--|-- worker0 | cli1 --| trunk | +->--->-handle_p2v(pkt)-> /---|-- worker1 | cli2 --|=======|=+ /----|-- worker2 | ... --| | +-<---<-handle_v2p(pkt)-<-----|-- ... | cliN --| | \----|-- workerM | | | ^ | phys | veth | switch | | To run the example, simply: sudo /path/to/vlan_learning/vlan_learning.py Serving HTTP on 0.0.0.0 port 80 ... Serving HTTP on 0.0.0.0 port 80 ... Serving HTTP on 0.0.0.0 port 80 ... % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0172.16.1.100 - - [04/Nov/2015 10:54:47] "GET / HTTP/1.1" 200 - 100 574 100 574 0 0 45580 0 --:--:-- --:--:-- --:--:-- 47833 ... Press enter to exit: mac 020000000000 rx pkts = 95, rx bytes = 7022 tx pkts = 0, tx bytes = 0 mac 020000000001 rx pkts = 95, rx bytes = 7022 tx pkts = 0, tx bytes = 0 mac 020000000002 rx pkts = 97, rx bytes = 7154 tx pkts = 0, tx bytes = 0 bpfcc-0.12.0/examples/networking/vlan_learning/simulation.py000077700000000000000000000000001357404205000271752../simulation.pyustar00rootroot00000000000000bpfcc-0.12.0/examples/networking/vlan_learning/vlan_learning.c000066400000000000000000000037601357404205000244640ustar00rootroot00000000000000// Copyright (c) PLUMgrid, Inc. // Licensed under the Apache License, Version 2.0 (the "License") #include struct ifindex_leaf_t { int out_ifindex; int vlan_tci; // populated by phys2virt and used by virt2phys int vlan_proto; // populated by phys2virt and used by virt2phys u64 tx_pkts; u64 tx_bytes; }; // redirect based on mac -> out_ifindex (auto-learning) BPF_HASH(egress, int, struct ifindex_leaf_t, 4096); // redirect based on mac -> out_ifindex (config-driven) BPF_HASH(ingress, u64, struct ifindex_leaf_t, 4096); int handle_phys2virt(struct __sk_buff *skb) { // only handle vlan packets if (!skb->vlan_present) return 1; u8 *cursor = 0; ethernet: { struct ethernet_t *ethernet = cursor_advance(cursor, sizeof(*ethernet)); u64 src_mac = ethernet->src; struct ifindex_leaf_t *leaf = ingress.lookup(&src_mac); if (leaf) { lock_xadd(&leaf->tx_pkts, 1); lock_xadd(&leaf->tx_bytes, skb->len); // auto-program reverse direction table int out_ifindex = leaf->out_ifindex; struct ifindex_leaf_t zleaf = {0}; struct ifindex_leaf_t *out_leaf = egress.lookup_or_try_init(&out_ifindex, &zleaf); if (out_leaf) { // to capture potential configuration changes out_leaf->out_ifindex = skb->ifindex; out_leaf->vlan_tci = skb->vlan_tci; out_leaf->vlan_proto = skb->vlan_proto; } // pop the vlan header and send to the destination bpf_skb_vlan_pop(skb); bpf_clone_redirect(skb, leaf->out_ifindex, 0); } } return 1; } int handle_virt2phys(struct __sk_buff *skb) { u8 *cursor = 0; ethernet: { struct ethernet_t *ethernet = cursor_advance(cursor, sizeof(*ethernet)); int src_ifindex = skb->ifindex; struct ifindex_leaf_t *leaf = egress.lookup(&src_ifindex); if (leaf) { lock_xadd(&leaf->tx_pkts, 1); lock_xadd(&leaf->tx_bytes, skb->len); bpf_skb_vlan_push(skb, leaf->vlan_proto, leaf->vlan_tci); bpf_clone_redirect(skb, leaf->out_ifindex, 0); } } return 1; } bpfcc-0.12.0/examples/networking/vlan_learning/vlan_learning.py000077500000000000000000000072401357404205000246720ustar00rootroot00000000000000#!/usr/bin/python # Copyright (c) PLUMgrid, Inc. # Licensed under the Apache License, Version 2.0 (the "License") from bcc import BPF from builtins import input from pyroute2 import IPRoute, NetNS, IPDB, NSPopen from random import shuffle from time import sleep from simulation import Simulation import sys ipr = IPRoute() ipdb = IPDB(nl=ipr) num_clients = 3 num_vlans = 16 # load the bpf program b = BPF(src_file="vlan_learning.c", debug=0) phys_fn = b.load_func("handle_phys2virt", BPF.SCHED_CLS) virt_fn = b.load_func("handle_virt2phys", BPF.SCHED_CLS) ingress = b.get_table("ingress") egress = b.get_table("egress") class VlanSimulation(Simulation): def __init__(self, ipdb): super(VlanSimulation, self).__init__(ipdb) def start(self): # start identical workers each in a namespace for i in range(0, num_clients): httpmod = ("SimpleHTTPServer" if sys.version_info[0] < 3 else "http.server") cmd = ["python", "-m", httpmod, "80"] self._create_ns("worker%d" % i, cmd=cmd, fn=virt_fn, action="drop", ipaddr="172.16.1.5/24") # simulate a physical eth vlan trunk with self.ipdb.create(ifname="eth0a", kind="veth", peer="eth0b") as v: v.up() self.ipdb.interfaces.eth0b.up().commit() # eth0a will be hooked to clients with vlan interfaces # add the bpf program to eth0b for demuxing phys2virt packets v = self.ipdb.interfaces["eth0b"] ipr.tc("add", "ingress", v["index"], "ffff:") ipr.tc("add-filter", "bpf", v["index"], ":1", fd=phys_fn.fd, name=phys_fn.name, parent="ffff:", action="drop", classid=1) # allocate vlans randomly available_vlans = [i for i in range(2, 2 + num_vlans)] shuffle(available_vlans) available_ips = [[i for i in range(100, 105)] for i in range(0, num_clients)] # these are simulations of physical clients for i in range(0, num_clients): macaddr = ("02:00:00:%.2x:%.2x:%.2x" % ((i >> 16) & 0xff, (i >> 8) & 0xff, i & 0xff)) # assign this client to the given worker idx = self.ipdb.interfaces["worker%da" % i]["index"] mac = int(macaddr.replace(":", ""), 16) ingress[ingress.Key(mac)] = ingress.Leaf(idx, 0, 0, 0, 0) # test traffic with curl loop cmd = ["bash", "-c", "for i in {1..8}; do curl 172.16.1.5 -o /dev/null; sleep 1; done"] client_ifc = self.ipdb.create(ifname="eth0a.%d" % i, kind="vlan", link=self.ipdb.interfaces["eth0a"], vlan_id=available_vlans.pop(0)).commit() (out_ifc, in_ifc) = self._create_ns("client%d" % i, in_ifc=client_ifc, ipaddr="172.16.1.100/24", macaddr=macaddr, cmd=cmd)[1:3] try: sim = VlanSimulation(ipdb) sim.start() sleep(10) input("Press enter to exit: ") stats_collect = {} for key, leaf in ingress.items(): stats_collect[key.value] = [leaf.tx_pkts, leaf.tx_bytes, 0, 0] for key, leaf in egress.items(): x = stats_collect.get(key.value, [0, 0, 0, 0]) x[2] = leaf.tx_pkts x[3] = leaf.tx_bytes for k, v in stats_collect.items(): print("mac %.12x rx pkts = %u, rx bytes = %u" % (k, v[0], v[1])) print(" tx pkts = %u, tx bytes = %u" % (v[2], v[3])) finally: if "eth0a" in ipdb.interfaces: ipdb.interfaces.eth0a.remove().commit() if "sim" in locals(): sim.release() ipdb.release() bpfcc-0.12.0/examples/networking/xdp/000077500000000000000000000000001357404205000174475ustar00rootroot00000000000000bpfcc-0.12.0/examples/networking/xdp/CMakeLists.txt000066400000000000000000000001451357404205000222070ustar00rootroot00000000000000file(GLOB PY_FILES *.py) install(PROGRAMS ${PY_FILES} DESTINATION share/bcc/examples/networking/xdp) bpfcc-0.12.0/examples/networking/xdp/xdp_drop_count.py000077500000000000000000000101241357404205000230510ustar00rootroot00000000000000#!/usr/bin/python # # xdp_drop_count.py Drop incoming packets on XDP layer and count for which # protocol type # # Copyright (c) 2016 PLUMgrid # Copyright (c) 2016 Jan Ruth # Licensed under the Apache License, Version 2.0 (the "License") from bcc import BPF import pyroute2 import time import sys flags = 0 def usage(): print("Usage: {0} [-S] ".format(sys.argv[0])) print(" -S: use skb mode\n") print(" -H: use hardware offload mode\n") print("e.g.: {0} eth0\n".format(sys.argv[0])) exit(1) if len(sys.argv) < 2 or len(sys.argv) > 3: usage() offload_device = None if len(sys.argv) == 2: device = sys.argv[1] elif len(sys.argv) == 3: device = sys.argv[2] maptype = "percpu_array" if len(sys.argv) == 3: if "-S" in sys.argv: # XDP_FLAGS_SKB_MODE flags |= (1 << 1) if "-H" in sys.argv: # XDP_FLAGS_HW_MODE maptype = "array" offload_device = device flags |= (1 << 3) mode = BPF.XDP #mode = BPF.SCHED_CLS if mode == BPF.XDP: ret = "XDP_DROP" ctxtype = "xdp_md" else: ret = "TC_ACT_SHOT" ctxtype = "__sk_buff" # load BPF program b = BPF(text = """ #define KBUILD_MODNAME "foo" #include #include #include #include #include #include #include BPF_TABLE(MAPTYPE, uint32_t, long, dropcnt, 256); static inline int parse_ipv4(void *data, u64 nh_off, void *data_end) { struct iphdr *iph = data + nh_off; if ((void*)&iph[1] > data_end) return 0; return iph->protocol; } static inline int parse_ipv6(void *data, u64 nh_off, void *data_end) { struct ipv6hdr *ip6h = data + nh_off; if ((void*)&ip6h[1] > data_end) return 0; return ip6h->nexthdr; } int xdp_prog1(struct CTXTYPE *ctx) { void* data_end = (void*)(long)ctx->data_end; void* data = (void*)(long)ctx->data; struct ethhdr *eth = data; // drop packets int rc = RETURNCODE; // let pass XDP_PASS or redirect to tx via XDP_TX long *value; uint16_t h_proto; uint64_t nh_off = 0; uint32_t index; nh_off = sizeof(*eth); if (data + nh_off > data_end) return rc; h_proto = eth->h_proto; // parse double vlans #pragma unroll for (int i=0; i<2; i++) { if (h_proto == htons(ETH_P_8021Q) || h_proto == htons(ETH_P_8021AD)) { struct vlan_hdr *vhdr; vhdr = data + nh_off; nh_off += sizeof(struct vlan_hdr); if (data + nh_off > data_end) return rc; h_proto = vhdr->h_vlan_encapsulated_proto; } } if (h_proto == htons(ETH_P_IP)) index = parse_ipv4(data, nh_off, data_end); else if (h_proto == htons(ETH_P_IPV6)) index = parse_ipv6(data, nh_off, data_end); else index = 0; value = dropcnt.lookup(&index); if (value) __sync_fetch_and_add(value, 1); return rc; } """, cflags=["-w", "-DRETURNCODE=%s" % ret, "-DCTXTYPE=%s" % ctxtype, "-DMAPTYPE=\"%s\"" % maptype], device=offload_device) fn = b.load_func("xdp_prog1", mode, offload_device) if mode == BPF.XDP: b.attach_xdp(device, fn, flags) else: ip = pyroute2.IPRoute() ipdb = pyroute2.IPDB(nl=ip) idx = ipdb.interfaces[device].index ip.tc("add", "clsact", idx) ip.tc("add-filter", "bpf", idx, ":1", fd=fn.fd, name=fn.name, parent="ffff:fff2", classid=1, direct_action=True) dropcnt = b.get_table("dropcnt") prev = [0] * 256 print("Printing drops per IP protocol-number, hit CTRL+C to stop") while 1: try: for k in dropcnt.keys(): val = dropcnt[k].value if maptype == "array" else dropcnt.sum(k).value i = k.value if val: delta = val - prev[i] prev[i] = val print("{}: {} pkt/s".format(i, delta)) time.sleep(1) except KeyboardInterrupt: print("Removing filter from device") break; if mode == BPF.XDP: b.remove_xdp(device, flags) else: ip.tc("del", "clsact", idx) ipdb.release() bpfcc-0.12.0/examples/networking/xdp/xdp_macswap_count.py000077500000000000000000000107411357404205000235450ustar00rootroot00000000000000#!/usr/bin/python # # xdp_macswap_count.py Swap Source and Destination MAC addresses on # incoming packets and transmit packets back on # same interface in XDP layer and count for which # protocol type # # Copyright (c) 2016 PLUMgrid # Copyright (c) 2016 Jan Ruth # Copyright (c) 2018 Andy Gospodarek # Licensed under the Apache License, Version 2.0 (the "License") from bcc import BPF import pyroute2 import time import sys flags = 0 def usage(): print("Usage: {0} [-S] ".format(sys.argv[0])) print(" -S: use skb mode\n") print("e.g.: {0} eth0\n".format(sys.argv[0])) exit(1) if len(sys.argv) < 2 or len(sys.argv) > 3: usage() if len(sys.argv) == 2: device = sys.argv[1] if len(sys.argv) == 3: if "-S" in sys.argv: # XDP_FLAGS_SKB_MODE flags |= 2 << 0 if "-S" == sys.argv[1]: device = sys.argv[2] else: device = sys.argv[1] mode = BPF.XDP #mode = BPF.SCHED_CLS if mode == BPF.XDP: ret = "XDP_TX" ctxtype = "xdp_md" else: ret = "TC_ACT_SHOT" ctxtype = "__sk_buff" # load BPF program b = BPF(text = """ #define KBUILD_MODNAME "foo" #include #include #include #include #include #include #include BPF_TABLE("percpu_array", uint32_t, long, dropcnt, 256); static inline int parse_ipv4(void *data, u64 nh_off, void *data_end) { struct iphdr *iph = data + nh_off; if ((void*)&iph[1] > data_end) return 0; return iph->protocol; } static inline int parse_ipv6(void *data, u64 nh_off, void *data_end) { struct ipv6hdr *ip6h = data + nh_off; if ((void*)&ip6h[1] > data_end) return 0; return ip6h->nexthdr; } static void swap_src_dst_mac(void *data) { unsigned short *p = data; unsigned short dst[3]; dst[0] = p[0]; dst[1] = p[1]; dst[2] = p[2]; p[0] = p[3]; p[1] = p[4]; p[2] = p[5]; p[3] = dst[0]; p[4] = dst[1]; p[5] = dst[2]; } int xdp_prog1(struct CTXTYPE *ctx) { void* data_end = (void*)(long)ctx->data_end; void* data = (void*)(long)ctx->data; struct ethhdr *eth = data; // drop packets int rc = RETURNCODE; // let pass XDP_PASS or redirect to tx via XDP_TX long *value; uint16_t h_proto; uint64_t nh_off = 0; uint32_t index; nh_off = sizeof(*eth); if (data + nh_off > data_end) return rc; h_proto = eth->h_proto; if (h_proto == htons(ETH_P_8021Q) || h_proto == htons(ETH_P_8021AD)) { struct vlan_hdr *vhdr; vhdr = data + nh_off; nh_off += sizeof(struct vlan_hdr); if (data + nh_off > data_end) return rc; h_proto = vhdr->h_vlan_encapsulated_proto; } if (h_proto == htons(ETH_P_8021Q) || h_proto == htons(ETH_P_8021AD)) { struct vlan_hdr *vhdr; vhdr = data + nh_off; nh_off += sizeof(struct vlan_hdr); if (data + nh_off > data_end) return rc; h_proto = vhdr->h_vlan_encapsulated_proto; } if (h_proto == htons(ETH_P_IP)) index = parse_ipv4(data, nh_off, data_end); else if (h_proto == htons(ETH_P_IPV6)) index = parse_ipv6(data, nh_off, data_end); else index = 0; if (index == IPPROTO_UDP) { swap_src_dst_mac(data); rc = XDP_TX; } value = dropcnt.lookup(&index); if (value) *value += 1; return rc; } """, cflags=["-w", "-DRETURNCODE=%s" % ret, "-DCTXTYPE=%s" % ctxtype]) fn = b.load_func("xdp_prog1", mode) if mode == BPF.XDP: b.attach_xdp(device, fn, flags) else: ip = pyroute2.IPRoute() ipdb = pyroute2.IPDB(nl=ip) idx = ipdb.interfaces[device].index ip.tc("add", "clsact", idx) ip.tc("add-filter", "bpf", idx, ":1", fd=fn.fd, name=fn.name, parent="ffff:fff2", classid=1, direct_action=True) dropcnt = b.get_table("dropcnt") prev = [0] * 256 print("Printing drops per IP protocol-number, hit CTRL+C to stop") while 1: try: for k in dropcnt.keys(): val = dropcnt.sum(k).value i = k.value if val: delta = val - prev[i] prev[i] = val print("{}: {} pkt/s".format(i, delta)) time.sleep(1) except KeyboardInterrupt: print("Removing filter from device") break; if mode == BPF.XDP: b.remove_xdp(device, flags) else: ip.tc("del", "clsact", idx) ipdb.release() bpfcc-0.12.0/examples/networking/xdp/xdp_redirect_cpu.py000077500000000000000000000041021357404205000233440ustar00rootroot00000000000000#!/usr/bin/python # # xdp_redirect_cpu.py Redirect the incoming packet to the specific CPU # # Copyright (c) 2018 Gary Lin # Licensed under the Apache License, Version 2.0 (the "License") from bcc import BPF import time import sys from multiprocessing import cpu_count import ctypes as ct flags = 0 def usage(): print("Usage: {0} ".format(sys.argv[0])) print("e.g.: {0} eth0 2\n".format(sys.argv[0])) exit(1) if len(sys.argv) != 3: usage() in_if = sys.argv[1] cpu_id = int(sys.argv[2]) max_cpu = cpu_count() if (cpu_id > max_cpu): print("Invalid CPU id") exit(1) # load BPF program b = BPF(text = """ #define KBUILD_MODNAME "foo" #include #include #include BPF_CPUMAP(cpumap, __MAX_CPU__); BPF_ARRAY(dest, uint32_t, 1); BPF_PERCPU_ARRAY(rxcnt, long, 1); int xdp_redirect_cpu(struct xdp_md *ctx) { void* data_end = (void*)(long)ctx->data_end; void* data = (void*)(long)ctx->data; struct ethhdr *eth = data; uint32_t key = 0; long *value; uint32_t *cpu; uint64_t nh_off; nh_off = sizeof(*eth); if (data + nh_off > data_end) return XDP_DROP; cpu = dest.lookup(&key); if (!cpu) return XDP_PASS; value = rxcnt.lookup(&key); if (value) *value += 1; return cpumap.redirect_map(*cpu, 0); } int xdp_dummy(struct xdp_md *ctx) { return XDP_PASS; } """, cflags=["-w", "-D__MAX_CPU__=%u" % max_cpu], debug=0) dest = b.get_table("dest") dest[0] = ct.c_uint32(cpu_id) cpumap = b.get_table("cpumap") cpumap[cpu_id] = ct.c_uint32(192) in_fn = b.load_func("xdp_redirect_cpu", BPF.XDP) b.attach_xdp(in_if, in_fn, flags) rxcnt = b.get_table("rxcnt") prev = 0 print("Printing redirected packets, hit CTRL+C to stop") while 1: try: val = rxcnt.sum(0).value if val: delta = val - prev prev = val print("{} pkt/s to CPU {}".format(delta, cpu_id)) time.sleep(1) except KeyboardInterrupt: print("Removing filter from device") break b.remove_xdp(in_if, flags) bpfcc-0.12.0/examples/networking/xdp/xdp_redirect_map.py000077500000000000000000000043771357404205000233500ustar00rootroot00000000000000#!/usr/bin/python # # xdp_redirect_map.py Redirect the incoming packet to another interface # with the helper: bpf_redirect_map() # # Copyright (c) 2018 Gary Lin # Licensed under the Apache License, Version 2.0 (the "License") from bcc import BPF import pyroute2 import time import sys import ctypes as ct flags = 0 def usage(): print("Usage: {0} ".format(sys.argv[0])) print("e.g.: {0} eth0 eth1\n".format(sys.argv[0])) exit(1) if len(sys.argv) != 3: usage() in_if = sys.argv[1] out_if = sys.argv[2] ip = pyroute2.IPRoute() out_idx = ip.link_lookup(ifname=out_if)[0] # load BPF program b = BPF(text = """ #define KBUILD_MODNAME "foo" #include #include #include BPF_DEVMAP(tx_port, 1); BPF_PERCPU_ARRAY(rxcnt, long, 1); static inline void swap_src_dst_mac(void *data) { unsigned short *p = data; unsigned short dst[3]; dst[0] = p[0]; dst[1] = p[1]; dst[2] = p[2]; p[0] = p[3]; p[1] = p[4]; p[2] = p[5]; p[3] = dst[0]; p[4] = dst[1]; p[5] = dst[2]; } int xdp_redirect_map(struct xdp_md *ctx) { void* data_end = (void*)(long)ctx->data_end; void* data = (void*)(long)ctx->data; struct ethhdr *eth = data; uint32_t key = 0; long *value; uint64_t nh_off; nh_off = sizeof(*eth); if (data + nh_off > data_end) return XDP_DROP; value = rxcnt.lookup(&key); if (value) *value += 1; swap_src_dst_mac(data); return tx_port.redirect_map(0, 0); } int xdp_dummy(struct xdp_md *ctx) { return XDP_PASS; } """, cflags=["-w"]) tx_port = b.get_table("tx_port") tx_port[0] = ct.c_int(out_idx) in_fn = b.load_func("xdp_redirect_map", BPF.XDP) out_fn = b.load_func("xdp_dummy", BPF.XDP) b.attach_xdp(in_if, in_fn, flags) b.attach_xdp(out_if, out_fn, flags) rxcnt = b.get_table("rxcnt") prev = 0 print("Printing redirected packets, hit CTRL+C to stop") while 1: try: val = rxcnt.sum(0).value if val: delta = val - prev prev = val print("{} pkt/s".format(delta)) time.sleep(1) except KeyboardInterrupt: print("Removing filter from device") break; b.remove_xdp(in_if, flags) b.remove_xdp(out_if, flags) bpfcc-0.12.0/examples/tracing/000077500000000000000000000000001357404205000161145ustar00rootroot00000000000000bpfcc-0.12.0/examples/tracing/CMakeLists.txt000066400000000000000000000004241357404205000206540ustar00rootroot00000000000000file(GLOB C_FILES *.c) file(GLOB PY_FILES *.py) file(GLOB TXT_FILES *.txt) install(PROGRAMS ${PY_FILES} DESTINATION share/bcc/examples/tracing) install(FILES ${C_FILES} DESTINATION share/bcc/examples/tracing) install(FILES ${TXT_FILES} DESTINATION share/bcc/examples/tracing) bpfcc-0.12.0/examples/tracing/bitehist.py000077500000000000000000000022431357404205000203050ustar00rootroot00000000000000#!/usr/bin/python # # bitehist.py Block I/O size histogram. # For Linux, uses BCC, eBPF. Embedded C. # # Written as a basic example of using histograms to show a distribution. # # A Ctrl-C will print the gathered histogram then exit. # # Copyright (c) 2015 Brendan Gregg. # Licensed under the Apache License, Version 2.0 (the "License") # # 15-Aug-2015 Brendan Gregg Created this. # 03-Feb-2019 Xiaozhou Liu added linear histogram. from __future__ import print_function from bcc import BPF from time import sleep # load BPF program b = BPF(text=""" #include #include BPF_HISTOGRAM(dist); BPF_HISTOGRAM(dist_linear); int kprobe__blk_account_io_completion(struct pt_regs *ctx, struct request *req) { dist.increment(bpf_log2l(req->__data_len / 1024)); dist_linear.increment(req->__data_len / 1024); return 0; } """) # header print("Tracing... Hit Ctrl-C to end.") # trace until Ctrl-C try: sleep(99999999) except KeyboardInterrupt: print() # output print("log2 histogram") print("~~~~~~~~~~~~~~") b["dist"].print_log2_hist("kbytes") print("\nlinear histogram") print("~~~~~~~~~~~~~~~~") b["dist_linear"].print_linear_hist("kbytes") bpfcc-0.12.0/examples/tracing/bitehist_example.txt000066400000000000000000000022701357404205000222040ustar00rootroot00000000000000Demonstrations of bitehist.py, the Linux eBPF/bcc version. This prints a power-of-2 histogram to show the block I/O size distribution. A summary is printed after Ctrl-C is hit. # ./bitehist.py Tracing... Hit Ctrl-C to end. ^C kbytes : count distribution 0 -> 1 : 3 | | 2 -> 3 : 0 | | 4 -> 7 : 211 |********** | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 1 | | 128 -> 255 : 800 |**************************************| This output shows a bimodal distribution. The largest mod of 800 I/O were between 128 and 255 Kbytes in size, and another mode of 211 I/O were between 4 and 7 Kbytes in size. Understanding this distribution is useful for characterizing workloads and understanding performance. The existence of this distribution is not visible from averages alone. bpfcc-0.12.0/examples/tracing/dddos.py000077500000000000000000000073501357404205000175730ustar00rootroot00000000000000#!/usr/bin/python # # dddos.py DDOS dectection system. # # Written as a basic tracing example of using ePBF # to detect a potential DDOS attack against a system. # # Copyright (c) 2019 Jugurtha BELKALEM. # Licensed under the Apache License, Version 2.0 (the "License") # # 14-Jan-2019 Jugurtha BELKALEM Created this. from bcc import BPF import ctypes as ct import datetime prog = """ #include #include #define MAX_NB_PACKETS 1000 #define LEGAL_DIFF_TIMESTAMP_PACKETS 1000000 BPF_HASH(rcv_packets); struct detectionPackets { u64 nb_ddos_packets; }; BPF_PERF_OUTPUT(events); int detect_ddos(struct pt_regs *ctx, void *skb){ struct detectionPackets detectionPacket = {}; // Used to count number of received packets u64 rcv_packets_nb_index = 0, rcv_packets_nb_inter=1, *rcv_packets_nb_ptr; // Used to measure elapsed time between 2 successive received packets u64 rcv_packets_ts_index = 1, rcv_packets_ts_inter=0, *rcv_packets_ts_ptr; /* The algorithm analyses packets received by ip_rcv function * and measures the difference in reception time between each packet. * DDOS flooders send millions of packets such that difference of * timestamp between 2 successive packets is so small * (which is not like regular applications behaviour). * This script looks for this difference in time and if it sees * more than MAX_NB_PACKETS succesive packets with a difference * of timestamp between each one of them less than * LEGAL_DIFF_TIMESTAMP_PACKETS ns, * ------------------ It Triggers an ALERT ----------------- * Those settings must be adapted depending on regular network traffic * ------------------------------------------------------------------- * Important: this is a rudimentary intrusion detection system, one can * test a real case attack using hping3. However; if regular network * traffic increases above predefined detection settings, a false * positive alert will be triggered (an example would be the case of large file downloads). */ rcv_packets_nb_ptr = rcv_packets.lookup(&rcv_packets_nb_index); rcv_packets_ts_ptr = rcv_packets.lookup(&rcv_packets_ts_index); if(rcv_packets_nb_ptr != 0 && rcv_packets_ts_ptr != 0){ rcv_packets_nb_inter = *rcv_packets_nb_ptr; rcv_packets_ts_inter = bpf_ktime_get_ns() - *rcv_packets_ts_ptr; if(rcv_packets_ts_inter < LEGAL_DIFF_TIMESTAMP_PACKETS){ rcv_packets_nb_inter++; } else { rcv_packets_nb_inter = 0; } if(rcv_packets_nb_inter > MAX_NB_PACKETS){ detectionPacket.nb_ddos_packets = rcv_packets_nb_inter; events.perf_submit(ctx, &detectionPacket, sizeof(detectionPacket)); } } rcv_packets_ts_inter = bpf_ktime_get_ns(); rcv_packets.update(&rcv_packets_nb_index, &rcv_packets_nb_inter); rcv_packets.update(&rcv_packets_ts_index, &rcv_packets_ts_inter); return 0; } """ # Loads eBPF program b = BPF(text=prog) # Attach kprobe to kernel function and sets detect_ddos as kprobe handler b.attach_kprobe(event="ip_rcv", fn_name="detect_ddos") class DetectionTimestamp(ct.Structure): _fields_ = [("nb_ddos_packets", ct.c_ulonglong)] # Show message when ePBF stats print("DDOS detector started ... Hit Ctrl-C to end!") print("%-26s %-10s" % ("TIME(s)", "MESSAGE")) def trigger_alert_event(cpu, data, size): event = ct.cast(data, ct.POINTER(DetectionTimestamp)).contents print("%-26s %s %ld" % (datetime.datetime.now(), "DDOS Attack => nb of packets up to now : ", event.nb_ddos_packets)) # loop with callback to trigger_alert_event b["events"].open_perf_buffer(trigger_alert_event) while 1: try: b.perf_buffer_poll() except KeyboardInterrupt: exit() bpfcc-0.12.0/examples/tracing/dddos_example.txt000066400000000000000000000041001357404205000214600ustar00rootroot00000000000000Demonstrations of dddos.py, the Linux eBPF/bcc version. This tracks ip_rcv function (using kprobe) and elapsed time between received packets to detect potential DDOS attacks. The following steps illustrates the usage of dddos : 1 - Start dddos.py : # ./dddos.py DDOS detector started ... Hit Ctrl-C to end! TIME(s) MESSAGE 2 - Launch hping3 (or any other flooder) in another terminal as shown below: # hping3 localhost -S -A -V -p 443 -i u100 3 - dddos.py triggers alerts and reports a DDOS attack: DDOS detector started ... Hit Ctrl-C to end! TIME(s) MESSAGE 2019-01-16 11:55:12.600734 DDOS Attack => nb of packets up to now : 1001 2019-01-16 11:55:12.600845 DDOS Attack => nb of packets up to now : 1002 2019-01-16 11:55:12.600887 DDOS Attack => nb of packets up to now : 1003 2019-01-16 11:55:12.600971 DDOS Attack => nb of packets up to now : 1004 2019-01-16 11:55:12.601009 DDOS Attack => nb of packets up to now : 1005 2019-01-16 11:55:12.601062 DDOS Attack => nb of packets up to now : 1006 2019-01-16 11:55:12.601096 DDOS Attack => nb of packets up to now : 1007 2019-01-16 11:55:12.601195 DDOS Attack => nb of packets up to now : 1008 2019-01-16 11:55:12.601228 DDOS Attack => nb of packets up to now : 1009 2019-01-16 11:55:12.601331 DDOS Attack => nb of packets up to now : 1010 2019-01-16 11:55:12.601364 DDOS Attack => nb of packets up to now : 1011 2019-01-16 11:55:12.601470 DDOS Attack => nb of packets up to now : 1012 2019-01-16 11:55:12.601505 DDOS Attack => nb of packets up to now : 1013 2019-01-16 11:55:12.601621 DDOS Attack => nb of packets up to now : 1014 2019-01-16 11:55:12.601656 DDOS Attack => nb of packets up to now : 1015 2019-01-16 11:55:12.601757 DDOS Attack => nb of packets up to now : 1016 2019-01-16 11:55:12.601790 DDOS Attack => nb of packets up to now : 1017 2019-01-16 11:55:12.601892 DDOS Attack => nb of packets up to now : 1018 2019-01-16 11:55:12.601925 DDOS Attack => nb of packets up to now : 1019 2019-01-16 11:55:12.602028 DDOS Attack => nb of packets up to now : 1020 Remark : Use Ctrl-C to stop dddos.py bpfcc-0.12.0/examples/tracing/disksnoop.py000077500000000000000000000034211357404205000205020ustar00rootroot00000000000000#!/usr/bin/python # # disksnoop.py Trace block device I/O: basic version of iosnoop. # For Linux, uses BCC, eBPF. Embedded C. # # Written as a basic example of tracing latency. # # Copyright (c) 2015 Brendan Gregg. # Licensed under the Apache License, Version 2.0 (the "License") # # 11-Aug-2015 Brendan Gregg Created this. from __future__ import print_function from bcc import BPF from bcc.utils import printb REQ_WRITE = 1 # from include/linux/blk_types.h # load BPF program b = BPF(text=""" #include #include BPF_HASH(start, struct request *); void trace_start(struct pt_regs *ctx, struct request *req) { // stash start timestamp by request ptr u64 ts = bpf_ktime_get_ns(); start.update(&req, &ts); } void trace_completion(struct pt_regs *ctx, struct request *req) { u64 *tsp, delta; tsp = start.lookup(&req); if (tsp != 0) { delta = bpf_ktime_get_ns() - *tsp; bpf_trace_printk("%d %x %d\\n", req->__data_len, req->cmd_flags, delta / 1000); start.delete(&req); } } """) if BPF.get_kprobe_functions(b'blk_start_request'): b.attach_kprobe(event="blk_start_request", fn_name="trace_start") b.attach_kprobe(event="blk_mq_start_request", fn_name="trace_start") b.attach_kprobe(event="blk_account_io_completion", fn_name="trace_completion") # header print("%-18s %-2s %-7s %8s" % ("TIME(s)", "T", "BYTES", "LAT(ms)")) # format output while 1: try: (task, pid, cpu, flags, ts, msg) = b.trace_fields() (bytes_s, bflags_s, us_s) = msg.split() if int(bflags_s, 16) & REQ_WRITE: type_s = b"W" elif bytes_s == "0": # see blk_fill_rwbs() for logic type_s = b"M" else: type_s = b"R" ms = float(int(us_s, 10)) / 1000 printb(b"%-18.9f %-2s %-7s %8.2f" % (ts, type_s, bytes_s, ms)) except KeyboardInterrupt: exit() bpfcc-0.12.0/examples/tracing/disksnoop_example.txt000066400000000000000000000030631357404205000224030ustar00rootroot00000000000000Demonstrations of disksnoop.py, the Linux eBPF/bcc version. This traces block I/O, a prints a line to summarize each I/O completed: # ./disksnoop.py TIME(s) T BYTES LAT(ms) 16458043.435457 W 4096 2.73 16458043.435981 W 4096 3.24 16458043.436012 W 4096 3.13 16458043.437326 W 4096 4.44 16458044.126545 R 4096 42.82 16458044.129872 R 4096 3.24 16458044.130705 R 4096 0.73 16458044.142813 R 4096 12.01 16458044.147302 R 4096 4.33 16458044.148117 R 4096 0.71 16458044.148950 R 4096 0.70 16458044.164332 R 4096 15.29 16458044.168003 R 4096 3.58 16458044.171676 R 4096 3.59 16458044.172453 R 4096 0.72 16458044.173213 R 4096 0.71 16458044.173989 R 4096 0.72 16458044.174739 R 4096 0.70 16458044.190334 R 4096 15.52 16458044.196608 R 4096 6.17 16458044.203091 R 4096 6.35 The output includes a basic timestamp (in seconds), the type of I/O (W == write, R == read, M == metadata), the size of the I/O in bytes, and the latency (or duration) of the I/O in milliseconds. The latency is measured from I/O request to the device, to the device completion. This excludes latency spent queued in the OS. Most of the I/O in this example were 0.7 and 4 milliseconds in duration. There was an outlier of 42.82 milliseconds, a read which followed many writes (the high latency may have been caused by the writes still being serviced on the storage device). bpfcc-0.12.0/examples/tracing/hello_fields.py000077500000000000000000000012471357404205000211260ustar00rootroot00000000000000#!/usr/bin/python # # This is a Hello World example that formats output as fields. from bcc import BPF from bcc.utils import printb # define BPF program prog = """ int hello(void *ctx) { bpf_trace_printk("Hello, World!\\n"); return 0; } """ # load BPF program b = BPF(text=prog) b.attach_kprobe(event=b.get_syscall_fnname("clone"), fn_name="hello") # header print("%-18s %-16s %-6s %s" % ("TIME(s)", "COMM", "PID", "MESSAGE")) # format output while 1: try: (task, pid, cpu, flags, ts, msg) = b.trace_fields() except ValueError: continue except KeyboardInterrupt: exit() printb(b"%-18.9f %-16s %-6d %s" % (ts, task, pid, msg)) bpfcc-0.12.0/examples/tracing/hello_perf_output.py000077500000000000000000000023661357404205000222370ustar00rootroot00000000000000#!/usr/bin/python # # This is a Hello World example that uses BPF_PERF_OUTPUT. from bcc import BPF from bcc.utils import printb # define BPF program prog = """ #include // define output data structure in C struct data_t { u32 pid; u64 ts; char comm[TASK_COMM_LEN]; }; BPF_PERF_OUTPUT(events); int hello(struct pt_regs *ctx) { struct data_t data = {}; data.pid = bpf_get_current_pid_tgid(); data.ts = bpf_ktime_get_ns(); bpf_get_current_comm(&data.comm, sizeof(data.comm)); events.perf_submit(ctx, &data, sizeof(data)); return 0; } """ # load BPF program b = BPF(text=prog) b.attach_kprobe(event=b.get_syscall_fnname("clone"), fn_name="hello") # header print("%-18s %-16s %-6s %s" % ("TIME(s)", "COMM", "PID", "MESSAGE")) # process event start = 0 def print_event(cpu, data, size): global start event = b["events"].event(data) if start == 0: start = event.ts time_s = (float(event.ts - start)) / 1000000000 printb(b"%-18.9f %-16s %-6d %s" % (time_s, event.comm, event.pid, b"Hello, perf_output!")) # loop with callback to print_event b["events"].open_perf_buffer(print_event) while 1: try: b.perf_buffer_poll() except KeyboardInterrupt: exit() bpfcc-0.12.0/examples/tracing/kvm_hypercall.py000077500000000000000000000027601357404205000213360ustar00rootroot00000000000000#!/usr/bin/python # # kvm_hypercall.py # # Demonstrates stateful kvm_entry and kvm_exit recording along with the # associated hypercall when exit_reason is VMCALL. See kvm_hypercall.txt # for usage # # REQUIRES: Linux 4.7+ (BPF_PROG_TYPE_TRACEPOINT support) # # Copyright (c) 2017 ShiftLeft Inc. # # Author(s): # Suchakrapani Sharma from __future__ import print_function from bcc import BPF # load BPF program b = BPF(text=""" #define EXIT_REASON 18 BPF_HASH(start, u8, u8); TRACEPOINT_PROBE(kvm, kvm_exit) { u8 e = EXIT_REASON; u8 one = 1; if (args->exit_reason == EXIT_REASON) { bpf_trace_printk("KVM_EXIT exit_reason : %d\\n", args->exit_reason); start.update(&e, &one); } return 0; } TRACEPOINT_PROBE(kvm, kvm_entry) { u8 e = EXIT_REASON; u8 zero = 0; u8 *s = start.lookup(&e); if (s != NULL && *s == 1) { bpf_trace_printk("KVM_ENTRY vcpu_id : %u\\n", args->vcpu_id); start.update(&e, &zero); } return 0; } TRACEPOINT_PROBE(kvm, kvm_hypercall) { u8 e = EXIT_REASON; u8 zero = 0; u8 *s = start.lookup(&e); if (s != NULL && *s == 1) { bpf_trace_printk("HYPERCALL nr : %d\\n", args->nr); } return 0; }; """) # header print("%-18s %-16s %-6s %s" % ("TIME(s)", "COMM", "PID", "EVENT")) # format output while 1: try: (task, pid, cpu, flags, ts, msg) = b.trace_fields() except ValueError: continue print("%-18.9f %-16s %-6d %s" % (ts, task, pid, msg)) bpfcc-0.12.0/examples/tracing/kvm_hypercall.txt000066400000000000000000000033661357404205000215250ustar00rootroot00000000000000Demonstrations of kvm_hypercall.py, showing eBPF/bcc based hypercall analysis This example demonstrates how we can statefully save static tracepoint events based on conditions being met for other events with which they are associated. Here, we wish to record kvm_exit and kvm_entry events which are linked to the kvm_hypercall event. We are interested in kvm_exit with exit reason as VMCALL (18). This may be useful to analyze latency caused by a hypercall itself. To test this, while the python script is run, induce a hypercall from a guest based on the following example: https://gist.github.com/abenbachir/344822b5ba9fc5ac384cdec3f087e018 # ./kvm_hypercall.py TIME(s) COMM PID MESSAGE 2445.577087000 CPU 0/KVM 8896 KVM_EXIT exit_reason : 18 2445.577122000 CPU 0/KVM 8896 HYPERCALL nr : 0 2445.577129000 CPU 0/KVM 8896 KVM_ENTRY vcpu_id : 0 2445.577136000 CPU 0/KVM 8896 KVM_EXIT exit_reason : 18 2445.577145000 CPU 0/KVM 8896 HYPERCALL nr : 1 2445.577149000 CPU 0/KVM 8896 KVM_ENTRY vcpu_id : 0 2445.577155000 CPU 0/KVM 8896 KVM_EXIT exit_reason : 18 2445.577160000 CPU 0/KVM 8896 HYPERCALL nr : 2 2445.577164000 CPU 0/KVM 8896 KVM_ENTRY vcpu_id : 0 2445.577170000 CPU 0/KVM 8896 KVM_EXIT exit_reason : 18 2445.577175000 CPU 0/KVM 8896 HYPERCALL nr : 3 2445.577179000 CPU 0/KVM 8896 KVM_ENTRY vcpu_id : 0 2445.577185000 CPU 0/KVM 8896 KVM_EXIT exit_reason : 18 2445.577190000 CPU 0/KVM 8896 HYPERCALL nr : 4 2445.577194000 CPU 0/KVM 8896 KVM_ENTRY vcpu_id : 0 This output shows a sequence of exit -> hypercall -> entry where the exit_reason was VMCALL. bpfcc-0.12.0/examples/tracing/mallocstacks.py000077500000000000000000000035711357404205000211570ustar00rootroot00000000000000#!/usr/bin/python # # mallocstacks Trace malloc() calls in a process and print the full # stack trace for all callsites. # For Linux, uses BCC, eBPF. Embedded C. # # This script is a basic example of the new Linux 4.6+ BPF_STACK_TRACE # table API. # # Copyright 2016 GitHub, Inc. # Licensed under the Apache License, Version 2.0 (the "License") from __future__ import print_function from bcc import BPF from bcc.utils import printb from time import sleep import sys if len(sys.argv) < 2: print("USAGE: mallocstacks PID [NUM_STACKS=1024]") exit() pid = int(sys.argv[1]) if len(sys.argv) == 3: try: assert int(sys.argv[2]) > 0, "" except (ValueError, AssertionError) as e: print("USAGE: mallocstacks PID [NUM_STACKS=1024]") print("NUM_STACKS must be a non-zero, positive integer") exit() stacks = sys.argv[2] else: stacks = "1024" # load BPF program b = BPF(text=""" #include BPF_HASH(calls, int); BPF_STACK_TRACE(stack_traces, """ + stacks + """); int alloc_enter(struct pt_regs *ctx, size_t size) { int key = stack_traces.get_stackid(ctx, BPF_F_USER_STACK); if (key < 0) return 0; // could also use `calls.increment(key, size);` u64 zero = 0, *val; val = calls.lookup_or_try_init(&key, &zero); if (val) { (*val) += size; } return 0; }; """) b.attach_uprobe(name="c", sym="malloc", fn_name="alloc_enter", pid=pid) print("Attaching to malloc in pid %d, Ctrl+C to quit." % pid) # sleep until Ctrl-C try: sleep(99999999) except KeyboardInterrupt: pass calls = b.get_table("calls") stack_traces = b.get_table("stack_traces") for k, v in reversed(sorted(calls.items(), key=lambda c: c[1].value)): print("%d bytes allocated at:" % v.value) for addr in stack_traces.walk(k.value): printb(b"\t%s" % b.sym(addr, pid, show_offset=True)) bpfcc-0.12.0/examples/tracing/mysqld_query.py000077500000000000000000000032401357404205000212260ustar00rootroot00000000000000#!/usr/bin/python # # mysqld_query Trace MySQL server queries. Example of USDT tracing. # For Linux, uses BCC, BPF. Embedded C. # # USAGE: mysqld_query PID # # This uses USDT probes, and needs a MySQL server with -DENABLE_DTRACE=1. # # Copyright 2016 Netflix, Inc. # Licensed under the Apache License, Version 2.0 (the "License") from __future__ import print_function from bcc import BPF, USDT from bcc.utils import printb import sys if len(sys.argv) < 2: print("USAGE: mysqld_latency PID") exit() pid = sys.argv[1] debug = 0 # load BPF program bpf_text = """ #include int do_trace(struct pt_regs *ctx) { uint64_t addr; char query[128]; /* * Read the first argument from the query-start probe, which is the query. * The format of this probe is: * query-start(query, connectionid, database, user, host) * see: https://dev.mysql.com/doc/refman/5.7/en/dba-dtrace-ref-query.html */ bpf_usdt_readarg(1, ctx, &addr); bpf_probe_read(&query, sizeof(query), (void *)addr); bpf_trace_printk("%s\\n", query); return 0; }; """ # enable USDT probe from given PID u = USDT(pid=int(pid)) u.enable_probe(probe="query__start", fn_name="do_trace") if debug: print(u.get_text()) print(bpf_text) # initialize BPF b = BPF(text=bpf_text, usdt_contexts=[u]) # header print("%-18s %-16s %-6s %s" % ("TIME(s)", "COMM", "PID", "QUERY")) # format output while 1: try: (task, pid, cpu, flags, ts, msg) = b.trace_fields() except ValueError: print("value error") continue except KeyboardInterrupt: exit() printb(b"%-18.9f %-16s %-6d %s" % (ts, task, pid, msg)) bpfcc-0.12.0/examples/tracing/mysqld_query_example.txt000066400000000000000000000007631357404205000231340ustar00rootroot00000000000000# ./mysqld_query.py `pgrep -n mysqld` TIME(s) COMM PID QUERY 17450459.549910001 mysqld 18608 select @@version_comment limit 1 17450463.822668001 mysqld 18608 SELECT DATABASE() 17450463.824042998 mysqld 18608 show databases 17450463.824570000 mysqld 18608 show tables 17450465.602717999 mysqld 18608 SELECT COUNT(*) FROM words 17450479.944897000 mysqld 18608 SELECT * FROM words WHERE word REGEXP '^bre.*n$' bpfcc-0.12.0/examples/tracing/nodejs_http_server.py000077500000000000000000000025331357404205000224030ustar00rootroot00000000000000#!/usr/bin/python # # nodejs_http_server Basic example of node.js USDT tracing. # For Linux, uses BCC, BPF. Embedded C. # # USAGE: nodejs_http_server PID # # Copyright 2016 Netflix, Inc. # Licensed under the Apache License, Version 2.0 (the "License") from __future__ import print_function from bcc import BPF, USDT from bcc.utils import printb import sys if len(sys.argv) < 2: print("USAGE: nodejs_http_server PID") exit() pid = sys.argv[1] debug = 0 # load BPF program bpf_text = """ #include int do_trace(struct pt_regs *ctx) { uint64_t addr; char path[128]={0}; bpf_usdt_readarg(6, ctx, &addr); bpf_probe_read(&path, sizeof(path), (void *)addr); bpf_trace_printk("path:%s\\n", path); return 0; }; """ # enable USDT probe from given PID u = USDT(pid=int(pid)) u.enable_probe(probe="http__server__request", fn_name="do_trace") if debug: print(u.get_text()) print(bpf_text) # initialize BPF b = BPF(text=bpf_text, usdt_contexts=[u]) # header print("%-18s %-16s %-6s %s" % ("TIME(s)", "COMM", "PID", "ARGS")) # format output while 1: try: (task, pid, cpu, flags, ts, msg) = b.trace_fields() except ValueError: print("value error") continue except KeyboardInterrupt: exit() printb(b"%-18.9f %-16s %-6d %s" % (ts, task, pid, msg)) bpfcc-0.12.0/examples/tracing/nodejs_http_server_example.txt000066400000000000000000000004241357404205000242770ustar00rootroot00000000000000# ./nodejs_http_server.py 24728 TIME(s) COMM PID ARGS 24653324.561322998 node 24728 path:/index.html 24653335.343401998 node 24728 path:/images/welcome.png 24653340.510164998 node 24728 path:/images/favicon.png bpfcc-0.12.0/examples/tracing/stack_buildid_example.py000077500000000000000000000060421357404205000230070ustar00rootroot00000000000000#!/usr/bin/python # # An example usage of stack_build_id # Most of the code here is borrowed from tools/profile.py # # Steps for using this code # 1) Start ping program in one terminal eg invocation: ping google.com -i0.001 # 2) Change the path of libc specified in b.add_module() below # 3) Invoke the script as 'python stack_buildid_example.py' # 4) o/p of the tool is as shown below # python example/tracing/stack_buildid_example.py # sendto # - ping (5232) # 2 # # REQUIRES: Linux 4.17+ (BPF_BUILD_ID support) # Licensed under the Apache License, Version 2.0 (the "License") # 03-Jan-2019 Vijay Nag from __future__ import print_function from bcc import BPF, PerfType, PerfSWConfig from sys import stderr from time import sleep import argparse import signal import os import subprocess import errno import multiprocessing import ctypes as ct def Get_libc_path(): # A small helper function that returns full path # of libc in the system cmd = 'cat /proc/self/maps | grep libc | awk \'{print $6}\' | uniq' output = subprocess.check_output(cmd, shell=True) if not isinstance(output, str): output = output.decode() return output.split('\n')[0] bpf_text = """ #include #include #include struct key_t { u32 pid; int user_stack_id; char name[TASK_COMM_LEN]; }; BPF_HASH(counts, struct key_t); BPF_STACK_TRACE_BUILDID(stack_traces, 128); int do_perf_event(struct bpf_perf_event_data *ctx) { u32 pid = bpf_get_current_pid_tgid() >> 32; // create map key struct key_t key = {.pid = pid}; bpf_get_current_comm(&key.name, sizeof(key.name)); key.user_stack_id = stack_traces.get_stackid(&ctx->regs, BPF_F_USER_STACK); if (key.user_stack_id >= 0) { counts.increment(key); } return 0; } """ b = BPF(text=bpf_text) b.attach_perf_event(ev_type=PerfType.SOFTWARE, ev_config=PerfSWConfig.CPU_CLOCK, fn_name="do_perf_event", sample_period=0, sample_freq=49, cpu=0) # Add the list of libraries/executables to the build sym cache for sym resolution # Change the libc path if it is different on a different machine. # libc.so and ping are added here so that any symbols pertaining to # libc or ping are resolved. More executables/libraries can be added here. b.add_module(Get_libc_path()) b.add_module("/usr/sbin/sshd") b.add_module("/bin/ping") counts = b.get_table("counts") stack_traces = b.get_table("stack_traces") duration = 2 def signal_handler(signal, frame): print() try: sleep(duration) except KeyboardInterrupt: # as cleanup can take some time, trap Ctrl-C: signal.signal(signal.SIGINT, signal.SIG_IGN) user_stack=[] for k,v in sorted(counts.items(), key=lambda counts: counts[1].value): user_stack = [] if k.user_stack_id < 0 else \ stack_traces.walk(k.user_stack_id) user_stack=list(user_stack) for addr in user_stack: print(" %s" % b.sym(addr, k.pid).decode('utf-8', 'replace')) print(" %-16s %s (%d)" % ("-", k.name.decode('utf-8', 'replace'), k.pid)) print(" %d\n" % v.value) bpfcc-0.12.0/examples/tracing/stacksnoop.py000077500000000000000000000064701357404205000206640ustar00rootroot00000000000000#!/usr/bin/python # # stacksnoop Trace a kernel function and print all kernel stack traces. # For Linux, uses BCC, eBPF, and currently x86_64 only. Inline C. # # USAGE: stacksnoop [-h] [-p PID] [-s] [-v] function # # Copyright 2016 Netflix, Inc. # Licensed under the Apache License, Version 2.0 (the "License") # # 12-Jan-2016 Brendan Gregg Created this. from __future__ import print_function from bcc import BPF import argparse import ctypes as ct import time # arguments examples = """examples: ./stacksnoop ext4_sync_fs # print kernel stack traces for ext4_sync_fs ./stacksnoop -s ext4_sync_fs # ... also show symbol offsets ./stacksnoop -v ext4_sync_fs # ... show extra columns ./stacksnoop -p 185 ext4_sync_fs # ... only when PID 185 is on-CPU """ parser = argparse.ArgumentParser( description="Trace and print kernel stack traces for a kernel function", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("-p", "--pid", help="trace this PID only") parser.add_argument("-s", "--offset", action="store_true", help="show address offsets") parser.add_argument("-v", "--verbose", action="store_true", help="print more fields") parser.add_argument("function", help="kernel function name") args = parser.parse_args() function = args.function offset = args.offset verbose = args.verbose debug = 0 # define BPF program bpf_text = """ #include #include struct data_t { u64 stack_id; u32 pid; char comm[TASK_COMM_LEN]; }; BPF_STACK_TRACE(stack_traces, 128); BPF_PERF_OUTPUT(events); void trace_stack(struct pt_regs *ctx) { u32 pid = bpf_get_current_pid_tgid(); FILTER struct data_t data = {}; data.stack_id = stack_traces.get_stackid(ctx, 0), data.pid = pid; bpf_get_current_comm(&data.comm, sizeof(data.comm)); events.perf_submit(ctx, &data, sizeof(data)); } """ if args.pid: bpf_text = bpf_text.replace('FILTER', 'if (pid != %s) { return; }' % args.pid) else: bpf_text = bpf_text.replace('FILTER', '') if debug: print(bpf_text) # initialize BPF b = BPF(text=bpf_text) b.attach_kprobe(event=function, fn_name="trace_stack") TASK_COMM_LEN = 16 # linux/sched.h class Data(ct.Structure): _fields_ = [ ("stack_id", ct.c_ulonglong), ("pid", ct.c_uint), ("comm", ct.c_char * TASK_COMM_LEN), ] matched = b.num_open_kprobes() if matched == 0: print("Function \"%s\" not found. Exiting." % function) exit() stack_traces = b.get_table("stack_traces") start_ts = time.time() # header if verbose: print("%-18s %-12s %-6s %-3s %s" % ("TIME(s)", "COMM", "PID", "CPU", "FUNCTION")) else: print("%-18s %s" % ("TIME(s)", "FUNCTION")) def print_event(cpu, data, size): event = ct.cast(data, ct.POINTER(Data)).contents ts = time.time() - start_ts if verbose: print("%-18.9f %-12.12s %-6d %-3d %s" % (ts, event.comm.decode(), event.pid, cpu, function)) else: print("%-18.9f %s" % (ts, function)) for addr in stack_traces.walk(event.stack_id): sym = b.ksym(addr, show_offset=offset) print("\t%s" % sym) print() b["events"].open_perf_buffer(print_event) while 1: try: b.perf_buffer_poll() except KeyboardInterrupt: exit() bpfcc-0.12.0/examples/tracing/stacksnoop_example.txt000066400000000000000000000054671357404205000225700ustar00rootroot00000000000000Demonstrations of stacksnoop, the Linux eBPF/bcc version. This program traces the given kernel function and prints the kernel stack trace for every call. This tool is useful for studying low frequency kernel functions, to see how they were invoked. For example, tracing the submit_bio() call: # ./stacksnoop submit_bio TIME(s) SYSCALL 3592.838736000 submit_bio submit_bio submit_bh jbd2_journal_commit_transaction kjournald2 kthread ret_from_fork This shows that submit_bio() was called by submit_bh(), which was called by jbd2_journal_commit_transaction(), and so on. For high frequency functions, see stackcount, which summarizes in-kernel for efficiency. If you don't know if your function is low or high frequency, try funccount. The -v option includes more fields, including the on-CPU process (COMM and PID): # ./stacksnoop -v submit_bio TIME(s) COMM PID CPU SYSCALL 3734.855027000 jbd2/dm-0-8 313 0 submit_bio submit_bio submit_bh jbd2_journal_commit_transaction kjournald2 kthread ret_from_fork This identifies the application issuing the sync syscall: the jbd2 process (COMM column). Here's another example, showing the path to second_overflow() and on-CPU process: # ./stacksnoop -v second_overflow TIME(s) COMM PID CPU SYSCALL 3837.526433000 0 1 second_overflow second_overflow tick_do_update_jiffies64 tick_irq_enter irq_enter smp_apic_timer_interrupt apic_timer_interrupt default_idle arch_cpu_idle default_idle_call cpu_startup_entry start_secondary 3838.526953000 0 1 second_overflow second_overflow tick_do_update_jiffies64 tick_irq_enter irq_enter smp_apic_timer_interrupt apic_timer_interrupt default_idle arch_cpu_idle default_idle_call cpu_startup_entry start_secondary This fires every second (see TIME(s)), and is from tick_do_update_jiffies64(). USAGE message: # ./stacksnoop -h usage: stacksnoop [-h] [-p PID] [-s] [-v] function Trace and print kernel stack traces for a kernel function positional arguments: function kernel function name optional arguments: -h, --help show this help message and exit -p PID, --pid PID trace this PID only -s, --offset show address offsets -v, --verbose print more fields examples: ./stacksnoop ext4_sync_fs # print kernel stack traces for ext4_sync_fs ./stacksnoop -s ext4_sync_fs # ... also show symbol offsets ./stacksnoop -v ext4_sync_fs # ... show extra columns ./stacksnoop -p 185 ext4_sync_fs # ... only when PID 185 is on-CPU bpfcc-0.12.0/examples/tracing/strlen_count.py000077500000000000000000000024561357404205000212170ustar00rootroot00000000000000#!/usr/bin/python # # strlen_count Trace strlen() and print a frequency count of strings. # For Linux, uses BCC, eBPF. Embedded C. # # Written as a basic example of BCC and uprobes. # # Also see strlensnoop. # # Copyright 2016 Netflix, Inc. # Licensed under the Apache License, Version 2.0 (the "License") from __future__ import print_function from bcc import BPF from bcc.utils import printb from time import sleep # load BPF program b = BPF(text=""" #include struct key_t { char c[80]; }; BPF_HASH(counts, struct key_t); int count(struct pt_regs *ctx) { if (!PT_REGS_PARM1(ctx)) return 0; struct key_t key = {}; u64 zero = 0, *val; bpf_probe_read(&key.c, sizeof(key.c), (void *)PT_REGS_PARM1(ctx)); // could also use `counts.increment(key)` val = counts.lookup_or_try_init(&key, &zero); if (val) { (*val)++; } return 0; }; """) b.attach_uprobe(name="c", sym="strlen", fn_name="count") # header print("Tracing strlen()... Hit Ctrl-C to end.") # sleep until Ctrl-C try: sleep(99999999) except KeyboardInterrupt: pass # print output print("%10s %s" % ("COUNT", "STRING")) counts = b.get_table("counts") for k, v in sorted(counts.items(), key=lambda counts: counts[1].value): printb(b"%10d \"%s\"" % (v.value, k.c)) bpfcc-0.12.0/examples/tracing/strlen_hist.py000077500000000000000000000035001357404205000210250ustar00rootroot00000000000000#!/usr/bin/python # # strlen_hist.py Histogram of system-wide strlen return values # # A basic example of using uprobes along with a histogram to show # distributions. # # Runs until ctrl-c is pressed. # # Copyright (c) PLUMgrid, Inc. # Licensed under the Apache License, Version 2.0 (the "License") # # Example output: # $ sudo ./strlen_hist.py # 22:12:52 # strlen return: : count distribution # 0 -> 1 : 2106 |**************** | # 2 -> 3 : 1172 |********* | # 4 -> 7 : 3892 |****************************** | # 8 -> 15 : 5096 |****************************************| # 16 -> 31 : 2201 |***************** | # 32 -> 63 : 547 |**** | # 64 -> 127 : 106 | | # 128 -> 255 : 13 | | # 256 -> 511 : 27 | | # 512 -> 1023 : 6 | | # 1024 -> 2047 : 10 | | # ^C$ # from __future__ import print_function import bcc import time text = """ #include BPF_HISTOGRAM(dist); int count(struct pt_regs *ctx) { dist.increment(bpf_log2l(PT_REGS_RC(ctx))); return 0; } """ b = bcc.BPF(text=text) sym="strlen" b.attach_uretprobe(name="c", sym=sym, fn_name="count") dist = b["dist"] try: while True: time.sleep(1) print("%-8s\n" % time.strftime("%H:%M:%S"), end="") dist.print_log2_hist(sym + " return:") dist.clear() except KeyboardInterrupt: pass bpfcc-0.12.0/examples/tracing/strlen_snoop.py000077500000000000000000000025431357404205000212220ustar00rootroot00000000000000#!/usr/bin/python # # strlen_snoop Trace strlen() library function for a given PID. # For Linux, uses BCC, eBPF. Embedded C. # # USAGE: strlensnoop PID # # Try running this on a separate bash shell. # # Written as a basic example of BCC and uprobes. # # Copyright 2016 Netflix, Inc. # Licensed under the Apache License, Version 2.0 (the "License") from __future__ import print_function from bcc import BPF from os import getpid import sys if len(sys.argv) < 2: print("USAGE: strlensnoop PID") exit() pid = sys.argv[1] # load BPF program bpf_text = """ #include int printarg(struct pt_regs *ctx) { if (!PT_REGS_PARM1(ctx)) return 0; u32 pid = bpf_get_current_pid_tgid(); if (pid != PID) return 0; char str[80] = {}; bpf_probe_read(&str, sizeof(str), (void *)PT_REGS_PARM1(ctx)); bpf_trace_printk("%s\\n", &str); return 0; }; """ bpf_text = bpf_text.replace('PID', pid) b = BPF(text=bpf_text) b.attach_uprobe(name="c", sym="strlen", fn_name="printarg") # header print("%-18s %-16s %-6s %s" % ("TIME(s)", "COMM", "PID", "STRLEN")) # format output me = getpid() while 1: try: (task, pid, cpu, flags, ts, msg) = b.trace_fields() except ValueError: continue if pid == me or msg == "": continue print("%-18.9f %-16s %-6d %s" % (ts, task, pid, msg)) bpfcc-0.12.0/examples/tracing/sync_timing.py000077500000000000000000000025531357404205000210210ustar00rootroot00000000000000#!/usr/bin/python # # sync_timing.py Trace time between syncs. # For Linux, uses BCC, eBPF. Embedded C. # # Written as a basic example of tracing time between events. # # Copyright 2016 Netflix, Inc. # Licensed under the Apache License, Version 2.0 (the "License") from __future__ import print_function from bcc import BPF from bcc.utils import printb # load BPF program b = BPF(text=""" #include BPF_HASH(last); int do_trace(struct pt_regs *ctx) { u64 ts, *tsp, delta, key = 0; // attempt to read stored timestamp tsp = last.lookup(&key); if (tsp != 0) { delta = bpf_ktime_get_ns() - *tsp; if (delta < 1000000000) { // output if time is less than 1 second bpf_trace_printk("%d\\n", delta / 1000000); } last.delete(&key); } // update stored timestamp ts = bpf_ktime_get_ns(); last.update(&key, &ts); return 0; } """) b.attach_kprobe(event=b.get_syscall_fnname("sync"), fn_name="do_trace") print("Tracing for quick sync's... Ctrl-C to end") # format output start = 0 while 1: try: (task, pid, cpu, flags, ts, ms) = b.trace_fields() if start == 0: start = ts ts = ts - start printb(b"At time %.2f s: multiple syncs detected, last %s ms ago" % (ts, ms)) except KeyboardInterrupt: exit() bpfcc-0.12.0/examples/tracing/task_switch.c000066400000000000000000000007631357404205000206110ustar00rootroot00000000000000#include #include struct key_t { u32 prev_pid; u32 curr_pid; }; BPF_HASH(stats, struct key_t, u64, 1024); int count_sched(struct pt_regs *ctx, struct task_struct *prev) { struct key_t key = {}; u64 zero = 0, *val; key.curr_pid = bpf_get_current_pid_tgid(); key.prev_pid = prev->pid; // could also use `stats.increment(key);` val = stats.lookup_or_try_init(&key, &zero); if (val) { (*val)++; } return 0; } bpfcc-0.12.0/examples/tracing/task_switch.py000077500000000000000000000006641357404205000210220ustar00rootroot00000000000000#!/usr/bin/python # Copyright (c) PLUMgrid, Inc. # Licensed under the Apache License, Version 2.0 (the "License") from bcc import BPF from time import sleep b = BPF(src_file="task_switch.c") b.attach_kprobe(event="finish_task_switch", fn_name="count_sched") # generate many schedule events for i in range(0, 100): sleep(0.01) for k, v in b["stats"].items(): print("task_switch[%5d->%5d]=%u" % (k.prev_pid, k.curr_pid, v.value)) bpfcc-0.12.0/examples/tracing/tcpv4connect.py000077500000000000000000000045441357404205000211120ustar00rootroot00000000000000#!/usr/bin/python # # tcpv4connect Trace TCP IPv4 connect()s. # For Linux, uses BCC, eBPF. Embedded C. # # USAGE: tcpv4connect [-h] [-t] [-p PID] # # This is provided as a basic example of TCP connection & socket tracing. # # All IPv4 connection attempts are traced, even if they ultimately fail. # # Copyright (c) 2015 Brendan Gregg. # Licensed under the Apache License, Version 2.0 (the "License") # # 15-Oct-2015 Brendan Gregg Created this. from __future__ import print_function from bcc import BPF from bcc.utils import printb # define BPF program bpf_text = """ #include #include #include BPF_HASH(currsock, u32, struct sock *); int kprobe__tcp_v4_connect(struct pt_regs *ctx, struct sock *sk) { u32 pid = bpf_get_current_pid_tgid(); // stash the sock ptr for lookup on return currsock.update(&pid, &sk); return 0; }; int kretprobe__tcp_v4_connect(struct pt_regs *ctx) { int ret = PT_REGS_RC(ctx); u32 pid = bpf_get_current_pid_tgid(); struct sock **skpp; skpp = currsock.lookup(&pid); if (skpp == 0) { return 0; // missed entry } if (ret != 0) { // failed to send SYNC packet, may not have populated // socket __sk_common.{skc_rcv_saddr, ...} currsock.delete(&pid); return 0; } // pull in details struct sock *skp = *skpp; u32 saddr = skp->__sk_common.skc_rcv_saddr; u32 daddr = skp->__sk_common.skc_daddr; u16 dport = skp->__sk_common.skc_dport; // output bpf_trace_printk("trace_tcp4connect %x %x %d\\n", saddr, daddr, ntohs(dport)); currsock.delete(&pid); return 0; } """ # initialize BPF b = BPF(text=bpf_text) # header print("%-6s %-12s %-16s %-16s %-4s" % ("PID", "COMM", "SADDR", "DADDR", "DPORT")) def inet_ntoa(addr): dq = b'' for i in range(0, 4): dq = dq + str(addr & 0xff).encode() if (i != 3): dq = dq + b'.' addr = addr >> 8 return dq # filter and format output while 1: # Read messages from kernel pipe try: (task, pid, cpu, flags, ts, msg) = b.trace_fields() (_tag, saddr_hs, daddr_hs, dport_s) = msg.split(b" ") except ValueError: # Ignore messages from other tracers continue except KeyboardInterrupt: exit() # Ignore messages from other tracers if _tag != "trace_tcp4connect": continue printb(b"%-6d %-12.12s %-16s %-16s %-4s" % (pid, task, inet_ntoa(int(saddr_hs, 16)), inet_ntoa(int(daddr_hs, 16)), dport_s)) bpfcc-0.12.0/examples/tracing/tcpv4connect_example.txt000066400000000000000000000020471357404205000230050ustar00rootroot00000000000000Demonstrations of tcpv4connect.py, the Linux eBPF/bcc version. This example traces the kernel function performing active TCP IPv4 connections (eg, via a connect() syscall; accept() are passive connections). Some example output (IP addresses changed to protect the innocent): # ./tcpv4connect.py PID COMM SADDR DADDR DPORT 1479 telnet 127.0.0.1 127.0.0.1 23 1469 curl 10.201.219.236 54.245.105.25 80 1469 curl 10.201.219.236 54.67.101.145 80 This output shows three connections, one from a "telnet" process and two from "curl". The output details shows the source address, 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 function performing a connect. It is not tracing every packet and then filtering. This is provided as a basic example of TCP tracing. See tools/tcpconnect for a more featured version of this example (a tool). bpfcc-0.12.0/examples/tracing/trace_fields.py000077500000000000000000000011151357404205000211130ustar00rootroot00000000000000#!/usr/bin/python # Copyright (c) PLUMgrid, Inc. # Licensed under the Apache License, Version 2.0 (the "License") # This is an example of tracing an event and printing custom fields. # run in project examples directory with: # sudo ./trace_fields.py" from __future__ import print_function from bcc import BPF prog = """ int hello(void *ctx) { bpf_trace_printk("Hello, World!\\n"); return 0; } """ b = BPF(text=prog) b.attach_kprobe(event=b.get_syscall_fnname("clone"), fn_name="hello") print("PID MESSAGE") try: b.trace_print(fmt="{1} {5}") except KeyboardInterrupt: exit() bpfcc-0.12.0/examples/tracing/trace_perf_output.py000077500000000000000000000031001357404205000222150ustar00rootroot00000000000000#!/usr/bin/python # Copyright (c) PLUMgrid, Inc. # Licensed under the Apache License, Version 2.0 (the "License") # This is an example of tracing an event and printing custom fields. # run in project examples directory with: # sudo ./trace_fields.py" import atexit from bcc import BPF from bcc.utils import printb import ctypes as ct class Data(ct.Structure): _fields_ = [("ts", ct.c_ulonglong), ("magic", ct.c_ulonglong)] counter = 0 def cb(cpu, data, size): assert size >= ct.sizeof(Data) event = ct.cast(data, ct.POINTER(Data)).contents print("[%0d] %f: %x" % (cpu, float(event.ts) / 1000000, event.magic)) global counter counter += 1 prog = """ BPF_PERF_OUTPUT(events); BPF_ARRAY(counters, u64, 10); int do_sys_clone(void *ctx) { struct { u64 ts; u64 magic; } data = {bpf_ktime_get_ns(), 0x12345678}; int rc; if ((rc = events.perf_submit(ctx, &data, sizeof(data))) < 0) bpf_trace_printk("perf_output failed: %d\\n", rc); int zero = 0; u64 *val = counters.lookup(&zero); if (val) lock_xadd(val, 1); return 0; } """ b = BPF(text=prog) event_name = b.get_syscall_fnname("clone") b.attach_kprobe(event=event_name, fn_name="do_sys_clone") b["events"].open_perf_buffer(cb) @atexit.register def print_counter(): global counter global b print("counter = %d vs %d" % (counter, b["counters"][ct.c_int(0)].value)) printb(b"Tracing " + event_name + b", try `dd if=/dev/zero of=/dev/null`") print("Tracing... Hit Ctrl-C to end.") while 1: try: b.perf_buffer_poll() except KeyboardInterrupt: exit() bpfcc-0.12.0/examples/tracing/urandomread-explicit.py000077500000000000000000000027471357404205000226230ustar00rootroot00000000000000#!/usr/bin/python # # urandomread-explicit Example of instrumenting a kernel tracepoint. # For Linux, uses BCC, BPF. Embedded C. # # This is an older example of instrumenting a tracepoint, which defines # the argument struct and makes an explicit call to attach_tracepoint(). # See urandomread for a newer version that uses TRACEPOINT_PROBE(). # # REQUIRES: Linux 4.7+ (BPF_PROG_TYPE_TRACEPOINT support). # # Test by running this, then in another shell, run: # dd if=/dev/urandom of=/dev/null bs=1k count=5 # # Copyright 2016 Netflix, Inc. # Licensed under the Apache License, Version 2.0 (the "License") from __future__ import print_function from bcc import BPF from bcc.utils import printb # define BPF program bpf_text = """ #include struct urandom_read_args { // from /sys/kernel/debug/tracing/events/random/urandom_read/format u64 __unused__; u32 got_bits; u32 pool_left; u32 input_left; }; int printarg(struct urandom_read_args *args) { bpf_trace_printk("%d\\n", args->got_bits); return 0; } """ # load BPF program b = BPF(text=bpf_text) b.attach_tracepoint(tp="random:urandom_read", fn_name="printarg") # header print("%-18s %-16s %-6s %s" % ("TIME(s)", "COMM", "PID", "GOTBITS")) # format output while 1: try: (task, pid, cpu, flags, ts, msg) = b.trace_fields() except ValueError: continue except KeyboardInterrupt: exit() printb(b"%-18.9f %-16s %-6d %s" % (ts, task, pid, msg)) bpfcc-0.12.0/examples/tracing/urandomread.py000077500000000000000000000020101357404205000207630ustar00rootroot00000000000000#!/usr/bin/python # # urandomread Example of instrumenting a kernel tracepoint. # For Linux, uses BCC, BPF. Embedded C. # # REQUIRES: Linux 4.7+ (BPF_PROG_TYPE_TRACEPOINT support). # # Test by running this, then in another shell, run: # dd if=/dev/urandom of=/dev/null bs=1k count=5 # # Copyright 2016 Netflix, Inc. # Licensed under the Apache License, Version 2.0 (the "License") from __future__ import print_function from bcc import BPF from bcc.utils import printb # load BPF program b = BPF(text=""" TRACEPOINT_PROBE(random, urandom_read) { // args is from /sys/kernel/debug/tracing/events/random/urandom_read/format bpf_trace_printk("%d\\n", args->got_bits); return 0; } """) # header print("%-18s %-16s %-6s %s" % ("TIME(s)", "COMM", "PID", "GOTBITS")) # format output while 1: try: (task, pid, cpu, flags, ts, msg) = b.trace_fields() except ValueError: continue except KeyboardInterrupt: exit() printb(b"%-18.9f %-16s %-6d %s" % (ts, task, pid, msg)) bpfcc-0.12.0/examples/tracing/urandomread_example.txt000066400000000000000000000012431357404205000226710ustar00rootroot00000000000000Examples of urandomread.py, the Linux eBPF/bcc version. To demonstrate this, the following workload was issued: # dd if=/dev/urandom of=/dev/null bs=1k count=5 While urandomread.py was tracing in another session: # ./urandomread.py TIME(s) COMM PID GOTBITS 22592556.392825000 dd 14228 8192 22592556.392949000 dd 14228 8192 22592556.393068999 dd 14228 8192 22592556.393183999 dd 14228 8192 22592556.393298000 dd 14228 8192 The GOTBITS of 8192 matches the workload of 1 Kbyte (8 Kbit) reads. This program was really written as a simple example of tracing a tracepoint. bpfcc-0.12.0/examples/tracing/vfsreadlat.c000066400000000000000000000016061357404205000204160ustar00rootroot00000000000000/* * vfsreadlat.c VFS read latency distribution. * For Linux, uses BCC, eBPF. See .py file. * * Copyright (c) 2013-2015 PLUMgrid, http://plumgrid.com * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. * * 15-Aug-2015 Brendan Gregg Created this. */ #include BPF_HASH(start, u32); BPF_HISTOGRAM(dist); int do_entry(struct pt_regs *ctx) { u32 pid; u64 ts, *val; pid = bpf_get_current_pid_tgid(); ts = bpf_ktime_get_ns(); start.update(&pid, &ts); return 0; } int do_return(struct pt_regs *ctx) { u32 pid; u64 *tsp, delta; pid = bpf_get_current_pid_tgid(); tsp = start.lookup(&pid); if (tsp != 0) { delta = bpf_ktime_get_ns() - *tsp; dist.increment(bpf_log2l(delta / 1000)); start.delete(&pid); } return 0; } bpfcc-0.12.0/examples/tracing/vfsreadlat.py000077500000000000000000000025501357404205000206260ustar00rootroot00000000000000#!/usr/bin/python # # vfsreadlat.py VFS read latency distribution. # For Linux, uses BCC, eBPF. See .c file. # # Written as a basic example of a function latency distribution histogram. # # USAGE: vfsreadlat.py [interval [count]] # # The default interval is 5 seconds. A Ctrl-C will print the partially # gathered histogram then exit. # # Copyright (c) 2015 Brendan Gregg. # Licensed under the Apache License, Version 2.0 (the "License") # # 15-Aug-2015 Brendan Gregg Created this. from __future__ import print_function from bcc import BPF from ctypes import c_ushort, c_int, c_ulonglong from time import sleep from sys import argv def usage(): print("USAGE: %s [interval [count]]" % argv[0]) exit() # arguments interval = 5 count = -1 if len(argv) > 1: try: interval = int(argv[1]) if interval == 0: raise if len(argv) > 2: count = int(argv[2]) except: # also catches -h, --help usage() # load BPF program b = BPF(src_file = "vfsreadlat.c") b.attach_kprobe(event="vfs_read", fn_name="do_entry") b.attach_kretprobe(event="vfs_read", fn_name="do_return") # header print("Tracing... Hit Ctrl-C to end.") # output loop = 0 do_exit = 0 while (1): if count > 0: loop += 1 if loop > count: exit() try: sleep(interval) except KeyboardInterrupt: pass; do_exit = 1 print() b["dist"].print_log2_hist("usecs") b["dist"].clear() if do_exit: exit() bpfcc-0.12.0/examples/tracing/vfsreadlat_example.txt000066400000000000000000000070431357404205000225270ustar00rootroot00000000000000Demonstrations of vfsreadlat.py, the Linux eBPF/bcc version. This example traces the latency of vfs_read (time from call to return), printing it as a histogram distribution. By default, output is every five seconds: # ./vfsreadlat.py Tracing... Hit Ctrl-C to end. usecs : count distribution 0 -> 1 : 4457 |*************************************+| 2 -> 3 : 447 |*** | 4 -> 7 : 2059 |***************** | 8 -> 15 : 1179 |********** | 16 -> 31 : 63 | | 32 -> 63 : 0 | | 64 -> 127 : 2 | | 128 -> 255 : 0 | | 256 -> 511 : 3 | | 512 -> 1023 : 1 | | 1024 -> 2047 : 3 | | 2048 -> 4095 : 2 | | 4096 -> 8191 : 0 | | 8192 -> 16383 : 0 | | 16384 -> 32767 : 0 | | 32768 -> 65535 : 0 | | 65536 -> 131071 : 4 | | 131072 -> 262143 : 2 | | 262144 -> 524287 : 0 | | 524288 -> 1048575 : 4 | | ^C usecs : count distribution 0 -> 1 : 241 |*************************************+| 2 -> 3 : 17 |** | 4 -> 7 : 2 | | 8 -> 15 : 4 | | 16 -> 31 : 2 | | 32 -> 63 : 0 | | 64 -> 127 : 1 | | 128 -> 255 : 0 | | 256 -> 511 : 1 | | 512 -> 1023 : 1 | | 1024 -> 2047 : 0 | | 2048 -> 4095 : 1 | | 4096 -> 8191 : 0 | | 8192 -> 16383 : 0 | | 16384 -> 32767 : 0 | | 32768 -> 65535 : 0 | | 65536 -> 131071 : 0 | | 131072 -> 262143 : 0 | | 262144 -> 524287 : 0 | | 524288 -> 1048575 : 1 | | These examples show outliers in the 524 - 1048 milliseconds range. Since vfs_read() will catch many types of events, this could be anything including keystroke latency on ssh sessions. Further drilling with bcc will be necessary to identify more details. Full usage: # ./vfsreadlat.py -h USAGE: ./vfsreadlat.py [interval [count]] bpfcc-0.12.0/examples/usdt_sample/000077500000000000000000000000001357404205000170055ustar00rootroot00000000000000bpfcc-0.12.0/examples/usdt_sample/CMakeLists.txt000066400000000000000000000004161357404205000215460ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.0) # This sample requires C++11 enabled. set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -Weffc++") add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/usdt_sample_lib1) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/usdt_sample_app1) bpfcc-0.12.0/examples/usdt_sample/scripts/000077500000000000000000000000001357404205000204745ustar00rootroot00000000000000bpfcc-0.12.0/examples/usdt_sample/scripts/bpf_text_shared.c000066400000000000000000000032271357404205000240050ustar00rootroot00000000000000#include #include /** * @brief Helper method to filter based on the specified inputString. * @param inputString The operation input string to check against the filter. * @return True if the specified inputString starts with the hard-coded FILTER_STRING; otherwise, false. */ static inline bool filter(char const* inputString) { char needle[] = "FILTER_STRING"; ///< The FILTER STRING is replaced by python code. char haystack[sizeof(needle)] = {}; bpf_probe_read(&haystack, sizeof(haystack), (void*)inputString); for (int i = 0; i < sizeof(needle) - 1; ++i) { if (needle[i] != haystack[i]) { return false; } } return true; } /** * @brief Contains the operation start data to trace. */ struct start_data_t { u64 operation_id; ///< The id of the operation. char input[64]; ///< The input string of the request. u64 start; ///< Timestamp of the start operation (start timestamp). }; /** * @brief Contains the operation start data. * key: the operation id. * value: The operation start latency data. */ BPF_HASH(start_hash, u64, struct start_data_t); /** * @brief Reads the operation request arguments and stores the start data in the hash. * @param ctx The BPF context. */ int trace_operation_start(struct pt_regs* ctx) { struct start_data_t start_data = {}; bpf_usdt_readarg_p(2, ctx, &start_data.input, sizeof(start_data.input)); FILTER ///< Replaced by python code. bpf_usdt_readarg(1, ctx, &start_data.operation_id); start_data.start = bpf_ktime_get_ns(); start_hash.update(&start_data.operation_id, &start_data); return 0; } bpfcc-0.12.0/examples/usdt_sample/scripts/lat_avg.py000077500000000000000000000077021357404205000224740ustar00rootroot00000000000000#!/usr/bin/python import argparse from time import sleep, strftime from sys import argv import ctypes as ct from bcc import BPF, USDT import inspect import os # Parse command line arguments parser = argparse.ArgumentParser(description="Trace the moving average of the latency of an operation using usdt probes.", formatter_class=argparse.RawDescriptionHelpFormatter) parser.add_argument("-p", "--pid", type=int, help="The id of the process to trace.") parser.add_argument("-i", "--interval", type=int, help="The interval in seconds on which to report the latency distribution.") parser.add_argument("-c", "--count", type=int, default=16, help="The count of samples over which to calculate the moving average.") parser.add_argument("-f", "--filterstr", type=str, default="", help="The prefix filter for the operation input. If specified, only operations for which the input string starts with the filterstr are traced.") parser.add_argument("-v", "--verbose", dest="verbose", action="store_true", help="If true, will output verbose logging information.") parser.set_defaults(verbose=False) args = parser.parse_args() this_pid = int(args.pid) this_interval = int(args.interval) this_count = int(args.count) this_filter = str(args.filterstr) if this_interval < 1: print("Invalid value for interval, using 1.") this_interval = 1 if this_count < 1: print("Invalid value for count, using 1.") this_count = 1 debugLevel=0 if args.verbose: debugLevel=4 # BPF program bpf_text_shared = "%s/bpf_text_shared.c" % os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) bpf_text = open(bpf_text_shared, 'r').read() bpf_text += """ const u32 MAX_SAMPLES = SAMPLE_COUNT; struct hash_key_t { char input[64]; }; struct hash_leaf_t { u32 count; u64 total; u64 average; }; /** * @brief Contains the averages for the operation latencies by operation input. */ BPF_HASH(lat_hash, struct hash_key_t, struct hash_leaf_t, 512); /** * @brief Reads the operation response arguments, calculates the latency, and stores it in the histogram. * @param ctx The BPF context. */ int trace_operation_end(struct pt_regs* ctx) { u64 operation_id; bpf_usdt_readarg(1, ctx, &operation_id); struct start_data_t* start_data = start_hash.lookup(&operation_id); if (0 == start_data) { return 0; } u64 duration = bpf_ktime_get_ns() - start_data->start; struct hash_key_t hash_key = {}; __builtin_memcpy(&hash_key.input, start_data->input, sizeof(hash_key.input)); start_hash.delete(&operation_id); struct hash_leaf_t zero = {}; struct hash_leaf_t* hash_leaf = lat_hash.lookup_or_try_init(&hash_key, &zero); if (0 == hash_leaf) { return 0; } if (hash_leaf->count < MAX_SAMPLES) { hash_leaf->count++; } else { hash_leaf->total -= hash_leaf->average; } hash_leaf->total += duration; hash_leaf->average = hash_leaf->total / hash_leaf->count; return 0; } """ bpf_text = bpf_text.replace("SAMPLE_COUNT", str(this_count)) bpf_text = bpf_text.replace("FILTER_STRING", this_filter) if this_filter: bpf_text = bpf_text.replace("FILTER", "if (!filter(start_data.input)) { return 0; }") else: bpf_text = bpf_text.replace("FILTER", "") # Create USDT context print("Attaching probes to pid %d" % this_pid) usdt_ctx = USDT(pid=this_pid) usdt_ctx.enable_probe(probe="operation_start", fn_name="trace_operation_start") usdt_ctx.enable_probe(probe="operation_end", fn_name="trace_operation_end") # Create BPF context, load BPF program bpf_ctx = BPF(text=bpf_text, usdt_contexts=[usdt_ctx], debug=debugLevel) print("Tracing... Hit Ctrl-C to end.") lat_hash = bpf_ctx.get_table("lat_hash") while (1): try: sleep(this_interval) except KeyboardInterrupt: exit() print("[%s]" % strftime("%H:%M:%S")) print("%-64s %8s %16s" % ("input", "count", "latency (us)")) for k, v in lat_hash.items(): print("%-64s %8d %16d" % (k.input, v.count, v.average / 1000)) bpfcc-0.12.0/examples/usdt_sample/scripts/lat_dist.py000077500000000000000000000062231357404205000226570ustar00rootroot00000000000000#!/usr/bin/python import argparse from time import sleep, strftime from sys import argv import ctypes as ct from bcc import BPF, USDT import inspect import os # Parse command line arguments parser = argparse.ArgumentParser(description="Trace the latency distribution of an operation using usdt probes.", formatter_class=argparse.RawDescriptionHelpFormatter) parser.add_argument("-p", "--pid", type=int, help="The id of the process to trace.") parser.add_argument("-i", "--interval", type=int, help="The interval in seconds on which to report the latency distribution.") parser.add_argument("-f", "--filterstr", type=str, default="", help="The prefix filter for the operation input. If specified, only operations for which the input string starts with the filterstr are traced.") parser.add_argument("-v", "--verbose", dest="verbose", action="store_true", help="If true, will output verbose logging information.") parser.set_defaults(verbose=False) args = parser.parse_args() this_pid = int(args.pid) this_interval = int(args.interval) this_filter = str(args.filterstr) if this_interval < 1: print("Invalid value for interval, using 1.") this_interval = 1 debugLevel=0 if args.verbose: debugLevel=4 # BPF program bpf_text_shared = "%s/bpf_text_shared.c" % os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) bpf_text = open(bpf_text_shared, 'r').read() bpf_text += """ /** * @brief The key to use for the latency histogram. */ struct dist_key_t { char input[64]; ///< The input string of the request. u64 slot; ///< The histogram slot. }; /** * @brief Contains the histogram for the operation latencies. */ BPF_HISTOGRAM(dist, struct dist_key_t); /** * @brief Reads the operation response arguments, calculates the latency, and stores it in the histogram. * @param ctx The BPF context. */ int trace_operation_end(struct pt_regs* ctx) { u64 operation_id; bpf_usdt_readarg(1, ctx, &operation_id); struct start_data_t* start_data = start_hash.lookup(&operation_id); if (0 == start_data) { return 0; } u64 duration = bpf_ktime_get_ns() - start_data->start; struct dist_key_t dist_key = {}; __builtin_memcpy(&dist_key.input, start_data->input, sizeof(dist_key.input)); dist_key.slot = bpf_log2l(duration / 1000); start_hash.delete(&operation_id); dist.increment(dist_key); return 0; } """ bpf_text = bpf_text.replace("FILTER_STRING", this_filter) if this_filter: bpf_text = bpf_text.replace("FILTER", "if (!filter(start_data.input)) { return 0; }") else: bpf_text = bpf_text.replace("FILTER", "") # Create USDT context print("Attaching probes to pid %d" % this_pid) usdt_ctx = USDT(pid=this_pid) usdt_ctx.enable_probe(probe="operation_start", fn_name="trace_operation_start") usdt_ctx.enable_probe(probe="operation_end", fn_name="trace_operation_end") # Create BPF context, load BPF program bpf_ctx = BPF(text=bpf_text, usdt_contexts=[usdt_ctx], debug=debugLevel) start = 0 dist = bpf_ctx.get_table("dist") while (1): try: sleep(this_interval) except KeyboardInterrupt: exit() print("[%s]" % strftime("%H:%M:%S")) dist.print_log2_hist("latency (us)") bpfcc-0.12.0/examples/usdt_sample/scripts/latency.py000077500000000000000000000103111357404205000225040ustar00rootroot00000000000000#!/usr/bin/python import argparse from time import sleep from sys import argv import ctypes as ct from bcc import BPF, USDT import inspect import os # Parse command line arguments parser = argparse.ArgumentParser(description="Trace the latency of an operation using usdt probes.", formatter_class=argparse.RawDescriptionHelpFormatter) parser.add_argument("-p", "--pid", type=int, help="The id of the process to trace.") parser.add_argument("-f", "--filterstr", type=str, default="", help="The prefix filter for the operation input. If specified, only operations for which the input string starts with the filterstr are traced.") parser.add_argument("-v", "--verbose", dest="verbose", action="store_true", help="If true, will output verbose logging information.") parser.set_defaults(verbose=False) args = parser.parse_args() this_pid = int(args.pid) this_filter = str(args.filterstr) debugLevel=0 if args.verbose: debugLevel=4 # BPF program bpf_text_shared = "%s/bpf_text_shared.c" % os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) bpf_text = open(bpf_text_shared, 'r').read() bpf_text += """ /** * @brief Contains the latency data w.r.t. the complete operation from request to response. */ struct end_data_t { u64 operation_id; ///< The id of the operation. char input[64]; ///< The request (input) string. char output[64]; ///< The response (output) string. u64 start; ///< The start timestamp of the operation. u64 end; ///< The end timestamp of the operation. u64 duration; ///< The duration of the operation. }; /** * The output buffer, which will be used to push the latency event data to user space. */ BPF_PERF_OUTPUT(operation_event); /** * @brief Reads the operation response arguments, calculates the latency event data, and writes it to the user output buffer. * @param ctx The BPF context. */ int trace_operation_end(struct pt_regs* ctx) { u64 operation_id; bpf_usdt_readarg(1, ctx, &operation_id); struct start_data_t* start_data = start_hash.lookup(&operation_id); if (0 == start_data) { return 0; } struct end_data_t end_data = {}; end_data.operation_id = operation_id; bpf_usdt_readarg_p(2, ctx, &end_data.output, sizeof(end_data.output)); end_data.end = bpf_ktime_get_ns(); end_data.start = start_data->start; end_data.duration = end_data.end - end_data.start; __builtin_memcpy(&end_data.input, start_data->input, sizeof(end_data.input)); start_hash.delete(&end_data.operation_id); operation_event.perf_submit(ctx, &end_data, sizeof(end_data)); return 0; } """ bpf_text = bpf_text.replace("FILTER_STRING", this_filter) if this_filter: bpf_text = bpf_text.replace("FILTER", "if (!filter(start_data.input)) { return 0; }") else: bpf_text = bpf_text.replace("FILTER", "") # Create USDT context print("Attaching probes to pid %d" % this_pid) usdt_ctx = USDT(pid=this_pid) usdt_ctx.enable_probe(probe="operation_start", fn_name="trace_operation_start") usdt_ctx.enable_probe(probe="operation_end", fn_name="trace_operation_end") # Create BPF context, load BPF program bpf_ctx = BPF(text=bpf_text, usdt_contexts=[usdt_ctx], debug=debugLevel) # Define latency event and print function class OperationEventData(ct.Structure): _fields_ = [("operation_id", ct.c_ulonglong), ("input", ct.c_char * 64), ("output", ct.c_char * 64), ("start", ct.c_ulonglong), ("end", ct.c_ulonglong), ("duration", ct.c_ulonglong)] start = 0 def print_event(cpu, data, size): global start event = ct.cast(data, ct.POINTER(OperationEventData)).contents if start == 0: start = event.start time_s = (float(event.start - start)) / 1000000000 latency = (float(event.duration) / 1000) print("%-18.9f %-10d %-32s %-32s %16d %16d %16d" % (time_s, event.operation_id, event.input, event.output, event.start, event.end, latency)) # Print header print("Tracing... Hit Ctrl-C to end.") print("%-18s %-10s %-32s %-32s %16s %16s %16s" % ("time(s)", "id", "input", "output", "start (ns)", "end (ns)", "duration (us)")) # Output latency events bpf_ctx["operation_event"].open_perf_buffer(print_event) while 1: bpf_ctx.perf_buffer_poll() bpfcc-0.12.0/examples/usdt_sample/usdt_sample.md000066400000000000000000000207271357404205000216570ustar00rootroot00000000000000Tested on Fedora25 4.11.3-200.fc25.x86_64, gcc (GCC) 6.3.1 20161221 (Red Hat 6.3.1-1) As an alternative to using ...bcc/tests/python/include/folly/tracing/StaticTracepoint.h, it's possible to use systemtap-sdt-devel. However, this is *not* required for this sample. ```bash $ sudo dnf install systemtap-sdt-devel # For Fedora25, other distro's might have differently named packages. ``` If using systemtap-sdt-devel, the following commands can be used to generate the corresponding header and object files: Also see the CMakeLists.txt file for an example how to do this using cmake. ```bash $ dtrace -h -s usdt_sample_lib1/src/lib1_sdt.d -o usdt_sample_lib1/include/usdt_sample_lib1/lib1_sdt.h $ dtrace -G -s usdt_sample_lib1/src/lib1_sdt.d -o lib1_sdt.o ``` Build the sample: ```bash $ pwd ~/src/bcc $ mkdir -p examples/usdt_sample/build && pushd examples/usdt_sample/build $ cmake .. && make $ popd ``` After building, you should see the available probes: ```bash $ python tools/tplist.py -l examples/usdt_sample/build/usdt_sample_lib1/libusdt_sample_lib1.so examples/usdt_sample/build/usdt_sample_lib1/libusdt_sample_lib1.so usdt_sample_lib1:operation_end examples/usdt_sample/build/usdt_sample_lib1/libusdt_sample_lib1.so usdt_sample_lib1:operation_start $ readelf -n examples/usdt_sample/build/usdt_sample_lib1/libusdt_sample_lib1.so Displaying notes found at file offset 0x000001c8 with length 0x00000024: Owner Data size Description GNU 0x00000014 NT_GNU_BUILD_ID (unique build ID bitstring) Build ID: 3930c19f654990159563394669f2ed5281513302 Displaying notes found at file offset 0x0001b9ec with length 0x000000c0: Owner Data size Description stapsdt 0x00000047 NT_STAPSDT (SystemTap probe descriptors) Provider: usdt_sample_lib1 Name: operation_end Location: 0x000000000000ed6d, Base: 0x0000000000000000, Semaphore: 0x0000000000000000 Arguments: -8@%rbx -8@%rax stapsdt 0x0000004e NT_STAPSDT (SystemTap probe descriptors) Provider: usdt_sample_lib1 Name: operation_start Location: 0x000000000000ee2c, Base: 0x0000000000000000, Semaphore: 0x0000000000000000 Arguments: -8@-24(%rbp) -8@%rax ``` Start the usdt sample application: ```bash $ examples/usdt_sample/build/usdt_sample_app1/usdt_sample_app1 "pf" 1 30 10 1 50 Applying the following parameters: Input prefix: pf. Input range: [1, 30]. Calls Per Second: 10. Latency range: [1, 50] ms. You can now run the bcc scripts, see usdt_sample.md for examples. pid: 25433 Press ctrl-c to exit. ``` Use argdist.py on the individual probes: ```bash $ sudo python tools/argdist.py -p 25433 -i 5 -C 'u:usdt_sample_lib1:operation_start():char*:arg2#input' -z 32 [11:18:29] input COUNT EVENT 1 arg2 = pf_10 1 arg2 = pf_5 1 arg2 = pf_12 1 arg2 = pf_1 1 arg2 = pf_11 1 arg2 = pf_28 1 arg2 = pf_16 1 arg2 = pf_19 1 arg2 = pf_15 1 arg2 = pf_2 2 arg2 = pf_17 2 arg2 = pf_3 2 arg2 = pf_25 2 arg2 = pf_30 2 arg2 = pf_13 2 arg2 = pf_18 2 arg2 = pf_7 2 arg2 = pf_29 2 arg2 = pf_26 3 arg2 = pf_8 3 arg2 = pf_21 3 arg2 = pf_14 4 arg2 = pf_6 4 arg2 = pf_23 5 arg2 = pf_24 ``` Use latency.py to trace the operation latencies: ```bash $ sudo python examples/usdt_sample/scripts/latency.py -p=25433 -f="pf_2" Attaching probes to pid 25433 Tracing... Hit Ctrl-C to end. time(s) id input output start (ns) end (ns) duration (us) 0.000000000 7204 pf_28 resp_pf_28 11949439999644 11949489234565 49234 0.100211886 7205 pf_28 resp_pf_28 11949540211530 11949574403064 34191 0.300586675 7207 pf_21 resp_pf_21 11949740586319 11949742773571 2187 0.400774366 7208 pf_28 resp_pf_28 11949840774010 11949859965498 19191 0.701365719 7211 pf_21 resp_pf_21 11950141365363 11950152551131 11185 0.901736620 7213 pf_25 resp_pf_25 11950341736264 11950347924333 6188 1.102162217 7215 pf_21 resp_pf_21 11950542161861 11950567484183 25322 1.302595998 7217 pf_23 resp_pf_23 11950742595642 11950761841242 19245 1.503047601 7219 pf_2 resp_pf_2 11950943047245 11950951213474 8166 1.703371457 7221 pf_27 resp_pf_27 11951143371101 11951176568051 33196 2.104228899 7225 pf_24 resp_pf_24 11951544228543 11951588432769 44204 2.304608175 7227 pf_21 resp_pf_21 11951744607819 11951790796068 46188 2.404796703 7228 pf_21 resp_pf_21 11951844796347 11951877984160 33187 2.605134923 7230 pf_27 resp_pf_27 11952045134567 11952065327660 20193 3.206291642 7236 pf_29 resp_pf_29 11952646291286 11952660443343 14152 3.506887492 7239 pf_21 resp_pf_21 11952946887136 11952995060987 48173 ``` Use lat_dist.py to trace the latency distribution: ```bash $ sudo python examples/usdt_sample/scripts/lat_dist.py -p=25433 -i=30 -f="pf_20" Attaching probes to pid 25433 [11:23:47] Bucket ptr = 'pf_20' latency (us) : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 0 | | 128 -> 255 : 0 | | 256 -> 511 : 0 | | 512 -> 1023 : 0 | | 1024 -> 2047 : 1 |********** | 2048 -> 4095 : 1 |********** | 4096 -> 8191 : 0 | | 8192 -> 16383 : 1 |********** | 16384 -> 32767 : 4 |****************************************| 32768 -> 65535 : 3 |****************************** | ``` Use lat_avg.py to trace the moving average of the latencies: ```bash $ sudo python examples/usdt_sample/scripts/lat_avg.py -p=25433 -i=5 -c=10 -f="pf_2" Attaching probes to pid 25433 Tracing... Hit Ctrl-C to end. [11:28:32] input count latency (us) pf_22 3 7807 pf_23 4 36914 pf_25 3 31473 pf_28 2 10627 pf_27 1 47174 pf_29 1 8138 pf_26 1 49121 pf_20 2 29158 ``` bpfcc-0.12.0/examples/usdt_sample/usdt_sample_app1/000077500000000000000000000000001357404205000222465ustar00rootroot00000000000000bpfcc-0.12.0/examples/usdt_sample/usdt_sample_app1/CMakeLists.txt000066400000000000000000000005161357404205000250100ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.0) project(usdt_sample_app1) include_directories( ${USDT_SAMPLE_LIB1_INCLUDE_DIR} ) link_directories( ${USDT_SAMPLE_LIB1_LINK_DIR} ) add_executable( ${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp ) target_link_libraries( ${PROJECT_NAME} ${USDT_SAMPLE_LIB1_LIB} pthread ) bpfcc-0.12.0/examples/usdt_sample/usdt_sample_app1/main.cpp000066400000000000000000000116141357404205000237010ustar00rootroot00000000000000// std #include #include #include #include #include #include #include #include #include // gnu-c #include #include // usdt_sample_lib1 #include "usdt_sample_lib1/lib1.h" void print_usage(int argc, char** argv) { std::cout << "Usage:" << std::endl; std::cout << argv[0] << " " << std::endl; std::cout << "InputPrefix: Prefix of the input string to the operation. Default: dummy" << std::endl; std::cout << "InputMinimum: Minimum number to make the input string to the operation somewhat unique. Default: 1" << std::endl; std::cout << "InputMaximum: Maximum number to make the input string to the operation somewhat unique. Default: 50" << std::endl; std::cout << "CallsPerSec: Rate of calls to the operation. Default: 10" << std::endl; std::cout << "MinimumLatencyMs: Minimum latency to apply to the operation. Default: 20" << std::endl; std::cout << "MaximumLatencyMs: Maximum latency to apply to the operation. Default: 40" << std::endl; } int main(int argc, char** argv) { std::string inputPrefix("dummy"); std::uint32_t inputMinimum = 1; std::uint32_t inputMaximum = 50; std::uint32_t callsPerSec = 10; std::uint32_t minLatMs = 20; std::uint32_t maxLatMs = 40; try { if (argc > 1) { inputPrefix = argv[1]; } if (argc > 2) { inputMinimum = static_cast(std::max(1, std::min(50, std::atoi(argv[2])))); } if (argc > 3) { inputMaximum = static_cast(std::max(1, std::min(50, std::atoi(argv[3])))); } if (argc > 4) { callsPerSec = static_cast(std::max(1, std::min(50, std::atoi(argv[4])))); } if (argc > 5) { minLatMs = static_cast(std::max(1, std::min(50, std::atoi(argv[5])))); } if (argc > 6) { maxLatMs = static_cast(std::max(1, std::min(50, std::atoi(argv[6])))); } } catch (const std::exception& exc) { std::cout << "Exception while reading arguments: " << exc.what() << std::endl; print_usage(argc, argv); return -1; } catch (...) { std::cout << "Unknown exception while reading arguments." << std::endl; print_usage(argc, argv); return -1; } if (inputMinimum > inputMaximum) { std::cout << "InputMinimum must be smaller than InputMaximum." << std::endl; print_usage(argc, argv); return -1; } if (minLatMs > maxLatMs) { std::cout << "MinimumLatencyMs must be smaller than MaximumLatencyMs." << std::endl; print_usage(argc, argv); return -1; } std::cout << "Applying the following parameters:" << std::endl << "Input prefix: " << inputPrefix << "." << std::endl << "Input range: [" << inputMinimum << ", " << inputMaximum << "]." << std::endl << "Calls Per Second: " << callsPerSec << "." << std::endl << "Latency range: [" << minLatMs << ", " << maxLatMs << "] ms." << std::endl; const int sleepTimeMs = 1000 / callsPerSec; OperationProvider op(minLatMs, maxLatMs); std::mutex queueMutex; std::queue> responseQueue; auto dequeueFuture = std::async(std::launch::async, [&]() { while (true) { bool empty = false; { std::lock_guard lg(queueMutex); empty = responseQueue.empty(); } if (empty) { std::this_thread::sleep_for(std::chrono::milliseconds(sleepTimeMs)); continue; } responseQueue.front().get(); // std::cout << "Removing item from queue." << std::endl; std::lock_guard lg(queueMutex); responseQueue.pop(); } }); std::random_device rd; std::uniform_int_distribution<> dis(inputMinimum, inputMaximum); std::cout << "You can now run the bcc scripts, see usdt_sample.md for examples." << std::endl; std::cout << "pid: " << ::getpid() << std::endl; std::cout << "Press ctrl-c to exit." << std::endl; while (true) { std::ostringstream inputOss; inputOss << inputPrefix << "_" << dis(rd); auto responseFuture = op.executeAsync(OperationRequest(inputOss.str())); { std::lock_guard lg(queueMutex); responseQueue.push(responseFuture); } // For a sample application, this is good enough to simulate callsPerSec. std::this_thread::sleep_for(std::chrono::milliseconds(sleepTimeMs)); } dequeueFuture.get(); return 0; } bpfcc-0.12.0/examples/usdt_sample/usdt_sample_lib1/000077500000000000000000000000001357404205000222345ustar00rootroot00000000000000bpfcc-0.12.0/examples/usdt_sample/usdt_sample_lib1/CMakeLists.txt000066400000000000000000000035141357404205000247770ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.0) project(usdt_sample_lib1) # Define variables. set(USDT_SAMPLE_LIB1_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include CACHE STRING "USDT_SAMPLE_LIB1_INCLUDE_DIR" FORCE) set(USDT_SAMPLE_LIB1_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src CACHE STRING "USDT_SAMPLE_LIB1_SRC_DIR" FORCE) set(USDT_SAMPLE_LIB1_LINK_DIR ${CMAKE_CURRENT_BINARY_DIR} CACHE STRING "USDT_SAMPLE_LIB1_LINK_DIR" FORCE) set(USDT_SAMPLE_LIB1_LIB ${PROJECT_NAME} CACHE STRING "USDT_SAMPLE_LIB1_LIB" FORCE) set(USDT_SAMPLE_LIB1_GENERATED ${CMAKE_CURRENT_BINARY_DIR}/generated) ## Start - N.B. Following section only relevant when using systemtap-sdt-devel. # Create usdt header file. # N.B. ${USDT_SAMPLE_LIB1_INCLUDE_DIR}/usdt_sample_lib1/lib1_sdt.h must be removed manually in order for it to be (re-)created. # i.e. after making changes to libt_sdt.d #add_custom_command( # OUTPUT ${USDT_SAMPLE_LIB1_INCLUDE_DIR}/usdt_sample_lib1/lib1_sdt.h # PRE_BUILD # COMMAND dtrace -h -s ${USDT_SAMPLE_LIB1_SRC_DIR}/lib1_sdt.d -o ${USDT_SAMPLE_LIB1_INCLUDE_DIR}/usdt_sample_lib1/lib1_sdt.h # COMMENT "Create usdt probes header file" #) # Create usdt object file. #file(MAKE_DIRECTORY ${USDT_SAMPLE_LIB1_GENERATED}) #add_custom_command( # OUTPUT ${USDT_SAMPLE_LIB1_GENERATED}/lib1_sdt.o # PRE_BUILD # COMMAND dtrace -G -s ${USDT_SAMPLE_LIB1_SRC_DIR}/lib1_sdt.d -o ${USDT_SAMPLE_LIB1_GENERATED}/lib1_sdt.o # COMMENT "Create usdt probes object file" #) ## End include_directories( ${USDT_SAMPLE_LIB1_INCLUDE_DIR} # For folly StaticTracepoint.h: ${CMAKE_CURRENT_SOURCE_DIR}/../../../tests/python/include ) add_library( ${PROJECT_NAME} SHARED ## Only relevant when using systemtap-sdt-devel # ${USDT_SAMPLE_LIB1_INCLUDE_DIR}/usdt_sample_lib1/lib1_sdt.h # ${USDT_SAMPLE_LIB1_GENERATED}/lib1_sdt.o ${USDT_SAMPLE_LIB1_SRC_DIR}/lib1.cpp ) bpfcc-0.12.0/examples/usdt_sample/usdt_sample_lib1/include/000077500000000000000000000000001357404205000236575ustar00rootroot00000000000000bpfcc-0.12.0/examples/usdt_sample/usdt_sample_lib1/include/usdt_sample_lib1/000077500000000000000000000000001357404205000271065ustar00rootroot00000000000000bpfcc-0.12.0/examples/usdt_sample/usdt_sample_lib1/include/usdt_sample_lib1/lib1.h000066400000000000000000000026631357404205000301150ustar00rootroot00000000000000#pragma once // std #include #include #include #include /** * @brief Contains the operation request data. */ class OperationRequest { public: OperationRequest(const std::string& input); const std::string& input() const { return _input; } private: std::string _input; }; /** * @brief Contains the operation response data. */ class OperationResponse { public: OperationResponse(const std::string& output); const std::string& output() const { return _output; } private: std::string _output; }; /** * @brief Provides the operation. */ class OperationProvider { public: /** * @brief Constructs an instance of OperationProvider. * @param minLatencyMs The minimum latency to simulate for the operation. * @param maxLatencyMs The maximum latency to simulate for the operation. */ OperationProvider(std::uint32_t minLatencyMs, std::uint32_t maxLatencyMs); /** * @brief Asynchronously executes the operation. * @param request The request input data for the operation. * @return A shared_future of the response of the operation. */ std::shared_future executeAsync(const OperationRequest& request); private: std::mt19937 _gen; ///< Used randomly determine an operation latency to simulate. std::uniform_int_distribution<> _dis; ///< Used randomly determine an operation latency to simulate. }; bpfcc-0.12.0/examples/usdt_sample/usdt_sample_lib1/include/usdt_sample_lib1/lib1_sdt.h000066400000000000000000000031511357404205000307600ustar00rootroot00000000000000// N.B. This file is not used by this usdt_sample. Instead, the StaticTracepoint.h file from folly is used. // It is here only for demonstration purposes. /* Generated by the Systemtap dtrace wrapper */ #define _SDT_HAS_SEMAPHORES 1 #define STAP_HAS_SEMAPHORES 1 /* deprecated */ #include /* USDT_SAMPLE_LIB1_OPERATION_START ( uint64_t operation_id, const char * input ) */ #if defined STAP_SDT_V1 #define USDT_SAMPLE_LIB1_OPERATION_START_ENABLED() __builtin_expect (operation_start_semaphore, 0) #define usdt_sample_lib1_operation_start_semaphore operation_start_semaphore #else #define USDT_SAMPLE_LIB1_OPERATION_START_ENABLED() __builtin_expect (usdt_sample_lib1_operation_start_semaphore, 0) #endif __extension__ extern unsigned short usdt_sample_lib1_operation_start_semaphore __attribute__ ((unused)) __attribute__ ((section (".probes"))); #define USDT_SAMPLE_LIB1_OPERATION_START(arg1, arg2) \ DTRACE_PROBE2 (usdt_sample_lib1, operation_start, arg1, arg2) /* USDT_SAMPLE_LIB1_OPERATION_END ( uint64_t operation_id, const char * output ) */ #if defined STAP_SDT_V1 #define USDT_SAMPLE_LIB1_OPERATION_END_ENABLED() __builtin_expect (operation_end_semaphore, 0) #define usdt_sample_lib1_operation_end_semaphore operation_end_semaphore #else #define USDT_SAMPLE_LIB1_OPERATION_END_ENABLED() __builtin_expect (usdt_sample_lib1_operation_end_semaphore, 0) #endif __extension__ extern unsigned short usdt_sample_lib1_operation_end_semaphore __attribute__ ((unused)) __attribute__ ((section (".probes"))); #define USDT_SAMPLE_LIB1_OPERATION_END(arg1, arg2) \ DTRACE_PROBE2 (usdt_sample_lib1, operation_end, arg1, arg2) bpfcc-0.12.0/examples/usdt_sample/usdt_sample_lib1/src/000077500000000000000000000000001357404205000230235ustar00rootroot00000000000000bpfcc-0.12.0/examples/usdt_sample/usdt_sample_lib1/src/lib1.cpp000066400000000000000000000036331357404205000243630ustar00rootroot00000000000000#include "usdt_sample_lib1/lib1.h" // std #include #include #include #include // usdt_sample_lib1 #include "folly/tracing/StaticTracepoint.h" // When using systemtap-sdt-devel, the following file should be included: // #include "usdt_sample_lib1/lib1_sdt.h" OperationRequest::OperationRequest(const std::string& input_) : _input(input_) { } OperationResponse::OperationResponse(const std::string& output_) : _output(output_) { } OperationProvider::OperationProvider(std::uint32_t minLatencyMs_, std::uint32_t maxLatencyMs_) : _gen(std::random_device()()) , _dis(minLatencyMs_, maxLatencyMs_) { } std::shared_future OperationProvider::executeAsync(const OperationRequest& request) { static std::atomic operationIdCounter(0); std::uint64_t operationId = operationIdCounter++; FOLLY_SDT(usdt_sample_lib1, operation_start, operationId, request.input().c_str()); /* Below an example of how to use this sample with systemtap-sdt-devel: if (USDT_SAMPLE_LIB1_OPERATION_START_ENABLED()) { //std::cout << "operation_start probe enabled." << std::endl; USDT_SAMPLE_LIB1_OPERATION_START(operationId, &inputBuf); } */ auto latencyMs = _dis(_gen); return std::async(std::launch::async, [latencyMs, operationId, request]() { std::this_thread::sleep_for(std::chrono::milliseconds(latencyMs)); auto output = std::string("resp_") + request.input(); OperationResponse response(output); FOLLY_SDT(usdt_sample_lib1, operation_end, operationId, response.output().c_str()); /* Below an example of how to use this sample with systemtap-sdt-devel: if (USDT_SAMPLE_LIB1_OPERATION_END_ENABLED()) { //std::cout << "operation_end probe enabled." << std::endl; USDT_SAMPLE_LIB1_OPERATION_END(operationId, &outputBuf); } */ return response; }); } bpfcc-0.12.0/examples/usdt_sample/usdt_sample_lib1/src/lib1_sdt.d000066400000000000000000000005141357404205000246710ustar00rootroot00000000000000# This file is only relevant when using systemtap-sdt-devel (see usdt_sample.md). # This usdt_sample uses the StaticTracepoint.h header file (from folly) instead. provider usdt_sample_lib1 { probe operation_start(uint64_t operation_id, const char* input); probe operation_end(uint64_t operation_id, const char* output); }; bpfcc-0.12.0/images/000077500000000000000000000000001357404205000141145ustar00rootroot00000000000000bpfcc-0.12.0/images/bcc_tracing_tools_2016.png000066400000000000000000010176341357404205000207640ustar00rootroot00000000000000PNG  IHDRYysRGB pHYsgRiTXtXML:com.adobe.xmp 5 1 2 O@IDATx$Ն[eq.-E7\.-,$!wߚj@ BlezZ}ӻ^gFᮿzcYf믻6((\sM㏻:z-ʝ+_5A)^{mZkU4mFN(݁ @O hjdȐbXnUW]8]v٥g mٜRN_r뮻nEM6 @ V@*@}3Ff>G@AR/""e]) >|xs~w+)uBDnr ^xa<IJ^KO͆ @Ps!@ZD`wJ.6Z'/,6Zlq`IAF-w *=һ pC N{_? @}|O9n7ojZj)7c^z·~P|# @ "O @^7zh;qO4D;֥Zfiu?/^@ @#= @M 駟>=es=W_}Ur^s{[feo~7xy׭z )K2lT*;'igy&N?a_d_-+?|%ԒoZh!7dq4LnWt't{*+7p{l}.?c=zK/|}AWv3ˎ~n1lz0S?w}70jr?OB"9,dh(Qm܈#j~"e3$V?::tgy"r{!3| 6}PRhaAq(ܚYvf-eI/b&W+䭷 +:,yi)^{m7묳:)8w'4uiӟVvqXuU2Yhx_i믿jpw+}k=g'v~馛-ܒdRN=UN=T7svje 7tsL袋;íZa>.? )ڧfFGy$(bJznJsj_P6 @H# 0 toNwW^ ezYQ"؛_pLMaMiY^;^|E634ڙ.bNB=a%7whY/g}Xg d]>|7ެv-㡇O?ׯ?}t1 ~xmoJxlkㆴ[oufk=1]]w5ge3`f54kFnӒӿi]|pȧYo< 0%W+Kt ,z[S:_~9&83+t}6l3%aeg+5"6 =mQ| wUW $0?S+c;ɝxTrcBPk6ܩ)eN$mhֽ .l&Lp7-.r9@O2L`lK,xXDlbfW g&1b-ryޭ2H@"VW%p PCj.w]_+eZw0* '.R^wun&KVh߹̺2(k)@q͊HK+?RX"Rf_܃emfeَ38M:w>/3R'νj0Em3.v (&!C8"ϓZsIWlk±HI*E: Jߵbí hƽR{5+H ZHQ++w4%٥3Ad>KUa^eg+%R"Z\m)Ų?^zi`P F)-8rꩧ.+;tuYNzꩠLRү˖JEw~ dug5LJEA~P4Аp^q4_a2j62=]׈ŕVьd/,{/wL{wqQu<3qK@ PZA P[/~ _㚬kRgљukM6$(@GR6CJIhP 94OJx qE.n|Ͳ\,;[ v-{(w{ijß5YBgqtrЕhFՈ,-n?,Hj"})ү2[nݺO)6h@:iRWV^ymhR3;]+EDғDr 6ؠPOz=,;sRNO*YьɭZXPW+2` @:#½3: -T PY(#2ɕa,¥pYWK*+J[wR:vVGT r"KNY)Yv7.n! Bx%@_ܽ.P ,[˛jE,Q Eeݘ`РA].k^*̓V^yk_рbV e֛OŹX}뺗i0UbALKK*lhpW (`3)*o6lr I{@h$J,Q#SVD>ʠ ]A]S!R#jxZꫯ""̲m1bDp'Ry6-۲ՠ 2\3j /td8&d}}y%vgS3R4xV)Hf+JmmgE\h|*Zm*E,/U~'o9g[o5rK$lC @@w@] C)n%ŵ,YdwA6\s5QDەʒwfpoF$*z]^9ȴR Ѥ ^Yp<voza4LSs7_GʺeGI,TЖ[ eyJ3ٺ*m7^To${H_:*#KnVJVX3jZJ]v`e 1)*~@ hMm4Qʃ A =n*.RKdeRK5Mx>,igTEɁ4tHy=nqeֲ.߯Y'xb-YI[% VT+ ,(5+D3 `RK\pA+@^?;[?S.ΎyOʑc24XIcݝ-u-h6K/N:$+~{!I;op9 Y @E p/J| >BVrcrM79Y4˧nZi4w}"X%}?lfF/Q0.eRPC(Х,O=T*;ERRW}%f(t&;kCETiREHϔ)yb|?0Thv4Z| \+H@}G '`W:,B ѣk) @&(kEb@@#PB E}Qw6 \-Zo|5geǶ>Nm,%i=L31FZO@rK'-˂]4ni5\q3FT(e]`+f_{i= 1'n7XOKTXzC< $ <`Rvj#!@PsC @ !AwsV_;Y4"^LdxG2r),+gèG{p=p׀ fmN?FfwmݞȧVk tDL]tQwuZ${-5Xv߻C9$f;z  @&½D) DW ђ/qRlt&s=o[w&xݎ;{1w䦩gE]48PJRmoX >ZD4P+u],{ʖ; )dkg }>FhA5Xa 53:jr>|@ZkՂVQG2jK݊+XVF/v(foy$ˊH@@$½Ov: @}.\x5u['|r%7K4l⡲)gwI'[nxRK9VĺTm~eկnsjw7^c=6ڨee+8vuW77rH'%wa2h>y7TZDVm62裏J+TE~DbUL=ԍlRCZc5Œ AZ B]y=IϫSjv߫-cԨQa! @Kѽ<@ȇj}Qw=!V)mlQn`*ݫ1K-Tda?beq sN3UqpvA?Kra9YEBR\d'X}!Hw,HnQ\o[gv*@[l,P5#:H ?Z>t +m5zJ5]NGnSI(z 6q+tfm*%nJz뭪#z݆nةkf` O: *7h` @&½D)  2QnX;m-{urү_w˃}'7Mv*?Fsu2,,+Q0SYm *z<̠<_r% *X|6lX]V@[);iPAuk JW_=5"%=vC [ vCjyk8VNwwyg{ljp/=O.&I1n6"3lZ7Cr{YW.4`ꛮR]~^נbEH^@fnIɮ${e^_@@{^ݡ7 P0άiv oMߌtRtK[EnNM55_O>SXp .]"r"Dz~.4__p[}c5VXTPw]CxPd.kU "[~e*Y5\Nkd5 )㩧 [t֥eK店Rw ׏,%7^PHvŶS.,+ AuN34w&R$OΙf跭ߏtu/_OK#tݽ.g}6P}B|R2cWI2$ e[=1Yf.Z}Wvm]x>?D9`˭F48սLmnvy\4)%4I?ůs9*+} >3q5ׄzni @M!`/! @)GMYU͊YDz!g,zT?x5ŕlL}4iIlmD̢@7?OU_MԪz/2Z?)UK<{>SzuL7<9]NW鷭4=]ֳJ?7e׿Jɼ LxsTx#T{dBy~d6\ ˛ەl֪Y~gB16YgzN6`mP1u@ Hڻ@ wR4SYg5Xmx;?w_~3aSVYC,U X-6+Z,Rv}ur@ aFfhv3~Qs^}Ety]Z_f虠M袋{ehvyOk=4C1] @h" kM$L @@ )fj^xඪPd @v@ @!=jPŻ0=!>DyI@ >posIO @ OdIb-FZ̚kB? @p@ @ ! #n2-e{MH @ڏwN @ Pѣݲ.S i);cGob)mZjBu  @-poH' @ x7ܙg._w\7>(kI'un;COg @O@'O; @"ߺ'x½s~})t ,@hs1*D @mO{۟b:@ @ @ c& @ @ ;~ @ @  `@ @ @hw( ?@ @ @h -L% @ @ P @ @ ([J @ @ @ po3L @ @ @%7^hIeT@ @ @h7ۗC9yۭ @ @ tp*k?Sio:n* @ @ 6&]~&{a@ @ @#½u  @ @ژ 6>t  @ @ZG{XS @ @ 1m|r @ @  ֱ&@ @ @hc(5@ @ @hcMM @ @ Pɥk @ @ :([ǚ @ @ @ poK @ @ @uP55A @ @@@'A @ @@pokj @ @ 6&½O.] @ @ @:@ @ @mL{\@ @ @#½u  @ @ژ 6>t  @ @ZG{XS @ @ 1m|r @ @  ֱ&@ @ @hc(5@ @ @hcMM @ @ Pɥk @ @ :([ǚ @ @ @ poK @ @ @uP55A @ @@@'A @ @@pokj @ @ 6&½O.] @ @ @:@ @ @mL{\@ @ @#½u  @ @ژ 6>t  @ @ZG{XS @ @ 1m|r @ @  ֱ&@ @ @hc(5@ @ @hcMM @ @ Pɥk @ @ :([ǚ @ @ @ o5@駟vl[l17c?Ct|)p_}{[ofqF ]uw/=zfi /i>O> N0nqq>{WB\M7t]UPo~楗^ׯߍNԒ(!@ @=}D# n!puYfò﷿KKt'8I7SSݐ!CCN:)7mNY>ɚg/oe}u|rNsLO;[q9SD}=gwn9Qo5o#Q~!@ @w˘|~/M Iso}P7q#Fp/Bp #e\h裰퍔ג%X}\}a s1j+/sf @ @d@ x*͟8p|̓Jb3B\D*se2wyߛ$Y'.YXc ?${9+ٷ+{c='pN.cҢ\/sYs7'Ͳ>՛?9{)þHL^{mo_~òhp']t A7t_iwۮ66k3R @Rv E~@ˊӔ3{̔C!ùɪ4+ & tMCyN$-F@n^Er':^` V_}ug_grmV: 7cgw/K.qrGl@/I০tO+ 7߄{i6h#7Swf!/u Sn=CIݲG'+38Ù|ٍZꭇs=yme q)86S)} @mP3֤J}e\:v(K6RU#__afxj5$‡nؐr)$RK-gWk=ʻW[^&}Ll)u}KF^z%'3 ȝPrG|7B\Otk=G鼬Cx@E]@iЮ'I3ۓG[ @ ЛpMgmŖ,%RP]?;)dI' y*+*l7W46||)K.Z62  ʔю_~9o." 7dE}'%\2,gm$r-odJ5i*/y?|_punùvzyY@=!p>`=kxf፤@@ >y?ro )եչ"wR BZkһ=zfirSL1EuiPA.&HC:haR?^ {c @Yu\_z+(X`.f v 8s=\;M2$%]]w53]I"07WYJ\^[s5,QռmH_9lU=pHGY$ZzͶsv;гN\, +f)\s ?A9JHK9gX7믿&U)Otmy~)<4 NP^ݴNܔ;=OuL4DemOQd]4Fm+lɊUSzXTQ&q-7Ju͈at"sU;]]'zOӵ뼳k+d @M^X>J Hٵnl㏗Q2ey,mg;̺>)e% 6̛i/}#G-CLW'K1e\wu~Yf)+.{SO5?}&blIYƒ؀@0V`f+h)_roc=JzC."^OÇ'yf$8\DxS6_o~V[m.2_!)(ŹH*XcOV;,9n$Ƿf䘂R?U}>n׽5JùEQ7ӟ-Arm随#3%0Xqw޹9op "jJ$ V78KSt zFRԛ"$O~OP^m7K$m/yZ3̐7l|keߧ*k[T)J*o-7zϳ q}--6M]PyENwxiyG{)}C͂@IDATkB -ͭXiC @5Pv& %Sc2Y=[|s?!PˇY9#tw߲- lbR4e .f%U }pviI}٧bXOzi_ަ'Y@8#,MZ2WnJuK:[X܂yK `O׺fI,;ꪫ&`p|֫r'o=(<+u0IU]wk{ u{"%g6ҿ߾,M6o)müa*o,w]^=>XuVeUYۨ[͈#΋c@bJYg>g׵gz5@ @EPt|{}jE $Yxƿ3<3X#k~ȢSJ;dȐY w}ksy'}ޣ߲VM>d~QG%elI_coFa. +-%sN= d%?44WBPJ5 7l@_!`V񲌍Z=*f,2W_}k{)?t2X,e-sR4Iea+eʒ^hOtR{u?ț-v|ͤN[o,z{b,#`.Ӽfk[F ϭOמq=X2|}o܃$ҺLʗEGJ[LYk&8nͥLWL"KF)-^CkfcZj$]D^ZX.o.eR񘖍T*"7z'=/͞77~ 6z7_zvf/)os p={llC\JYOV]D?vpGoC %Ult'r_W"xTK&FVܓ\j,DœZCrNt'Y@_!`hK$GtѠ3_ Lc=d]I B)ͧvEEa6o۵pUKFlخ%\<_@;3 tivZE5+REVyAr Eo1q)X.%*ueM?5R^U^o7z$=OGuכֿzuϗg? @ !M7tw0k eJd;Y jUNaMY>\}rO Adk8;u16tl2wA9[H;Ss hgYeiv&{6Ӧ]T?__+E(meI]p'oVHf|gwl+PnǷ\Yg51{g3SJk sg) f9s UO(ff_|ELZ2֫[@Y΢ V~8Vp9.ȍ>xu @@=ƨ'3y!J6=ܹ8}}I^z)Y/b㓬K-T]dW{N"N#TJbH0WL {̝E2kXgTv'QwoN9gNQ<Ȱ,KZJ {:=9mAҝw?PX\rɒcqf%͸ޥQ EXvg]s~oaUO;}1Bz/sl`+ @M >_5f-IkAN+TR޸_Q,32qdiKmޞ)hd)ە IbV G Hw'7 NJ$LI#}8GǾ5k@܆+"XwxAmvs,ȥ a晹qrz^OwaCQ~zWV~¦l=;EJnn:-F!EXӎzo]mI&Y 'vXh׃iAu].!ΙdelǩB^,0lLHH[ȦvF edvڙRZgݰaWzni;O.2V[gtz岰x+NuYw[PP̊+,{Yl{7r#%-ܲx>(_꩷ }L:ӛtIΟEODv}Ne! @PijҢ6-|MgNCkɇͲ`ʺBy뭷OVYJTF .cX)o0`@?}`z_y܎V>E%Rv+3GtMnРA΂:rU>e9㏻gy&(G,iwlTo>VSe1wUW7@T[ނѣG; d)Ls!r~uLʃY`wrq% A` ;."]Fe\%9j-jӳ&4JiѠL:&*O=TbTHݵ^6d4O-zXnwe7;k~8QԎ5X1[xJ x Tkܔs3ɔZQ]E3d?|p'nzV]bw9{@ܤDQ#Fvv ?橴,Jo7Ji/Qt'{_ĥPs:*U;G>(@q|by,!P v+~Sv}[g _m@$ J=СC]]qo&˔.ŲM1]^kVP%y7aoSK mJt<7EBG+f2Lam}I>6 @7>|;S_e ^'2fI$՛$ :{.IszHYތtH6X(e,f`hHv@'S? jo}Umyd @TSO=uaY|Uc5$+uYEߕ5e{j7ziy680kBmmuIc/=44ZANh]:`UVLY yޗI'x "YB&}jƘfE&YE.Ygb:kxvƴoyKY:\TfV0SLYp CMmZkviUprS4glsμ UUWV4Mge䐬 VW& UUQ3)sM6Yݙ*! W^9V hffjvOW"+oҽJ-}=PtSSLe67= N@;ܓ\?.g aFۉ'fKoiW1۩GrEwwZ![ Ӑ@om<\FF`<{>L39#%Q]sv'X>:5^JՇ@z+)wNYYjD?T+~ZJj+Qri7e\AfK3XUZo-txC(4+a*F2{%MRɨK.@TfK#6R>z͕E]4NH"WRH" )W_}'C=Ӝ r˅4Pyqe L;3ĝ D7wnZ`_NJ|FԪZ k Ha.eDܫU+isikh"e,d7xkAsT F A0Ťey"57 nts5W:_}ӀDF6Y&CB[ -mrgq^AM#ŗՈbL^ŽRL4)Vtծ>s8IL9Z C,='4˗s! Ȑvy<b|gabܬz@$Г6vЬwF{{h? 9[ kUVIGP ڴRKoJ)oʬ !0RǾcú67E?#KdAv)141F%aJi#KSh^^PˏyLwoJ_z \@146`oJYJ/ʔ^tYZ7e~+w|C6L*V[yJ#\0wk^KFT\l\ PY{哛/od$*Vk9J4n(F^*6A>ccS0(8\(ͤ J%~Hgt=#~2j9rdIZ3FFLpԨQf%AZG.>Òb%"*śo}Q;S҆XWoIko͊Ys,W:b!1^;z\PoVPr6lذpH]N:tkR%6tM7wGWGoL鞸k!SE1YdEBz {]'Y@":thbvKrQGeݛ'3HBM>Ǵm~K:j`M櫸#A |2ikYЗ6 k/`=)拼sLb]i rb݇\%dEN_wu$a{ i,``j[nR[d=݃=LZ,h`əg9蠃JXPpLʢX^s5SeIFZ,yeio mo-{kڲ[֮6{mE{ ʔ5y}Zk_J;-^5m)C>K][٣駟\צ`w_26-:Jyլ#ͰXI^s-u%]gO}ūH\Tɭ׽ޛԧ+"tr+GDx+c'5~@J`iuRWgӵhiS+p2_!PU+]P`sL 6@ m-E, M,e,dщ@,QF^YO7t lKVyAKBWY-2% Yg. o0s硇 SHjq\$dSPv)nen1Be1s3d 9~ Ҭ_f@QEr @4Zǥ݆nؒޖtJ h\E ?qH' 1R J:iFR.f"7RH̿zp>)kվ~A ))N+\&M̻[DF :(?)vF[b$owl螹&8Њ6t1lO_VlG%2yoC6+[ϙ}6+蔀͆ ǥpUdE w 4%1 ,iK YdKmNp%\rEt}7M^*wKguY?$A o_rLue.p!@gꫯQ3[]6O #0cV k% 2RJYFEeRHy5TKsUb֦*IVY``TV=:@aYVkTO1o|(r,?6, Y?{^Kd.cYk]JJYkZH6MMksd$;\@%t6_ >Lֲ(EGYчŧ~VK^ b>CG><ʺO> M7?BDӲFыďt*G/d u nz"Q6jBbk~l=lCD@Yu-d͞)ZL͊9gGRLu.Gθ>Z 4(TPs=eE*JbטH RD)Xy+w QG,8Sǒi& L9唱aj67ڨm41r)CINuM)`i"-r$A Kw) uMHd Z ~!-YTk)A80'EVzLJ6s@I\t!h*# 04?9 c$8dfM\.L2I6/4R~607pn$Oܨ^QFU4VCӛN)S^df%_uyPmʳɾx̦{_[';!@f<;4zS{7䘞+٠sOx>9fm؛B3<^xa.pN1ެß,qS} ا 6ٛ"ݛۆp\y9op.y?r-l<3%}R( dM6?製*EۛF YKWU+nh-R=t:?z[l:GK6PָQk6N)+` ^ĦxͧuhCt^ssc `3H̏9+#Io ItL\R1i+5,@-BZo5U ޝ'1hV[?QlvLRg~k4U?X$K4]*A!Ǯ^t\`iKY5Hg .,Y?y SOԫEk;mkf jz}Ӧţҋpygf>4@:thUϕ]ϛf[JL "e9zG0te[пzG1kyob1KXJqw4%yWꪾڂ֔WdzjCmV"׆ }RVJ4{|Gq3k`Pv%I!vەe7Of5ЀE]EGf%_Y w ~:[4& w&a (ܛ"!@ s=Q/Vk&Y{2Rq>|ŗCb-)EWK7I:7Uo\VYh,azWVR/Wm:^㲤1bud}>餓J\$oddSrU,XOk,T7l>( Izh Y3z؟pWY9u٥ՊTVpV.yQZYjDbs R z|p}?BgMW?| `| s)lqO=[iӼ[Om 3ʒgMtfǣa] C?dWGoCլÂP)tIyU@gk37=oh3kxg*a˦d;EK @S+ɬ JS+5,[kBMHt6Pl@-408 ,i|#EލJH@ + j PO:?+@>M)y碞sbŘQ3@3!\t^W# 쪺^wb;Xgޔd1uZ_ᑿ}jkg>+u9ڨԞ(+/{]Ӻ3;Ij+w%tc蒀޳%VнVbWrdžJC%O]LZ7dȐ;YJWf%&R+tV%n#Om:NN@qD;{^O/=^$#nC%ieW7T̓^)(ՔQ$M#-t[rNm.ª|zc[͈*ʪlajngTO½%4URE2(^VSAE-_K)Y?vYG5eh%=+'I]ebUgG5^Z9|2b5{/ˊ+XruJ#Eލ`$ Kx8蠃ZY5uA[ :ṠwYK@fE^ u%wq<iYtE} &Q[Zkt%qfD]f_5&v$Q4aRkD^SyYBj_6pl=BzHq5b;|G+j՛olw=c^"qZͨ7ՌXNgAܸ㎛4Bd5WNԹc,dJiueQŦaղIEKz&يέM1MJfY#' ;Xr6攨YqGROP@|WNN":>krl +8 n9ߑPj| ӵ_ 6gt[<(mΏ7{}યJ,ew4c=ouч|N랫vWs9P^$wJɚ2uB v$`AX^g33;;֎LS 80<˥|-PsO>)c1dmeNWt]mLZ-!}E}S;mcLM!`/=Nw'uR㫬4Og#e0 j|6lvIXF)zz5RkE[/F&1Mm.*zX)6 =y{xM*FM/+ޔ%ueA ҉͔݉M>',J~m._e[o!o3UJ/BެMcfY%xS0ySzs#v~ybdo ά)wO)o.;Hb-6Ȓ{qsZUEkJ; '$6|oA/6_6>9nsoS?~Z~ZJ=y>sД^IOÇF l馁 `h9yo¦UnB~򊯐>oq}Yg,_Qk;a3 ʞzKϕ<(xշw-Mu~|/nd Jp:i_laKK֋rN `O0׳^y>)f27O,%X\E^K>J|baeI7йo\ĦfmPv^7#[yfy ]Oj]K*h`O;/dlfDk*C!pCi )eH7 1C(꺗JB]}眽^߽w|}{}w-ժO9唔6Æ Kɓ\'~).u wL@c"=C.lB繓N:a7~cS2H]Zt>oXX^x_~)jɒ%i'cu|+ܣ<7#Ou[u rpNHHHHHx`%x[ovYxI0aե.PRrxߎ.ŷ@̰ f嬓T_k)}U1gQ8{ggWoVlFqY_iu]:Uݢ\;8qb.e}M]LIggT^F(a>1cDܷ-|s/_,W `?|&-e+Jqhn/Uaq,5e:ϟ_e'a BykQuw°Gu1I%_&rm7xRO^{rºuiX (e'af X ǔ!p+%ـQG $?[Aɝ#G'O |611=I8a?\bal3*+ԕ5 `Dv8xI"eSu "(ԯkor6XC}s]DϞ=vc@?!N W؉*P8fت?JZ2%X:@>u{gu\$_}Ҝϛk(Pxꩧ(Cs\ʺ8+b,'Pߋ-JtJ8aa;\7LzL\; G7L4teq\jza:;>9'gW'$@$@$@$@$P$L^AS!$Df}?0R~?@@)UZ R3|dҕ68&.+#DDwZފK@gc_'GZ-n,}yUX}Gz!*,+TkEvA_]\-cg-d+"S6R,T#tuOڠї_~źV?+G|b~o㩅|qwEWIKZ;Y'|p1A$@$@$@$@EH <\]:kQ++*1gQ媲Jo Vt^40hC@? h/AԢ4NW}Y( .܉Z%)5(;Q yMG+Y7{B(ipH hYtժU6!eZB1\ $U?i@٫E]HYFWdZ3-W ԟl~EeX۷o/ =p]޴J`\%2LvHHHH Pជ߶m[_ 3%ɓlWx+(94hpA[~E,՗h@2s=tz݉bu+v胓v%Mv` iذ..s&W @A >,]D6Ns[YHk\-cDvʕrG~p0i|mu,1 D ߰P9'a~#w}S p.$@$@$@$@$@{:dMGI\)m7^z25eGN(94&EVPc?믛<nd4 s w3nh=sc@HU #7nnIzSUb'>j()=k0g&Q5Wc2EN' &6eBeK6a{_~ ']/Y$x)ù> TT/_\q?o[ w= 5 |b4 =nٲO<񄸲w%m x㍭lVl[nҥK;?i$9 .7`$X%2sLS^>s&|vQ8xr;^tQUk/3Rcǎ3ϔ>^lE jVM$i<_9SD J{Nw3]vEʹVyh-ZfOp%b%7m}?~¥ KhVGq/駟& C 2xjڴ-_\\,\P޽h\+7O>'ZzBWw}7@6W,n7Aߡ84ȸ\}LקO{Vӟdp~G6ָEy~E}Q#&+`p衇!Ls L,{ή>nI ]=&X:e}>p@9yO]_yWLJ &`J.:uػt#lxHʖ@DpMF~^[ ⋄jUm9W*CPe:'%"8՗ng~qUkg=@MN ϨHުmN%nɗv4w9 _۪ek[ (k:ٙ*n=U:uʩ}Qi|6ѷE5P7)5`g/qek $e\ڤ۪XlRk)Տ7r0AEL@'S|Xz|v1:[twJg٘1cexL[ޥuhqʦIH< 5KDr3u/X Y`*4,$]YX*m8]~'YVcyc=1`_,u+- 3|6j߁<V WVXiSIRJ(uJ2:_6pwUE:XwK.Kq&} (wk`2Y[j-Fx~ŠWMWi1+C2z(_/PqVUYf/wvx<״aU}c$@$PSWYSW߰p?rIG P5!b87=TF\*c?ICpw|>f\%N%I)\|](Rcy晄jDo:S6]}YlYo>!T>E?*sssxŊ%d'&GL wp naSFk]mLvU,zrmwŊ++!9L~w1!6V1ӝÊ ݌[^GYwWxo@IDAT\| +X9b%Wlr]' ` wȢ @ h@WxӪ -W{[ K h@儼ȯJk"lW.]Xpϥv BW)e:A+t?T㜮 T9fΜ[v\_4`L[ sFQ=ý|p۷oJ m45UficP\r%Mر}rSS62  P>KB$@$@$@$@$@$Pa.\($ُ_H̞=[N>cFvMTp;ܾ;pWVY9 ℩]4tB5\ӧO Fp "dW;SNM[ɓS)RYk2c [0&~XtGc=|XEzU>…h)\IHwUI8V      1P冓s9\ HS5kv70E:vxV b64 ~Ќ7FLO?]((}7uH>}dr]wZJ;cJm:9|Ǿz3fMl8 w} \'x”͸zY)V,J,oZ'4 q9LA,r.zkB'NzHnf QÄ+ϻDnI ϗv\hs]$;wI'Z󓀮 @PŮP_߫SN9Ŏ5jȻ6=zw_X?vL}O>/j{챾?Nqٺ+PsU/bف*MX_~jFu#G}pAU%vJ|8s3QepGjiV%qWU!-]|$@UjD'_eʔ)RU֭\}9wNPf*Jyb P^'     "pYln~auYj|>s1#䭟PeNkY3hw}wS]|[w \l cS6w ndv8g5UTYͽ+K/v%P.f&     (V͛7u%c)4i"nm]z뭫S7X: 9eM7ŋ[NXg#RF+~M7YN^o)SD{XgAr,:Ν+=+!ۆ+S6\OBk?9s{a63-[vbI1]r4kbN{.:uvJtݱ\u]UMkDt06;], $@{y^WHJB5o<{91~IVXaKeyXb! d 6uZ`7>cI/^@ԴiSD\Ǜ>B4۶m+;s tcΥ,kx膕܂ B 8N!  |K7Gp{仙??p%O8,N@Iwq-,~է|pu6ʶa2v(۴icAEo6yD'<)ꏲ-TQ2$ xiWb@yF 7|\?OB K۠A;{iA]^$a QFq7vc`n喔UNݻ+\r{ aC޽] b P^C# b&nAF)+xhBbn12q] ,Ա}WM,a=W!_~{,0a2uT;LJ8SWk]!p>H,Zȶq>Ǥ>~qȄ û>tRý xXrm7\֥tǗ,Yb:s:w85EdUw)=M^`U@!  Z'ЧOQ7.}% W_2yUWk@_No9\r[n_ľpb̘1W[ZB.Do>O}g}I#n'ײ}؎cު{x9nIHH xS~KEL @]$SdJc]vQ:thOrKOrU? W<>ªʩ]7T9eBY5pO8ƢJx'jB^ivmj LqQv:8Xƥc1#Vv^ JR% 3DW}gJRm q`t*P#" }]LwxQÝ@c):dOuAΝ}s_npY4U:cA}%,QEyu]M]IcjAUҥKˋV%?Ao N6u2x<ovE|!sفZ]rȥݰcvV.lЕ.l[_3IU+ITǽlP裏t)tb~CF\+0tpK zSŌ!JV(N٬ȐPf@ L\Asg}ÇC~0Xcb=ۧ].,a~>}P;nXwL}XIQP?NM/|3U;%@{K%Mu_:S,(fd\v pK PO~`þ\z2=]qҽ`͓,a#O^T5x/n,><4.߃>hEa{exvxU$t;$@$@$@$@$@{Ƈ\+Qն*UW]iLz_TyHv w$x?q>쳙(v۔&` 5D z.xtb@P=(G}TtQxڽQ(΅jn2, ; +ч- ٱcG ΅Æ `P:ӥ [eV2"]R)C Ig)eq͹cn.)jb8e}eU2C>ChV ?(VRE}'tZ.Y!Xŀk4֭[".dY‰B] K$@$@$@  w(5h %{kڴ@gw3fSidk=C&+faa:tn[M6 8=C8)ڮ{7mXy "蟠Gk䭷޲| ;a @ %9Q_`Ajǻu\"FW6PN:_(k9뫗|,@-#-n r%Mr[>}pB?I[E%wsn_>rk[Ńcl2h@g*DLC_ 4/̈́2[\^K9Pe?<:a#`s7mDWa'?~׏V-EtR ({#}3"43khH.ْqM/s ԲA}(w/Dws\1={vnZ#ϫeBdbՃD/t%"]v?hbANJI5V|#(@m'8xyРA:y뮠e˖|r8e>oCgV2M&n LG (<HHHH 1^3‚*,u}+xD>.n^z)Jw |(eM2@N]!Pwvu;8TƏ!—C=};[o Hg `5Oؗfe=<R>}`=i$S@!W\oޟ |y(Lw}է.'2%7UW3VVSN/] +ܑV w6f_*hWV.}:Q$A\+QV}ny5,3 :RF*g}J?P(ܹN8&ו}|}]&XP3Rqw[(a厇$yg̮&*ԇ0aB~}_VK*u33L71FwqGwu w\c<̪o&ƕ!m.*k7[#   "7MAO^}F]Wu @4nܸԟ{q2Y^m4\ {;GԴ?s=6CVRjDa h5wjpS &T(S~{L? UBLD&ݻwkV*>E}`Eqxpń+,Xr}˥lX,[p]lHHHʑ@>ȅcpEsyv%,wW}( w(nsXêP% fD9A٤T,ե,,q;{&ml 3{ז3V$Jqǂ}J wjRp/k/pϕ @o4hcʘ)AE ic5kyubtԵhU!GzI7HrI~ \U_D[8ucك>"!9xdEwG{.9wL:jvuɦ]' .+ͣ 'u)M6 Lռys8$P*N ~E۵U1U0b[[:/.D}]Q,:tԿhоfeYBQ7 QK~ˢ.̪n@ZWNҽs-<#V: |"j.j .>HԲ5NEQ}3hE/vBDP$"&F7m4gϞ+s>O`ᢁ}bi=; &($@$@$@$@$@G@ʺ뮛v`UK[I )GS:T4:\}[]gulF꭪1 r mڴK.<ѠE]p]YAtFn&K}pJ{L` %DO 42vi"VW6^1ҭ[Յ)5?>: z0 >&0>yI۶mݡR70بU-g}`7n,c@6dpuY[nB#5D z4ЯY[nWG@VP7be,$@$@$@$@$@EOnݺCe)zԄ衰KbGwRWw@إH;⭷28N4@!E>PHV[5k&XzԩS t]( &s{'MWKʔ)SD>g̘ai +z&Mر^zAe-A9ː܄\߀;#a@eȑ){#}+g({ hBR}/uwIwAC~k`-%pϫ9ѣcwe˖t1~W^)x"))[&    7@Po} r*3ekey ,Y$mteUٛP;TTUJn_Zj^eYUse\Bg&uReOi#0,Dā*lS_׬ߪu~)ym" _&_R7WUuk|5Yrm7|m?V|u6e$@$P ZlHt}} ()PM ) @/ǒLR:;,Z.o hݺ8 (Ǐo1u:T?<l<`m"X|=,p~[o>ei%95 ckժs9iΝmuGYdo{Xd>$˸qꫯN>{+gl3f{~ p7וɵ]W6:" Npu]Wͳ-     /xo3m8L 4*IHH*Mqtb 3p%8PC9Y [O?dp(̤ W f275P; $@$@$@$@A|Gԩ#믿gHHHA |Pd$@eON8e?fHHHʍ@X޸qrC$@$@$Pp/. ?ZKZѧzJ^u @+N{H$@$@H Rj3 @AvaL ?*܋$@$@$Pp/+ *#F^}U4ig2e3gFHHH @{HHH *cca J#йsg޽!C>)$eO3ƶQa9   P^=#   G xXH \~k?C7n\RI`VݢEdu P$ @½FR r&ЩS'ݻ"?gr!ШQ#iܸ/4$@$@$@%@{~y6   TT2 e]&묳[pr-ՖaD`4$@$@$@%@{~y6   TT2 @VsF)gr!?gG yG IHHP$@$-AI-7|#W_}uE=wHHHP^chY1 p@$@ lQFW_}v% p3 @;_f7i&HHHH _pIC$P;S *HHHPS^ 0&  D HXHG^z2l0cܸq2o< dCe˖>O?$PSHHHK N57h 6   PT6  #[ZV^-Y#WL4one< &HHH o wXשS'ou"   ppw$% ^{턀>̜93bm,VǽRSR|+GB$@$P<p/k @9½.F$P>`9|^zO3A{uxHH=?&  Ȏqb. ȊW\;2a TE p_hUe9    P#0f'  D HXHu]c'*>$@˖- Wye$@$@$@P L TM`ذaZkY&=ztx+[,8գ`HHB `d%$@$@$@½@C@$@$@$P=*ܫg$@$3L]ܒ%K˹<a?XA!  *ǒ5 d&@{f63A/_~%9 IHH"XtiB &sHHHE |d=$@$g)͚53VC$?[nԩSv`G23HHH ?d6`W^~*f-$@$@$@$D $ % |Xo+U2w\ @rg0IHH :dsdI   P^=#  XN8i߾oIcM p{y_kHHP^{ T:*+IjZk%#G]&M G pKG[  G xXHHH {Tgϊ9IH Ν; ,ݝ 2DӝBaa2_}OL D @{h,B$@$@$ewm&LYS}U+5X:㋷YA4iD]w]k;׿U~Q  ('TXHHH P^7W_-̝;(Z{kNjʭTͭ+^ߩS'ݻ?1|p>$kw#A?$@$@$@]e7^K @5pPMdM{>&Ѻ/_.?~_zm+\y Xr_eC.2Ygu,… eԨQ3LE½"oHH p3PVG$@$@$Ή^zmV;@+ᆱWYv}khժs9>5\#E!GS nIHH ?pGB$@$@$P=:g)/9smIoٲ*IJL`stRWhBvi'Yk2I8]T?ʛo)-(n;iذaBvX~ឦqƲ;H%W\i͛'W-R?J:ut:Vk~A넮mFU29&t2NeEmu}k[u/m۶xE k皥~zG}dVrl?è&eРA]qnbB @ w$@$@$_alߑF$@$@$P1TUoo=LSv}`-z0vU\pA曧A=mڴ &MP&y'Jn*>~KnUn_r(۾}`ʔ)i/1"MmРAUHiȣ`݄ND܉r}u3Jq~u + 8,=.$=餓=#[0qĄu BvmW_}(+L#ݍ7XCH*e 5nVpIHHjJ}Ч21cjŚP&veʔw9PsW$aݰb4Vr_~ aGFj 2O)},P~YCYM(sv:a'3^t3j^;P+&nuUEe=__W_ nIO?aEDVwJ½\,E$'$%    p_5|O ܫŷ 8P: iԨ[Sz~vע<5ke 7|Ir)Vϖ[oׁDvne\uU͖Ydfn6;0yꩧ{-^VyWQ s }s࢖뮻*eZnIx ˮ}e̙2d;&,˾k=ǣrun\_uq-};VTl4aÆZ>,J/p rZA{x 袋lEp_^{m6m'-Ͻ+u_h;䬳βn?k/3/Ȍ3DW@3l"C&OlcU wU1nH =n&)n0˟GH$@$@$PK~TCѺeg~QG,_{3v` ̥JL|#Yj+ARX38VHU;; #ʑwܱbE@;c=J!  F@|AL(   !epWȢV*eL_~9)CQHvsi+>jU%̙;UVYQ(-7|uǩZU99F9:޸FE*Jۘp]2ev[gǁ/qԩ?x+L@L L:$9p(   P32   Aw \ƹp\]JmvuGa9F}PAV ꈲm۶/Y_$1,>I (}lƢ;suu87xE>(z V[ŽdvpɄ?,}u]78v~;ndvJ9_΁9{N`%^}8   hpƍHHH(+;V ejFF>\W (B.^zi8pie}}1߀+5\.q}YHXcl/\'&%<\$ t7!I|3pݹ.k:38:p=ruz Y?|y^bB"C0_L=ZZlC6yϵ >H\1xEpWJ{1\!l`u- @½/ O$@$@%F`' ]v|5#"}ӦM͏9p7)Pj[nr.5Oe7%s55wqt]5kfBWaQѰaêP?G.c~ J([L;s7ƱzbRttL-* s$@eL@W7]U#HHH L`${#]"+1cd cXO2[zdm=Z֝ᡇvm<\`I7phCY8GXׯԩ~,Dnv`5D{f{N:$,vo޽{ugA]aeQuel\|7JŒs7\9X?3iC꒑#G&Jqyv `>gyFO.ڵsYn VnHC@IDATS*@ح V} #@ xXHHH 7k@[ (pk3p0I&.-ry梖ҨQLSGm7Z\`xDpO_p@يZ/4n:t0%{q9zsmוz}7jQŹ\Q;Ǝ g61 T嬳β_rJuLw4{0&rB5E'OP .X/ *;!_46F$@$@$P)~TCep+q c!C2W wA6VvرIHH*)ҥLE\>HjCv dԩ1ܡ"s߾}}zo>C.e*Zs$@$@5C@bye;Zeo􈵒 ;* s|$Pv.:(?қoY46BR.ȻV#1 @LK.Ma 7L/{O4Bܮv-Ж-[w}쳏/>߮|V $@$@$Pl#lHXs5IU/cx+D x*҈@[E =0A$@$@L[nŁ<}3eH bA/_!Waę*dKuluu< 6IF/Ny?sBڵ6(X) x 7`C˽[JC`_c2Vp 7?=&S' ,a{99d׶x/x^#uԑΪtMo(믿_~9rl֍0# "*k6"x)qkֺzW\!'|/1 :Wl S?]v ,JQ7o\j3@c]/|scAlҥl63 6*k8# K裏~ȓO>C Z8YŵX+u? JRq&fĉ2Nj O<۵m˖-m(رceҥ yqO>Lѣe٦3fEL'\{dܳgO'yq)$@$@!@2VIH ';wX!C~?ʜ*e ЪU+=tiӦ$N @ T_~(d[}wxI6tS}3ZhgOKXFm$ڵgeXY N~w\Bg (:Ga׿9(ht̙U?=z07>s̑K/TN>dln3גhC~B0:J4ibJ~}]w9cHHv ½vy5 LI' d&k4uB2$@$@$P=JUZ+a;HUI_(;0VvՂ6nW]uYr-U1)(b Χ_z%+OPL9ȏ?(>㔵 B.^ɧO$@$PKp%lHngf-_^ז## YpS*|l?zDŽcǎƟ{?'Kۆ|gv8NYW/k{ws:t`/v% eTpZl6lcrJ-&r!k --$Czkd5jT!ök@XUKͳ  (Yp?Ovݠx{^(똩p?˖-~۔`[=^TENaKl;`8e]]b<'dvKHHjnvDC_xfϪc=|ՅH*3SMv)\>s2 g5FQʛ@8p*]ʔHHK`7ا=Q^=S8wE￿l~}YO?ݬwyg fzuY?Д0,!x矟n72gu: $NY ~n%ê yĤHH04Ï|-#R7|S -BWsnD]vuدQ>Xz~#F|TRʗ-rd$@$@5GV餜0x%o7f6mj"j^ܳ{|/X@|IqXwL M6#8=l xDŽ?|);m4wرz$@$@L@Z">zy}ݷZ̮uKaB4yvPVbFU*W\?\6Q!zשS'XzuìMGqty~m6ͶHHF0Z#CjNJ n>oyI<.oxJ|9$ve˪zʞqYRN <*ucYTIrtm;lذԟl5ү*>|ޓ>,Dpob:A=m۶,LwO$וKYXakYf ܓ)gd~5%r}هoѣGTsa|ws'H +6O$@$@MYkٷGwf~衇]09\!',{&͝;}P-u6a材|=c̲IxG?=N0 U`ǽWs='.),ìjm;x^;@;>,*9VxB7xþ}L .UQD@u-^EQ]25;]ޗ ס.a z[]hÛR M4 G? 6lR&.3a„@\p-ڂI&%q;quuDݖ5҇@_ <#_}UmdnvV]}!(YV_o]\`Nj-܋ڰg$@L2vDuJX$=O]>`~X;Z}T[u%r* rl eiBH7L(8TÁRF7|sG-(Ғ@:ZdPRVfl'Z|Aڶy0;l OuQd$~EgL&@{q_H w{s=s%HP^D]! L?j(1XF  دJ! M7 v!hV\yݮ7nrwEVqՇuqy?x[qOg]˃- tMַ#GZP AP?Yⴛ\W.vB\ -@(.}‚c:cF]tǖLwyzE\˪OqcƂD Ϋ4<"30w$O`4s*dNHHHja'tiX5+-nxϭV2=믿nsH*@f\gѢEᠿcu6nẪK5jw߀ip_z%ݹR'\BS8 t5^xQq&r'@LaWTr%m?7U}kٯ'@ F! @n.Ry'VIHTdU Z$]g7HH ]yg Q7 iqX{챒l#>#%Lq\O.]v74_a駟J޽*_}[{ž;u<ƍPrF:1c+ կU>w믷 Q 9*J8eQ>,\rs,^{MrgG@ V8:uR~ - nIHH 3Zgf3C@ce,VSHH]ᚨjE)M[#| wq~U #C \B_KZBˋ/(pѱcGsc;c СbJF qw]2ev9g8e ~4F /c:8@tK<ا(a2_|E{%GB$@$@#@{Xhg? PHHy&$NZ'zIÆ 8Q>[]ỻ_~ҭ[7[@&J)eV\)|oVz_$v`Dd_?;X:NY_&[G i t+;vm7_+L|ADyī[)vy   xpǏ@&M,> ߶nKe{\"HPក#ڎL/K[ З,Yr k;U*1 0a=kڴ~bD뮻X~ᇦtGC: ψC9N[q孷+ga>hAU cP8ǝRF9WԟV yf\]p#sYg.&~PU2-:GB}B&( {caV3Q- [j11 x4caHHH P={VZYMPvWO} L Z`=ږ(a-~M7 ZkPQ3g)J4@dڴiU;JKf͚%wu)aq{ۆ,<͛7 0GoX,?cr9H-dܹpwuk.,k.$я.Lq-4h l50õ2f/p}vl̙SN=b 5j$;2qـ @ΝM\nRwyg*Y{B$@$@$ @ p51fuhf+s9rj<gU9T9%'N#FH+wC NbRԇLdmQ(._ꪮ|Ƒ T,]ub9p$@$@$@Fiڿ|CCMNvuî4.r@[/̚ XBYf2]:uI'd3Սnvݻd={7\נ2묳Cڴiu|:.ȐKˆRFa긖\Ap~-4pOa5BXVdjY>PHX`uvmgo%~3)K0U  ( S)`4 @i@Ggվj*/X~ 7q ͟}嶄 `T:+($@$@$@05Ĩ>hΘ1\q *Kb$P.ڵgyƺ<}Mǎޫj!@0VnOZ> >gNݑHH PȣJN<B$@$@&@{'2!`$l)W\+-ނ?DNG $=GY(y%ЫW/i߾5@ʸF'`NhHpK$@$@pOQ{O=ZNy~   (:j &M(A`֒kF8Д)Sl2d=(=6" 0TW@uL @! ½6 @,WU w]v}w?A4I l>{M$@$@5L \g+{BG @ P^ I8Ӥnݺ2thTP#FN>]&O(=a7|# $=G9M6M~'ZLq @½t{J$;(ǎ+p>|=l йsg9|!Co(-l4hw:(dHH*|k9B  @{I\&vH L ۷̚5w`RM.A'YrPQ$ lE(IɈdD (AD$*Y@ݯ瘞{5_u6߿-\B'N~ S pj6:J$@$C ZlVZ H A0Β%>SIz)pB ',YR5kfگ_?|]g#8eeXޒ ^v){W^ݶ   `w $@Μ9e6km۶MеB$r-z> Tt,R$@$@$` 9sƶl h";{Wre   `w $@5<"?f̘_/O̟?v(z9FpfSR&p=%  SNիWmg [Ap׮];hŁ ܃>(4i"Etѐ~פRJB.]d۶mv Ozt(>;-83x$@$@$nfb-/#/^ƀE K0A7#)3gWZ<:/CϽQFs.C{vȑ#ĉv P@(f);v̮A$@$@Npۭas /_\o= 2ȃ>,C8HHAr#Cm0EDDa={6ԆF!=)S/SsoӦMjtQfͪWQh")S&{9ee[  % SN&yYNZjI)$  X /Shܿoϟ?͜9[N8 +W?lқs\"O֧+^Rw-Q#իW}1˗g!njڴi5'>a֭,X0ɭ +kٍ~ x|W`=~#-[f;HH\G@hHk׮*o۶ ~G(T-7ވqf͊PXsͼyF"KJ3ze9s:BMۮx\CJϞg*;2mdFiv^>D_vGȑ#Bh$gs1o8sқs'Nm}7Pr"_ Jݎ[oغuk+9 edx^3 <&/*4h` *8IСC>hC@j=#o9;\lvc3lrƌRxhyL 2eJ{aP/uiCpYdHHHpΆe{pV8d~Q S5@HH (0qܹꫯZ2eرv>ApH8ƍ-[ZlٲG}$h#`@o2uUưlٲE>cۏ͛'*ڹٶ?]G#_|%$ePR60g$C.\(o$>|?xK 40 ~O%eEr49! 9TVB={~O=ލ/$h_tyxڵK^z饀Or:mJ]zlo^; )cwCK  L0,^vڶ @0&Md}áC6hgϮnZo\UQzJk*q]w S4ͮ8%e*(o;%ey G4Q{{U%=*3~x}=bŊCuFo>BězP91VZ*mÈ˗/{ +:uǨ ~ٱ݈?0l+ LNK.Mx%^{$H~H 1()z<HM ?]MҗP3=dUU #IHH7()7t&tqUl+2dQ:<Ȓ%`LE O>d͚2wq]|= RUu}zA!J^ U/#twY#G3b7>'sb΍w޽= ךSQt7O>E$U_iI2TRa ծA$@$@L2yQ,U!҃˔)<9PHH PR&oanO,[mGm+Wnl;PֆO:**+WHs=ZLd#1sm5hÎ9b6蹛:Hl8k 0AٳPaڴiv P2WHHp-o_oSW>  C)A1h\f;v찻^ЙW8a\ҥnӅE_|Ed᜘sΣ-d( 2etH@ȍ)S ]oӦM@)9GM0oȕ+W*.&,,b $;ܓ_:@1%i\ 0[JJw֡CI22ڵ 馛LSj(I$InygϟwvoۧN6Q:ogE_ݶmȄO K Ĝ^&Lه~<" Zt)(klԩ2zhu(Dl),LH%©7HH\c,,,4 `[nyg| n37~ڣ|8iаdDlDԻo.8"|ZYlۼy>^G>_|zesUSA&|۶m>L#GU[zv xfU>|خ7/ TXWTXV;ɓ'07n~@30~"r}Lʔ)$pN̹ U`H\/Я\rӂfaVi-*a KxFs7f;  VLp=Hr2id#/^HHM7ZKRP{t9Bئ UaP[oPz 2QgֻuEBϞ=ox#H 4DYg534䣬T~D#  ЙpWOahKGFJ*Cɜ9ԯ_c Iz$M4z;?3-b$Oƌ#ZAV4.8E~i{8q?6y)tAOHsm,osxY͛[}n~dpCY̦ƍK\tK-3k4)|N Ĝ1`޽۷a(|aRK 4lʺ8HH @vSͮnC3f̚5K5jQ$@$@$*1^Ї~嗂 W׬YuzPI<2@jԨU y2!@%zsJPp 1&BsUVtU.QD,iϘ1GG/8xFs'Cٳlٲt^i}(9i7A$@@r-,30x:aPyv,Dg$@$~!_˗A4P"v \;t K,ѿSNҥK{;RJ8YxH6|CȚl#G{I$@$@$@$@A`ƍ4  N@g}R'ھxb;+W[:%\H୷ޒ7|3FX޹sg4˗/ן Fu> u駟*blOH2Λ׮CN=ʤrdI@M<R$Lx @iz *mAJ;aH|B>"Uʌ3܍}J!Q/hɒ%KJbtݷD20s֋C!S' 3M}mk }!-|7Y>N@w@l E$6$ ̆&RLK.]g~z-[5%N-77?s=^@.X27;wNLB*fѧNZ/px-q!_Ru߼yԮ][-[<aH elS؎ >O^u=/B.;߬Y3]0]T)om]]E`Ԥҡw->Uљ!аm;o=Z8H@w>n7m@F9jX;ÇkW>X<ٰ$_y=w͛ˀNCɓʕ+z3=Zy-تU+A(X)iӦ2anIH\y4h3gc9^ϲH})Ss׳gO#ءV.&M&Mח JרQtpß%ʋ饗4?vX9׆kzsZ֬Y8 L#@_u*>|U\YF^P$̼3۹$@%н{w9z? (Nk^J[n@ RT֪4(/rn%F$@$@Lva=vC`C>OlȂu;NomϞ=+ԇ߿۱A CYov`;#*X.XرC3BBfL4Z͛7ׯvlCz۱qG5BӦMv?gYQsq/ix5؎(N mڴq6 @< e}߾}RZ5=}pԡKLL]2w|7f;$r!ʕ[e2yG1f5j$HM)zir"V,pkqA$@$N^Ϝ)ؿgϟZ+{$&vY Yߋ/!= լ8^Zgk;)qZ۶m=ׅHe_~dfB X|Ȳ?~x"~uGޗSW^"P]۷ކ; CFSifm&*U'Nus!wD|Cf>|Fwܡ‹X xG ~;T0"+Se[J|=gK &={vAQY̅M 9Y|q,%9sZ}E {/g_$@$@$*'O_Sa"g6I:/KTv;aYȽŋ<xݺu +5kYXCh#ٳm9ճG}$#GϨO>@!hho,1"(=~x})H #kARpa!ާOn$Xd{dT6'O)[ 8P 8N˫\sǍX ݂vC|HorG&t%$@i?̜9ӣ &Ft6 @p>C;i"Jڰ|1A+Ȭ) ۭK̹X"6VbE&F, ?S ğ@O>edj o#Qſ\nu&K..IBܫVjGvvg7؉s0wCVfOAw:IHHHpdt!_Yđ>~ hC8 $UbqMK!4d~c{TKl,P 7vD3R)S駟YPUH*3,K0tst/%J9rĶ 17>G@g ӛ!`{ܹA]pAP)a‡ڊI$ \m<0iC /  .I =իkPە,ECGH/:׌p֗m   %TBgMLBiA5VLlFLd#1.:$kժe~ms$:.[LQ[o…6<,p| >\^{\ӉsՌ] q^, uH yPz6?ٟejtx)'ȋ/2.0SĜm޼YkCw?YHj?AIh$?-)O0[X{j꫺*sAbF$:m[op EnQY.픮wK9HI L$@$ON9dE6þA`5lX>k'$N0뷞ڱcG>}!ɉzҢ' 1dE#n_v1ݫCPH$_ lP-Z%aPAmH@q$ӑ]< 1YЭ.}d:V0̤cR_~^;;;v*\x`SNVuZ+L xAQc2s5rҥ%x(z CQX7 wnRtT;Gc**+c1ě >cnèG@IDATDQ|ÃڤI쇜% @@V2C%{޽cHH PS+4yJUz>nmN?N’Yʔ~7OJYqU}Nl 3f̐FߡdCҙ3g Q|4!;|ԨQҽ{w'11#YsĈz_T{Koqk׮P߯d XWT鞄 =vSAv\1⸘PhѨN$;mw #%&VT6[ opi$@A/ڐa .#ҘZʖEeUtWYFL$'Tz ʖMB04$  !p~{*HeS%1珻7oLF0.0d&(1-Hj*9\#!<葙ެY3CmDK`; ҢxiP@)V,;Æ |AGc9rʕ+UdcɈG3>GBi֬Ye q@b5%Ȟ={/K6l(=zoWs$@7&Ƈĉc)R>@,) SvF$Ѕs?~<`_wl\F)"HU #Gɘ5!m SKg  ^2QƢUuTlT[hܹS>F4$}VH]tԹw+|yۯz#^4dסCŋG1 O\ 8۸ Л ?hK믣m13A"yXO̹zxq9K \W^P&>|>Sy̦D/qM舡5`›>h[aZ H ލaݖ-[jH._TzfzHHrp۠o]B;HHB%eV=#wG{, |  $ĵLW ( { @; lc(ѦMmI9sX-\ܹsů]Z$@.%|s깣:|(cj从bTLo02܍ ?h\ @`=o5ۍ=3KgV$H$|ŬC5j H iD1:to!]vؖ+znG! c'OԺ3$b#n@w_ xhMԖwFb%]m6HHH TP=Ց픓 ϦJ*F H$'g̘[r3֩S'ׯYMe:V}źhرc4"\@p *>}`Mj] ZNM (?rN @(`{`mGu_7 +|rɛ7R'wRzu9HA.{z/_F3IՋR©ϞIHMda3%L*U< {2חےIFܹu֭[f͚!tm`w$7Qz,!O?c!W\1\SVS} %%`ތE+ycn}^Znƃ@K?2em6OkYGnQA$@$RYjժ%9g!-wL}q*THTQoQ@?DZ /Uj=>}ABϽ|ꫯ;Ȝe_*Gg J.S$W7. $#OH/ŋXfՕͿn:vҾh^CXM,ufز}\l=J[npǭ~{8px\r$led݁[4apٲ @l\k.-ro /=Ǎ՗%KHmϫV'xB28v'$@AE {姟~ 1w0E V#K=f9{LZ3^9mUCJǟ?Ջ :~;ƒ=- /VUzI6YHHk(©F+],X@ҤIc[x<3,jwA$4bsoܸ;99heI*>p?t§8$5gC $; fEc/Zɚ5 n'o [yү'ptdV:1ꬍ=$U+Wm6HHH  "~QyGÖaeҥ!C͍+ܹs=f/-[7}"H<G2̕=tlnxx9嘭dw mF 7yQ@{lR[/Tzn-?w+@@;PϤ'Q05 &=">ھvPRef#4ThgBEϟ?3A%Kzliz۷:Ĵ;۾[̸b7t'JXX Q4 %=K޽ QoRX/ TH +xo Gv{̒Yi֚{챬oّԫ'7t@6~,ijxcϞ۶YUϢxc٩OE⪆Or:tJhWen9Joə-1{y,f6spUJYLȞݹ;^#o$X qɟ_0,.ۥ)|̝G 8^|tYrʕK< УGYb,_\{ =w~{s1JӶt4H_tK[H9^*^ox 8^ZT kڵ }WkCCх+WI^6m@/ ۵6nV9epҨV-X&fV]fR2x_s^[MZT=tL5[iԳmV䥧TQ) yOꪗu& űvuO/30?Aﺑ2OOEM뼬grh/ &Ι+vǎjSCt7voH~6{ 7NvgKVWU̜Zphu >hCiӦG έHI ĝ6gɒ%ѽO#)-q{7 xGz^~YY- D-XCp@ h-\lɮ~ CmT~Tm,Z!{Iu8/3/^<8o7wRZٵ+kwK-؎k9qB:<۹Kb*ڴ13Zؕ}N]k]r2F7ns=#HB3Q`2}]/qHЬ_>Cl_>T,z%PI0/D$sYU*Hz!ث$ZDH̄!ي-{]ؙ2+ h! c{“6f% $5dw֕o֮ӫ8qR% fa >{~ݳG]! CQJ< uLF%M EyPnIRYNz@nѣl;Ggtnkƛa/pdEy˿S?*~βe2h'ҫ͵L~ƈs5I0WS 1l۽[7X6 0fcAZ竏?5j r~Ɂ:;@Q VGʕS5RJ|ƈo쳘 i>]3|MHhTgy%¥/u_4vu=5u#hrʪD#')P\? wp柴!R}Uy 1S߷XzL# `!;@FH"PJ=3gIq}6ˇ*3np-$|(/'$eh$v;ZMA쫃ΘƍPfwˆ#GjIt5U[2&Aͪ)=-P@RL<3}lkDjgEbM1Ҥ3[ .TVA' xܝ ow mTUӁdz$r`t7r 1pr>=||}~˔^&MJAꜥbdY#^{M^ii]u.^7ӵUK@HrU[l%ihA#Τ׬"؞:Iᅲ-#?N5kV4 =GE$*.)*9N =GyQ"3PeiC@im10c7\KY,Rҥ6DʑYULԭV:Yu=YG+QlYfvBcܞֵ 6n7FoXj272L^lyn;5Q:lzlz (xg̪8ϱU#S f_2eMSE5+Κ x*ԍ.RDN_uE`!˓gNہedl!d}»}FCRa^p!.A$* v @'`s\/*r. zPddcǂyq\xQ?a r:oN9u똦\DP`56wJXAJk8O,ul 7+ٴS2SaPT>9JV?& z$X-?{R]?avItj;MN8ph/G?GOV7ŊU-|ysg5i.|  p +,Z* q >}ZԩcRRX~GH/IH\!)rFtH"=?\g29BQ}`Rw7W|fWypҫ"(6yz1$WlqÝ$/>>C&,bƠb1yK\|![!Ѽb#GtaҨq )b-_5u%shXCؾCL3Nl'F+;,S7 !*ANRB(k, .!)uֿڎzv#AA୷ޒOǀ6m*^D# `&{0]H>e e=w Wed^F(/RØWM=Ǻ*>C_d㋽7G!}t@S1a77m2nlzh\/ ڠMuTK~;ylLs2+{nL9xWLE'_}%~ɮƯ{(VUiըm'۱w(󆺟Qekpm狌纟SN'ҩz&4wXzuaƃFQj֬.T̝;7'I I4 $=jl텑1|p*g0(m]o;D6MoΏ79+k7o]W߶ck8(`E!*>'//ߴk79i=/hmp. ?Qd yqj?@i}< V|cy -#ٱE },m{܌2H%C~EEg-Yb9FN׆K\b.-Ի:6o!/[Rc1//רnNOԲj2 ~p>~\o✹z!ƪU`\9gvU8~NIZJ"\חK`6,s̲P}^@9 @Xs3 @ %_S*/w_KnFƫ ] (h;#y`zԦF@#4p gSȍ*~ ]+>/̩8xLyzd l+?NuX)5S[}1b1%3w,ǽP0y5m%/nhXQ)q{?DT# j *QGaCI:jj<,ZZY^'8>Oo9r@_Ksvm}|N g#ɨ7^WKdω})u͝wɸ9:r~AgլT4 Νu wz's̑"E8 fǏ" DIϽb64vl2Eetsꎘ8Igb&CW"i5GeH- 2}"ɖbitM*ݻx9z˼٘>KTL2kC,oְ5RuDGN&|+m7Ν#}V*ߣ] 2Uh^e,dԩRŹY _/mDo6{U/(j c3}I2WO؏z1]P05)15[i#={!C$u"p'UX5  s? pUiܸl۶hJ_ fXIJ?#P~%SЪU+9s_U睔 m 1oU!M¥C؝nKFooE㴟 4=Zb%Hi|OԨ!W.[v}ItLɒ@wLVjovɆ9cn6ޚ:|:s}mݹKS$>,61ke؏ d_c~)"c8Ȟ]ގ`eh;8n7ğ>\*Srefhc'>,̀'~AScj{Oed26x8q5EO>bsݲDF{D# Kr Rrxc +B?q^R$Ʋ~Ԩ{I|w!3-p0\˽քڪ 4-8ƌcL_ݮA$@D2t9V W{u9iGi&Mȕ+WA=^Y84J e]ˮ]~LKw?}ٺ`6CVYHB!}9x e=]Zmu6uܓc0^ckX9Ԕj*XD$@"puXL͆,^:TRef#tEP( nڬrI$@!O  I^[R%5l?oIqK#i} ܶ { 5lhnx0  ?p\V荅QݠpuV6-@k9Rnno  a$@$sw's =:`ޞpd ˀpNLy#6lH 8d0L.9dΪVyti tRܹuSy  q /O$T!ooܸ=&'E V+hx]g#q2HxvxnI2e*S+X0A$@n O657>8d-_^RZ3"MI@6nX^C L4ik H|@w@f$@$sS} G# 1sV +Fs:tE_|Q2^\=__t} Qy8Y8բ`:df ƣvr SR&,cFyFW +W/THep7$l4kL~TƆ :$@$@ 0 DsիWP#޻M}oǎu6NH=m ̀xVeE?uIHpwmpjH9U*{㊻ ;ɬY:uu6HHb&{\HJzSL\33:~u0:g%gʗ~Ç'C/w"u = D ȚUUpx 5pb˗eѵ+3;-w0a 2:UV-Fju;': c؇}']tvlGP[nax9QYCl~dyXB}po-؎N+W F~'o   Ds/ua3-mU!Gc>+HwoO@63g:$@$@$pw[\ʕl w2O$xft{$$H!3~zٴiΈ ɓ'{_V-Yn=Z>)XV~ӧ@B&3l0dGmljI3 B!|:駟Jڵuxкuk&x1QF wpAK.ղɑ#އ&LAR:ףGI6>6Mb|1_U_/}q1H ?O  ,TTDа &ӮLPS[pA,~ON4dK5ӹ+0N-dh$@$@$pw۩21\㡇hz,gݵk&Jj˕+pD aSO \0˗Df0F&?'8qBo6mjvsʖ-+M6M#{WoV܇1FVS+3ί\gϚ5K81"ܬY3ygd_k goܸ\J(*f%@nRJO `?o H$7x1zsMuz-SwQ7ݯt7EM*C'˽{$@$@$pcp1#]?\`@P$5h-Q:ǂscGfΝ;w/>qdq )22ȿ4j(C>dCF3l٢C}gdVҠ0|?Mty0t3|= $@ tzLՃAZVɡ͜)]Z FZVwc @8yBXL͆\xQZKeA#ouƍ*UhÙ:uކ3OcٳgC+=63vo|ƸÎ͐tʍ,~d?:~Ӛ< n߾]ؼ z@r&1>̙SsAIcB#[0;A?HH {-WݻRDtS_lXLT% #ԇ@H|}JXl 0f}2nQ@Xʚ1GB6aXI{ G}$'?^^{5y+EcٔLGՅV7o : QF97gd)2!cI~Q2.b[W^36`߱lQu^ nw75ktaCwpg$@${)U H=wJ'뾆 ;)9NHK5N=+a'k6l&ΝOlAE@5X zwq2LF =($t-jw!K*Ǚ=P}/7o^|O! @ޯ_x"3' C yZ#5Ǝ?nǴD#FxJϸ|)S;v1 .?e_LpW.nUoHH z#<B=X Ճ ߯5\A S rc HH%euYAJ1cU=-޽Q(I{!K,`;-Y w K ?81^P!I/'!xĨNϯ3!b T!YQ3ġ[=f+V,K`l} 6, Af* Y$dA`Ǚ516_H @@ 91Y`5X*iUvx|9уr0=c@?#lw@^ R2f4M.D`ѪնsolN Ub rvC@1H@ %[^';vҞJC3E2}h ;y)PfعsVAidϧOju$L<ߵkAaX1f v'g+ d  /^xxs .Ip%mE$@> ?hFs6a#˽MAz"-nu Yq/ u- OyzXeD%fLq6(2ׯ_o3Q͘@IDAToZ VLw7F$@ P|y=w<|#%X=E؞pdƌ`Zv7M.IHH 8ҥ 7 rkUS#BݘKP0v:DMԩS uf7tōPg 'O:ue xtv. xCzN>}?%\{lQزQJǏ;g~C$@$@.& S7jHZ*٤{:9rDի'lXc…ъ_ $ޠgL׾նkc?$@IGIǒW" Q{JdA1XxCVY %^,w}5@9Yp JʸF  HNs4h"{~Q2^/v1q@il*Kl%pE$@$@$lQ3Gٓl׏t"ϷCZnm  =f9{$ ԩXBk9WXQʖ-S{i#{O_r*ԱEsfn'X|qN]㏺+N-$3e$@$@AJС2nL=49 ڴ[λJlG|köd*Po5j >ܺѰaCyw:$@$@!W 9O4IJ*E}Aʔ)I_~Y>39zw?ƈIE\p|v,{mۗjoj" H#{v]4| b;}Fx-YDPc*7i$@$@%Obg$@$S(4}t]H :tH+ @[S:t@!{u6" ɟϮZRv $3gXŲucwիWu.d۶m&MXwqI&MHIHmpw?$@$ʕ+˱$sz=&E }xT'kz /j:M$@$pw}_jufJ̮4q S;wNw6mZY`ȑ#:IH"Wx0 L?[k&ׯ't/XRnlٸF@y!ؿW#v_   'Ϝކe li<;l,UjWl\&K.I,UtiӦIɒ%G^HH !pO5C$@N׉'Jn|!iN 5VM,YŽr07RJ%yr洛n`HH@iL OI,oc/gUH%y~"""iӦ?ڣ 6HN<)͚5{G.ӢE$@o$@$@IFWR\3e5o<dBu5=~q_B==pW敃3wL  }Mٱo:4¡מ)}zANcgƟ*i S :l2y 8dʖ*%w.g϶}tQڷoo XjL<9ڡYd-96|:ѫsҵk$W`+n  Sz3IkZ#}h" ɟ߮9nl @ m͆ 2w2#JG5eI;KHGK?׬cE, &kXONyѠ$ߝHƍ'_ 9sȱclkKF ]HH Ȏn:/C:mn<ΆH|y-dHHHVkvwTyW`-[Id$!BlӦV%n;i$-[oUz)ӧO3gjIoϞ=O7\璀 PR]|ӝQS+_SfffܔMҵ}zJ;q'j[giƑ`P@@g˖M@Ͻvt…P8{Ȝ"KtTR%Z*q`LQ_~ :YyP90#8S$ĉ,Q"Y`%d! ¸bQ _  7oޤjժxK( Aںu+E%c|UHLy}Qj~ϯ_~C{*v2_͟~bI,sc}Q bK_lvK.IFЅwށ%Fi_|m}"˟?D =xbׯF 'bMEal+ p#?˚ ϰF^֜w@?Ϟьe˜+vownG1390#8 5M{ |Ґ;yFB};@pĈi&}6k+WEƍ+ V&LrũN:*U*4i :&Rj-+VIAD%Ƚ 0n9$}1N3Rr!Yre~ҥKG}@=xxxǏ ȶcVZToK*%ۏ` #[bTēR50˗/U6#t0t0@ ѣG=^t%6XوYMv/j2K=za>3#8 *MW &pu䨮5vK|MD`4.]*X+3N>gkgϞ]pWݻm!WyU]jdȿv횥]2ɓ'9Q/}26ݻw d2=k,IL[:3Ph'|pmp` `n Gxd`"2ze]#|tB O͟?;~۶ -X> ObZ嘹sicX7LkE;>/)l#4ܺEkwgO)V2iʘ: /H<?ބEV1 :=v~ۋ,L <a_#gthѩx|2Y3=wn><#E_;4OF1[!pN|4 WQBxx 0J*= &gÞ;wN~h׮]rDݺux%Y̙S{1A8oڴitex?#AwAs@'4-6h )2(!ݩS'NH 8+&iKS31UY6mqƔ+W.5ba05iӦKҤIxVKIHzB*Fd&Ey :Ԥ-{B8!L;E&3#xzzsYʼn5kJL么:i40j݋u bzvaÅ58zlI  /D/d{c O`hc5ٓ>V޽E?@"¶̞M|׮SL[촃Ab&޶m)Hv[w4S/{6Κ O˗/uhтv9a_@CJ ܕ@Q/Y%%J}9᎝DL,۷oCc?c: f͚a4x؃nŎq e$XVK َsNZHC#30ďڶm+ wq <92kD%e\rF:zZpV=Mw iT_F3N|M0 #w; #K*ѸݜTtah֒(gjr0yC؂@"Llg~@~Wd !1L<3ARdƌjסWlKHs%GUeY lWeS @ 1)S$|3˿H"2+ܕ9j?{ =P:Fp1>fݫO"=X"GD ֬叜+vmsn;᮱#`+u=e(>3[L~yuCŴiϘIwۜp,.EE,%t٩5ǀ]H X*["26d[ܵ=zԄxM/֮]K a =Fw@ *(iѢE ){5kJvK>8;|v+gF F;N8!o$m>1٬e4aBzD;a"4ٺa]?}c^/;* /Def!,Uo\2Equs>&MPb]f2.%dKj#bdIm,4ڡ m(ZhN7*~M95K^)ao/։X0h*U WӧO=F&0#`޽{SŊ~L M)rL"Xj=Z)hќ3uîmg/_@tоec8.4UU Ν%ŃEB +W&'/^X.8ߧb0W|LRCZI& #_` n{yIy/`eDϗK f%JD WGzH;wl?O]$gI͓ŏgrGu},SNQ"G$Pi^ܶ<ErPyHnpѼp` `)#tق˕+GoD Xx>ɓr!DP2r7}%W^go+_$xjժRr:0xۗ-Zd1Kv@mK tbgswj[< @JJepJpwƍfF t""!^# r/Gr]7.ݸ)k8*mJ @!jf10#U {PuզɺAJ(/@g Vm]ݔJP37|ϗ]IE<׭“(5ٲѐN!Z1=&E B l|e9k6M]eS$AΕf$^~5`]6BϵnWI ~ˠ8GQiMFuFUU&AS#lAh=&m؞=F2&H^(}vpA})ܺ/X"3}K&#:5jH76fޭ[  u`AZpA!=.nQ⮸g͐>q@"]Y@2?#|j֭[ޯYy}֏=@޼y3A"Ot2hPkz(S;ߺu U ^=f,``Oq?дi x?fVPe ;t@NI>|Hg ( 8v@ ߰anZ]Dє1;wR ̳6ko A;Rz$QިQ#ub IA }5jE@boi1΀s7FF`A VTt\xF8*XPxf)ˬ C4to9p@$j~,!Mɝ -pd/TM +W%[%<ȔկTQʉ|T6Gw/=:|6 M1f$ƋKyVy*(d{%hԩa{ «|&.?D}ig俉]Qa?ʡb&vs=*SmNcel$T.p gF=ܝqFE8;r4g*߉4kWc:=Wx֩P^oCreS'Mo^SAޞw5S)DRJgFoyb<-yZ3p)ϣYeLx\*ܑ߷m:M7eݽu֨F[s̓{49iʬ;,lCXNFC9 $#d9؂1 P $5V&,"6 Сky̙h75!UgAA2ƒ% hB 1cJ9" lժU6U (a@K ? W0@( CL_%<ySz!;k*T[0aьS!)0#cIDQ*4?r*˄6lR628*@xʕ.TPm 1mhN 3s&J2LV+VjVgd/O7OH[OYX{لU!"/]F'!l~hVVGVmcM猣0wx ,b.߼EgbپpHgMzs~qM%U,J\=ҥ K$ UGT^p!$dxwhT[ <َT>$\y@рkp.>7nL+V$Ex?H 3&pFp0@VCQsWβ޵J j ¢qK N0B Ŵ@EoϜSkTDLV-˙Aj&b24F^ݎjϜ.:L$U /󩋗HB A+nCG,7"1(T(}yHtW$/"E& :;&'H-5!~8Zcy#lT>UJ*Y\{& /ș˗K" @S;b&sChPb0'(y4mgħN)Q=#o^ЯC/f :W/ *)j<\\ v9򝈱h+t ny0Kʄ|F`\N"H΁F-< BY,USLN "F,]K;\qzv+ 9nÂ*"ʭ]*9X1b[ ogI߾fQo/'8BCrt5Y Pg5[fZ]?idfï-SK?:?P2!⟥LTkE@`r]s],'@%kG+]ِ3A+#`"'cA(gp>x~癀K }xB/ɓ'$ŋyy&~  uϜ9#u]#(0(W0!z7o$tc@][/^IO| 9 rv9 WVtUE1}=Udz[%]Ox1ڪ)~#"AAH!CQ!Et ne nG͞CyLU$-1:e Ĭ_H%X!> Vr>mN CkI]Vȳ5ߣ6#\wO4hq/h`߮9B*)CJ(S:p;v*( ٰGu,6ŏG->v`cD*LÏ=!MI#)Tv!!9 @bg˖V{\(U^˰7RU]h'7R+VZ{/<' ]e:n|AM+w]kN!׶y}_Hg^k~ekP J lly=iFlmr'7?'qW,oސ;A%uH-bAM)%pN*U+v#Ō7n5H"9iϸٌs"`9Э~=oߦ:t[q#؂CG-:J-[PQes 2|,GiZn{=PI^3?,Af^ ~2ƠΒ>LW:h`l7$!7d47h^VIL_=e2ukLN\Ӷ$Q $5C0mK?~j0TH3\TIwxpA?b;!a!{,:݇9 #;MӬY  dӱ\2`r1 m$۳fJxdB)@yŠZrd׌@("{( 61w@(O0@!q)(0y } ZV6c2,Mr7]x1@ɝG:r,u=Zљf+ ͨCaPg:xre/hДr0y~4bjьƁUAJ*If6:S$ijAԢo?Zv6˺ZǘM @q->"ݞ/{6"VA+ !4Sʭ[z6Պ#8< 6 e slaY5j#Y N@A===-2#GRM٭Cb@:\ccF`IP [} 6,$<.qđ㴘6zOIL[} x_N^ȑ#S21MZfe~rh!4ĉRL')h !O&KYB]f\g\>}*_|kwBΤ޾A}x={J(4X8j\z:.q硗Ns`#ж^]ڴo) @+U$…'ȏSԥK%K Mn_2\[>=SVPau\3W EܯanbVM(|ɻ-!;wLSN5]F Zd $7F0/ޒڵ#W;XMhcYڵuvrhΝ  L"t-n6kc`CH*osl#T8/_>, {۶mlGDÆ iժU&k?bФ0o0# | n  C j4HgϞ]lG>>ݻw~#& ͗"%'>!o?Tj2'QA8o-nݪ6xhљ(Oy 90N@,i\Ϟ; 0jw0RUjSk{L9pܴ4So`FWn9ÆR$b6!-' kfN6+b~L1N&Y4ɓӸ^=ipÆ0z ߘ/LD=quhV3iZĦ= lb_۶[ US-9]Zڿd1]شb]HXpyp~!Ӿ`2*צdL p^v-e~8iYd{1#F _ȟgXh5nXW|UULu%JH? 1 A1 O>^+Փ,!6tnj=z$19˜93u֍ŋ'AxӂL+(/|xMV.;x 5`<-4 {w#@tRIc?<'N(ﰂ X@)esXohMJlc#8>gOk;k5<%<@b^6#]BhfW.[)[Nd[Sg*3'\ԥ1΅- %ޕ<~"d,QBJ.dB@dbH.߲,:HmТ CA@+V4AJ'ٶW1x?1"0dT<囷(bIX*02~FY,i\` ŋǏ2˟_ c{[pyp~|po+~G<{l$9C]L bS7*WL[lQY4{ljsי 8l 3gΤB`Dx'd‭t&!J4"܋-Io4iRj$޾}ۄpС7N{IzQ#qCIӕsٳRcx-otxQũS&w 1JO~ َa8 ȺҥK M6mЕ+W4#sVsӥG=G24leٴ^>{X g.{ߗ!+Vµ>O)۰Vf‹7XxwDHͿu qM,bɢL ny=ڭ\8~&!EU 'jXE d;fx3J"gXrUy\U׌#|oXR{_@/WMIF^^^v9G!H|0x,M#l7A])$^W.wi gs˗dGHdyvgcC zԨBx0x*R)g8CoG y.eGϞն+Ӹ% N0!"?NX^u 蹹rF`lA`a_9¹rQ$1:3f̐a A4C}ҕ}{AyH۷,X QTݻ 0io2eMHiܸ}̓'Om'NnϞ=AaPڸq#U7o^?tޮ.-Z4BUWW׌k"=Փ'Q:ug/_Q=hdW9btHw~$s3Ĕ.M8bSҦnnb)#DN fF`QNۭI8M0|^y [?#п6lI685kXp&;Chׯ 0[ x1F ߁/A@Wp0XPO̙ ]zw2ZyO#F n3$8/JtIMv]o@jnPQw<"E >˒3`ù WT LU @t6&SYc=7:Ќb@mۖ9E<ܰX31ҌΒ%K$RS3s1k#S܏I|^wDgsO$ S.^"/ALЀG+W_&]JsF`@PǎLk(tD'LHO8f;iժqYdshs=z&yA&cN۷oٳK3gΨ"X8*|>~;ƍKSN5kpl"ypQAW\ǦMJH>Xb,7dɒҡҸhѢIӘg)zlI)x/_pO 0>FtjiXӌ#7ЕF“8*)g Alj)B7JM 7ke˖UU5HDJO2΃ KӰaCe 87XS7AaƏA& rpq:4l@K׽ H9sVo;j'GI6"4v 2 #m&L08A1p>PYؽ{1|8u 0Bߚ s@~FBGwlK$AF uܨ^x!Z2d Ivc@p㾀 A I&֦"DTawz50#0a|ۋoSH`"`Zz"?S/3 m=39 |81u \/_D΢.)|F`FaA O'@IDATBޫaMлV/?SH)SL* y5jrX2?7,a„ԟl^hC$(VC2&\p~_l̃$Wc Yr??@Ōh6;b>}ZLNӦM-|#0W0@(!m@Xt)eҔg/SB5;Q9i6O%-G9 ݽsqP$q'Dy۸~(C 1#0@j/[P.PAgZwFѽi˶}"ii?-͛kl@Ν;Ir{9S&MDx2| lذA8 EҥK)J*ѬYMGpQo凔 gϞ<_x$f؏5@NQn:u0eAf!/vZP־}{hAશUȃ|TTD wlhF`92}c##S>cx5'-Doᆪq*Z$C7J:&b=@q#0"pMcaGÑ0h ޼Y-BNGmرcgϞzf+ IEݛ s8p Rڸqc'O4c :!KDrܸq}׮]N" h od3˗/79&]trPLM4ۺu+͝;WiExCvA]a}!ېpt L Y>|I@ŋ˺J,)b<Ռ ~4HZZlY=w}b0C'JHz;wNX@ ͎ͭQT;M%CQF ?e0Ydp###mcFyŴ2^uwׯ})9;g]ٹsWhj%֩S~Ý`F`wН-SNQ߬E/u+]=~}5(VH_e˖6VlȢ>|6lH?sCʥW^ͭGt:t(խ[*W,=!o޼R%K,s= 29r H(C;AÃkxzz22d h'Oܘ- ȑ# F৅3]-n+#0.@hQiiZWԠQWsϝ;+URo)DJ.&^.k0#05>|J7oӻ˖)a-qC\A;vLڶy@֠tE^Z5--*߿? 2Dm:D1Ǐ/sH$s $52^u5 xU| 5seΜ٪w:90 C7nX óf*MVZu1/A5Pv-)1 uZCCÇr00LpVuptpw+cF>|t1܇F7햺wʼuz.qR%իl _|'OCt2#ipӗ;0 0(=zwҁ^xM&/p ђ_~%Kk`Y 'F`E۷oqݿeu:,%ZKCwZ5ˇ%˗/S| jԨr˸}QC\ɓTR4iRZl5#ap?0]~Y c\Ξ=k DVe LFt1t^av$J:vhO?f=Xg:i8f3#0@ lH"R98v9<kP 򆺑8t rs#sdrog<1"^[4|iT 2۷-{^Z~۷o^7W0oe {֫G;xևiĨ@2>`Fpjv<_h>Œdkn~.,9aK?C@ǏKG=bH"ӧOeQո@b]0@ `=` - Ox,4{Bl q#kЏCBd{mg,&SY"`F@^#i~W8 )a!qEjCw5U*7ڰvvtؘ5kըQ G,Gt1!\eGepUXUK̘1]1FL0[]R~ ztEٳ'--9a]n|<#|?6D_;  xo'}?tԩtMw ә`F`D̙Ժm_JeZzEWl۾2 ;e߂R-Wo8Fhm|3zP 3 }"+S0 ,!x$ITÇҥKM)SRܹEpmiߗ9ҥ&[@[fW|Ki`OuLB2gL)R DTk22 ,F1Pz VJ}6O6XiԢUo٤KS-Ԏ@#Mj_ǏK%k: VL/\ӧϟ)E2ER*_Y>XAGnNw^|M1iZ-z2!Bx1Qbt;*JxOsq>owK7dT._q߇"8C/Na@G vl_kn^ݰ`,W)zFvߚlǽ2 ۶mKg6)Pn]nR7FCwx(yxxЊ+˗~Z|TR%?ʕ+'#{cО2e 4@.à7zhܹԢE ڵkC"VXRgm:m8z(! ,M6ȇDLbŐh:t_FC /\@zݻwٗ&Mŋڕ!wv 8lhSj׮5#8JϽCAJϽƍӸ s֭uchӆ4&YDc}(\ .L8Fga\$C4I-\/ yPX1-q*p6I6o߽Hܩ uLx}0jTK#޻X-{EVe<* `I@޶yt.% %`2n xW^-mٺOEKmp9%-uyNOl"TTa!HxԨN 0@P\z@::tۍo &?c6F8^7oɓ-{f͚[>vC:a َw.=ñ}5ʘ1 n$ۑ6:d;SSkv)jm7nP…-8ML@W#nӰ2h;#8sVnࡓ1fİ p?-LT7ޟlaçQ#ڸi7)^Gw)gJr0C7̎'=gvO4tTr/VGl8_U п 5 =z1v?dhmbǎa)/_IFϤEkS9sBp#}QU&tR#G'2no2#pAܸqiҤIc|(H7oJ ȳL0ܹsrjn0Wx{+AjPBR%AFta͚5gϊ8iڵ~@灴 }JƨQʇ'j,x"?۶m6mV@/-[H|N8A={;\^8СCիG8qپ}; 8P#8R0uw']ȕ+nN/f z玗3w۾q2+7Ӵl$jҸ~FxZ*/bA; H^A)RD;ӇwWr*Z$%!R^'wp"ؽ>gt.B0l6FwK}o׶KRŒs|g{׬Q j'4-Ci<0~…]gr`FLx#DرdGQDŋ"y~s'MT6JÀ7n[dI},x@ƨĉ›I>8_|y54ډ"ɓ')}z_-X A5q2+ٖ}ԩS.*9a uԄ#yly l麢EӖ&=9 j<\.  XdAei F׭IF[d&NgWH̙3?~ҧ3&ݻv'FֱxgȐAʿ!RF Kte~x" վ\oynË22YfՇիtRSY,#]ڶiGD#V*z-]Qm: Tp鞀FDCn`ۺT)Ra]ySUBE|cFIz/ki/-P%C] 3߾"k߁{E:F.O UFj|GWlfBDYU4J$*H1EB' u)nTZ+*S܏4 .[8QI 05Kryawr-ߩҺSR ^Tr ʖLS&M^Xx1pתxTJKʐlj>q}E~xtA פ12t+Wm)#i33) o@EץFML*U,NW< <աӘ9Fq~K"Ǐ^i|w>$w@17n,+P={X*&8ጀ+ 0(=wx*z1B^ʺ޶FBHM kX x+1\|1#se޸Qu= }we6*k͆4AZՄTW$A$~Meۼ~ٟ~^^ɕ޻oM'i#_|c5^*O=Y~e!ʕ[z7mΝ҅Mm ^(k{_&͂ii1མ#wE:v쬟;B}ǁԶ}(brr  `+8.P&077*]߄9yP*)ӽ[K*S]kÀlW8]=]|gz7;uB˖- *p mt,hW0K {^Ç "ț2d>}L\:|B:Şuջw'ONjOx0$8* v[u13fH%{FΡ+߻woڵkԮ];Yٳg~`2_d;}Ȗ bM|Xpֵɵس$#Iܱ )okVRIR I-ə>}׽F2s c7pJz 9Dr"˖{7m8Wt̑|b*[Bih9Q:zh խSI )"EKC{' >U` +8g|dݥ{]E'نΝBR8dYjY^uwnZ3vkҦ]:s)Q/a]AxQ7cft079A|nsx?[-OXRyQD䑌#[.(~_3*XBx3ftu^Cfr4hɯs59|4A?V8xFC_'N/ˬRB{i{M ?Fb3=*\6EO1AG jѼՋƌc ,y\FEKMre"M̌"A2C"Fhz5[uHp&12͓FP> ^܊l7}o9bV@@z aO0~BՒ#.`C"s[Dq{pM]*Q=&XbI]{ի -6Y~мf3f̠Fe0 xw9:8/ޑSG]kp!a$N" y Ⱥ @* c4Uʇ1$[`^~%fyi?^TDAz2:}EJtg4AIA+ @|߇%Ii|p7dVY8$[Ht!XTnJK4)dϞ' t1 'j_F߿k>IWAmLj;Xt Ul^'Hd;* 5ynO޼uژ+[3֯%5dϿ 8#`MVP惓:6:vuTH ,p>D;|"1KhwcF`#]@ 7$ HoxLj)@+j!v^|9͚5KzCZD<$bŎ(-[&MHMu ijf*=0OXbk֬чݺuKJ 8+p^=#E$Iy @_x!qΐ!]N05+S tp*ix T9dT>u\cTHʘ?|Dizy;2.^NS/-xnCzMET[y3) ۪UJӸ1}d] RZJ  椾QD7nxɡLޒʫ|K(/n*gl1ֶ3^^O)Ko k ѣB,3 x!Wv ő-ȴs6w]3i!a7aiVzMu:JVNٌD 17cG6v+BΪf:(1nl'b:eh7Yex4iaf9#0@`=x|tKTQmŊrA@[2ǹ5sK#/mڴ XCڹ0]!-ۯ4q|?jaܾF^ N2S\H(r8@&|޽]?YAM:w$6Ъƴ%K7Mzzgo2ƀIqiAa*XЀGPT负5 Ij,B׺U=V8}=~G$`HO LdIo(a9r)܀/T4Op\݌qƖ1(diyA&h_%Np濉s(Q"*1k*YSxӅ%EJ%n'wʇռGQRd3?[={Vpxcn Cpш#:la#؂tAFY];n-Ӫeq lFhiH!hBBAe ׇ6:On˃' A$a yխdn^y 5$˗+J7D9JZ9TZvC3knJ";`Tic"aAP-3&RL6'6U0y:͓]j;߄رM=qq{{XzD'5"wZ1y}[zU*]4=}TW%Jlbt'F`a ӽ,1I$h"8`gC`t^ćsxnŋvBÆxP]d;@rvhIH@!&[!`ЀG55f<|ԛ mn&KyWn֯j,J .Ǝ#@2F!)S:4nT( b@+2xIEV6.X%{Y9}6zdOڶQbgr0Zv>Lqp2{U0=wuWN7K^ni'qkZmǁ^tiSj}}eɮƌ]d3Mk@ 1 Ak,6~tF9HP I.UX uϡ˺' d"pQP\A۷Uv߂bF6?!q~ubǎMkצ[߀ n#06"ūWLӡ_|#HZjBXwoP}DŽyS]Ӯc sU@emG)Q@ w,SULiwAƖ %f^&̭[*&=5g 2h⡭ m,aB_}_1on 5FLf PZM <8َ!i6uSICN A7kSgVh1gmX ZĘ*f7]y!!]P\ss^*7 .>|42/^BQ/i=f,WCp sV&xK9EE#AXT ޭ[Yv A`|شi(Q„lp1&}a#0vCknUrE<0]tq +;1#J(=w'JD5—pǿwLxػ=0h,&k}WP,Fv4q|Z-A6 Ap;6sҟ~AZlz(,QgjŻܨ=y\I,qSM;FS-*XjJ%ɽpYEUu"h *+ y-yG;``"}B"$^AgKt,^8^1bD#7r3h# ) H&>{$ϙ6$u-H) ݁K>餈p#\Vh372iQoTDpIjeXнyB/]Aճ0w e )‡gp/LFe_ h*0 v6_͛GժU3ƾlٲI4$`F]t)S&j`D{B;dw $k,w\;wYNLf{5]^g&3nÔT߲iYvMi"悱 ߼yGGN5ndՊVaBǏ2 ,e<5l܅@B),_jʊ4OlKN8/;BJÆt.|8qVW-,k: ;L7huVݾ}_y^Y|+T(Aq3YP'dP{ǒp 3Y`;LtbE~|H" tV nf0arA ]h0D y4n8F4,޸zְP-hBP[c!5tni!r~uJ\G# Ps7.d"Nxn7I>ϝ!dl9n\e:G6J11( GOB ΋grD *k@߳gOd{fh޽LǚWO`" d"Bl۶S12Cp!Cz왬iccCWA+\`=TTIz.vAP-UŘ+W*- S"rNdme V]/QF  4W8~xw9غ#ݽ FE=i+^X V9y@=%3ٳQҤ_#H|S ,4!u`%izSRpz6"2S>le%H@IR`ki QXEܢEE4? ɴ A+"d(2-, ^א:q℔C|gWEy>&'L͋ c;5sU0|vZ1*Vi!%6l@M6 A WZnM7n4TA&0#_wL w3p2#*+ᎇ4_OˣGʒ%cF^^^l֦,Y2rsski|0#q0qXk=]tuFժU… SժU Y'OL^#0@(XКfLr8| /6tp7-$>&L1xBs"p![Ae4q ڝV,F7ZD HF`b%E iOϳA;vփ R^RFF# '-[6O0@E vBɉ, vZx"h޼|^'N)RDX~x>r6l 8ʗ+N+T&ML ݇,;ϕL瀻'ݹw5H ԧw;">>.>0Wod_~E(O3r {IK5Ԃ[z$IӨ ev8`F`8ܺI"O{R:%A:0و#wQ̿vyy^ <_FL=w"à'={vz׳RLi3$l?cu_N@F*^8ʼnGD9{)]vpN:5ɓGh=`r9uҤICEgS|Q.R ?Vϟ/teI$V>}_j>\.ܹsS| ʫz8cD=cYv,#UGQ#吔Q}#e6i3ܯ۠' 37<߭k+=wPvÄ{xm3#0XiBN&mT4nL?'mߡ;wKLM6xb>0#D8JѣG5kVItϜ9SN$4hRtљ3gd;dϔ)ϟMҤI6*V廻kokk+V Rb5VAWZEiԨQPBԪU+]\rr+W;}%rA5jԠSݺu%J̙3˶+c[cD:uv7nl@pcAJ*r| #lS" oݾ(Hv`\xA ǏWMjիWg`ʀ/\ó`ݺuF !Х 5oVW~ ŧO)u|?~naM?Hgii!GX#0#>{y]ǏG{ӝ2wl$,֨Q#dAhٲeLk`F j {8?ǏӃA 7gFW(eٞ<gggO@WApFkߗ -ZSv)z mڴILG9 5%E`=}S򽼼 aJBO߻wRժUp˗)(2( 6F,?A4"ϒV!>GoD9m"ʖ"mٺ/ж? LxhҸ|/D%K nmF`F`퀧AVFn߮$ڱ1Aͱ UF;wԦ];ef#DpgQL:{,0̙3G;ΝKG<Hnnn~ @IDATZpt7֭bCeڵR>U`Vl;{A%JжmBHvGGGBsezRΝ;6xј1cɓOWWW鱮K $͛Ӂξ}`X(_<|RR23fiذaYrϣ^zy={fΟ?/ǎL7 XPkh=Z3#D1q\!G z6hqHgᓩn&3b*We4dXp o%16^0ar#0#0F`媭Tzy䵇/$vYuѵ8a~5vZ]fF #ApK½Gf{F:V䳱Y[[3[A XΜ96y 7\J`{dmJ.djAࡵֱp~a'Ok,--"tHYfDuE+.&L(ec 3qD!CZ>b@_pA[|PXL 0ɐ؁#1

,Vv+o͛7SUF` LGCjҤFvL/eWdJ9[lһd5Vq>?,bZP $I"*={Lh ?'^09sIiчaY^_ V?AE߼y#SUCh2|U}A?]ܮ7eoʰ۷8`{iHn?~B;xDw^ZhS޿O#0#0@D1ڵkg}B tQ&ۣ# /]Dݺu1&x}6662.BS|1+<qTT)fMg{8?%=fgy\d;vvoRŶ3: tQ.<ׯe@Py#ö4|pCH"/_>r =Zdm۶-YXXdfi޼yFTȊ/{h!b:db5'9SNָ۷o(B;wiӆeѣGfZ7Pc:5vrxC~ل?0dL2I|0?B 7־>|ݻ'1.L_4i\]] |o޼I֭;շ׌#y?{(Z2or1*^bbetM3X C=ʝwg2#0#X9_0~w>h&\xQr) M]SE4'N)Rkڮ_.뀻cc&Mm9::Ҵi r@@k뷉&S Pӓ&L@{Պ:$Y6tPF!wޕe2ϟƍ5ftֲeKv h D9wxRd6||0":Ʊj*yE9x#hZhA&Mӝ;w$>` zEO+WJ-ʕ+k԰`53v^^^KTH!gF j!Sأ%]8{3#0#nv8@[׮]iܹ ؇끻b "8QӧÇҡ2kp$)c@ ;I9.;(( d͚5@V^]z 6mZ}Y&Ν[$9p |sm/oe yزfggGJ68>Q}+spp  AR -7A[W|K >.g,r` 8URE 3gN+Hߖ*6w.w 蟇3#D.o󧏂=wNP+ԪߜqILf};Mq9F`F`(vc'1َ...&gF){쑻 -4uM6}||4gI,Bג&M*Pl`cpK4M?JDvIXm5d^pEsYw9HmYiŊ3^X$ѿI27tAʪƞ8qb9n݊7I@$1<|~1!LC@2eR#i9F}&=yt2ZX;(G\=yhP 1#0#D EdCU߯pҥJs4F^ϟ;q hѢ. %.,4uU{zp\y,Y4j*ҡ~\ J"L2,K$I4HU yhO@X$qDžE/7x}h11O8oI?7mp!A 2AEV~F)CT8"AC/a GH J>%KF j0-ozl# ! 74jIw;7iri3HeCR2{.Z$uoS_Қ;#0#0@F$$Uܦ ,vA9sf׺uk}"hy U@n߾]WCSW8Pذ1ʖ-+%! i^0FxVm4kLSzxxhNO 2dJ`'÷lB5RMիGj۷/z ;b ,ľWȗ-[F vy={FL?=쬿}AvhꢡEI)]Sd;O:% M6Itَ7oĉ۰atƄ< 3} 1d2/_6 MAyؠz/vQd;|"vaƍ3(P3f?uu ,6 =fiƌR$7H|S伽,ҪU+ ƆI[nI|2[bg-[x/IV[(=Ƌ^6Hy'''_ӏLGg#fF'X=h4m?%ԱTZX\=Ʌg Ϳx2dNfF`F` @V*ˑ#$UDdQ@Bd0VA"aÆFȑ#]vpexz锠XH>\z\w3gNI#^Og%H>q^Z q~&Mx~ 1%KPϞ= L+ g#Aڣyj;*sÒx7@#U}Y ;v(o!JSqqWǢ@͚5u&c8 _E%e3#ibIUFj}z$;A~;v)v@XwkIsC=ܣ33#DҤ 7ҡqdOz+F~U?E[ʕ aG@`v)^ː)++Xxߦ.V ?n?Ri.t l_Kׯ^/(%elI֥\F "wڱy7]|?|B_>!,QFΛ+Ak:sWҤIlRQn< F`!Wh9 rxW>/zu5gJ{H+p ]yX…v1cF:r|xkܨ&OOOzJUg+|P ;Lp J].5`=j<#0@ErTt%1q_9_勧teʙ;e(;k==zpWV4uhWhpǂŔqkH}k}kHd:Paƍ+ ,@vG3^ŋ'h^H7e RrG#ukC=4+Sepdy~Pgm[ߔAHTv߁v@-f#{~<;F`0Dp2yZ(HG jm?}QJ5Bp`-d٪$Yr^]oܲ5{N0~d;f:g!_sZ=)\ fo^^JZ؄ j*Q~:_.𑎋GNu;7ԱuOᾞ^)xرϯ_h̘1ڌAWP|Kh(B0:uO.u>k,mLe;t@lٲ8Tل =%KeP8w bz%UdR]\-rJ!n:1}ڵk}V-]Dbnݺrs!UL."Pʕ+%vO<佞]Ǣ [E0Dg#gFD v5de>ClW70U,DiO{j(Z xA]:R]5p.P(K l_|MGCnGS?3rsHֿ{ۛ矠V qo_3CCܧ=iƝqB,L>m\` @vxn߾hbjq N1\#0 M2chScq\zu/~yxVVVc!{nllɓ'w<{%={$޻v i2l޼yԦMo( *U5"70 vpKzC'ŗzIOu  ,ę2<*UHy&M!L?5-#0#DzYo^s'pzɔX%NLH~Wz}'wdNs҇dUngF ~rw˜N9]VЂYKk߭تPdIȮ ɟ"aVGO> uٞl6džPƷi^Ȥ-ДYs>( !3,kԮ-u#@mUԔטŻ'k҂9{Z(\ ֋T(01x֩SGz遜lEɒWĦ3`;Tʔ)CI$Xh?8A"$9X/Hk̐j @"7kL_,@x?v`4_>_5k苛N \?mx[XXF+ذa)w#cD)na0HESmpZA HxVf–B7 +aX+P 0a.HE$ 5X]"Ƃ 4-,UccF@V&Sl/.?'LUVi93گ^:'~աC';#(P(/]pUBkjۼ+ gR.Zf޳:u2 gMu.gR& ߵm_OjA_ų4vD:}[:ݤKyIã:r=4Ib^LW|.>[A; ^GT#GDT{ƍWD}F 6F`C@/+}A*]\r2 < {6N6a'3ZdkPv 4T{SkJrܶMlB S挴f`C#1Xhܾ Oj+Sv-Ё߾wJ8R5re ]%qVQL>I{q^^5Y|;,~ejJ!ϐ1=,SO\ l=wnޕd=t{kGM7Ҧv$(~REZr1U]{)SeJYdɤn%4 6ːVł_ҵ+7)Z8Nq!{;筻) @bc߱+d{ٲeiΝ4iܹqyF 0ƍG r!Rbc+ AS#u2XA{!H ]\rR*q$𜓏cl#0ዀQ2miayQhi4^:E3&ꥲґF Px!rqGP//^B|T`5W[Y]Rf va3vhniM=jd(.z,=kԩBY,-.?y}9,D˃F1I?~TeEݜ vt6M-$g\F2HӬ^ 4<K|z8lWhБSd{/oo.ҋ{fTjoBoANA#_^57Uh bŋg.X{G~ΌL _ʔ) t#T$>_2J4K7o-H *#VX T8\~]nÅV [sҾC24؆mDƂ$ W56hx!-֞->}$g?~|3gcF ܻUe^.gOּ'HH+p(qߨANG)dL˶=k:sƒN;D<ҋ~?}>>ӊM@ᒦ4F #520Ayݓ?{G;WPb;W@r+k{~:4w"YT_AK!)K&7p6yVoҢ.ѹ^DX+.4jjϝ'ԑGL:nX傏>cZrӖ54SJA-֑$Owe.ݠKWnQW /p*h}$\X tQ: &Ƀedmmm!) `XݹB DCm5mڴ}vIhرC^I!HWRJIX&l^0wwwjٲF_2^[~{'" ztԗ@^G/Q >}4K ͙3_DoCJ:}%zK$ԢϞ=K={$DFDq_Ȉ lvJK.նCc</&"#SlJ݆59]rCJlXg){wwH2iBFٓϤ9{?T:_@J -];_2G G`O`E||Fl{ e:2vl#$`]S!7Ӗ&KA@D v:iBO(`! 7d֋k!U0yAۿTOL<JR""ibۗ ;-]hqp@TA ]t`L#i\,d_x!qxWqy_v8Y7nG-[_`mݺU.dɒEfGGv ^WkxA8|#P2ܓs;o+uG⬬V2gOW&OI ؙlspZLW2!UNW.~oLK[zn<#5ƍcc3<|w" Cl4_J@cy}ՅnF,MT1ȇ̮CAO!w 1?o"!ms6]%d5oct{Vvݲi9-Mq*u;Fݽ#^׍U%!ݨ@\*yv7?l2=䰲"Bmh±#&N[HV~M(vtT([85K2FX`y a dk7B7^Nњ`!v{/d(ڵJ/`\D/[-HzԤ mO0Ġ=ga?/j$ kSQs1c@cfe%NZA kc ƓtpC0-3#Ԩ_ K/=,ץ.Avnu.8'*kEϞZ)HrATh-BS`wYL ~?FF6hw]Զ%EJC¥V=?I jPR>Tc!AsM,<-#}k0qQZy-+GH_%n?~Vkhx41m\ڷ.ǀBsƭi|=4~1{w>-U3]oڹu/=צdY^9U:ޗ 3j'\Yf}G J@gQ"F6MZp?խ"fB=r;50͚JsOR x*d du}ɦJOi|Vdg_wBr 0,$t7el_2^0U&iSf.%+7˝ lNt>j8OZ"_0#0f`I3iYJn&,mxH`%Z G>Zl!!~H gcF Y)-;ɠujP!7>E4r81~XI%C{)|E2 e\)[A@8pp@ YF#b 9$YzjI|d ' `8^ ACeOQWcY߼GHֱ%5휔NW%Ls58g2*AH@=x "X(P/psVjYZn0O{[dJgvNɒ&t5~, XÎiRݴ߭X},ݲR w6F`F (0" h@#@[;,dɒ93gΤ#Gl֭[F|I,č7ʒkr L' S N(޻wLZp3fV_>ѣGt!qp5{6F`"%+=|=մphݾLN gM ךMU^*^8 d"v]ZS9_=gDx 7Om;&7Ev2Z*Ikx D]G=ɠ w@Qev!E_]J^L }X|A=e괔.YfM yͅ?Dg M'K"GOmO%҉(`ѱ{y@r0/YEiӧ H8LIgM(!e͞YA}7=o26msVDal}n^E/_ҋ8K?إj'i3VxJJ"ʖuTH|񊒉z60e| .H K:JIF׵ǂJܸq((@7BbJ3wb7ӷdٯ2 w1J@IDAT.9A,U L@w=RՏ^J@_#l#f'IJy GlƁ$I yJyse]Tse̥XbYʄG:u!1xd, A9`yުy׮LpGw HʢM-X`~Df^P.;}vP]gP6K sK̒I{_ cS~4$@c=8CPVriZh 9 5 "1Ku2\sr2@TA;v. bL.gpApq yķZ*]F(a 4҇zUfccC .4YR,n*X+@؛ڮ vW`ϓ'j^={ O!,l˖-D˖-ׁ_3#0#0b}{ pt0SsQ0e-uc1SE'Od;i&y@z]Go~lݵe/^V{bgJp,f9sI9+]3jN;ʆ԰ذl U?͚: Ņ"81Dx $zmw 1aò1n?rQr.sO-7Z窥~s8ٳKx߹s=zDZAz1buI@ȶ$O@+f $˗v^Xnd|^* $sEH GT gcF`F`b3pJƯ-aha0޻¤ !0E\"u<9(8. "i iV'3~8l[֧-;eSSS:4D]dZAkom{a )Uyv,ءq,ϡ#Jқ }>)k<\lm̪O^ڎmUVNK?.,iDԺyq>#e1qD);cƌ(3>|.&M*]+0A@ E$;48o+8cI#"-bQBm)o0#0#D6pDỶj4D y"oQTD!ʐ>5}^C+ֈ _N>vΜ|2lDTLQ+sq\ȴPo3eJM͂览YЮu#J 1p 9cPXҹh#w5ut9ZzFcjy7^+H~/v?utm؅m!H pX$_8x 8bAɞ9sfsO) ŐM`6vׅ%7. *dR@Qs= ǏgO˴$ID\9@_tI $/%a5!dE9'bp4'UT1 B X3;vL:^,8kӣ6LGãHvZjժ%W6J3#0#0#RM8 DeYx!E%tr4(S| ;'8W3_alqY<8/ PÖ{hbdS<~5S=$yF,. @ Ӂ8/'k];4scX ʌ]0_., +Ozb q#zI?TBI8iw.%λA@nt#\)1D 51a@ R嫷h}A*/te‚ӥ7eٲf"li+_sQi k,v2\J 'H~-EYҤio}V9g6slل׾ ֟/;OTT!+x0:5+.Cnѳ篤LWDZʚ;; PJ\=#v<1!бcG7u˖-e1݌u*֝"`N*Qpɒ%ԦgΜ) bx̙SqđA۷o/I_WWW*^x@Ñ+2N哞*俭 :4xwjՊʖ-+ nm_~}.^(=t-zjzC=x?N8e)SFzc 7oL֭1p6<{,)~rooo/ mtA[q֭[Abt6sLΝeQ`g?_tic48kEC hx n1#0#0#A/,JZ466 4`5c=˗'tGЕ+Wj@wGi֓HG<5k֘3 l;^َ>@d6ezyH6e~ix#hhXٮoxQT2qcK>qvUp3w0#0#0#0svO\Аoڨun,<͍ӣ?9Sͮ<13#h@ + M^jp>2mn*hP*zH2,HX[[ `]-*]f=:?=;#0#0#0# 0r,{Bn${fuq>3!A2xà7n\>j(m@Z#i $9)ȗ@f0 U!^ڂd )u(DHWiA)c4xc|M4WR%J]4N&c0#N|toNs#D_|\<Ap@4Gr D&O%Th~lgB y͛E4e)˂4ȧ"8WE:rHyEmg觃3gG .]*+u1n*U]cq@q9M LP]vC86=<<&:yATY8[cgƒ"E Z#bG#{c-2#cx왜Oi΍1f^<F`\pp1@^1#w4n/RZzlRGm c1L@ãڶm+Ixʊ+'E8Ç oAPOJjT]\\dPRbQc6<HVJϟ_͘1$+V({jդg=ӦMKϟ?K.ӧKz#hT๟wQ_NNݡ<B3yyl6BHo +g,=׭['" ٳge RUg鹏 }mR^(}ߠ:? >>;3azr}hs64ڷo=# 2e*CY`͂?ųeF \-XqHqAtuB<#0;ۿR9S(]1Qn}_?-ZY44"mA:u֋ō-b\}z7w;_XCİ"3gL a cAL5AƂ:d ;wL{n7n;lu͓@۷Oz#+'Ie=lSٳg׊5j$B%;wԯǸzD 4.W7n\}"'''0apr,0ȿqV w搯AМ9sZՃ… CjAAO2BsD\)&PlpK/҉'kSd6zh]DgZ7*?=zTsY>}2vF`F`F`F O)$Mj?fE$H`̳w͝;5jeC_~Ro44y9sYBƌCǏh1>rM.] .1Ӎ@Њ'OʣBFM6 rD?wɛ֭[k ^ylz 2a0􅠰RRk׮4c 1dҥTxqwDk=|PZ%yqiF`F`F`C34njӼկm "N 2z]VĹSe p ˭rk1_<7oNF )Qn7~`pSNh"#8iX ˈl7Xd9'A?~lxͯXBe:Gbʕ+F۷o'k;d1#b4>F w<̣RJҳ]t`w^J"y{{kdz6<փS}6/_^.]:Y ~i .ȓ@wF`F`F`.۶ 2NV)~{۔<7%l`:F]™MO["Xg888.6F 2#{[d,m۶ |ڴi)ÇҥKiСiӦ[?ee<<'NL3g={zM&wwwI~ؔ^zi3 ʕRf!bOnҊ˖-+7Ii3谆 Rvds͚528)K*%=`4qDIˆ(۱>xcCϜ9Cݺum۶l wB>^+샌G0Hkȑ#pBI#oԨQZ?H̓v#X`F`F`0GWA-7ky*r=Inpa5x$--dgYհz5_F(0Ư42fHRK d+5hЀҦM+/5UV9rh;6mڨ*$/R3ܹ$QsEV^ ӎ\\\$i\VЏ@xW oѤI.A*F퐽q$e wss9*T l԰kΝF;IFX MG|M=.G̋`F`F`F!'Zl#M<|bv"aEË'J)5;Pq%Bu΃8`&y ,YiC`OW42絏n!/ GM:U#Uӧ ի/V?v@3l*' /M0l!,d:~fF`F`FWohƼUZI/_ZWVU!L=M sEt8慇8V:N1# &CM{޼y;>}*/h#(H Rn9<1mkkʶdġd)ocx*$o@ R<iF`F`F`(4eRZt#}٦O[/wx?Z_*hѢߦNCX d"As}Jl7{<#0A {Fϟ ^ƍr( ! zپ}{ʚ5xɘ'OvhCbrCZ6Qr3qRL)?~(!bA+ ߗ~~~2Hk1w<|q73}y3#0ႀM~bƊ=XC<޾1xV/T&7z1;}T։ -Y>XcF.Ӟ=zx$LLiS٩JF=?4>F O 7HŊV,mi!:~ؓ +(d2}nUQ0aʜ-#%Ij=P׊th}K63z>w=~T%K ͯ5wɘ9*0Xh#؄5}U?*?{ n8nނĹDק[wcs;F`E WJ4o\zC@Rc 齸A̛W^hްavL՜#tr2djr͛7SX$oZq2)籱qFz92{ǃ;ҡO}:7+MzCiOB:&z3N/ehHN#0@!0q4x]vJep߻2޽}giO2 Gu" Á=i epI>b ag(GEsӍkdW'R׶7ZI YwX0{fR im{ر({,X?L Xh#XEԙK4nBr߱ B5%ϕ8Wi5mzF`'L1#nNd m;vL-Z@;A/_\w8߮_Nk׮%_d6.^( jܕA?~ʙ3$ &Bɉ 3OwٳT 4ARÓ ^=5b#I&xx;^Ϟ= :;t@G-[@իir: !,K,2 #FP֭庠^y'~ǎrt_n]rppyd90'N&nz;4'L퇪N3#<}5(^𹃍Ce)swnmV1u˾\x`F ""G #тKޙQV4m8U5*x7 O\ysXJ^5D?fҤIRe;<;vԊ@wM~4½{d\E4a0Ej`rB†.s6K̕q#p\D8:ե ^)SW_;N\;t"T ks,Agt뺷<1%GP2DZ #W@;r5l9LA*T' ֫@ڶyUTaYfwL{Hv ^nC)eF,Z"=/4hD>}^㐬}(:E/O(kجJt[w閐vT`]ͥmXM,ۃjwF@h.ThQ)Nx_3z[X/9wp@ DtIùJɥZK!΢_~F-8!™ws <8nݺUCkpyUd3`=_3xXo۶A]qP$;4 Wxc1eKi\\3KV+d4!h-39{%oeҤHv-;ҍV7~ofjԢz4רۜHa3sԬA; :*¥9л7e֢{p2eRZ.9V.r\b i4l\A ^mdՖzQ_צW$9Q}iӚSRTv`@21ޗ GQՒ׼ZӺ߯ov9]p(dT}kD@Cu^POs'Ӡ’ٳ^K}q~"`z؎ͽ3#0|[؍\d#{>l (7PlGz3Ez:Nl^nA~_M ^ Յ ĝPǖզc5=Cj*XEϚ2nklG<~py+~w0ԈlG??}#vR#‚ڠ- yXbڵiӆ Bn]ߥմ$걩)3fj{Ĉ/͛7G[(@؜A?˗O+ѣ6u֮]Q 8(eUVO WS iݎroAgQ ?h͛շwZ`@@e˙U%k^z6S۠#4x3dJMo恖ic,lʨچN;) `!xrWڹt^=e(9H.g0Xd$AeA#]eH{e~aӧOOb;CfŦasMg\W8>}$u { #G U^H֫{#s#2#0*ٮ(be*d ޯ[3㸈y/\j '+=QoET^ I*GgW$*SzY6v iu%r 4IU%N(O ucHȠF4}=$oy_9Hg eЂ]"Ӑik?O픎6*/s@R_rQƿ{V?#Qyl^DI! ~CJG[Ŋ6/׻|4hO}bĄTVc-ϐ gFgxc/?у:*,]6' }3ptJD7?Q#X5wD0 tw{ Ϯ7Go\mƱ }Z?-dɒh Y (2wdjݺ5A[4qi{.]T{pbQF;uD/_V D~ժU)G~ cAV@Eo6p*A$d&^)1C#0@ m4tf /ѣSߡv)@/jE nxk%)ڳ>/~WE6s-D}[(:NGO5҅?wҘf.VVK$"2xgΚn\3u)N*R+By:PL﷮|G(K;ɣrS .yIA+]Oʪ_QV="I 0?~h:LAn &ā7=ׯ2GQN󂷾2l#UyPwkX\nm_:w"/\ucF jɓepSnA֢ 0Dްa` va\T) ]vHXBzn?|̃lG&y(t?&/S+%H޼~)IhAF;8YxWi3HY˶Q^>'`^;Y" s+ a3[:/3Ѣrj Gm2fΠ~ gmAd{Y5[,%[@ רE@ %hE 6}yڴқoh2;N.].i]K63mC6֞N8ImjHةH/B wF}@<++RJK^΁*SU@?|Pz+j ٳga }r-<4-gW=v[ ' =#ZT[Y3|F`E1W}mm1Yx?Bs$qd,Yr-ձyL6zqŧuJʏܭ~n*%mp$ 2-J& tA|3tLuPO$+p8)sb1iPYpۿZ,)uZw(NTrf16$zlҙWum# #K5Om2Bcc>YrdF AR~7:q$BFsgé8qzΖ-< Htdn-?a)˂<B-T< UI7x/X@e1 ,@Ϝ9Sbŋe@T}C:7md 6 o8pI8 {h7'G0##`)(A=#9ˬ-29]syʺ!ϕR"JW:G6iڨ(ˑQ,rKk Bie?zpy_<#*AtE_RMyMӟ5h_omFXyH+pS ?=*xCTq@ {άTa sAiEms܈)ZQi)IuMㆣ !;c2g˨Q>Gc=&;LYLO?AkSrzki3wzի'NM͛78SO]\\xzP$!Փ"E z1]tN:%[GcŊw^jdr3FֳvZחpݻ}vz2a#z,8p@U ۷מ8x 6mZ-^p^CcZǜ7p7y F`F RB$2!rEwm^6nkDҺݿ+mTnPQp?w(MݏzkVy2 1VA+3YnJd#P)#dy^ P2F_Q6}fC29z7١ǵn3ڷcINJGsh2'@U;Dr A;^}Pib<B]|3$m/H|EgΜ|2e$: ACGUqY2wwwݻw6%9]͓ix۷On^ +G lrl D:& R^Yݺu*Oݗ.]rJ}ƍ% GO<=z,B]b38yD!{A1#6󧏃mGAߦrkRQtlX홲䤢%ۄ]#jz;c)RŵN3 ;BB*up5XZMh}ϟliFN;ֆΞץ*5RނГƺ&(f$GѪ3H΂O1aC! ȵu3O'OfZ-;arnwAP6hTT*P(I픊@f8dQzTKRqbQ߼mc%<=sv{#')W=WV}JJ7"}G#,g*|}ܣϟ *wH4lVG?rA\lZN]zvZ F`G^iҤ(.\>@Ν믿6HeÖ /tM-Cmٲ6< k)#F ڱcTعs ̆;wd<ʫU38<@L8SL8>^x+;_"dpzÆFڵwһexf$hIۚW<]f%J$=yНwuut#{c#0#8]0M՗V-a(e*F.9EKSiԑK9rt˷7aԬV )AFϟ^D90ӼKlŚ}. MgP&mF<xZ;}D|5Uo $Ѓf&4r$QwԭmmuM^:6wED c+<٭/DQ+ 2hڴ wsp#0Hk\;ޟ/NY%$m1*re@|t|UF|O0fTR%IBTj\=O4M)zV >bǎMLGX<1શC%\.sfsH☳X~9/`=찍=`6WEQbHF@diPqڧ >92jYGkTH蜏ԙn]6ZFtRq{ju%I¨,oEK~qiBy/vݖ 2z½vdZt'RЂc׹@0~Լ] !$=2v) š: \W1Q;En/HING UC}ATnNHz9whڄ".7m\Ta<8մȦg`Ϙ9=̓r⥋RLlj^nsB(:4QCӢ9)n󗬧.FWRQ *-~z_~r`F|oc{mzÆ x/i(PHڈ0F`IVv/2{r=:mszy-VlX0e2D Hf{Bn{vQɒF54ҡKkkU,$ +G zeo߼(oSe|g"Z7 g~-Ecs:ƌ#0Q܊Ë\Bx1$ys<6#0+.w[*j}ǏowSN;hSmڦ#0 Mjر/j\T,W]MN^GzesCF`{kGHȡhѢx׾|R!n<\{F%dR4~~~!̸ecׯ_żgUP!y2s5aXNq޻w@>kVt$Nh>Hx`$IBP XA7,gΜr6ixYѲOeFɗ/_dwm\̙3[|킚31ޗҤBE]hƢRgx{I'7_R΁,R(3F`Ff߽3|v@ x׬[- S@$D :up-6Lg/\ ԮLѣ[G#0@A pxAC|„ B;шeAddɒIXZtBz=z.ŏΝK5Ҳ_~t)G@C~̙f#d$3f ?o}SIΝ.]D;w… 0Ӎ@'O M͸Z .ЀhϞ=ZJdʔ|}}i{X5dVڵ+͘1C] h4("cǎi&6@'s:kqϜ9 J@l.zk7h@iF&o_"_?NZnhe͑֋`{{`U`0a@|Eha;w0@G)]{w 3F$Fu=w*##"yR:k95mۏ$o>},b V;0#0:uE D..xi 2"6 A wǏ7_WX!ܲeQ̣Xbtl}J 5kG2ݺum۶i1cƔ- CYڵ\׬YC˖-J"l@bƞv/_ӧ Dڷon4߼ys>'F6NEGh<.1$t(3=yo@`F`$?StNԭW>|DKWmcԬ+5o\Ö\`F #{VE3g*%EҦMK~]hҘ)D[p.#0#0#@TBI:gcݜ/-JOnK&QJgq*[oСqFRezfF0;o1bFW3ѣGKYykL@A8[hA>>>6"ؒk4JkR=Qyъˎ@qZ _A* s1m\rGt\gH1@`wF3Y6uV;#0#0#rdSQ܁hRS6#:5[oKoc߻M[~}TױcGԂ3#DpqȻ:!;vlc޾}P`OIܸqC( X޼yvXv@[t_ 15&OOӪ 90290#0#XA!ER:{ը-/ѨAʁZ%KztiAn+G/H(QB6Gl۷o3F`"?Lk Om8jwL tw!A ojzoߛW^˗/ezÆ 8 yʕ+qlڑ=rKzȭ޽lx#lܹe9 *hjЃO*ԗwtt; )COvzi`=c09Z6C`5k$-280t#XG;XUakפF\0#0#A vXy ѹ,uR$gf@V,(AtȐ!ty8q*N1B/^8͛7O:UF`HyqH_e@!bǎ#xâE&'ON*78<%vZ_~EzHܹS>ŋ%ь2l»:gΜte6lyyyIOsxwٳ%lL8zW૸co{xjy97oҹshϞ=[X 'OiJ̜9֭+"СCG=4WkRi(7#0#0@p@,RtiD@TjP93g 'I8qp ZJ58WfFOvvV&l2I*F@2ʫDn Νd3ȿ3ܜ~:a ^ !ҴiSkUa\xC?T4XL2e$Q:fSC 2h$isiti9?f͚ݣiӒ4 * ~~ x#2{A]ok>AjU8n̙͘3,3 lbUZMЉ)d[|}/]x>} ˕FӞ_)KFJ&ȂSwiCؙ墼kϜ`F^6^@+P8e͞n#Xb?r^jVDϜвef͚Q)[lZN0#/xh#0Nnc?YW笣DZhٌ#0#0@@ C 4bs=zڷoO $0Z7gN  777zQ~`F{p{yF`5e?Gw$[ F`F`F *Q͟?PuTzuΝ;GJ\]]iÆ _p`F `=nF`lG>6FkPJjڦ F`F`F0F zT~}ڶm :}tʟ?Q%H5n߾4h@)R:бcߨ?0#L#0<}um}wr@&/'n0#0#DM&MJݻwgիWih۷oiTdIJ>= 6nݺeTF`B!ǐ{`FzwjH_=D@qƳGn0#0#DmfJcǎ%___ڿ?lْƍkݻwiȑ)S&*V͝;^zeTF`CX˾>#0#`G}u91Wn~{> %?J> ϘEf3#駟lٲ={6m޼/_.IWӓpAz-Z*U/#00n;VQ/_h޼yR ;1$IrvvJ*Qja2fZzGz2^|I={ .зoT6,X.]=s`"'{vnKfjפ=nZ{D4mP{[v]f#8sa>G]Zנ6|WS΍e_sg] gFB\5kӝ2J;QY]p7B=i rrqD:>4CtԭϯTf sF`*bŢMǴj*I_|Yo+qԨQ#j޼9.\X F``=hdgϞQѢEg̘A׮],Yzqׯ_>:tVXaMОccȍhh">p49ק3Azoۮ u*Tk*"O"xZ][Y+2F ՛ԥM/lݐFMJy#؏G^.];)S>M-UT;wn(Q"bUJ]9~azQ~`ȇ_?Srqk:oA=RzO?@%T8ޝjeEJ i9"^>*_b*XFrD+r#TV4_z)mٴ?5Yd/]Z~0#ɓi„ w^Ij7oҐ!ChСTT)I4{xwIawF`0GiH޽[GBɿ@Hl1cn2e^l#=+ݼptu6C,N4Y6[$?DU0\ȃرe Y#ٻR@"efܣgr-׀ 5u> `!+Wiƍ|c w<ڵ+լYSJTXQJoD0@E 0|mqUɪڻwGёr%Mf/^s; J T'1@ذj>mYTkӦS_*_ ̡[ 볜dgVivz/=rRlm i0ȼq͋ܤR"@bݡ)C3s&9UfM[b_"4yJJ)%J<)|C(~ҫ(V"#eʒߞ_HϞ>1 Q)M 'S.p!UcrF <'6 3Rw}7EnY.,*V ׼/N&ZM'BH̘;JHFAթ&mF-tAZa3EԹf7vesZH: 5k]7߾~EkOEe2hxCVGA#n_]4rPg!X;e:XrPtI#MFO?=~F<WvS1i[پd"xA+<~򜎞8+g(a|*_w 899ѠA:@>}JSNW9 xHβ1# Q5Ë2|6%Q~zʖ-y{{AL}DvŋF,^rA=َ>u...FQPZO#ĉF7%ۍ*6'%ݔl0͛7Oz#؋gz(4{mi{6molGZ[J`J9m)َo߼FQEG"OQz.h$6BMgghvf߾yMFnXO%Iȵ6ضwFؼl7UW_foӠlU<%fVفlE CYh0G26X&MF VGߧlGE_Ԧa9zp|`s '=nEo6F !ྱGDt^y>Wߡz˫Nto˙1w֦ !NSُ )soQ Q8al JgGt֭+NE7Z,x~z8X>~hTF0F0u^~ť9lذ5j$g9q, ֥K|?W^^g.A"Ezo޼!H@FְaCΝ;R0|" v[NӧxU^]z3>,9{*UӓfϞ-O@dU ǏS^w& @vK,!b={:t #r# |ȮфB 9U@G`>:FRU?ado:s|J!hs:V卑 m o=>e72t^陙Gb1R)\yɡyȗS1e-x o,&Fu!:Zz椡BssZ,u!@̯G 2;%'EsSW,&jk롮+֢mgЮsJ !3{-ƒe=SB,@z^SF?TÑvr85#R|V/۠O4ؑ29PܯҖz+XˊoLJ:o9k0~AvCŋ4ooSjhĤ}`4&1A&C:<^-61@`+U/Nv]mٱvoe۫( T_[fʟ7N*#'-x8A] R48 3&MPlF{[=C Qs8qS%~EE ?.F … "˄ 3fLBvbŊc7+dB`aYd .Pڴi5 g /*Pէ5@C3|@>Dս7YeS:MLv ],AMڨ L@GOc(hT׬RZRʯ/^3\{aMJ:{7qVYkX2i}Xo|[AYRACgan|ǵ޺.B 2(~}ٍWPg7d^Μ8$&N*9oodzOPEK7]H=~t=C_+盔[Ȕŋ0Is^Okz{ݛtn#ӿfrF^]&;rk>/HUccB;գQԨQBTy~aMVϿb[wm3#`=bY17#7L1 ۷oMO(O<> MyE`E7.#xGl2yyP^=ʛ7 *ZȀDFN#,xNZL/+sGOH|׬L&iخ62k?/K 8vx MJk2f@7Ie`p@@IDATᎀWoxɲկSdB{x ۹~.UXRJ=Pmסf,ZIsY.Wy? ytRfG=>^k[ϩ;mغW^Κ4Z1K\v8R8];QNlN3@PU 8޼yCk֬3x8lll-ŋg7# (Aռys-СCԪU+鹍<-g@P\9 ,֭o?8,;^@və3'?^zc]0h'"!dt?fLL\. j(*@Bт: uzF+T-r7Y;^sg[ڍ$ +gA_lΔbU5MA &@x_ W x19)I8k:mLL%L>b"ED1bRA{S_l{@Yk?/#Rd|VH<#`h:G\ϊ#7I;fʐr:'Ι\,nYd~4q\H uYMUV^ߗ<<L-[=z͞={&#zkS3f ,p)LlJ:n޽\1~KhGbWŋ.^dݻwKmu-ڸÇR ,~Q}Lk0HǀL.zâv TVMn94vq8~5k&?@DE pCӧO[5宇^?pѩ!Y`"+#ts)Ho_S1[x9٦BW/yOSiLʵ _^ظ~%9ZW&gxH4>SIN^\[,jx_rs 2._8vHU#hm7(lX߶lG0aApXأ 5Qqks40@A@/+ 095a^NF,դ/u!.3c˖,$[:8u"h0l$Q'Oy?ЦדSըP\WZe 3pSg-A oXGP2gL@Ò|GLO#W )6#""D%"H7`DfbŢR߾}eֻw?!7xΞ=[eAvĉR 307ԃ;l߾=͙3GÖ/hÇ9;;SŊ ̍ ۹s'URʖ-KC a0?&-www)tR{#|yOS.MTvی4buzYKOҚemgيOFo 5^ׅ>'HzXx B@򇄥ͤ+ĶDܼ~Q |>} A}&1dzMfTy l/;1i!%HX]k KcgȜC.6\\~ OCyiF`F2zS Ǻ8ް|*ٗ.\6mJourL5M2Z4$.@A_t6/vGO]<TCN0D;K.- [l3...*-l=k,4V̟1<.K:>mh˺%&1` ոz"ɼx*%wEkۧ #4yUuMZvU{Vk$<޿kKR|8`de4w8}Mt(Ktڿ:ǿC{,Ǿ MvOv`D8*6lؐI&89@׮][v"h/_&8DB%~PxU@ bp@``wc\$[!ᢗq1?̷njT<OV; h i @MuZʔ)ܹBzڵkRƉ'&"E ;pvhH{Cǧ>}?nXw̡QLTNT`"H$obTVCڵe5ac9s.n/{IgIC_N_\voUׯO]N5hc~nNي5@RRoՎ Q^)[;)ŀMVc7mC]t1MY r3~9j,R`1bƦwoЊUg!%J APW !)HnHg.^tn7mIׄlNU(Vlʑ;?-U+etk&.ۅ^|0H\2_3#|8N L/'PvEôk}eAmX$0_(xݹn޾O!Cޝ z^DN@={Ǖ+W $r?s#C inx7,FkK.)NH_^~}Y'L7nf߿osDph8£%Kp1]vUSNCj^g(lV+ZDj UʸF;t zΞ:"I|ɣUl9h ˌ27nG*eS@tǡY}۵`=csZRS=3K~^d0,z½X𫕭P'߱@2uyOH6H?Dlo߾RŢidAηtNhXv_ZsK}*!c smذAj@:u̫zvhԺS@_mؚAx[,9kh ih-4t\\ks¬UԮ tGdz^& F`!zY{?C[w'4_l6US1!OsCc, 1 JOНW(j N->pX2ȳ\陋kTo qYTii2 -<}*ւ₼.Wꉓy ʮ|`ԥIޔyL uڎ-%NOĔ6}fήwǼ4D̡Qy/kIMID@_[ό@@hա)dT@"y#>vFɸ9ƻCi-vWM&HwX %#(YRSkO?6"3fLj֬<3?͇kF*xZ[ħ0@P#@0 6_{j:f2,ATLm[ӒGh5|bЃaeB8m s) _炼V{ZZ4g_\ʴ3$p kmzsK/>3#0Yt$1Qgzp?A'i' g>gIM/h(J(z+[GXƁXq ޡc'Dt~4jHƀT@N2&L p}{ē#mڴI?x`܁yFƍKsbفx ^Goо߶m(P@eZJf{w֍p yQ-1ʗ/2eЁKs*UhϞ=bH3fаa$鎆 !d>ӧOS˖-&cVZ4qD:Dizxٷo%@˗vA1F%eٳgRB(ի7=~1#T?~ *$=}cرZlؼRuY媩kN0@mWn߸,5fb${ԫ%xDf#^@^bu90#`}2y|.RLfu=O>J)oU߫WߡKWu@(@ l߾n-4AK^~677ȶ ˗Wy9dpիGd;깹I3g7#xvo~ex۸:'-E!n5&G:o޼2.<5r޽MH"vw َ&MJ8FN1lS.]h&P{ժ$qM P BG6F<oܢ \~'"GBc.5:p#0FTTD~ۤ8r2&YH$!})7htjڰ]38wByESOo ɄyZ6?KM19n\J:9=t{J ЈA){ D*ztr5!=SRHJ23DB!-wWWW5-1h yΐ!*7JPi9Aëdiɓ\e…ԦM)8qb>|$!Onx|c޽{wwBuV#2* r!pF /\@E Rti}LWTIGԩSʺ ȵ@6^qhGT]zEbrơa]X0dsٲe⾁fXĐٿxb=g"agvٳg8}3#pj܆`PYL_NmwZ7'GԽ" IIȕN~$hɼIz{pďgCޮe}2k)=y~ yCh)]+߳ AvE*[VhOwrN08qHd7< %<7n(ei<3p0A&M )S&IÃ& BFo߾lokf͚>7oNf q5;jϐ5\2eϞ]i{ܨ=vZ?*U*jڴ(ǜ5ܹ3M4IJ 5zxc,"Q5DJ615Cv 0< E'#0@ /}л `d; 퉸.Vݛ'd;|ɛ8{5%H_peF{s4o.٦>C&h4kV&}k0#$ee0@AmtQ(PBFFBv-%=1 ;c1AZ7sYQure$躉ZhiF "/!C`l׮ MoxHfUjUYرc:1cNPo%>'OlBkuLe_JR]+ /gm|ndy)Y"29sfa]7lP Sv׳# ulӐZ:ա+nOeӚPwy/!o)_l*K>0PVs+OH yUmժ4<ŵkY/fx,!"%îKw!w,hk15y`dg-ZDqX&ܭ(0@0"[鹮bhl3ܹs3 dd[ cN^OیѮd*Լ]/tuF`F`@BOt})'!5xȗSFmNFED s vq޺ukʒ%(^I^Y\E)id;8reip#0#0#ZE]#Uv${رCOR۶mMʵ޽{Ky5j @ *I&g4et~W\r^z9}- v ĉ#ᑎE ,ԩSGJ`Ƿ% $IPݺuG|gcEA'!U`K [M4 Z|#[k<`G ^ VK*eYx9rϟ?~_OD/Sl#^>wa'6E!b#+~OF'+Jܮq9kNJUr#3#.8 UjTE{`!ZQdI 36F`F odx/_,5Y=++R\``F.~M52< 6l@*ނ LO$5h$/FL?HKđ^fEʖ#*vcIkq?F`HvFXA:dff ny-D;&%-pCF`FL[BF<<}jۗ/_NХ ͈#P5k֤ݻkN0#0#8y=rfd6ow1D s? ,ϳxkڃf(Am:.-WdF`|C wrF`ҥKJ*'+/7nGgϞQ t0#0@DE _l'ٸ" {6+=~J[vafU|r6mܺ5w nІ-{7 pM܅k}ku-}Nkg/-U |x۶8OrT|#J<7[ rnj#0&#dЂѣy)ŌvA[)?H^cF6nl26v<#0#0@@OԮ^oYVF/'oci(3i>%-FiڍP'EAΐ^j+;|%R";ܔla1R";QJ'i-Fw=US$(O:]Rĵ.H2l6nu@4#rvq>} s̥^ M,kAȑi_hX>6)[$cƈnR#w:feSoL n> F`F/DK%0#p@rmۖ/^:I$ ڵͫ#ѧO:vj̙;wnuT[<,)ҟ$YJJʖl3s:s6qQ;֮@qJ8UužYY)u s =J _JcmW~DJ2砤=>سs_7 kTs'[򅫲~ @a;?劌@hC _R ݼG&ϙ=4Ž}!8a|/]ؤ{jز7j^D|]{RApo[;2ܟū7&?=e2'wtjOFRל4>⯛'_ /i8&>~zu(J-&&|AtM$f xಅ^ׯFG͔9kIcN4IٳCQ\Y p$ɟx-=^szgfg!RJΡ BZ6h yvSzuiAŧBu$'\B NԦPע\yA>wh݀BbâhЩYSx̟2Nug$I'OJ u,ԮfO҇\ v8u*kM:]hQCM/tdᢱI2)j؅މ͒%IHeKXh"tϞkJq`E?&, H(OUwq0#p cF@+D&d;?dYfIȑf ;ɩv s3',#pƕ@T6!Q-⒐@ITJyxSO=!24#eV<$!CCVn0/By:::TUWk79Zvnu r'x-YYVɒі@ի)RByh Զy=YRzVd fNmQj-K8t1>gfd:)'Z9&yc^FyڵqN2U*_6FZW;Uĭ~JtnZx͚z, Ւ:nD̈,^7q۷/mݿs@Nuh(I2zWCs'K:BC7_T2𰟱p3 [D oܔLN!)"WNDnOq!)RT'cLH h[V*PTmnxN8Lf/XM{1cT@G^{JA?yBօϛCe나³|ᓲ!G7itoo2)0fh7E\ZZ _Nէg)ZZ<7o\[xi@^M5['44\KPYFNXC5;4oca1}s_.K&ӡCڼy3999QZ(F p`F B!{z|#>|X~G4iҤ!Cŋj.ВϘ1]b԰y'Ӗ6Y(o^H?ĂA5۟FLJ4w+:gų'`8kyHbײCߐ6@M,niVd*^vp@U"uw)}ۮpͻm^ 9y|L?BzHoY2/U:Fw#cB]3hV`Ik3Ť oD^d;C2:g4 $jZ5"'8<<}&^I]...^n]jҤ .]Гާ`F 2Y0@(@`TBvlYtɓCld Vg?ʀ{wnk"0${o}]+4KC .}ZÛۗ/_nV{{{)ٿqy5fF{x|#<L8>GNk֬իFz*uY.\ƍCkB0qRJسޥ Mc&-ɐqs)s\Kr~DדNXvc{{HִeGl84ʑSfż~`tO%`acgm9+NFf-P4޾yEePk*;|v+7 ([ b(<8Irj޾kԖ cnvr _$ 7'5 hއmXF{MѹԳܷ@kEнve=QPMxt邎6n¶3Ё^$slg ZWآ.Thvo[K*d)R׊SnlKUM ys.E7mHڳc8HJeGLDvB ]ҦKM6! 5+}qI,=JY'B:iCw.K1Ӣ+N/o> B|OqEǑɽZQ!^cgx&AKKNFxiۓ4brww 61n哒30aBkOcF{(|(<%F[UvO>lǗC'W f hzpWsKN6qQ^$y *秋gŊу:~$"%ʫnܨđ;U\y iAC.NKOF/Ҹݩoޞdt@bϘ0؄lG;= (ȳaͪўMv6D gdtoЭM]E)oŢԧS#C@Oޓ&㭛\ g3AWnhZWnRS6޴3zeĐ>h dڑԮi7EV.d;7͝P{k wFO7!Qty(U/W_ %cVoIwWAFa?mjUmtF_b'M~*,O |Պ%e3ԃhwe^)KZ2gLϧϟUV}ܹsԥKJ,ըQCR[-`Fn=,'F`(ڵK} ٙҥK;ѲeKsS;?VZE)R\s|!?}pwn^!gA^^8*ˊ(G=VR7V2hԞm|uڼf$Gy R^3z&ԨEg7D6]L.4V}'PT}kM ׸YZ-6}f՗c|,҄^-!HĶKNɣJz݊yb ju1- ;g 3ΞSwW$ȇ7{c(_.^#s>:OfG QYcSiTG<>WnOЌ) n^/+6^>s|3ecֶ$Ѷ]+&Nl!t,IKQX1PץD$P"xc %5ޮes+ɐ) 9IWdy=GDGz_ʘ9=Uzc`mNI;hNQ<#W6Zzbǯ@ B:,,~jJMRW}ۮ׎q3~1V~랬zeJg%Ϣoj Cf!xhP>3#wCw»~>&ooiOݲ󀬆 T  DA_&LXΟ?f۷#N87)RD#0@@ R F`/_Xൢ0t`?O2Enk6l/塬yل') 7 \}fR6VduhՄ`WSQRe<>)c|iԼ3L@IDAT1M];4jCUEK1*Wc= icaEkS4A W4x!ҴQ'-vuETvi(oT>\TXlX-˔Acޜ7Q(B'0`^Z=Uf)qQՎrHv&Ԓ^ 6]M4D<뢏$m\$Na /~B*eԅ|dR@H4x#&-XЕaY)ܴTLe\M?[Ʌ7yh$U!HJisT/53Ov!dW y뵇C&*o3GQȖR5(lGI?F-"h:cUkV 3FI%-{W`tjT8VJ߰s!~`I&}y=]&n_xjXӦ.R?f)N(QF:5ote9劉x_FmҠG٨ F23܅kCyS]ZKb_ n o*`D˓M<:{=2,HѰ^v&ck׮]qMhxO'7&[[Z;>3# XR&l<'%#2n߾MDדkצ(~)ׯB DuXI@lT2_#u%{Dȇfi׾[~!l@NBνGȴ<{~V)OBܒAE[TH)H囎 َAk22dnBLC6D5HwXhѨl vVd795k޶"۵i#L ꕲ&}jcͲwo_9{ݽ}M'M. w#CPWn5de4?^[==@Qϯr2t^ֹ~E{AgK]ېl,4X:vL($[4Â,H?CrD4d̥^xFޡBt~s5pYZ9wo,yߪX,Ϙ9{"=q]-[|f'*Ck3od9}Yr[GdBpR $f 5?}lЙnrut3l>Zҫ]aYPdfJNz.NsaݩhEn֨&MؤMx11 doo/HRnڴIJ!}ԲAc ޱFv痶5xoa5i!:P-!m֬q-*V8/6\xܟSAa1ȽNs\b"A|99υ86z26̶#$!#SZr$߯_w]%JD 6mf'0@DB ^F PL6z衶G2\N@ki߷o qӓP ӽ$>\k E%O}ke6T9Kz- ق6:"VPۙ)Dfz M8\J&TH/E>;b7^hC~7\Qx~99[nioI3hd׉~19)I8k?mLL% Zk `} ٓpX|$ hrE;22KMi? p>ٱ}+}*&-e,n6u ʖ% riBpѬ^>ՋɓS߾}˗/kҫW#!8e&w8ڠ-#0@D \xV#xϜ9S AQARCڠ9zh5uRΝuXLČ%!?𑬶ME*mxȫ6noA$jBG2jyAH#pqvv^;vqrZA߿*[$kժ%8Pu80#0πg0o߾-[zKpƌ^fH݂5!CZxqHMj^:R0qRvTg\m?Ҫ>wzxv)sO>8.Hr# dæ*W&qF"/7Qxע񭙀TpL]ZTzdn 5yO1+eɞn^Ho^B^,ZƼ;\#0:~dm\IG/^4xt2Awn^E?~xH^׉@(f3 RյBs5B}UIw̅e6z!K\NQ#/lpߺakcPY\8/_ Oh)[Q`n˓MrgjTMn:~qUtAX5J 7;r`0_a&xʌ#BZLғ秓'ORh ^zy¢5+V#XhZE :6*VVI#{Kmq}CL_x }ٙ梬9d:cjƛَ2hV5$e%b>qO bvtiENȑuL+07eU%pW}F r?Mӏ@*Uw4 5?׭GEpSs[4g*Y_?3Fg*J]>uu{ -d»_/"Pެ}7飇ޘ쒾;7ҭY*Q %rE@PkZF CaX_> ;RnCa9v?UWUfN/ck-bZ֮ߺU}-Uec" !6gy:p䔼mE :ҥ3[gZlxO:~y+U&0# p\\`" ݣJ*Κѣ׹gϞƛ3gIkyj,rq6 V{U)K@/դTGjT4 =޾F,Zf*RSe}{Y A6l@R!xQOGkJ5W~%/8*=gvyU_}BMjPp&~UiQmǖڥ<{:Bl{Tb ?z DxȌ_*h@Ej7W5 WI[#(obYϣ3'~pФUWgي5 A\^ Q^yg'PNOK7mCC:)"저:5$|9og{<rӭumLمL ;oڕTB,Ċm#h;i 5e伊 KLwҿVRJCҠxݏ7UuJQeŮxt9ڸf+=qs噳f$HUR +@']FԳ_'ʞ;=sN)LD {Ǔ4zmPS+U+U3#!,v͞2 !7(nl +V>|Paq/_>H &Tu80#D !xF`N[85q̾C4c EІmܸDS/͚5 ii|y8|3x3RM[UYNu=zPgIBtanbIQ"cѸ~|yT)|Ɉ ̳-^g̒ϝ eЬ@J}  Шa59sG07s4nH!g ZYsU~M-kI{̰i!‰py탻7Gzbg$UF' (GB \w5(;b/y"˴tN &MJ鼯X8\arԈ\w4;(dG#I?M߾~a.h*z{ڄ9RRfތERJNK5L2k1SQ&rѾyw!z JF$zREG!#N\ús&#FhPRJFĂSihĆ[ i+Vp J!={Lvx;wpIDZ*E%D΃3#`_| dd4r cƌٳgݻԲrܹiY/-,U> !0l0ܹCoR[ebHz͛unݺZEZj:)鬅[CUmԃ/.^y\`ُ7o'I<T;:/1,'9$ޒ&Fq_eϛU[%oBį8/j,nN߫\y'FjrY=2榷4)ߣK/jLe$Du&PF?λQήp_-QK _6ݵ !܃wc'%gJ&L }eǶ_ԩ$_<1&#bkoeɐ)[`ݕ,?wt%u5Ixz'O|jbfo9:U~Z/_%]7۾}z23 Zzꩧr54  S}MzY D|~eРAӧRTbυm7n\9aI2(gcp<.W)>F=z:Iy8!䯄^%["ɼE{?)#LPU~y X*zc RhJ=! {ʻGf&d{֍9O(鶌 @+uܱRw…>c pႭ$a& p D{JŊeٲevp?,YDeK%A?wΝ;0,s2eL gu,F$@$@$@$@$ Im۶ڵk^owO~ +F$@$7:f$@QF*+T ;w#kժ̚5K%kLkυγ>+XaY_|!ٳg$@$@$@$@$@JL22tP3߇6l()St֭[rr咚5kʤI?t+Ãk*~Rxq*_9sSZ5Fʕ+L?֬YgQV X򙌲1ä  #BЙ07ߔ={cweW_پc @znݺuY--3aY| W25UVuӅ7$8b1ya[o5o\"۷o}/$4w`k8{MeIBL&7q,AO$1F)?C o1c8ݼkkyW1wHHHHHH ҧO/mڴ/8q~ٳ6$ǏבO=4owtt=%    ?ܩS'uDݻw}̙32bXqҧO!Gȧ~Nysq{opj#r… ͮZ.ȑ#1&g9{ѱ=w|Ou"iVLZDӧ=d-i?Ԯ][?Z(5ug>v~7:t?~ܜr"Wv; gW<޹, @] d۶mvFv0me _H6G-H"2zj&4EϱnQpHHHH /^\  ,БiҤqݻu48ژ1ce˖Z%K,yyfٴi5JgϮC!1fz|ꫯuC>ƃ!)yklbsnfy뭷믿?М۹s_|!=-Xތh_~E*L`FuZm7"}6mb +J>p@͎ILD;Vd@ɸuwpw# 'Foy"ebX-4$m͙yԮZ$)Qm_T96T5H |UӤUw It=! qO[CMJ! %]N򕮟PͰ^+.-56:D>|̘1C;h#S_\rݒB2ARZ5pyg}Vk^H@ԩSi$5 VZC8A;&'O4ݶw}Ɇ yvOMjgF}E;`9c<I <sU5ڲe~~B=}va&A{Xq-qb\ȡm,>dlZLtd3$@J3m۶I{unDCumȑ#XbIm|hJ?Ͼ\r1I7 r@{`e$@MIRd>M6M2u;ydm:t Hf>|dΞCn8t)q]6gVL*1 @ʥl;_&X $k){w $k>gΜ:Z=\4v[8H`5uzdo>Gٳ> @LFK.INdŊN`qddJ&9{>pB5\V6;Ƥߩ=x@3 l?L,Y"'NGiE"_Ϥ?+C{px7 @-2#3*Uh=ӛSam׮NNb}fQyՓ:"u'    p'%Kҥ_'D2̞={Jsu͚5[o٨{Nn&]I|ݻw,\vrd\@X$̗/-HXYP!5k\^?;ytRAz $w`hΜ9r)}g8D's:uܾC"y^êp2eʤu?{u#<"UVc.>'2 /5kV9zZJ'D B'NoSN}1V<x6 BU94r?\/>%Ǐ/-ZB 4 P5k{nC$CmM6ղ2Up-$Ç6ꫯJ߾}q YL]5\V@BjKbWǛHO> / @(T͝A./װIKQR&PY 8]M:u4jDEEDo^b[n2`7$n^hI봮]JΝVR-[Vǎ!Yyz:'ԛ7W.>,]Mjbu+W`Y6{iϳL(IP y1ZlvwmOk}N~W>=C2|_~>FCOPzJ DClG>F2K_}cݺul$0o8g"s5 $1D#BnpP#w1(F1D괌3a98֭+5jPp4;ǀT7裏 Gi}w^s^g{5PΈƪ⋒"wHaB>8CѮgs|SJs!xؠA+< WgIx7F`w`xIK()Æ 'ak̙3ĨX>R 6m`5%NiFyyҹѵDZ.V_x-$F$@$@`{0x/ Mq3J-W'D̽rIC@nxas#iz ۷o/@:!'-,w?a?vH',2ɓ's.ܷPzg18iGءҫ~w~bA    I[nR> r!{c€"x+R,}.ZE _t{Þ (F36! 4"0i6p]#pYx&r.DtG @p˓ $ .HƍomB_ I?0Yx4]K/d0yM LU*J%      KH#ldʕlX4i&L5Q6֢+R&6]:*s*B$@$@$@$@$@$@$@I$gٰaC8qmOF$ql ӦMG@E_e(5ӯ˗/˦˔_E?w>VF_|5Qd-ߓTHs;*V.Mܪ~WͅfB @p $<%KH ̙3Ν;{=z6l)SrRH!e*UᅳfGS]e%rE9䍝?{6%h!fgq'gRdiu)Pxϛ֫ Mus_EpOl(C$@$@$@$@%e nI)Sf͚~СCߏHg;p5C$2pB G:o65kTPKʩڨQ#6mZ>}N:@`ٶt2lTj"FɁ@ qy*tJΟ=2}Yɜ=u`;/JvCNb0tFbzHHHHH 6Ipo߾|rv]n…Z"`kٲԮ];X?Utcfd̙v ywaENǎeݺucƌ"EcDeA_- >*EJ'+N[(Rꩋ%;7o~U9|N mȁ}%G.]cs`D_]%T+HI`}vU{S̒Cș7"<5lHHHH Hr~*={ kǎsÇѺ4 w'OuʪU tov{.v 3zhmD?;Gwuۺv`cC?rqlUauym}GKW{˭3ѷ;k~+Wkpp7i4jΠdWO]v|eȱq۹zNXtܷ}Zץy[u]'pjC]%Lqmcq9q1ww~9z`niɋ|<ߵv>IVx2:}[)_FcEÃD_m\ACݗٟ9< 8m_}SUy$@$@$@$@$0$Oj RH!Yf s -"/_V/HDvڥbˏ?m۶IvW>+WN ķ< ,7:2e8"0v;~=im <=MZV6:sMKQ9wHLټj<}EY3l7߲f%yz;oCu>`E٢&<->c#q17m~ܼu޶/@M{釯}oXg]eusOl}M|Ϙ+SzŜHHHHK #[h!cǎk׺!o޼.]:sܡCɔ)>Hx ؿOo|"- D*+WSN!<4#iذ\pAM&7tSsl*"]i$ ;Uwߑ6JQ7I#wKtء "Nʈ"vR;BҨ{/]V,$wRhQ$ǘ$+[%fD#z⛴(2d~NfN+ׯK/Jue=[tŴml̝/V E83JD F~$7p|m~I*>f%}V1ƛMeDߞJh~?{>wHH!g |KᄄoܸqRl`qŋ_~rA}-_|2gε)1n  %T5WTIvi>}z[՗%Kw}WK.(pkp,D!$0c y'rH0`t-$MU[Y7qM8QrΝ4aF5QݢNk޻} |x k 3eΪ!ƆM;@qOvӧ%ɞ'x4.6#劣(o㜯l]5o1l/:vzҀ!mAɂrIi;sZV@PN]X0cpsgσ67>'E5x*ԗ*џj:/{*WEt je*6*>z0H5%Ӵjʡ;ԫ!pcHR wHHH /[nq Ҋq!Fo'#`r7n+V<6'U"@XBrai^z1g/͛uq {r裏¬ $-CJFtʔ)/ g;'Olcu?l-]>b7 e*UrdJ`u-Eh5;}Z`Xml(3"AhƧ#$ g'vC[  ` >\ڶm}DUT XץK.+׵{U,Z(zT&PHw3}_GݻWjժ%ΝMV^]l(1`C! \/ 2̙3nWv4߀^zE? 8=UƐǠ@vl\'of;dae1k`Yss{Z ilfW߰b@[3lr/[6y(,H%_aӂP{cY-Z #O<%HL  ̌1L(3n1H.L/-%tw{u ][  oUZ5*}_hQyȑ#2e?N?[B9Mq%6w`(^̝;WjԨ!%Z3FˏǏkG.)SF+j6["Iyz̰@~H)RĜi}9{K{ľwH$Jj^ NI(y cǞzn ?u>=`{~f-+.` S_8anjE9=xԕK9oP%*당} <qj/_AopoڴI wgT%$qO>o]'qR~ {nە;C;o/AH˗/[/ Tz)9p >hM:U2gCc$OEB3X2ܒ@ r8ow`~aɚ+H?rQoaɆg*j\v{nEj4h7*;P@IDATGGZ%tſ4i&B' Xh"˾{V%GlSp_X̄HE·Ol}F؉h%̖Kt})V,|2ד IH'89>p@AӐsi|,r _|E-`OO+P5s W^yE kӐW p`; RtcId^:gDwB}Vspx#]t+W[9xv@{G… 275p籂7Q`CNdX0}FnD$dPN;&A=ڵkkX|gwZ0-[jԿ~ӄd60ϋ10ׯ9tw?I;"@X:܁@X>ҵkWK3|pj;gDANffEYJ&F$ fS^fxiFϬ[Çc>hOpxתx $pIIpnτMv˾?F^!)nQ7tpcsoTزzUG[!+; שe拫SG&/y&n"ĜG-l&[{f,`1^罘4)\ܴANdG8,J,{O%T[̓%ﭤۯ9Ԯ_|YMv e6LyTC  dHQ8UapllRK !CʕWo.MQ7@v…릛n{N˚nޮ+ʫ*x߷o|Z9'O%v0A%w` e/ޖ$4JlΘ16~&;ɜ~`Syע ib  aQ3H$NߛEalj 0oO]ؑ|]|V;G'_^5sF9.=StK{;kz*|>Wݮ\ȺQ_D )RHG(Dw ^m{<߫[qg;.Pŭe@qDa^G7|>(j!A=i]}Nǿ<IH8J)Shp[BNuH| ^4F ciH#!׮]㱵@3g8xy嗵D& &MNu8+WŦܹs_ n([`ŷŷߚqX,X |.I*>FH8kժ>;§e[e|ۅl G uL@44h߶mڗ |]V4lP!Tmk;;zf  lᅬ1ۥ $8 Gmk^3)!ap'L9J=VmRHIx ӧOhђ͵p-Y&yhCJ`bpHX]2CY8+'|1]|.7%]};{vKUA߰||u^ju#bNi?_oX^kϋޗohjTII啭kotUoՄj,XvaK_zaS@$#cj'{)8L )s(\VV,߾MY o   piY>a@Ӳ+CΉln\;1ɘݦG/W3>_W@^Z·j, _`72gԔJ싔,mݶ 'w;۠dW#ϝ)xy=!L x+s$@$@Ƀĉ<R L:B;TO?Ҟ @?k֬too%F8tc5klEc뇷kg78L9DCz&Ă19gNIh{ C I=>υ9pA/:xࡇ˗'lyߐL3Q+f[E#N-ZOK+ڤ0%]ư ~GQ?-Zu>wH Pz]˦u_E{OLI8_+F[VߐJ_nER/( i--")mp_VtYS.\hrOA_wlsk25O(.ioB1q"P~TW$Ou:HYIBiزdʜU>}ظγy<ڢ4Й1 $opz{`u44\/Ϋ.[P@pԯ__.=!ԍp"(gZoff˖΅5 ,Xvt)c+uc;wԲ2ع3bTRfn1@EHxt3R>ex"TժUu6d8qm c:KΨ34!y+;gj]23 %={ъ Y?4NK6\51z8<&O*u|YPn=y䰖AWE Xh!Ľt6cG zK\^9Wڲsu[2yfݟ][7Ġ9TT;SMs2c1um(ڼTs#+lPV=_~S$̭ܚrܒ !}s$D:$4p#'t}ߦ=l)wܿt 6L't>MLD7GgCgL0D7CT2e^'d mN:i=vOY(stAw%+~$+'!sI2P,@8u8dLXYqL̛ߜJV[8 CJVX5)@/ EacƌH\Igٳ<2ظq:08o+A2)~Ak$RE.zi0TSn YfZ^رc2o<ֈFt1h/\Ў &"AN9GZ6qdO0Aʕ+ǎIcz8 G4r>B:GHXjNl "N:vO(a?}v-uݳgO?"ӡosӎN@[+v* #G'cySd٣·C0 '̜0, ;޽{˛oi1@VH&m~혌K./pQڇ2~olT^`Cq "0{'bg.U%H TlҒuRnI@֮&USJFV`1bD݀-7g1|m5o" "&=P~4 &コN纯gz! ޽{Cvl1LHxhh +66֭w眔$&qė9sfĉ!^l"Ns}u[u8:ݹd3Xꃙ`Zl}ʔ)tF…  lGvndXvPuGZTy #ƍ g|FNg;Ƃ(p82AYcSF076h@GB򈶮]`yΜ9id+WRَ2dȠD"x=$}oo0TFGcV:m jzޜ(hQ38K֫hbpӰJ`رcG=hw@NZ3t04c3m?[dt ftSː@X~h4Gq\T0#ܝ4DPzO(Z\k9 Mʕ+k&ٰr@'juvs/&_[^# M|H a 0=ac퐎Yn)g9S=# ϝ;?x =xDu#b\trW8Bg  {bŬWDQxu|}М_b 6h}pd$##ZrpB)0+.H^bM¬ĽdtBe F!i tۍS%9ۣ f\NJ]]~L}7y"%KW4S8J@x#woTxHHHdLJL+Zv&$K<#҃/{Cgug~\:"1)W|->-H F@DJʘc7#Gtpk|$-Dty+C=zSnMA駟֙M9,@h0㌦!C܇S\K,-g07n٧پM; N-d6.g>_$4$F52?}J2<@@VX"tb$+ޠ>}A-S6S[     "dԩSR՜` Ҋ44v p7czWK.P&!*f/0eLҥJ2 ^O]8َe%J~X/;B CӰbm۶ZfJ*KaI 'x6aIV HΟcATUgR[;$ýdcܙF-S1 @-!?"06Qn "|'NOaIƣ>*?c * `v飏>2kG+Vjq'2Aז-[tz$FҥRB/_HC2]`/gM%6m۸x;GDrI&y͎{M0A*QNƲNh; MHHHHH ,@e}ft?*;αc e|7A2TD//[L .l߰#FH^1vpzZ>27Ih{ "Ƒu,x0Q fP͛_"JݟlݐW=)RH BCe' CԩS۪={=Frޛn;wwg0& W:ZJ8 QL|oԐuԩ~V ֵcz}+_2C~'N8.͖n/rHHHHH " d˖ME?yÄ`\Y>Α#gyiժCH[nDHppBН:uR(GҦM+]v p݀N9x| Ǿs&YjIr9OhX$a _~Yׅc26cƌ>pCS|1Ɔ\pc МeXѳgODŽc?[޽8`̽R!?/?TEnMs%nϒ#d͕;*y? @=,6:lpΙ3G/0u =f8|Inu-9mOَZFOl98!,w1=uFY?CMqnvfhY5gڵ1i]~ݜܶo^k{HѣiӦXvNfL$wo߾tFNK϶?TsBAp6X @w "~w}~}z۶mZٔwDW_}%orGKXB3a-)ӦM8V' +I8 kݺٳGc‹/%av-Hԗ9Xfvـ,!۴mw:_`NY/uLD`駟Koٲ֩GT;C Ѳ<(DY8J,9`ǎgc5AB9|IT9ۿkZha9wp޼ժUs^rt9w;JjQ1_ٲJR&FY唷וu^S []jsW:u\*ۥz{ {qFe+-|{~lVƸ+s5)J2d~Ǹ?LȄ>| 3|nHڲeKlyHH > HFcp0D"rIx@y䈴'-ID}v VaH*:tPp=CbYPHjFgɒE+Wo@F'O<)iǏבa+$Ny/=ܯ$v ~ajbRLO._\J/@Hl@w|)ZhT# / xz;|=$Mի'*X;Ν+zE9;ZHC›u١9BE`ɶʕ+埱C$@p (P$@$@$@$ dp5j4ܕd{̚5K#K,_-bI21"X'gTűqƉJ3eʤ5םGy3e:$Mn ?ʕ+>1c۷;Coܿl2pAbpE{nݖ Vv  xʕ+gNE CǏGx9 ̟?VRn]=6+(j(e$@$@$@$@$@$@IG:$Mrr!$Vrn׮]sΒ;wn{lٲ:IUR17nkD)kHE4K/$nAL G5k0,ۣG- OիWhsqH* g< fR܋Cs%@̙СCn{solG%m۶޽ 0s< i_&11'%C`РAE0C*F$@ >}z9wjÆ RdPT:HHHHb'RH$eb IR>p CxժU͡f̘QG;OَR$X]އ3 Yd8aD׬YS|vgߝלz;#ڵ ̙S6mj:uƍ~<-EҢE 7vl.0DŽ8)ZjE_4;uj}CnI+ ~l' JƨnHpK$@$@$@$@$@M%#C!ՂŊp w8Wo)Xu]:j֟>C:ѶLr办XXX]eʔ?&bwbH\8 ` `H\/}K8F E=w NU   H :jIƒkDC+αaKɓz29`۶m{aav,l߾]/UZE>  :HHHH ` o (vtޢ=̙3ubyF$tR[]%l4C$@$@$@$@$@M$@>k٫W1wH 6N VH ^*UWHHHHHH ~ {D$$'NH&M˺ J`zZ$UT;$  HlBQ-      t. 7|#!ҽ};$b(BN;dBU @= vH xgh޼\.]YѢEeԨQW-[Ȟ={:A;BM SCM @=| H QFri]C4id6mxے+9sء+WRwBEٳuV[#- @T=*F7Ν;˚5k,D+Vs%oX.˗/+q2g,  v%     st N/?С4ks%Wp(BNJ*fVE$@$@$@$@$@$pw} ;vH6ms{;$\rEߒ-[6)QD , E~ab!     XtG[ǎ@&piذD dڴi2e 7~{ISi$J/^t~{(.     t^ Hm۶}v}&LyX U/_ X(B֭$y!sɒ%CX;"     tû> D`g{z!tZ܉ŋٳgRUƣBpVH"xHHHHHH ^Ľe0 $ok׮]ZpsC)'SF I&M|=$++ʨQd۶mҭ[X" @d=27%3gHFҥKzHl9yd%:t͛g+[ @֭C]%#     # 7]!MrO?-Ӆd=K,o?,;w%kժeC$@$@$@$@$@$@$@=Z,K$d'NW\9Æ*UΝ;zǑ $*:7#EIϞ=Փ^zs!t3n0$y/ |HšcǤiӦr| ~sA+VRݢ @<hH qO)S%}Ν+.eʔ1s$@$@$@$@$@$@$@ Gc˚I$УGYdeذaRti{SN_2$@$@$@$@$@$@$ г}˜Yd͛7֭[c@_l5o(C$@$@$@$@$@$@$Oto#H8{gyF\.n;#F$\9YX|oz7xT^=Q8]VM_xq݄l䯿VZI%0N_ַo_[2Q4      )e  @tt4lPΞ=t-hԩSG9!0{lۗx@nV{;&Lmݖ%JݫWO>ĭ?ypQپ}>u%{@7侙2ea0^3 "w_dxH ItQ֯_o3f.\sBENL9.]Hݥ@JSR%ywX9t4<"3f-ɲ &M&,Z( PC,L$@$@$@$@$@$Fn8x@$&N(G]xq;$*;v~a;!ÇOnAv*J &"EnΝ;{aQIHHHHH @{XH l۶Mڵkg(_[T{;$3gδ G@8D#y˖-r)Y,XP>hmȡC… qhcuc~=@ɓr}I\L-+ٳG"Ev 88u anIҥKg}n3*޷olڴIPB:76K|!믿?TPARH!]w]ρ4d^< dҤIٴ>OuLR6l ?VBdϞk[Γgݺuk.=Qߡ{'8@|rgx7w}gɴ- @P_0h$@$#ɥdc!Uu$n5j԰+$Сr\Tbc~;sN}S _]!R)y.Gyĥ>ԡ.% R9yw1]*Jߖ3UNQr/^vrVri [9o~rm+[nnR .v++Wl\{(mҤI.5 =lQ/pk:m֥63d/m_|\w4>.D?dyw\VҜ0ydJܾ}{}@*\r#GdKILR2ݻwۂ"o'μqk/9rą¼x.^h>;s2D_VP={͛WGJR0:*ʒ%kĈ͛7rԨQn{gkVXѭXqD4ibcuEln !jHHHH 0tƋIBM< 2J{8Ͱ>p#ТE JRq#|n݀sZ~Bb#6;}k͚5.HDlNog=;vt]tYmg]45ƽ*Ueqs͚5upBK>+m"J^]|9cp֩SGK]h0<1^:[j{N & 8\t}1-Zd#t]J^3;t2rBoRzR01}RI8'Ά f- $ :3!   O{66MF$@ AF]=<~~%KA@'*gO%#JVɭy $)5 yҥ^8lٲRZ5Q nu!yf\N*JrCԛ)Ghq}Iiiv<ˡmy:A3k5WwQ1d/rؚgdҥ&t"L%Uļhs3#HN.U:A鿊A*")]bհaCɜ9[JHz&A J.^3;Hk0dzCsCMy4hMVw8. @4=]”aO=( C+JA@BPr"r1݄G|0!Kɪ~ZTrϠ2z(]FE,57?}VɘE(eJF%}dH*-I@({3f;o}c}&;ul{vsGISk+S;x.M bŊ[     ~tG{@PnQvd9s젔F( w{);UZѺ*!4kLGd.B>DtSf7~q.N(MyA;L4p 9rD6mdG gXR$&:Çײ.E^he H`=Z;!_a(74Y^rz!}ٳ2k,-EMrq*UĐɀN?dt !ۼyWmۦccp֢6lw^ą:4}Zv@Ƹk.6}lːѾ}{;PeGnت5Lt'x;&>TrP=٢jMrhΨ`E~a2<晃ѝwu,e~X%mԮ][Mh;ƈ J+*9SGD VT JjփWW艘] [cP Z $*F$@ Nq)7Ro !SEs:ѷ .0ضkNOC*β*aWIuҞv)a 6*Y_;_zK9LOضq.4%;cϣN{}%R![C샒q)msJR5lʁRNme&l/R1j$q]MfVi(c2nݺ}8JIey-R1 H ҥϥL& ˥#NRMX(%rp)" єCb>DdըQC0@b `dq'|iMjժ*$/ݻww8,/x5H N9߂eʔ6u+UGI=nc#QF = ܈B60mϗ/ujDgΜ9JF)3fq מm#Y%Ƌu41n!⌢ӧD[N K=DMhI_xrȡc5F1$|nѢF{BV?<3gLe*?eIH)B(IhQʚJV[Y)J"$Ri%P ٗV"D"D-:tf̽3s;s}sM&Mc{*9G|nܸQBTR3;cmkhP "8!bodtdA* ɸ<7R-Bj7dFF7m^y4HG@IDATHHH 7>#<@֏Aڣpxu7H֣G=6~ ba/l{5 zpgp؈Hħ_$E2>O'+p!FYI>spf*Lm5kW%S)bXasَ c8K$@$@$@$@$ $+1\$؏޽ۍ.~-HCb:z9&rPZ>$@$@$@$@$@9@;ܡ i$3k{$o3f}nT-/XG'|RnMLukB:ukÆ ꄇ5D';T'K.t : N{!2* o#1W3>Oǎ+}I㐆7FXv;%q`]w6$WB6j?]6PLH/{i斦09 3'{$½J\!L"0eg\Y=(W<Ímȭyɸ!+j<ζ2㩾ҥK{Rv [N}2xj){ip$@$@$@$@$Hx)$={héHnhAݶm[I0?F#G. ^lYS=._bEt .0DuQڴiĆ*^z8ŋeĉ·b6۫(~7o6~WK׮]U*  !J̙*ɜAWg~Α;$$\mL {/%#F࠼픓I ̔|;LՔ)N$~N*n@E#     HBZ3`}c"ҌDuFf9fzvbFݙ?~zMKۛm PxF1ʬiK?m׮];[=`r`1c 똽9G~8nF27ӧ'8#et>p$@$@GHg}˖-K=1 d}pϾ bjD}Ό!׬YSUVZft}"={ԭ[W)"{SO=%AHHHHHHHH ͬ#g8IB̕+W]Ќ,L ˙motY Lt|ĭ2dH0? cHPY2PcdY5 S'=&$-M6͖1$@$@$@$@$@$@$@C H_=#6iN҄/ ˠ:4h #k;w7$`[nuBXN)e~ǶI`۶mҬY3ZdIARc9r*'0`X>Ed$       xHxC&OFWR% $(gu(۱c& /+F-ŋ:hE]S/Hjjt1!ZժUuPem;d:Gqs? )o/?~J"Wk$L. `4       L2XF$Ilۛ䨲rJiժro:`ֆ &;OVҥK6:f9zm0`@;pȀYđW˖sI$@$@$@$@$@J`޽mߞ]3h&ɕ+w{N'akk"~c4zvئM 2dqoHߩS44/=cM6;#jk:V hk$fyFhɀۙ3foGќ#o;g?O>115ddeP KL2in  ˖-Kys$@$@ݻyaE\F$ЬftbΪ ޸qnE-~9rD MR!POP~T @P{f 9\4Y@'mhȺMdB:HXBuG:v8tMRI'{"c,YGe˪ $y0O *nF@^&Ϙؒ1qk _|`9S ` ճgOp}HHH U*THvG<\pAL$ w߭2Ľi&9Ji 4yy OKz%M_pᡢ./Q 'kԨ1q&9cN% v0&tՐ7n81Q 8qB$@J`2h :tns$5!$@$@$@$@$@$@$@)B9ќ& ăI+۷wZ6WH  r\ŋKʕaX @ =N2H ~eϞ=AD4H$r2HJ#       @{VP1H 뮻4q1ȑ#5o9rDOΈ. d:ܳ2AN7ߔQF֭4i $ YfΝ;u8y:u$8        @{ dNXltf͚ү_?w+$Hr2Փ|%8       N~9=C7t:1uw~2{{@4{Qp4M: ۘdlg뮼X3s%:|p4$l"͛7#GXJ.-oq$]B{8R,'     $0uR ?_R4}tk$]WeϾvj*T\N=R^Z1m.:>#[Ǐ9eOwCxŀc=F6~SN9`@yѣG{arLa#AY44X0.7ߔO?C5H"~m塇qҴEI gyF4طo_2l>|$HǥK+L޸q<HHHHHHǛjw\FqF6}Rd޸~Rԓ+ջD\߬\/Xhyҿ3e2Է&;ep?@+eo͖onqMw?w&s?#rꩧ//Z;w\8K}Mc8yc{rK~7 %^Do߾]m&xʀzN*b Ob  $:ڳ_mq;q$\{84{Qu3ΐʕ+KBUWg7^;餓4[uDnq%_|O?ɒ%KXbRV;v5kh\rYg'O>dxIxrGsM%Ke]m>L~ںaesFzɓG (8g19C(TȊ+i&UTѺ!;{|E{~;>|Xnfu  &21>ȝt:uOq+qHHHHHH x ~S9p6r0/Xvk*Y7q!ٳ|W_#U)Ŋf uZ؏oyLM,L Le*rkb϶mJڵ5㭷: -ZhѢ/7nK{թ G9#rԭ[W.Bٴi~Æ RL=6aG _^fu$S-U\r%Q8.ǻwNj'u8g`5!,TVMʖ-+Gp[ ԄJpcڠ58sкuk73 BP7A[o s8 oLjsΒHH }]e\ @N pR& w*Vsף]/j/f.{Wޝ6W=yXjiSO.(CQ"j3]ٚV*<$?_hRzE]&w h`c航ڷ~޲P됙ċ-nǾcjD=db[Xv˪U^6 ټy7mljv}ҬY3Y;rVdРAn1"nn"@u쳆6?npxrJiذ& qm<؝JNF[Hp U.IHH e & 6vhS#o`UF;G,^.]*>!,^C6ǽvwKŊU]pr-;vTɓ'СCK/}skŋeĉnȢ`z8ἆ!}*ɂ@ytn?~>bY2D:t蠎q$E9G < pD< ~FlRĜhsګW/-{'?^Fe`no:@+PGW_}߿4iD9P4H&_l'k6$@$@$,^4  N3oڦ~3%\7>[9x谖oRqؕk6jrTl\njvwJ R~ΒrŅVZc/!t[$r26'pC"`ҤI8t޽@C>7)p.`xǹ?>̕9:u@gnԯ__&ݻWw}u;Jhx㍂% 5dOO1^v 󺓅nłdeӝe[ jUp+tb~AI3!yD%O˪-󖭒 KWweEϔU^R^}Uuox׬YS-[:կ8<0L>9˱SO+D~z:ù?ӭ) svomw :$f2<`Nz!6Io*{wV27ne8pvԨQ8@pS OXC6$@$@$@$@$@$@9?wL(g[!_7!]&NV?r䨼=ߟ'cȞdaSM&}HK3n$,wPvqǩ7A=ꫯ sΝ݈moٲE y'U2.OPeT k^Y[f^h[~ӛ۶mAk9 gs-^fjeVf6B?vKHs0xLQ̉$     W&la>-o׎#dg䱻ov}Y>HMTn5~4lk3ЅGHY+?2pm6noiI߬=^R?h+/&8-VXۆL 4yAd68;P{{O[o?#zO?2vۻQ(R:Qлwo-/X@N R{1.tڭA~t!ER'>r~ | $# d08HHHHHH z^ȳܹrI<6dZ:pyCŚWNe DovQpbپs|6삳u:ի2%eHx; $]tIC э6̵!a PYC%; HoC "ʡImD{~] 8kM22:uҢN8As=Wehp7o\5ޡb@Yk^&0a{24ܼ&;C'eh\,Mp=m4wowQpHHHHHR? X{Ѯ*m&Z'_Jhѐ#GjW V8mv Y8ac?#1!cctbH? p RmR)VZ m /ֈSO=u Cŋ K M2n8Sm?uϙ3gj3ΐMٹsL1B4Z0ܐ>M F4? K^Z5u/ZHf~-vکwkHNk QkqM8Q*k5ڼy>`M䊨ӧK|K?׎)pN↎=̝w}Wpi$Q! E#     H 쐎N O]_ݎv P87)񓡟Qf N?$Y)Vٱ@R$O߽MO:ʉ'3I_kujܹs $ghE ݺu8zvB7^aB:E4K.M}!RhwȔ נd^κMjpӧ%dgʞn w_@͚58Cȑ#[駟VIH@cǎ>k7%n`Ͳ^2d/A0؂{o#">C`G* 4H9PYF$@$@ICJ5N ?S[FU$==nIX CB/ NU+V٪Q yNj@sO v 1ZH{IH2d2*$!SU,d+*۠AA^#>k,ٶm[ӇdʕI'c+6HY:>@~ѢEnviw$f͚y%ccǎ2a믿PI&oJ"E=@ +oVdŊrWۻyfΜ-JޫW/0`{C m5VZ 䓂 =# . ؅Bm۶~ .$@$@$@$@೅qb-   0m4wqQpH {˗gPKqF}q}qގ;T K䙀 ^1b;۱ /7o.wy3rWʸq8I]F,cF?f9uݺu8pۧ'yuOj8x74_~ 5IHHH!tp @Xt똀dCgp @B@.d3+2d̟?_Pנ]fM)TXt"?Q s=W%?w\=zĠ޽{+q<3os D@˖A=V!  l%OH޽u nF$@dRdIٰaCᔮXkذL<9@;$=5:3={pCYfI5te[lQGW։v=t]C硇]^{Mnx1nXƬ'&9SISq(:3gD")c(v+O fҳիm>C55hC}/7x IHr|w43s8 Oɫ/<-/|v%|OIl<<4 @픓k #dMjX~     $($! hgqFm&P9, Fx:}>;"c1Hs@;O|̘1Zvjr4,8S4O?n+V*Uc9 E8V+U_RX1A*TȨ Ln $8ܭ5iĮrI$@QpyNP?EAzny AO> ޥHܴiS(ܳg&6lց$ "-1_uUd[6ڱ6h d?ca7QSjHk@?IQ5륗^1$@$@$@$g ?ΐhhƍz$<3DuI& BRǃڋE_|\ B F$SAo!۷o>bʸAH2c whqQpH xJc~e͐U9ssT}^x&M4q~+ڵD 9x֭ӄcgAM{X>S{ٲe%o޼j*9tVøƎ݇HءCw۶mt:7~<餓Yf!Xv$9w?Xpc0\ 0XCmVm.\(Ƒ5\^Ƀsl ItqC27֭[?qYvop N#   L4yԪټysY||RH&oJǎݶH5~xw;:uȗ_~V6`2$c0<^ ЬxʊqoY@?x/7 h"t2mbܕ+WWzۑk`Ë{oH&gsGZjףqGҜuH zgx8 '0uTL2?&W\uqxDW^iɝ;wvhꩧ:=zpen;5.q;}uZj\wuNv맷˘nh1O=XI1`瘃f;f92y1|d؏zׯwxD7l%ښD5\wɓqM{1&17¶{ y @ٳgς$n#9Fҿ7|L@?e.Zwvu=wFE= -Js2n£x$8=+TFW'*c1x]̳Fl֬Yj$DZ4 KH( 9i{ 8CvYgȑ#eb* D#%ڇkk;4NLyGfTXƌ!-[rF7D[gXdɒxv^,Yvz;p&N^ }G6 dn 愧2N< `  '… kb gma(:p̊+G 43xnaD*˪IsWmPZV1`ǿp`jN>Y#? ;w7lF8֌1I*fLLss饗:ƁH߀G7Qm=)X\!QD fiFsLԓn')cNNƇqQ_ پ{r~14OhX/i^Q[nme^{Nx%E$@3|_M4K1ajÊmIHH wAH\ w3j10YjEo+$RsxR/M4}۲eK1?p;"1{0A]G0aDWKʣ>pdžMįǷ1Dq`ϻ;w!&9SَYB?쳰mD̙3U  Ji~lI x8vU*2| 8P͛%tcf׆s C sH%d٧O1O^`C#Gnv}}3Iի|ϫ. *m۶վc}dX6-0.ax,$N&O4O>ILµf$@Ne5 e U?Wzj|>NzsJo_HHHHr \ٵ[n@tN8A@j%LiwjժIZn] >qһۭ^Ii׮ƻr;P޺3fpfhb/v#A5D#7jȹ{Uǖ{z]sdFn%cr.ܥqwyzt?t\ ZޢE -ҥ{Z编(P1{-EG5Jr?\LrB<} ua&$o g(Ax w[lLbM /\ģmK+7@IDAT]YV#HXKOv+믿&*;d d"}zdZx_hތzLWJ 7i$@$@$@$@$@$@$@$~Ygܠ۞7o^w;J:pRBvuQl뭷T9\#>#F侑vi6a&db2-n_bg| 4=3ivmX.Y}n 9 X:VJwaUV-{QvΕ%`d׮]:]2ׯ_ft^nsaJϞ=u'Qm5h#QFS$S9d/ƍp)뮻N_hWO:Uj˰6$~Z| {C7 a ^uբǘ cK]VaÆ̙3uP2.]o!Ctwu+WNǸxb3g0͝;λ +ohS?&#F{ ۸ikL2Ч1Q}RZ5h"۷^3hpmH(& Pfwk_| zٚ>o,ph 6mJ $AF9KK       'anl`%Jh`1åI#=5?hH;F1i-Z1N43mӦMc1ZH&1ksiӦ6su,NǴ9})iqΈjժ }ej׆sd_\S&< >}dؾ[n!?tۖ/_N:ny=r57sS۾OB-]M̀aÆ,GF$@$@$@$@$@$ s}PcŒf!޵kW7h@~Wʘ1c4o&u=ۤIEQD{ ( ͚5S}w.U EXb*^kYTLʕƘ1N~oU^ kÆ ʱ+WdD0HB"^I{:HdD4#=ePvm9G8V(K[>h#R/H8l 븾!uM5敡e… {1o.^n]oرr:\ @|2p8xj :õ[qFuCcTR}pNvB{^#/F:8m^dJ +pGⰵcjVvms I$hVf[v̞MbH h$@$@$@$@$@$@$]n{6g0x'O {IHAH#G;&֍1 $*ٳgk.]ȃ>串@v]ul@MI H^3uJI=1 @6mXg; gD#T!Sf#GL)sȮk#!VI'@6'   0p$@$@YOިQ94~T<" d <ˬ9* 5aY5f{<̪8YCW̚($@$@$@$ S IHrG|'Pp@ؽ{ o 2m0u$](w#͏?7LwM:tHH8xJ*I#ʕq&e60ŋ !^zv{޽rM}'˗/۷K bŊ:_[߻|wJʕsΉU< #]qo 'A;/|Hь9\W`<n KISi$@$@$+$֗q:CI _Njk߾&se9Fr)͘1 o۝tIN:u,ϟ>|عkΩ^z@1Nλロx 6vi"j-Z8s8n\n1 mUwٸqc4ѺOr̙㶱+~>.\'sرc[52V6 $x7뚜;wc;{Ӻuk]8f}iuV86 N:w 459N`.j~%HHH ܗ7:5aW$O%Khqg6l<i*yW~F/mmH:q;FW]Gq#=.C٫ٳg@ݻ>ܤ؇*Uڵkk[Ho̍4qwavbV6mG{\?sMQcn<9ƈJ#ϘYfT3f?m\Wb   H1pOtnxHHɓQ ]ٲem &d^|EMj3ϔ}jM7du֕5ja\rhnTիW뺟N0A[n> xn#,F]v4mf,[L۞p bnZѻx]qo`n^U7~Αa6SD[$ƓM1 /dxt{Ϙcm?V\'   ?\= @tr251k@ @ٕW^nTToq_~ cݾW 3o<_we6 1e4s1e5iYfboDϘcm/ZV޶\'   tgWJ$@$!JG (^n۷O>Bv,f$PT6f-:c /u?5OU~ >T6aF"&<7J*%FJFLV)VdB n?Nh%^0`l/R\s5R~}EM< Ϙcm#ZV޶\'   tgWJ$@$!O?T>!`GF؜H p#bn E:,-Hi 8Py[5`v~˗O;ҥKiӦKҥu={aÆ6"JW]u&yEȷzKw( IrdDpGbVHDj~XyaH o[?7^?~NNӤH.Cy79ֶ~+em: @:̏˗;wyS^=MetZl<3Ζ-[2ĉ#{BpDO^_ڥryvH؏=1x…4gȱڵk&<91Ȉ@>}vLf̘<ÎF\2R'NŊcqnfgԩ9 gΜ9ѴvlrF~&]4_>3_4X8 gwn"Vӧ;Fޙ9sctcpZC2Zj\r9 r6mdwkL|_yO[sθq$os3C[1avbV6mG{\?sM"9roM x/gnO\,~짭+wf%mu  H5LjgHйl21+ndRDkfcP|8p@gDhğ^C"%Jhov֣9nMi18& T)3261RdIey'f8Fb62Xb=n457O<rg{DnӏfvX6*cz: t&V^{51Oi^>_/fYiaҊFGT Me졌&6H2fZt7~̡]zv$23k"Tpأ ڛ(+{4Koնh1Rp4 y޽Ņ JC9g#1H91Qon/d D͸,9~I8Tk. y-b?& d"B::p]XBpTREP7=ϯ ntup&Tz%ԧ\};F\!T&縉W4hs9Wb1b6bM;_"YGb_IPuO[?UyDSK$@$@$@)E8/^GjXqz;x'QfNH1XCBҥKmqmgx,T!98ul{ eY1ЇqxcV; C6_xR$YcF"ς 9fQwhO+`0x|2TRR?x,ڶK$hCR<>1Xِ^hތyCj >=]G223`O>Hff"lS.Sp- A$H >S~駪EGH -^i+!pdXN$@$@K27pv ݻ8Dp3gN^ΞG>9ycb;/y\Ԏ1x똑x-BA5ss0hÆ Э >?pa^4Lc̙3U W5ϘMJz,]T}Q[nI8:40K뇳nC : b׭$ ,C~w}H һc'[Ho_͓% 9֬Y# fHi9&\n|?$r 7a FݮZjRƹzDSET=kLbSeltUmlk׮-X*[yƌn݌"cxc3PL[O laFJnpwyz>&ἶhB˺tg\0(OˌcZi{=ԭ[7 2^+?wN8P & UeEv޼yki$@YC}&cwf$< CdWYEΊ5IH sy\Ǚ(@+čp7駟.g(]$[|ݥcǎDnaVp<8WDI WQwK!G1cƈr9ū]2_9;8ǼFxJɸSN}':7oj֬)^˸"g͚eHgXp[W-wwCqQ8X>CS(H xzF$5-dx ["gŚ$@$@KoNȗW e0;@5#mAo}* 92b"qDI4 lDžS2o=y܊w u8q Gf[o:qoG4Ig]hXB_޳g@B fFC[[mJ@pfX,cΌqDg_;uk72v3`Z*U:F޽uŵc:-G䱑píDR'\[䬰h$@$@$@$@$X?B\Sm?m:ʆ { ymrB^Ij/?m܏y7tJ^mҤ:ڑx^ Hf\Gc:uaGHD#{ [4Cl~ZÆ muw&-[-C20)p#*k~pM Aَ% .# u~Op xU"_͛ Xv%Jhނ],>ѕW^LKpz]{#m}_$B֨nIpI$@$@$@$@{6rz5\O@'8o-[9*U>:mڴV3:Խ!߰}B uԼւݍ CM4]s\$=˻ϕk'j%#?-  (hqwwɒ%*=)4#hiÑgvlh>p1vNvw"-/hi |AA܄l;x>ub /܈q'0tP͑ ,4  {}7rM'ӌZrޢR@4YFgڕ iScȧxiMbZ4m3f{\ +V~':/c*UWVyny3Q]tQ;2IȗUApdǵ] d gc$E`ҹsguI}8O#>yCw>\A}d5~"o$b B|Xeo1.9L Y-x%ə9gE{Ÿ"Hr|ɇ1ƍ猉q$!q#G%V!6HU]Fv78dOH + Ss!ې'v֭Y(|>P<Տ'Q!y2eʤspl(}ݧ0 6fȍ9UH@=7?nL3)^$iݴ:˗|Ib]  Q޽{="f C#F`׮]:%iK9gH;Dek.sqj~<=7vϾ d&䘃I*{L$ ٹsvw'O$*ys1}WTmH[ƎH"'Te<5`oD/U⍿o+/%fݴu) @r2HZ $x$9/`=k>h|BsH1j<^>q<<9C4/)0D? Dsܠ:g<*6\*&P1*"Ĭ..0*u9q *f0"" @-\=3y}oŷ{{hUVY?'knC 7}t+8bǭ >|.'oXN3gmesa6 ϷOO%F)mD̍JIc믅!0ax_!L6c|^u_~P*ͥ+QȍT&NMY (\G" EG<ñpd+N4#aZ.̌3f;=={MLch )Guc;4rȐ!/yCs#J[@ĜtMC={2b?|/!*a+zh)и5tPRxơ˪6:*^ +>y?fCDN:-(NTu:sq~7no:|>xs)qz-o>f7Kߣ,jn{$77/ 9Z+y[tZ5FT*$wŒHGC;544_`߿#\7H-?e\dK/e|{lԨQK/sPlN، !` ّ#GΝ;$Ǜ͖grI{wrwxi7Y~)oFOfe;뮻2Ҕ;72 +E;7*1'Pnrzh̝#^wyiBlbrXRU% hui{%92|f5s\_覿5?wqGʌ7p{;b#~0;<1&l-|w3SN't|GN>dwZvj&ڒϊ7_y\ljbm߽IUL39Sotsa2Sq3gڳR:;D$WH%pW[mXO@jx+̹SN9%W_o2v~؎LŻ „#A}ŏj3~jY4 %ш@f4b|_~븑/;馛rP3x+|A[yx$>O)Oj< ã[n^,8#$@rFm.bF%$KƎ?w u|g.K.ڌ7;ㅠM#F!;oWw9i3 /e^UpW{ܸುHxr袋.5ɕ8Gz"82’`p17CŠO6"5+'{wukva]vg0/`|!ݰaƩX=zI-̈́%c@_;7?BPu=sB.K.!܊7MҌ>zOV>^wC_`Oujlgh p[m{9sgU(@T&" " MJ ?"/&K@K~yŗ.7 t|wC$"t_8oõ!kvn~t(jʩ7 qE]{FÇk9qq Z9yQ>K0u$6D7 p-5Zv5os#kAE^(rF9s2sgݤiD0IZkG"IC $/x_E72oFi8Q,byU{p(dCC$v5DqnVndt "PH?kϱȋ@S3o7zةᦨƇpx-vu]7/z4s駟ztP0k^_xfwe Oګ%6uÈɛ\c=cAL}_}3Ժ37¾;'üvd$z/L"ӌ+#Ljc;!&Mgg5JSY!m+1Jy/# ϲ.ڶm7,f 8Z[,"P㽞HCK9|W`OmKev~o>7Ym4>/+Uj&_H.Ê$/sV#KZI>j+?ڂ';{fY1׹K1{s8 g+u[oճ"2U5j>lhYyNG"P" 2K(0|L 6 y*~M(3czY Tٌ b}YdKR#hFrFXms2kDŽ4~e3_|\j18UBLe-9xoUqƒ0Vsꪫ\ZblT(K}OfcSnfwaCoo%i@&Fv{M>|#\ y9zl>n;;J?އ!\[U[o*]mPȘj qT'"7wгF~xbQ oפ*r3Xo/+~@c~_Wbn`ʙ!bdc)>}K[r^cU ث 3"|{韛*(GDn5+1t[VH9|&Wsz i(dɋvUT{^uND@Z#<7B{By|#j#EϞ=)%\w:7qs/`7kDXBnP4  AZBq/;7K}]l̍d_~{ɓ;0Ɔ׬6m$GĄ|qC`Wv|L|N/^qZHz]u\7`{/r^c?'VG-Dnz㹍X˸nޖ_~y3B~k2,cRFR-gnXS8o+0yj"J{TᇗSh,gps?*ýp[F{UWMz\m[('x W< tF x$7nEb M<1 ˄@C&2oMLh;W9SoZA&Lۈ1 AH6, ٠3ɡԹ,ץ[ܨ,az.1' *n }aǥZ` W9yլTayƼf{9 EH{H{ po>~/~ ,G(d:_?x`Ԋo#ؓk,[cǎ\qf$ֿD{QJY~gvd'm5.Ƿ2#?1sis1d<<%x9ݩSTyomIIǎm۶YqG?̳1ֵ zJkc @ǛG1wIb(%mrs-VJ=1fy9m-unS'yKܹQnKjr6,U)mԼ*n$mL)@CW+(}#Gtna0<@2Ó=L?>l}hx RVIAe %\3ϳdyg&^{CvՎ&K>O,GC~O`ر)XN;aL>nDs1'Fk?tP_T=ƼXguW<'D:mUi12m6&=㨓MdC=#bޮj Ӱyd庖^Drͻ\*Qj#P;"^zi#tzРAؾ[[o5\ך%,lpֽ{w9 RK-Vރ>,|\GFc=5\cY=/B *f0 le|Z_XÙa\pBpcvfj<ӏ=X?ψ\B|gsx;|[̋1D۹͡xg>p3Hr-K^ g~۟C7#>PJ{0: \|2^.gy&QMȍ6ڨv;@Ý~|g^y}ܽ^4Me8s駟=w&<fc({ӎΝ;U/dZRΕRtZ_wss{݌I3Y'p?$;ᒘ] 3fm'P>+$M2tu 07|c!tu 8JIx[  Bf`pΓou=@.N.4zsnOt .0dL.nAkFڌ;G>~D{Z[bQԍtIow>0m Su晗rSg|ơpG}.Q!.ZDF6Q 7XDp' @7ydOly'Z/bnc׎q裏ׄA\g3H|3ƍ=:.Ð6lPyi)SonIy6$%;Hw饗-uCfW_}cU \Jm&^wu^|]Enfa.uXNǵ60{7jǸ)/Ƹ)-ƏC,Φ>Ӏ{fl"̍xb/lV&x&Ӯ˯2xkX#V~\y9ҵmI QFN2 fFu<\5n$," " " " " "T;/vw}Od]Gy;t=6sov?"w7:<ѦO$vn%ݴ?w;vNeNukwN;8{uT/#2V68f%ahBq|9̿袋S:q|1摝ӳTqL8g\|gԍ'Vϑw||?Nۅ#\}MSK |"18." ╎ x#&ؐ#7tSK?#`rG9$/isXCM M Q6,׻=r83fI_΍BXH%sL8K.{}2fC`9} 8∬{&>b%ʍ7vuy晇Y f I@djsjEY{cc]yq#W{ݨkOt\>ޫu;qcpuw{f<;~sy.^qB٢ u":Kc  NFF 6B~S[9mLۉG߭[7٭m|.U9y1Ϊ h߾}E?k,7qD^lʃ1 )A&󩧞Zu9Fvf 7{݅2J`I=pݪ+,Z_N]~w}CaIm)vGS>e\7"L<иRf봃>+#W4}H/՝|\iR򹙑3l )Sr*]v..hbd59\L9Ŧ-VOKVй^!UPJTS^SåƊ@u <7nҗ t " " " " " " %1(gR [srmE,qzQ^ދ@E ͥn}i Yߝmzkt`?FȂ|m:ykM`:įu " " " " " " $f9ihݺ,,Ï?w^F9isTwW|7vn:Hp<( 26|X" " " " " " "Pi3>2.]y+i^#L7AzPH:puWD@]=ݰaÚ )" " " " " " @%;$9}w:oǍ#Ns2 =cnҤI%U&(@ߥHO\eeK{'^/>ln-KK%·i3d|%G7שR C I ].ܫk<bwn7|O sw?;fb(L6}Akv,oy wlq>i^PU RFImf7Z{Ř7<~uQύ/qz_tƾ19>N;ͯN}sVbjC@D@D@D@D@D@D@D@D@`kz=W3}CΑ3/=,QjWE$W`)" " " " " " " " "  ,TWAq#ϼޝ|шuxo$>F^n(lLSؖݐ#s?~In_ϋ.`{fe:]^c@8abE6v][keWt/>3> ߯; 7yM;y>г^w.g)_;B͌{MToZ&" X5K;>3 EY8٧/­:4idFoFwe:‹\s[m=k:$sS1):Wu"Uzu19'ߝo#_de?j-WM^% FNJ8묳QG{M7W^y46~?~?_&/>ݸqӶD}{%lvb=㧟~µ|m:αi%; !]9lu7lA?qSnʨvi xkέ>`?O ;찃}ݠAs󷬴]?J+ڴ-^@i>ߖZw}%؉%WV'5Fu2A1:ukG<)S%X­|j=?rv\۶x뭷رc].]G_G08 ҽSgϞ+6?M4'AT'^Zh"63\s7~7}8Ν;dܔmo6첋ߗ[nZ/-B@{`W" "/=1(OyPJ"HM?{<3 nkԁI \wu;!N> /t|MFx_p67"g(znmq￿lVrbx}ɓ'ݒK.({wߍ/qOCCꫯfӟ~ʌO*b֬Y3DЖ]vYm&iH࣏>rA!wi'ynE(=ܷ8@8ca=^*QNׯ۷o%["1M81@ތ55QV3E@D@D@D& Ŏ79Xb;oV==Y(^ڡN?>.z(sKڿo$)s<6ۓ5",7ۓi*o|~x.)Sׄ ܦno>Tz+o̘1++~{22$棫" "Ф>lkWȗ#GV|ゥ)6k cDj"89B5*;o6rp{Gwm^&4 /p!̚k+^{~W袋K/Խk> 5|;s|_|]r%>v7?w=.":^I7tOSu]cF" .蛁ѣIuQ?lfY#toF]]R(L)ԔGD@0{d (鹌/N,%#!l`F-֣ sa/=So%bc6j@'je{}mIlNGXA\d\Oż:s_~{ 9ɏ|V&*&o5͍d?ZD@D@Duwy};bx]wk.W^nb97bUW]t on¸֭_c5V[͝r)vًÇ:^z%7~r믿~=mASN.^ Blo$?^݅BJJꙀz}]D̜9/-Ay睽NjylǛ؉{ЫCul,i?!܌}#n9㓆(5h/=j7*{g$'d~8%$o9w[l\Wb'@@!eaG!@ E6I˻~QʉXjGb{FikbDldJy]KcD!Ƨ L8ӧ cr|vqb;9!FVZF/CL6x5GlsG~i㹔z2R]"$+= IZ-:5؞,s?Pz 3no9u{/,%cY)[Nj='YfyB$ 'b&&x;2iƦf|UٳguY`\'[Hwy'f[v}wz2gyw$F4w}wSᓥf$,7pCx7+%v"1ˉh*7v֬rdMRcc62F$a,x w^PaRK-Ľ4C禎%[noή3䭖a," " "Pψ?{o 9klGZ5>g^Dvg6QOU)_|qnߦ,L=!S_E ' 94?.(#]}>{(ʖXz+v2Fqj2: /w ꖎ/fahrƷ{naYD@D@D ?іK/_5ffWkBp5ʱѽ+Xُl%aKJK~Mt?Rڌ7(=aQ=0]Hd!@E6ai~agR/툷iq\zbc}1“h]wW\qƽģ,]S3Z9[Nd[bn$۠" " " K!/nӄG)9ɃrSwM7(r*6?^WJUy" "P< uܸqq:a2d/C|{ ZgYˍhSmrW_u}'7|f!qx+U/\s-ѣ>껊W; 7Nט1cѣ8ljFZ62{'0o?[0o9y-sҳT 4dmaU+MVI1u2f-o&U"" "Ј^,,E, tfhiy5ȈO>1YЛ=L' b{.#| 7,lHr97-6f2Mky/Ҽ]e˧e3*nFL.2~06 ef/KsgCq=_I(0ܤTD^nĝ ?dptfy/NTgCgFI;pQD@"%^fE~SczW^WNDf_|_C=9'N8Wnƴ\c[ ywMl'Fm1V V.uᙾfK\ʛkndtX/D@D@D@{b;>37x`eV:." uH@u8@}iӦ}:{LkԩS4apu!ܦ\13B2}رn_~q=zlVU.b7YW1yen$" " " >]1ܛ  >p (1g= mFZO%҇U-ao9y[SD@D@DճNh|YnE@DX=VCؘ>k,32jhnNE@D@D@*@0XV?n*$7[oNNj/XHvBO<(mڴ /\py:`0J(" " " " "@71{GքТ.d'0[$4TX(" G`„ O>7tqkQ Mՙ-䒾MUU]<[\޽sMU>=n֬YߙVڃvڹ 75\-ҭ6[;y{U*"P+z-㧟~x>{/2_.ϫC|;v{xϞ=ݪ|>e׏?7|^D_eUs̑5+a;7|=5x*/bos>~4O}|A{/h򱲠k׮aO6ͽEY8޽8o֗E=댹h۶mMa0&|ō *l%$Ϝsi/S[b|Æ08&MrXb Zk9ף/1p^Y9ٞs+v>gCE@D@D@D@D ;n7."P:LD@D "/ ]tiSyE`/(Sӧ‡4ѣQpקeit. <]ODjá.X_y^z0K|| 74D7Re&Z1糃Hl8/bXve?4~9rdB -fm_~5FBlC n̙3|Ƀh3نVZ)#y5yoHwرo(_n04lrHln>|xcmy"?<0'LMo'[b|!ڗ }  SO vg⸟q?WE$=mO^%2l0wG]Cĉt\jU)qa: ,^D@D@D@D@D@D@jb`" MK NM,;cog/wqq6YdMF!"s0"wcJz2O:$kה0g};Ҋ犍~{2*q‰NS>~ /0NrbĿK.XiC~ #}{fmxluYgG8mڄI^s #?40vE(=f>b|azBc 4E}-cP!.nԨQ%\cΆ?Ϸݵ^Ƕ'Kc1-;owuV>l# ! \aZb|i :{hWw{DDs=7;hFFJ/7rl3!C#c1wM(# Bp燢LD@D "!$3<ӈKeFHCT )C GBؐH0 DŽXn|yQ|úQ;qh|!e,ϑy7Dr!W2H0fGq}Ѧ^Dy6(YeőA"cavm1O?tx)>g}|8,P{mhظ]Q |Rz#0@IVu_s5hӢ8]$s4/!c. :Ta_/!;i拉n4q{- VP|(L/D@D@D@D@D@D@j)MS_2H:th#OmD1ڽG.ƃ>1-tƿ/(oT\'>KG',OZܘDO>ٟ#˜gݺuK-@ tLLS JoAteqgawJJ ":ux6rJ;ƿM_*[#Z'cH  x6 7ҥKdd vV&5o&>߿ԃo؎Pg 3|ڴi^ oZWgϞKbK=&_Qe$6_xXSlT{fm𝯜R#>˱yY9[q%Ɨ9~_@v?np$ 3_+iĺGlg/7pCJrd" " " " " " 1@ 7o & '\1W#2r!.0؆ǹ6 :fL'⊌si/Bӌ9Q6=oFo=5L3duȑ#X׿Q8 jdG\O!E lf;cuS O?FUyWfLXAs/י̗pp(a'|%wnZJi| -2իO9糯\D@D@D@D@D@D@5HD%+_P6eʔXE%&lĦ>ü7/u?8f> 1m朾=^g.f8F$F< i}0`﮻oOؔӧ{x/Hmb#Л馛Sywe_/-{wupċ} 79&NYC[fe.⮽Z7b:;찃oKc_B {hsTtȐ!> |A^nhZo3mnL<.e1bFOs/Fx 6̋ۼƌFv%/sg 6pC̘ 4fr法gVD}UE@D"!yC#>F K=H絃(w2"!-o7n7 Ѱ+2 vmD6D>[!vG D71PѮ|׋i;ܩ+I]UL´fJmcFǿH΍l\W̯Z?п:w+߇9昣f{]vwbq*}Dk~7UBx۶m3. ӧO8 %Xѣѵ D> k׼Fg̚kZ5@D@D@D@D@D@D@#`8i6mBN.]]]O͔$+ bMͺ9Ʒi7n7q 9眳VNb=|atL.V`57fܘx*.zuVUܨyixb$e7X,]K>g9kɖn/0PQR6U 51Nr E 9a[ZA2kl(V&1 5/gAʦйַrVfxŌ'\`_0:O 5YaeO]g[szs,/ʙ+Yr?wy_1RKEcYltVIUN0ϪG\|G|c#B~xS5wnؘ۵jx{5 " " " " " "P%0#& 6#os=$/^&j̟rUW]Շ fXa|_ -@(< 4DIXh+ Ckv}ɶ!m>f#Q}7fJ6\~9Gzk.2^_"I6hT`FӘ'V'[l1G?mVӕ< X7ӮyGLpcĉ^xcx.첞 }#F4pq=ƶis7lDž/9{"}s% W^9x*m(- O8RË1yG=~^{m/Jž 4UCYiX ʡ)y۽{vs;ӄX [B/WE6Q["?#}D&_Ҭ16~c"V&u!RP+iM=B7crV[0$|5"#`f_ 􏾦y/~/)Gifyf޷o߾l)sqaΆԹg̎2[ꘑ7i|&˰G67 ߣv͞Eo3v !>{"f|`1sm%~'؅`سgO/zaW^bޞi묳pGZ<EL Q!Pd<@C\D|#ºuN6 EIAny*,ls:6s6bY^Bǫz IC}W*"#B607Bq*Wb;8HLBU`6!1)"1*s9ӿ?SBjرcz,N4=܈+NyeMlg r-lH1ahbY##\‡;bf[y {R)}[8Yaf W9fJ+[1 gbG]KS3"/3>~5{kΗl;Xeap>ho5jID@D@D@D@D@DFqV5!V1F!kb"=53B(F&V5,[^d^>a4xQ_6PEҺ| tx ijDi*& Q-iv=M57~@&~dO3{1-&cfl(څ =vC<|VXyo ܌5^3m|a1K;gr=[[Jr,l͛J~s!|kxqs,7nd4Qyz7m ;ōVp!'iv'+T@(b[i#!f`y$Q @F Q(kezƻQd\׭mu-YW{r@FDBf摚LP5rʖ2ܤw]9m.uK>a:;.!4I2 7&[Baks9|$?nY;gDYas _öZ޳YrnZyie<َ{)s{1 V>[׬,`gPs#WXǹ޳63y632{nJ*9IpoNڪKD@D@D@D@D@D@RrNe@Bg8H nDX׼~b^xf#$Fu&r-Jb"[;p5/|e\6&YJ* Q`<1n;O(b<C{ a1ӷāOc+/b%bfU;\Ɨv>XۭZcfc!l7'O<:/ؑ>9+5fa[*ke3ϼa1g|5KkKysgeijGD@D@D@D@D@D@ Q٪I !ъbzGxDE"^t!DG,ؗ:@ ŽzL8KV-ryFlɱJKo7'LLK"j<> X ,ML|r[LKڞkm׭ڌHJgͰO&-x[_oii8d}ܥl>ٌ1b.Ekp潟lZr򥝃?{Z1J+){SV" " " " " " "PsaypZ\0!Xc Mcsutxo^w-Le6C mi'Y(lHUZ:;gL3k'a50 od>,Dž̍]1)7ƴ3x3z6iOûde9fgk[8lٜ1LgQY )7[ۀZ0H3Ipo)WD@D@D@D@D@D@J$jp4v-LiREóg*\D!FrRF 4AM %_~e@1CC=N: ##$ޥxylu\(0Ocƀ 127D? bb1cB#~څa:iymx%󇼌?<0bpjO8o/泂2w'Yh{h\JV.aE3ʑlI,gTrhG) 2s &5r1c<0.#D|BzCh#7>nZ1&[ ϊ^ 6@:ƌ9<X0NǞ1  /AD@ ޽{gq={b<e!;p՘̜CCtOZ떞 g 6N4f?]vY?y=>s&*LBDWkc10yM]5>عI>`mvf>GK+R>wg,}s%/g3O Vxs3gV;_0OV\qEdVؼMVm&FD liЄxeb6Y0FA<*ؘAt<Qq54F%t<ڥZʋay䆂n&0-*o Zr_\!VB L  Jچn"^G\`1F}0KqqDDÛpqxW5\1EbFZ;*7bIr`,DPd,V㪫=}yF bv>qMZ)3wJF0HMXD@D@*E@{Hf%@`bWM&DuM B+C4kU@^Cw)C1@Aq]6+Zh `u7JB/gw3?9KPfgP&x;̈Βsݕnw~N @.$[(\y!BG!@^N' @ @cT @ @ @H @ @F@>* @ @ P k: @ @ @`c"@ @ @H @ @F@>* @ @ P k: @ @ @`5* @ @ @`Q@ξ @ @ @)lIENDB`bpfcc-0.12.0/images/bcc_tracing_tools_2017.png000066400000000000000000010772401357404205000207640ustar00rootroot00000000000000PNG  IHDRYysRGB pHYsgRiTXtXML:com.adobe.xmp 5 1 2 O@IDATx,Ն놋[5{p%\/4|zkzzfgzdgg<]V:ujPb@ @ @nРAhlA @ @ C{ @ @ @ p/ @ @ ({X@ @ @(L{atD @ @ @Â=@ @ @@a( #" @ @ @ A @ @ @^!@ @ @=P` @ @ P @ @ @!½{ @ @ PFGD@ @ @@=,؃ @ @ &½0:"B @ @zpa @ @ @0 @ @ C{ @ @ @ p/ @ @ ({X@ @ @(L{atD @ @ @Â=@ @ @@a( #" @ @ @ A @ @ @^!@ @ @=P` @ @ P @ @ @!½{ @ @ PFGD@ @ @@=,؃ @ @ &½0:"B @ @zpa @ @ @0 @ @ C{ @ @ @ p/ @ @ ({X@ @ @(L{atD @ @ @Â=@ @ @@a( #" @ @ @ A @ @ @^!@ @ @=P` @ @ P @ @ @!½{ @ @ PFGD@ @ @@=,؃ @ @ &½0:"B @ @zpa @ @ @0 @ @ C{ @ @ @ p/ @ @ ({X@ @ @(L{atD @ @ @Â=@ @ @@a( #" @ @ @] @ >܍7xngl=n'MKx&>I7L3oƽN<qǍ;n &'jz1nI&q?4hPkL?uW^q~aL(ݫIM)/ @ ZB駟v.[}5\ӴUÄ^x{GݶnfypvСUs @ <* @]L@n+d:8Y";yp'|;W_~a7TSUH~[kVp

|7lذ{wo?|B[lO>@ @}E{_'_@ fwy{ܖ[nFe>cz7/o[kt_|E#@ @yPQ @>Sw衇{キ,W_}UrW^q;[p~;7ꨣg٭j SDQ)휠|I_?z;Sc<3>z_~q_[{g7#'-bcu^MiekgzFrN8[`38+|jʴC'taZ駟oZ꧶eN7KkZ^|#trHos1ns7>j*袋5&ᄇxhV{eFmfa7Xc9 +-v7{?\r~%G+U7[VOZ~,h:z^NN:_oNkJ  @P2y@'|w]wpV)y=SU ,#8"1Yz66HߟL2$ɠA/Ip! ~$f-l)ué-neZv6=#Cfg!m3Šf54ěC}Fnj5xe{rw'6SAi9眉)TS4'60ؠR2,$f R}8{muo2ِ!CSF'sNbVT~:c9}f sm7ߤa[Sk -7`_>I,H=DvsӔ<yʎ"j}APجfh$TOl`\:'6#+1|b3_~97!@@J!: @4[US% cJ,P'+*)Cڕ++)uK.OxM>h^ F%i.4|;CbzMZ,J1=Kd>ȕ? qЯ$L;w};^=~7ߛůYP';O?蠃|$fSxԪ@i?)4O4X(^N~ᇼ eǎ;8Yʗ ZՇk;W*Ç'+1Yl+n,=+set]`]vy犊鸢Uߪ=s40dnp>LylƖHYN& @腀WR; .r˗no"vz2l0@z\L7tά}ݞ{\~{0M7l9<1{kSN9Ż =f fH8S;=2l#raSM-?/6.2@S3IL>:(FTtpWWRi3SODfh]ao*>ywcAa.H{@ZA{+& @ se$.eK?'jSʢ\*V XlʝdMq )})++@j"Eܵf/.,&\r[yr-e[v63YK*% =][ G;XiZ8r ',KO;4'Ώ?W&JXIW^eKYR|E?-vh[}8ܭW/|?EfpW rz^+:/,2߁0){> ϓO> @M%0 @ Xmռz}k\u|T ?d9_~y:xhb7RBJIhP 9OJx[4'=X\ܼeZv63Ao}T!CTF"?rsw8[¸trЛhF鵈,m:[$G~RKA\IW^1/oe+\5\I)+I;p^}L2N}z\\:I4)Y\c5 YoffmtZ~QΎfHnƚ$4j]Ԑ[@ P jt8@h3[|PR:JlBI<8o.˺ZWYWrGhrӛұZA)+*E/bSp2oؾk!PEd1^5P;E]$n[-˛ZE}}rRK[Ek}`饗5wY^р3Oهױf+5b ,<#@Q3-)9$ŵ,sN>2\qQkDەҒwI'uZ$(]^rYnd-@ZA형V^ټ*n2ԩtu?${Hh^j ~*O.{9^{5?k)-B \pA&E48@MESM @ ~HϣʽY?--S3,>Kf7~z ;L;._2o-Nf=r!uq15`E%5E}ZB4@Rk&¹ٕK]}Zݚ}}vaPNʑ4b44XϤju!j[ͦzݱXb O?/(g3]d. @@Q(܋# @`ܘ\wuN)(hn8I!A>ACM۶2.E,JcY!wХ,O8RK[oRRW=>CKP&s5UZHL"'7_|QAV_O5ȧkI>;裝v}5 /ԓ4a!@@]Pׅ GO?~==Cnn`m[4 H# A&kfK+e}GX~Jz'/b/p \lZМx }p F QQW|+zBcBz!NݪC>n5SaIhhNʫ&1imUo @@@޹mC @M!AwsU_;Y4"^Ldx{2r)*+ghDy=Vk@ {+'tw#;C} nOSi/]r)4l9R:$.y;p_; BpR$OYWbV$f P_E+uPtZ~ȧѭm%O<ѯY w  VP%M@@o-TZlS#<һQfr=p饗z+k6iT9W/wcr; wyA؈MtAn &pAcL[N u 3̠C}&Rҽ볂 Ќd@,u&gI&Eri3¢c {H|g}va{#vqGjK3Z6,[V~C 0pxmN!@`Ђq">|+Weg뫮yK2ĝuYާ, %!rgZD2hYdwW;)\H7餓I2me\-H w}>Ӆ^H OIp^8&WRwNT>nrW]uꮷ^!K}~ML\-^@ l(ܛM @F JobȊvvrSL1;rnr?kr4rwWd%22,yHc}v->ZD4PK/UVY,zҖ; )dJ+ij]FhA~bVQM6}^+#8i bWlCICm׿ -^x[r%b-VF/hS޺{Nz,,"fD@$٩4  $ -wrUmYlwq 7|/pq ʶiܱnᆲEH/XRE,eY=NW& ӟZmikq{mv7׼oI )-ܲZO`gh`u}ꩧV]XG=emfX)pe5G_&p]D֪p Y;D 0$,t=ISm5s_5ʦqwCr#@ f/f׆ @(# .{衇?ϲyd*[Jۺ_~+bJqY(U#prgt>s9p 걠?C"R9眹ejEr#[lEn::7`oAO`Yg3gyCuK/_i!VS*QV .w:p JXc nAlI}~\?@oQSyw\p[s5juy@6gPTqzV@ l(ܛM @H@RtG~nͯ{4L6h#/=O.&IctMNlEfشn$BE\iUnCaJenuyʲ^>Z+BJZ4sKJv-AWՅc tUj@h-Vͺ֜zk'EnoWka 7iASM͗c=Tg)Vc eY LcY?VRgG|c=ۚpqŢ {+ߥ<OV ,].X&xYfpr]#%՟\vH,?,ʧ7.L;,b{)~md-uQBRm۪90.~饗Eo'h"][dVi6m]?}C^eK3pn_˺ >!>b)a$wqG?p-o=lV|͝f.Z}%pm>?c:`ˍ7^x?e*sM)5Ni駟WYk'ψ+/Q@ @ {{6>u[@ӧiLw]wLyYvf]㛢jpiL)>q71i:ٴͿsb dMճcJz>_S5 [-ܒ2=QԎOL |'|pb4Zv"2#2#)ېꫯ^SڕOLYMLJ_|{R}yiMo{wߥ6Y6lXz.hӫtm+Lu~~=_O?2:y7R&sT|3O_y~YHHJJ6jM~=W^9-ǪSyn360JlP1> @h&o RA @8L)-㦞zjo- ZQ2˸-Rnvr;ӂ> >=3ꫯgo JC @5oݣ>~m;O?I>f[05E @ '½뛘 B @ @@;xoڑy@ @ @ noa@ @ @m!½- @ @ noa@ @ @m!½- @ @ noa@ @ @m!½- @ @ noa@ @ @m!0oqÇoKfd@ @ @6|%Imu> @ @ 0HɩLю @ @w ]T  @ @G{X @ @ t1]ܸT  @ @G{X @ @ t1]ܸT  @ @G{X @ @ t1]ܸT  @ @G{X @ @ t1]ܸT  @ @G{X @ @ t1]ܸT  @ @G{X @ @ t1]ܸT  @ @G{X @ @ t1]ܸT  @ @G{X @ @ t1]ܸT  @ @G{X @ @ t1]ܸT  @ @G{X @ @ t1]ܸT  @ @G{X @ @ t1]ܸT  @ @G{X @ @ t1]ܸT  @ @G{X @ @ t1]ܸT  @ @G{X @ @ t1]ܸT  @ @G{X @ @ t1]ܸT  @ @G{X @ @ t1]ܸT  @ @G{X @ @ t1]ܸT  @ @G{X @ @ t1]ܸT  @ @G{X @ @ t1]ܸT  M~{뮻Jsy '۝a_qnw=]n%Hz7iʹI  @H`p"A ?;Sw 2|+|'^Y|G̪K}]?s=i1~ߥȜ͒ @ ip< xwc^ߎ ' :M4DȲK}Ve|oyQ2%@ 1, r}w3UVY/i;t7poݱk|w6l3nN|{ng}w}M- @ %&]B @ |w|o"|WsL7(^znfsO?a{wR?V30Cx|ݠA~i駟g:nF*#o;YJ2O5Tv#8bYf[l!6ʖ~JC[Y/\7)tw(_O%~_qnn/RL0AScᦘb }V&{n?ɤNs\VHZkD}:l7vb iV;Wn:JڀY,@RbB -K[Is{vm>"B;#QKݳfe;Sw߅mJ) n$^xaYpۮc;CZS(<0uQoN;}9ΟAࢋ.J^xdf*ӳ螣Ҵ:Ex`Q0m'wVZ)V? ʐ]o*A @_0){tKR^=RHsl<ʉ'Xʦ%YئQ04K,MӸaǬ5aÆ(^QhrN3z62tgUw'źYѡZm7U}{yW#oOQ)o)U:*}]_ ew'[nez^}gI0'c"hfF7/lYu꣭J4qWRk@!:+l}dMzŐfOmhbŤ=* Y߷͕WeVI;9ϱ& e5\vu J~4J@3tߗY{$EN=wTfE-ja }w:m5㏯ FR]jMs:YmendB F&75 ,h馛*F2/Lato z! /R)(>i׋^"iph/2y׼HRe_9{ggM?/K#)uB.80^1+++.rn$n#mTV8 8j$}TDY0+hV@ŗrh B픿/iYzW;4&e^oVDSx>\{A"o,1Tz 鴛f3vՌR]=]dMhvHVd_I![4V+Ot<K,;CabݜȱA@>F,O/\6UAK Aג\)|\K 'Ws9uOmQweЕ SL2$fuh|][A3D׋%{ a@*-f6gN:$ôhbV0oto{;Zv"<`in|L;- :W_}O\Mۂ܌CoFgkqvXY8S9,;nV^\ܟg"r)b ^^K3e~ﰣb3ݚo=O_ߝviΔ|:f̵3nWt 8m6¹qi#['&@IDAT)}t_5rx8wK-T·ZUg76/j=KV͠mf3żK|YvuWgu~AbV_}uנ-zt EtCY=!H|_{Oه_9 [3r-l Ù?uOaw!xV/s炘q'ZV5/w/9}-Em%b3we~A[-p:%lYU~!NZ[ [`X 5 e½G{3v3⼊~A5/0dJ7h o#k(Z ] k_b3r:'f`YQgeŏokw%cZW>B3IN߉fMo>TKxީEz 뺷AZ%J"1.٭N-ccVf:]s5fZP½Lie(bLѳ,)@M$zgNA 뽰R륑}];mmsKqh3|\ߔF3XmUVY s}c꼾6%Th5!J &H\_kڎ'sD?/k񚗩,8?b)Yj!Lskeh|s)}8{1IӺ6+ɝwޙZȪΔo]˟,@c rG'ZTʊ7O侦Ң'76 J|Hܢmw'v+rgTT4CQYR!cf.w9Mv+K<_{|-2³kRk8~*vsӴn*p{SO=5=w͇pZ8~{W[m5lf ra% պӬs6[n,p$k{]bY6pVM.Y5+BZDPie.Aס:moܰxaұX7p^kpW/+ŭge]ٮl=SEs'UE2P`!]m5Xkd}]ɔ%eg/%P]NW[Qn1K8㙓]i6O]5qճg+)-yRZ§c=vYUl$\?4BԽT3 FBgw| 76g]mK53w7ʸϜ #%ٞ3㺟]‡o?= Y"n\ʴl%k RVFB.;ZՒ,M^vYv s9gbT6֋?c %۽O*I3QEk).r*xg?~U6¹EۨvKodbTSK2rE"GwVpW!box"*Yڵ%ԫӅx[I%{ɶnM4D)np>,}ê'7]=[ KfqƲRrYA4UjX(~.UUԣkd17Zߢ嬇ֻk},3 f ,G⁥&ZBUq9{ۗܤՀVoyM1q jqyvWejn, $mTZjyvsfUW-ɪx+Suoyd86oavojAft}Pl?h[HڷD\ >C2] uvo\V#">b,b΁zN9SNsSzQunajT`$K]o\L#z` 0RXXQg 7/Xu^%ʀ2WTB[.k~ s[C/NH{E^L(ҠܒK.(^c*!Ū^N!("B/ʪpeG//{> Qbd#6¹QIx.{+o}J%ńZ Br;Y)>xկeg 85";Od [ȓf曗~+麖P_ׇz@)f;K6>|W:ghP1(vD{BD[bQ#Ii%WrȲTzfö@VU :uȥS(w"5'izg0Y5(lzh rGu~H83m碩\L.H +9'\^z.!]c׷z׳0\*OYW20U5y2-%wƕ0(<(܃@wP atVUXG- ~\m.\3DuYH{^V+܋L9SzIgo"OQtJ5Y/АkZp/:,47-:}PJغO|*]YJ^nU_}/r6Ve_z_Gf5Gmvt?䭭G59r#$LWY1)^p44G{}y} >_li،|U"OR$n3sz&DPBv [)5XNj w]aPGo]cyRhVGd^f曗~"VTI>MݕhڷOJtc;<ԾS׽oWL>"Ay]&ʸ뮻 7l5SH3})N w Ϟا^9O~A7l5 c!~vj 0Y Fe/mF}p+k ؕPv+â:[(g^OArptVgʬűmZ,E}\$}"Fv)u_[s5v 핷 mQFҭv,5Qk\H_w3޼B؍ ꕠ`h,p>Ofz-^-{qp\qv3e3>g74) ꫯه<;)xfa-{S-bqgVGMØkm)_S;SH9S9 r VV;*$5YMl V9Tt6]ǍufpΔli4#ߐNQ}F% :MuSr8|qJJ{lRȔu ,/gS0qlgά`rT:xG8"̷ c Jzr3%g ,CtlzZٙ3V,WK~>ĝ) >O}:{?*Im#T?22gܞ9S*pC-)k-T2?U_џJ}N5=s$zߓ33Esӌ*C JljZzn6SQ"A>k6OAl`$:Xk;=%zO bfلNK{S60|O'7-jVܯB2V:gZ7Ώ7x;wzCJ:w'}#;^~܈ܜF95›UwZMzE, fy=)M™1Znhg^[XSW^>gu~Hbj͜m'Pk{ hPF} =(_Vb{Jx[)%fp]t3g#eQ̒^P+&$}xi aB@JkHAJ駟/΍틺"O@cDQRm3|m*i*裯T8WV(N7C䟽`wiH Y#\{ H)*c ]վ>X;O&̊, Ji(63Uo9zz%~>m-jVs;7m{Y*h+;ȁF[s#eֵ.@E- ]z'WK̇3raPf50 >Dg@[0WUl^%U񆔹Yip ͤ C~:&Xպz,ˊ 8l&?lӍ^ q}4'f-^Dq 0䓲P}9ZmTkg}M2 dܓ'2wucE9ys9bN;=QeGq$)lWJ==\, {DEz$a0@:`SeU|CT*+#½1~1[$z KݛYQ'7|e/o_itߦٝc^(ܳ] }@jF]~W aٶmεgɓO>-b^ p #@kKU^SQ AzKQ(\, ]CZ3%T{GJV}Tl/}`bhjsWM jnFg.|3)f)|4F4H.Yir7Vp*駟y-Yu.Je\,$*]RqRQk Y 7)܃hPVrA\]dԆ]wrֲp[Ϯ`fQhFOR٢>G}_Aɯ_5 \P䄼;aDz׵c,mϐTB8+r 鉍DyTzRi0@jthַ,ͥ3wWiP|)5@~%,m= 'Ef` 2ħ6{OWm{PF~T>)umBXj4}Y׎Q{M5d<mhA4;zBu;_kFRr{8kC*G1)oJ4̀B "6JS卿PuvA<5=e|f) qٖ {Y'i(ҀKiI{~"N09[738ě Z:++P~Y|Q+rΊQm4=]J Jpk+ |YNjs֦zEW=+."_ZC P/x}0\^MM&@5hiOZѾ/@D0FJM!Vr\I¢> )JׂeS-*G0E`6ۚ;@Y1׆%kqXZ(oև bid18"EFXE]V?>m'S鐗MkpdŔE^[aOBkNg^pL;Ut?硼b{CqF}Uh5$f%fm[viZ?U[87_c|~|^Ȧub-GԚOFfw}k ">_Ϝ(*e)љwm (zk_ `g}Y aATsef5 >ܵX4\WTJ@PfoZJE%(u[BǴK\JoZ0Oux8 ] pz@zG PYdTȑ$K&PY$f锘UU9F@JRfbOnGiP`KfVIwb&RuxMtR_V'}5W5KM_]-qC[ uod18"EFYRH(X2Y?}Xde}{Ha\ ,X[>>(%ذaq٥X"ᖭD8_z^dE;/1eMwά`"Y˦FJ2fOBfw,D a}ek!HOViрG߻y)-6oWגN4`Z\ulfHBV;l]]לiI. O˘݉soϜl\~нOmQb3zNgҶ%[pWWym{pjVK}[Y gZӭNϘgvH ʅNpaS&(Fo>s*pFZu6gz_t_ѳPn{˦O.WR .9m^z%JI/'r7~lڀr&Qr|%<݇TZXz.ݯP). ?J3uZ5hh] zi|v'馛Ҹ)2$Q47/:}0pzlaꨭ@ P󻞾_{_UckFK:[n;.tTY:K4Am`>8dOߍu*Ҽ27}3K iZ-m {bh},nK6vb?y1euFyyFeɭpk`nu^>-ňR%шM9s174JpYֲž Q}YhѪZDɦl$Z\_}j=ӯdxѐJip  tӌB-?"Ba`z ;ITy`DVx :Pe16]?YSj_-)XlT* &Z:l @YZ\Y%u:, $_~_([VToغ~~Ar=m ?H%jm3h7 zD.-čt>\t~QrVd[(fc2Z~- I @(@`vfeV1N;TbO_'bfZ}mCRLz(I^rrw7p F9ƨH).E&5#gٺ 7H $XcXcxr~7RFmgYwE󊐺!)52#7{!:#] rQ"4N@~zV- 0 V t@ o5"ZVflHշ 1M̀RK;ƺS%ק3'ο5 /Jf8gY?#iuy2 D lalwG{-])h8,kY'γ.XwW{R~B@޻QemZvF7i @6"ȲK$Ec5@I&aÆtza9X`: ɉ %[3}7>1ûi J@'>s(~ Tt3UZ \Nf}cg9昝T<:{2r hew)7,O4UJSq@ 4_lʆ=ꨣPD @]H2]بT"9' @!k;ygBnUX]uUFx@ 0``>`v~oʧqJY +?OϻNJl^u1U:.NkFѾ#. @.eI  nuu첋mjxʷB ' 'bOvRO,~7ܐ!CmS~/ 5xU̳ڨbAk81ڷ @ @kƽ .hk*߶V TNw}7-{ww_|ᮼJs=UimT( NC @3 @@X7lU_ض+_86h#rypkg]wջ[eU/ANw 7o{nJ睯*UP˵ݗmھ0#. @"թ3 5BVfM5Tv#8bn=_}%ᴨo5Ew}]G;^q8$gi[MWi[o4heO?u38uYH#T=>p\s巽FØc}KNj3zajɺ߆r?brQ"6<M6gQuI\yGyĽ[n)tCuc=v7OK u-JdeM;fX7Zfu"OE]/F͍>i?_]vOѽ>[kdn?$qO<{W4L{zx /6Ez_cOl}K @.bwgD3O[=_<@ꫯ1}# G! }x  <2,3oKL\ve%2%|2|ϦadHLW_WB %.KÔiY&fZG?Wi+ЗU<_}b%e7\(\Q}[oxbk$ˍ駟H*)ۦn,%V<+OK|z@I{'6 }vuI>ò*GiXySW}>S&q $kV5ʬ>$f-]t2o6%}{qI8]w] ;]6H袋(aoK/MiH$`?b) /0<;P6]c?;Sbeݾe@̥QbYL3V\q^tB}'ܴܗ\rI'ʠ{/lʢ / m裏t@o(Vx}UNf,͚7n271 m]qW]uՒ8hE+%LkyUo^++Y&f)[5 }|qӠ@E#=gVɳ>["Vfǿ!4s衇o[S!Fl<ʨ yuhҁ=أ,~7kB~[=t@-'Kq~'QN8nV%qj8Ok#뗴-b_5]mm>,Qg-wC4C(!ْ͂Hw_w@ /iDDi֗{Gv) 4 3%E@'nVY%oL^䡇JsĦknaYaLrg'?#V}sNbrnl #psё~)ҲHEo&r[8so}I5̯:Vn-dMtafEGI /?Gyd2餓avˎ,o2~ͮ-g3F%Ye5\xh8H3 ivo+BҔme\%X"OvZbҲiSOM^U!~|?-ܒoQHଳJ)9ӓÇ'r(93)ss%Ie8!CUd4okWgf)A^#oc%z.)y f(67Roȩ{?%Y=щ Ue{ rݡpǠ,r ˎ;tMP 0PX.A O[)]Z)ʷfTs6v.ZzY5ɦ?|:=/*&IQ+\.`*g+ )%A_|ѻ8 /{o$H}5rIy1|v*ۡ>ϓSN9% #wAk~4Uf)"**[nebO̷}YUXtIX+%f+"lh_)jci$ߐ,C eli̶AV:Y|Ӳk'OdZhI\% լ(m ¨LwdRg_}z^c?Lh H.\,x{ $5;wwwA@\kz+tV=nѯ{ϩSUzZ(>L?L9[-Uی[:x^fyxػ.VU)#1ѯ'xX4Ep?ûdGibPI_ߢ6+%.`U2^ieKV}&dc[|Oo}S {=ҝ2[0/-]_+^^spbQM;}Mb'_1r/1>@ʻI[VۂbC9^,7&sR6hkݰBRiUi*ܛm{cvO6,#B=+jjp@]Ghs:_ni^X@I%ͪ781bDt!Dؓ009rdF6& VlS<ӗU,VG}O'/鄺nlڠRK-t/ҬIꫯ^V+CU>a۝IIwQ=;@91Ŕ,t "h*8ge.8V (dRc/30Cs[TnYTOQsivQiՇ1[wo7X=]Om9GpzWU[&Uk:Zf#ĹLga̹Nls3hi3wUNpx㍕6pGaeš=P4VWoYQޝ,첊 7*ySs]çm5x >SG1sGlTqUVNk VV 69ꨣ/p`;K;6NH†;V(caӶݞz[y/%՗JJ`ϖ?zP_).=yY_ZpN)ܫ]g߶va {E%|yNw_mF(dgb<:PR^zzߨ½l: 0<#3<'ax w ]wݘb`!ges@"o}R rgkIҖt bA.EЧ\ag_ΕUPr['e\2oUW6 i,Vi;#WpV=Z˶. pLGZٶŊ~8oiH_O`fke .YKZvN7t٘cYCh ,)l0 FB"ϼ;{Ү*-pm־1h.Ҙ&F7US?W_}5tؓ"fT;7Y93e Z&UQ7[*XY[QouYG1ۧzʒnyw|aMPH ?cQ4cȂi)+ oπ;W/9 ֢F35X~<^Vg)nqb+к췒7SgqˤXt3)ožo6ҟJW\17R[uʖck?۞u* /?>XNe.gko[F#B<6aK-ROZ^hE=aR^+#6WisZ|VAnpoIX$ ^||~t"fbeʱ@t{ksg* Lg-{[V7p\3ؚb)lVUP25˖V[m {jG3FZUpoOx׫ Vio V>hI=_9ڗrR_4f&(cSV[m 9%/ARkC'ɗJYB 0NsL9xa%΂1x2+w? L?EYrtA?&,bnUI{: @ElU ]^Hv of1@Aau:@XZ`RÍ(k`_$UTɛWS>.>{4,JdpoqUEM~b=I3*s+*mUGU]IpIi3FbeǬ"49#j*A, \h-<)$VOe2]\v*(S<+'czjQP#(( M[RTCo1/e wN҅*-6XLd1%2) imw`>ٳ4lYd"+5@۟UmFNyW=E}e5LUwzC]-}wy5XںtE"aLbSXJ-YB s?E*ܛ̱R;3Sp￿1tH8op{C˭#?W*o?oѭ-T*ܫz72ohF΂yâ9GqIY{FisT>|$:J3ua%Lq2 £!pa%Ƕ# c ˽ а.AكP Y b}$i+ AZ֎*-sl VeoXU%MJ\B A,p<Bˢ˶1O\:$$ nA,~\W L}r2 lU޿ -m"j9_7_R4TJ&kA&@Z7+# EA,Sn<‡~D!z"D (]MA&A8Z\b#-2%.YOB$[Tz2o񊒶Hk0 s=*S:^F,ByoZ*E;Hݖj@N[l1-7fLŹmoEuiU8'FkT>R5xNDYmMmX.on\LV|l}}K_+kقak:i [pE;y!!f[qQdwL۹ 5[SVN P RFj4ojN,fT_J[lJ+I+򻙶+>,pmzֲ/5TG}d;txcxa5$lb4YrB]7 ,NSq, րj FGÇa+N=ѧXb+ UpcǭAym]V\Na|^O9[#/{(ed!MC( Om;AB >ǓYf+ 6OoYxyl_ H4o(<7hTeTDwT^kK!XQxFyn#bP" et@:FXAÚa Y" x kW_=w`o{ 8>eX vS^jqXi^YC,K/ 2hRo,w%dafeIjVVmz^8TnNVX׈ M#IMlVXZ9eXWF-Sq 7ЬxyT.VI^L^}m%A;m ofՍ8+9Vo1=gw?,@vO@{OIۆ, ӶVJb=hQblcF7y<Ty e*&WJ9xLm,XX^Uwb V{!qD6x69*"b\$XL&{U!x.݁BL$uPĀÚ-^[jњkew /PXiFv<4~<G)S}⺒Ȩ:*Xπ ܑ,%*% z TN<%Ul()P=+b PXGSp1<{&|XX@qXg+ϷC4&#x/@j5}LBZ6cd pn('(1 gAbUj[6)[h@]Z?@lvt 4,A4S%:FMf/$<7`IRN 5aᛶH|7-_UތLVO~bx< |Yޑ(tUYlǠQUb:X2}Lz=o܄N B궂Kd#n9FOkGJos<`ɀO!" #F6x'?`=Iϕ SEJLM{r*DզMK5!9ǟx6unse2Pظ8b :۩ږai:F_6ھtU;ڞxOUE1Pѳ\tO&]b2ϲgFR>;/֧Lc>hmi[#q?孷EXvp!*VPzi(n~/^jo(E.0ꭞ.96wZ%~x&V{sX} GyjD8.yKPZI\LԚB=/'˶6[^_~iCv QiVkF)E]bAagye`#%sY,eLb j:QݏXg4n|7yS~S5(HQ4[QP,Y".YT ^2ã>滝zL#~\aU#+evO7QhQ96nY[ߢ~t6W++{T+ ~1cT$n>{Ld(I)*H-M-yq"{|.,K@=@|FPOg5.bgUe,==J?h>zbٻ2]|ηVfz]F;XoɛT.Vzo;&MzSxԔi(ᖧ[mmT΢1qR.F84O! Q"7c$u-_M~LE.MKTǭz06A>Bz>裺98p.8sJI^s-wP`ƅp1]>;3UJKYzH6W=nG7t W\\p=ƅ_T\pE nYp"pn-dsOĪAWD"p,JA9{"toboKӰO^D = p|t 7iz1๷4 yKz1ԿK躟k/e:NGY72)%D#/܎1M7cLyI#ӧ0RoVJeU\* VnUm3.[X=vN&ye&՘K4D>XH#b־-ovZ?vGpGpGp v;z 5Zl, 1%nF&NH,ycQot6м U5 w $^͋bVQ TGV#Y_^G)ˤXLdbU,R9~p>Nj\tE[E_;m b*\z 2eU\z6\m%PHsR/mKLVi3\(ڕ>5ˁd V*̎#8#8#8#P 3.HTz]}ՕV6($ѕ Wz8S߯: 9D)PN]p&,HLb9s# {g`4eF)6\ :}xiȑaB]+ Ak) 6 ^)S Q;Qauжp|redò`$ps젫1j8>gԾ'FJ(LqWgvGpGpGpGp~A@%¯ṋapB1#U2˨pkTO\Ƽw}UYoK ?6*4=\{8<;v+l5iY@ζ! xTF@ Z,/pGpGpGpGp@ 70d.,0'&ةĤ /|,c1FV +a܎?@ ^ܛmZK̺e;ֻK+Uk$n[>5v. ZDŽuvp'm*i\IZ𱄿K\ׂٞ}po^#8#8#8#8#4/bXsJ ^WO(}Yaf9CbpbQ|e͗!\kJ3 5QӶ+^xA))E4ZURGpGpGpGpGB>޵i^#8#8m@eaw /=>F2{Cg3nGpGpAfՎw!+U^#8#8@ K/O港N8?17xP.#8#8@o#pÇj> 3̠4+2J;@+𠩭@pGpG~駟~߶MD~i7pGpG``#@z2XcջJ½|pGpD` Gyd5+k*}ݚv|?Zkk~8@^km s5\/pGsc= 逸 =XpιQduR#8Q^?}i |MaTwg?g}C&pšpw ?x%>{c1~(hTQG5L1J/A>p4V_WxGJC"3< Go&履~k>VsfqF$_LZS'h"9˂ .rJK ~gEف*'G}0txg'reuY'<=1s3zkݔOhc[2,gtpV"_$ţ>,G;S*ۚ;ϷövaK/tAa5he8@^{M,2aV[^u #-4ŋ#8@! l饗f˟e.ث'x"|cZQe.h&M7My+s'.(e.ُ~kl)mEf& .}ajeV;L^QBf2 s7X`s}aƷ/GK#QzgsL& ,^;gy "L(3YH#2:;=\[gxV椓NQO*U%lRviɮڴjwGevm$-+ rCꫯ]c yM5'OoMJޢ#8򾓙#8@G!(l߮_(hdbUilȐ!]Ҝzꩪ+.q2_Yg2o?˦2EYbͳ6[&5ټb\Q.EEyX+KLj 7xFOVvΈy߿;dbџ&}GpZ+[G mZ&qL) #Fd,4#B%-{0Zh2nW\XdIGpW^#8mt ;he{Wv 7d*s E: 6 V Y %JK/pbf3l^x쮻=lfwieKUGe(ӭLnv]LӟmFsB5\6/գ>:KX:V|GdB\xᅱ^i^%WLܩ=g E9[oYd}b( RK-yR;=* 3SVO w y%jTnR 3һqwvY-M#WR/-~;E^1L}e.㯞T[L?8@? ]x3\\rL%X"pr1x}z@ N8!+2c@_̂t0(S22K^Ps g:3HEh> gc@e?D`ћJm3mEr)4OLR%[og #r<x^b3b;g9p<ٝ%,qoF,RΠ*Y=ؙgm6lzФ#8DDb܋9眓x$URvY{gxN"ovv7gH,.2쯞5:iS:RbJ{ˠ/"M^_%oMA~8@{ ޺GuT"M'ԶK5pσ;j5NN{6 +W_]{TL1n,ƪ֩EOpLy(zLKO ?g1;/Q~XbJm~+m348ƇIz0/pY#ӳ܈}M7[Zw^%oš8Q^ B?HDkO{lF55Gi 59xW~t9 SۤQ캤Ka~p6B`6jˀh `%Ӿ bX/>4[sC@8muA\(J;E:/^47^G? b@;{LO^7";NV^y A-rw1Cse#`|gYLWFwHHaС5xb G|yk;r?pGpJ"`w,&3j4LbymRC] kDzk2,X_7lXsϭ̽C<`b/<6i̿øv9Ka~pvB@Z"%*F䣑Jx<# g R$L=]&P Tcӥ QkD%L%^[=x̦v.y僭U&㘞vz꩙(5kr&bsڎR|34lG22QפO>iమ*l},JlvؖybMRxk[X`'E>k(P,oBFl(ͲC=4E:C2Lh~O4):dCV21 ,f'tbwuW&|.fٍ7h2!_Q ȳn)#( {W7ƇN{,tVi3u LJnb& ywI)=`& aʏ{:ڶ}۞|GxdÇ^x{FQBz뭷jU 85#;qegL&PiewS`B#8}s1mV]8@UahýJ={o)^Zh!?4nO19J3R%o3xZGp (fn^"KL 3.LC1eQ}"MwyZ> yQif銶(Ⱦ꫚B2CSs(lf4}p|k.Up ]ebMjƭp6_xc;l}4]IܳhJeq>__ =K>I`5,{ݶW,kܯPB#(͋4pq\؉o+,F.j+ Sy)7Upb!VmgPZ?ncvuߑSrbKOċx[.4}֓FyGpW5W-R8x&fSJ޴f+[+S}p~C@Epg%_&;$yɄMc+l+(a.LLY@08NJS( 2IQcj7s˖Im]K,NHlyS;x w\Fxئ5*,\rXbkc+ₕ̶*UUYX;#yܰcM ܊v) q-dp܎"/"4-[`N/,Ƒ%_=l@g߶`XL걐p 3t.l7"UmSRx:AK5CՈX3e¹\l]ͬ.y2'4/@Qe9Uuv=Us=͋r=TGaoy%ȨYҜs95z*ϕaXg$fAA-,VMmz b -I; Jry(Yl8W,Y_YH+w;<ὑ.bkK+ofhw߈h4o^S~}!84\գ c owVP;1oDH<#8E½s#kF@1*a:*yroF^^WcGpU{T"Dw A&@XUk`O u !ء(̂XH  zB$u" WOY{ ,^p@* AJ(^`zh E'VITޕ@pTo>vϝN|6A<Ń,"Y뢎 AWƃG}4HL"R}KQ_QA m,rCX}5X,'1^7q z܌6P,Rzm&*m&B*|:9/FA ,3.0OMJ޴wG]Vcnk8/O-zmI4ٟ(#uՅ] !WGyВX:*VZWp/ʛZa^$aIφaV湲l ٳS]dEҪj[S@ L\fXzAIEk{ wBdVv_.M'ܽFg rn>vm_*y@\#[ t^ުR/F Ǽ "em6MU#˒6"awVX A>`zn L0;wqA>[WY9gq({PuqWYnF^7B`Nz6>y,o- X-P/X`5w3.e.S/uf$XTԲ p(vs뮻LVi[B8@" C ;찃6G:Ik(篿+gu1V\X!X&u~5?{7rfl~8QcM̂ߨv*;\u'xÓV(]³>$8Z"f ni\ 瘾L-\+<=餓s# x, 0 =r&w}.&w YqgjJ{Em /ԅV/5Լ( 3菅gse4s(>}w;>J+"uw¢ A'hQǺ'˔4u%XWx'u14-WQ38c-AoUVY% 6Noʼv7N8a>|x,ߚQǾ8#8#8#8#6>=;sGpGpGpG(+KG`0!@?>v[n >`<GpGpGpGpG\ρ#8 @W A.]vz< {; ՕVZI[wXӽ#8#8#tp[ vB#Us=ꫯǾ3x0>[/z衃ӎDnaXYud?ю#8#8#t p;t~G`jlx;z+v%#|'o&fo#8#8#8+;yG>|xeQާz*y}gyb|'#8#8#8# n\>pD`9 oy5r_c:aqю~7G:#8#8#8@C½!<#8>c =o:/ހG`F >{GpGpGpG1\NpSOvux|q]cgx;#8#8?l/ xGp+!pGK2d.T{OGpG@eae guVXO?]/wG}`#N8aovT楶_^۵8#8#wp  Q[̟k8#.. v8#il*/nx{K 0䓇)"[+VZDIpE>;S*X?x\S%oQyv gqF{kF},0ADX;:cs15 c"s9dMfɻl%Fsqs6GpG"Ʋz++;+s9md$+>D<d+Iv6k. x=ݑ jgXIOs݊T--LIe.Mz-袋KR|y}LX = P̅Vܣ}'a{i3܃ 7ܰǦ5oud~[m2QPԹkf{k__kd'!=vmO?@d&]{0fスc  X& jj6\޽&]w]MyE2Zl6K(f⅝ɢ~_<Wgg+id}&K,:erꩧf?O5J~{&m:߶f-wqLظ3Qפ[\ve18} }Gp*" R: '|dS@IDATg@"8)G~:|ᇅ8EYOg ʇrHOP]wݥxp%Qo<X{U] Vp ͞l=s {J.m׸K,D ;ցP(,(k2q{oB` }Q wy'<erNۼsz)ߦpz:KM9*OzJtww) 븂&X7Hd=Z]k۹η |RN8!==O>dC2xr\F}88@ ʖ38c;?\O#08mTqRKuWs&gyRtIÉ]v%*y՛NQzafY5N{6 PwM{o_[瞫bͧ nAa= bz++ⱦ}kӊ+N>F+ip@^{ VJewFmQJ,"ӱJnUP`AcN?O5fX,pGbe3^v#&@: h7uЬ@u·aH[wyg iV7oX~Ǽy{l i9X|M74m|g븂#hN?BE5h]P~@euد7%cz1 XX`, 7bm&*T6{ӤڿnI -oC@~4.#8-B`- tɠ pJ߸B%N)ӻwCH)eWYe||w LlK/e7rN坺>Lے6KuL^%XedMJO`=Jkň,o#8"~r)1 7& +w|͘^V@:@Y8'N־ٖ /x /p?9 'mE iV6|se?gJk˜"b-4+}1sSp" F&#li^X{!p8j&1jp ]XEaj +fDt 4,I#0u3ϬіRzD48X$hW<X`virC~^m+q7Y1*[hzDu&*_,[m &)p 9$>r>| iTz(Y/d1]9~@ܩTW۵(<}Kv[^{Uّ&cB'h5ܷzcBya\NB)e:ny[G2 42@%n&nHpC8J@u=+ EcQU8ڋxVc6ہq [2,P ae ,Vfy״ @xb hnCb \s@jt_(bŒ`b6˨X0Hbb'&|lgx֑_O6 OwmF'$I|p\កỎ#xyk[9\_|6 3FɃ8ܬpv3oW\X?c7 782,}SF1H"DneQ_Ԥ3+w,-H-n\[@@`u q5qX݉3zo쮻_~08h\hѠ][gu4y^Kj/2eqXhFC=!]9GEx#8@BMG};pG Y8( ZŚ0|zfkv4![;ИCɲjv6la'WzO 5 4j'7]=jU͖cx7uYQ3'S`ayWG Mu:5wC #A3WsxvF+%>?"XGԉwJw"芀|%li^ FӺƲ`d0߃1S`_D"tf| ,XHq [{S/w蒟sz {d/pAV[m:; cU bHlS; +oVHlǽc#<M6D7x*Ky#L|KT=gy\ԓO>SM5UfȫN=T2ag ]M,cRI%:ٚ7T-~<裺9AX0JZ(sErB: ^x!Hи -:s W<="C=X05 Tr»kRwYбPD5Fmzj :j:Zk-Vxx% WQ˜;[G~PFl6aСvwܡ=oo!a{㷡tplA "tT1b=a{g?g 라?C4?m-NE=jN:mE^,^NzT7bc% $h*T.'?ϢO:DsΉϔ(21.:{g bs;mTw}uYE[Qg}pǼ;; c^kK,ٗ[n.yGhm(\_zUĵ곭x$dbQO:-F@ RF~}.#8Z4u; LyԁyW@XPa K*XCBKc,ښsXwaLzqXm$Vә9ʠ/˴N[cLOtoXc֝pݬ-]kڗb`es8k,Ge#,VO wQ\}ݧ*p KebAi*z`Ti^wV%Vxa-d:CQ?'#aÆ]#@xѻۮWKt72hK*X\gN;-yR?KLY,r kiP!#6S+0&vζx V40-FA[{# vb@e`!3nJ \"]v@b&&АSZ6iacbFJ_|Q)CrV~S  Ph;*A٠Li; tTGnO3OT(j2kZ"R G,CXs55;=e\6Њ*#*YOvo-^6=W^v7R 2DL3MThucK9 .` #;t;7m*R7RV4Pw 1r%{$=OE8pgwG'.4g, B\ 6ԛ7p{oe;@# vC>xFYgY=5(GƂp<pޟtctG wɸ ,p7+TQ~A#8#8oL { Fû^, MO3r iǾu+9@ ܵ&|ٱo;8~sؑx #8#8@g#0r"kx;!biww%8W[tv@`ᑿO?mh!D41 !;#8#8@g"]wZ-Ǔc5V8`8G7#/r-+M74`20SNθ}`S#8#8#F gǾvmy!SO?ǎ#0@p-Gh?{py)wv?گޢR?§~ R,8#8#X@xguV^e` 8 GpGuQoO34a@xGpFMC\ /0xg?x};M׿&,}}O;Yvy[m' \mSMz!#8#8@!;{ŌGmx;#8@w½;tZp >Æ kAiSĵ^ypi>d{T5X# ]'|cH)e{kul_ᎀ#8# V_j7xBvG(+KL\P-2dk|Ƕ}8϶}Uo'.ϕvvu]7X{lN4oG "xuGpG3©.wGp~:hZluQSL0c1J, Տ?r'a'WuHz^x! c;ϷmIG=Qe&LЩerglEQCQheF}f_3YHOV3>Ú>-]fA1,DiT'[g?/szkQ-_kbzj=7:dS\m2L4h >j҉Kg Ԝ㹜x3gy;aߏK#pI'{uqYd#ީ.@@`֫yKB?X>ӊ"~DnS<%^u lI';)Mv6sQ1̌,6km6ugvM;sW6d'# /7-^:kK._MMAﯼ5y젷o7xêO<1CogzQ,cLK9BoaYt_{G^bq_,UiD}sUɪ|_gkf$nA/\XK>P"x.S?)GWpO p~@1XfSN9s[p xcpF}bqw)< t5yd#wxO"^xhekV&DM\# *y{j_w! CgwcaYfQ(.b& v[SA,|XQ)nk5.kz+qzTW]uU曵ʠ*"A}tXwuׅSN9E1 :Tkpgk];Cj'",d|Y[ӈ&߲Uw/*g駟֦C+u+QF+ 4J7pCmb:’K.D;X+gϺxB 5\S˴o]Fm+OYd8#F`gVꗷ~;|U&\G`!G;f1lǾ3p +ӿrGרCkbrag_qw;UvW_s^@e>*HoqY^|K/յ VvK],S* 'wK۪7yFτ;^N#zCx)KOEi1ɯnzzO8Lk_ʌT(QL: ,H38W7-7-V+oHg"E&u<%pg&p.,0Ը5BIػ'ٝw4nObdbС/!x5@Ws==x21N?L}ds;y뭷fXn34Ȣ1twtUe\K.$Ӆ}kv0FL]0-_S34NK^v_tEdk>oJϟI? 3-osLKvS wu'/[V Iz<^lŴey_yOS&qK?/U#N'D[7Obu; ?#I4Xnn˽DW<ŀF/m~hΤ9KZ=9ꨣ2‹ /$Xh,^sMU5h=%  \p^+%N/ gˮ￿ _=Pvli ެR6Lif߲Zo=Wm@ sgʙfɪRK,w$imwm&8&.xIlGp{#7M  &zj$#]{v9n\fejґV4<a"w꒟2ol)PwzK_v۪R?#Ec窐.j)1 wT믿~V0b|(Ӄ%0cHպsx`eE}?("Ra IKK&ѱ ˰wJ-f&mj.bi_m+KXإE\ĩn 4m>A X| ^oaz,ŏ 1IZiZ8}nx/v)Y<fn(jm>64Oi&o~|Ϣ}yhk15wKF@d&f~:r1DŽkF.AA݃(.zRzetny$Q'55!,+| SlQV7XyI,V:A5TJUVU[޲msUnw[{_N.+}y)JҖ2O,Xd2ą}a_Dh^u>x"E"X/߫P6(fCX7 |fb#$GL}hXQxF{1u1V"UK bĠ[D|I!L4u' ,@YIE 4cW\,/P-rVtL^ hQX&n1) ƌ( //kG㏯AgB&cz CA O  C:@da4^Yiq\|3\|NoVZox\oǃ4HA?C,6;%& Ase6{R V쩅;q5U-J% MS 8A2oo@je~$:n׭c&AzjɄؒ-|Xk[Uz zcG?c+|,Nb@ S4Fը_hsʩZ⊅|{GoxEB(Jd[~MӷXBCPդ<#[ynB̋``%e#ZCCzA uwB 3UAokYHu,SíJTw@zo)߶t&e>QqHIJEi`8| $V:AўDH *$\$P YguV&66>PD;?rx'4XHepZo ' H8r*+z ;*X^E:蠃b9ci3ekeM6_VozJmxGg8`X߆N./ױ q aJTBPʀ -Y#Gblmk^ ܕ%e&RQPD3eJ Hf )CB"cIeJ%)Qyyyl/m:l-uq=Κ5ˈ"C4*5kw.ܑ˯'Vb>3ϘM74^Virbg9ܾn]E@PA@m6YL:M&֫WϞ yP1߅Tw!_>Bq_PأE*tb4m۶.idQsNuë(@oXrm7Y} =[Xg:cZ~_3>|R6JOF rvQY\XTό3Bd*ٴic*8bal:/}}gNy0}b?޶]hW3d3uj\H%*54L,.e[IR~HO$ F=.w½t3} ݍ7χ=?.9b6 < %ШUR>|xRdk:x.\id!?ƽ3Qd!&0;.YT.p dmygÕ<ŀ ](v=gϞc`_E-kP fU=x\lѢGcRzUL9X˂op%[.eCJVJ@Rʔ:"Y w jyIP֢_ryiETb# b#^ƍ-1K0 !XAAGpAd J^z%w.ޒpCFSu$x Q /-֍ZQFY^}حs b.*Pi 9 t:;qB:h0jqB&4c"= %KP97^AC*b׈^lĈ>ڎu1B9C|ya~d;EjmZv8Uwee4"i5"9sbաpG0 ]S@x'v:8qŵ#6;yY4². ۰,mLxLly8Y/yXp˥l;po~45T(@rA6mWN$/UGvc&J!н{w?BU~=p/dw?O zxMT*pg{*qQK/퇁Siյ[%GcE& ,<|ȋ˼ W;aSB1Y%˛e$;eFϟ"أ+\Ĭ" +3,l'7u w:<,"awm۲½9u9ʗ]~*"PK駟[nw7d *lEIZw⶗_~ٳͽ5QH.2^bX/(~+/&; L_~BΣnDQ<>Y 3 P`Hký-#| L l{*z 3aT Y\{b B+@e 5Tb,h3fLsz0=p \opsTl >WIuc@({jyE@P5j/BD|gq[krV+i!iLx7be/4-[4lͳڃ4Dym"ASK 6(@ >3rlÆ 3E_ߠD$"P T^ E@*p_X.X曶=zeT?Ӵk̚5E3f֭[犀"P XRTTEV p 'x+432ʔiE@PExSC-ѿSmHN]w*kEW"*#4E@PC`W4_~2~xh"㎾o/OkBPE@PE :7ұcG&}M7t9r8|("T~(";w6[l gF)RjB 9sܚUE@PE@P OB vmǗ_~ys_UZ*0(*硶T1qD_1c%Yq{ˮ"@H+5GkE@PE@P*K.ďwm}M<쳾Ç3B'4(@P{&>};/ ( -L+Ukum9RsT$JA`v2; _vuA4pj'E(:ttKŎ5)b:(iJCw1<9Ӛ(/n ݻw 5[E!\qL>̟?ljj~PܠA[1 r,|wߵnTjFmdp¥*㏆6㲭 nݺqqg}f~O}M[!t9?l?a\sM~i֬i޼yZt+ .\vpFidRyuyYq '5䦛ni_d?{_eW*믿nVZiB5*@  oׯh.U 0>qGU+D3웦| w;߿onQQ\̙cN>dvmgnF E@P"(&U2@`ƌ 70&'Sc=SUjedlРA1 zDymbl۷Jٗ^z){W)GM69vظr'i.[QZ6&ֱq{;[n!㱣>:&qeɵ*fxH,bb86m׹(0]tRy;Cc|M^1y[nb[;5kƍ]=?N=Tc(ʵ" #1YO~LHk3rh!ЫW/;G[neQ;Vvsd&}؀bM4=˾,B LOHRMxn%#駟}y睕<܊Iܪ("a~.RIY!i`/XXz&Ob IGWn1^OMR}wqO9tAqeɵ*fp戅ZFo߯XZjUm| e >bQ'xbv@~ϓ ]BEGڳA@џJq 6ؠ-U 2>ڮpw?!{婤*+i63˙gFYjܥF]w#eUjܵ}E@pWJ #;s- +% jvj#&o 6"l%뮻,- e槟~7bun)ldr] <塬j^3bEin#cC QyUW]5XY5XR@ҰaCW_}yؙ|RԈؗ͵]_Qr#ya0ǝ~=ӌXy\`Z fF}6=zC ,0{ y7lG1mY'e9͞{iiqC(FeWD~I,=Z#߭J3ћQC(G@  xnJ/2K) s9_TP3>[omēZ:GGFyy7Z>ee坈6ŸňW2J!9-#uɧbN\ƠR _'+sW)/AkUE@( rג cݮbIyp,2 W^ۯ1h޳Vr.ӢYѕLL_リD[AJ j]1Zz-KB_ !kU_stYgY*Cfmvŝ:t=N&H %XTh1c6&}O-<Kr"kvp@IDATK+ YcUB#wMD1ϑGbq[N8JU,%f}_vbPÚSwq5KH-W_]Qk-^_'li]]gKڍV-ܣ8+7,^Qm!3!sy~LP"2 :/>Ŝ|Z"jTrWXė޷^x*k׮1dVjl, Hew[\SSI6ʮ"-UPwr (CUB" b'|h%_2ם\ =ĴxyƘPw@; 1< ;u]W#%@$t֑¹+f{=W@=BQIb%\R|!0w\o=e˖_5_k="pTj@@,ʍDQ2wsC,aIYhZ$j\wxfꅊFMqsg?ps1XwԜ+,pqDjQ7TG.evDƥE1NR),'pwgJXwu)B@Kqf6 PtM&|%f i(þIN#FM:ulDG{Ns [oHFw[J1Hpu# c#J K7G/su|30fͲt0{ÀjoqC f޼yoٜ3m4KqM/砋NiY1\SL1%jLjMcϞ=m:c6H(W^ŬNS)F`ȑFl\Y.h{9 Mסj)UI&{ijQE@P9 'o&[_,׺J9lv_JS^yهHF1ӧO7w&"C*k&L`B},Uzɚrr= ^p nxhkA!縫$v]ls>,P7N$jFSwN:ɝ[Rh ;7u'߹+ cC{ownYX=cQcrGqkw;0)Xg,Xk G' 9ݵuݴiS{>:#gJq6ox=ҖH1qA"+^35R9oV,V&T!Uލo,rW+4ލ(ccb #a[(qcg+8>TAeb;=xlyWIxKw}w_m;|߮bM1\\gWOT(|??澋͉gQ^ɷ"(eMtN>I m@LjVybc%ثMXI9${i0!5M ]%vg_Ns$TI1v/,8yzX*iS(ٖ Բ@&bfuqs"tTEhQ%KLk^E #T^3\(1JAz?p}ݺu ܏sϔT|W{*GQ*43=zp/],cB%cw5… umԨQ>_=sʆKtB%͎;_|RwNBc/("P=Ӊ℗T2m>:<7ŋp6pWm%&Ab~/CmF1qS2l,N1VSJXGL(j@a1>¶Hӗı mN~dڮ+ͶHch7uØ3CB.DqVթ/q5V+am%mCZEP½fdy^sVh Kf;]YXٺySW2Sy=&Q8;/I=F<)3m׵6]ze~aޅ^.&tUC9w}$*q=WiT^ (b(iݵ}QGem*+߰("PXM;{ocϞ=X"YEQFF>,'RxH*N6h\WJǧqdµNVQڶW>UهÛJ&{ 'QyyYP8kxe3MČ9!WMB=p#TXx::ɥpwm nG Dxܹ"ߪ(@iw%ylHq{T.F>&F!۷e]le=/ )9E`󈑆k駟ڀ!8 $+| RYp5#XfdnbL9l'>xEln#Ԏ6,q68,7=3n⇹ l,o۶mCx9\;:tu!vmmR/X5Dae~ƜviFh/( "ROON?}$kgWnb"@*;vk[Y"s} mwϲZ-(@4("D tAL|֔'i,BnnSQ Z׌,%sĭ(_mJW3alqȄĹcNU=yYe%:yֆw\Vy#I+zvV-ܣ2)\mLkzJޠՊEX ʍMEP:l>/TXa-6tS#|.2/KAM ,ܝ-%4nW"PdZq v쳏8v6EQj ݱPU4hH/~.]p8"TΉ w:MgΜidQ5gαH%?N4L`,*_sbR4O?oF=N࡝x;3#g"( м"P;@!~!) _bsz4ŝ gȐ!錶^e}=Ow=.(@輸F#P._{5՟pM\hhtis9ӕLMTrřzTcI8Ch*M} ݺuSV4(0S6z*izFEh4{R,m aRbT+p4Pn`̅@sϙVZGCRZ\YpuȊ@6` ?|{XD CVYeEAiӦٺ/3pnT^_GvT^W޶,:)SXϤuyx%Z2~(s57.iE@XU/\vBS"TA`W6¿jf͚e?.we;X_UG@=r J,(!Lu \PE@P2E-loѢ*#>.HSC{6vXUG|޴{"p7-Jvm |ꫯwc E@PE@Puرcz@?zUW~tkBPJC@6:E/#ylO@! +C2&L0( pDieUE@PE 5-2v0`=J4 ;>^*"T2pձ)e@޽!GpeS}Md@NLm!<>*A@ie V("(W_m~7;c9G[C6jf&MdVZi^PP{ iE@(~dMluQ?t4,Z86m2emP{fE@PE,ȑ#}kT|q>{ mQ:=RE ,:E@P@ݺuĉ JI"/^l{9+歝R~g{oEr}ݷ ? /"%8"("(@UnZgϞU3H G-F>N0[D0歷2oYwu6lcXcjol4姟~$нBJi7?7oak-4nذ`"M*A?X} 6No)X/oƋv_6oũEfK՜?N=3 ,AڪU*A"ĨCa,?h^yBTkȄ("i瞘qFsq;#ݭ1ys2w܊NjKv@[SEn%6D[guH>: }zL |C=4[}dgyflɒ%qeÝ?&q(ۨQ؝wfΤ1cbaB[eU1wI\*遒"C]E@H~=\4KjD@P{h /TB3X ̈́=#A{]uUc^:Z=ʕPzJ_=OZwg2gƊ|֭]ZbmڴDZ4G;.}]oU>?|L]5aS̊Iyo5jTL]lqńRƗ ;t 7ɽk!0SÈGP(""@p:'쒺U\~l׮]m@@@s5SL}⋍( &/+$(ݍXD2%Xŋ2$݈%=>|#^6}aH4v߹sgWO3]w* l(}_7c&h(AbI_r%FPTT6@XbwC(=nW]uUk"Jv,83'9;F8U<C`5-Ye+h#D?DVmj"+ovFU[pٛ… X-[mBDmD\jTJ=Oh"ć"ֲFV}/pf͚͛'Y:o]BCIE;/ÍD2}ѮpW}){fN3׶o/G⯿2fN7 v-nvjr VBŅ4SQ68dZV]bQCe5W1[Y˶ǁk#H?wꩧR$NZ}-ĂpqY`A\9vgˈ{Lm$>F 0DxlqٖؕW^qc]t B\bDwOJXx1$*#アDhwv1š3o *@P{z! T J|﨔{キ7 PD~(UVp-4RL ι ?1 =B@F J?,!oJtɈ- 8<ӯEs悀P" ǮٳgnE@P* p  uov6ƙ?" F} 8k8W;o߾VU9{챇n}u~x4++-[JÇVyªҩd<쳏i߾6mUx-#R @P("T&pyQ޴,jE{A-+`͎ATyUCZ(cR.4˸, Үк Dj2mZ]w5byɏ?H.eiO˔)o37|s^#TDkVh??WGgw";}V.yX=p EZ#jXknHW#AZmvlWPò٦s9aQ, /A`я0(.tGు*!k$ m :, ("Tp95P(2ݷa~[2Ao,۝AsnI5WP@ogoY81.T)TyBIJҳ= =ӗeHQ`팲={ڇ?n(%,*5K%@CF.&Mx'Ol?WGa]3gZ)-ZWa+A!ŋ@Vt$Xu)lܸu0`͇rMJڶLb]?¿o_.}\2yUm篻:{,%HboӦVޕ̡QaM84os%"hӺ("P,T^,rEz Tˆ8]*G .0=::}M(@~;,g<ҡ1Aj~ZZLJq/|L_V,ȑ# Xzǚ6mwN. Ƅj #sbTŸvj~XUi_,cDcN̛/ w[VDjʢo/}lj(cB8ي:&h~!ѹsgX^$Omv^(ZqQ鯺ꪼԫW i.Exk(0ɭEG]Bw1mǚe# pCywPqh`-yi;[iDB{<-Zo/Tm'~2j Q~ /|ʫ-.\pr7Q]v"pn'X{lk7k,;DlƚPgu͕a*, nAJom۶ $.,Z:(Rq'a:?~ꫯ\pΥV xfE@ue+qKP\DncB?TǘpKf"T>:#8O9"(@":sb 0|pI,&*ŀ[m ^v%@0 믿y߈y@.#>! ):j$`&'˽2 y:?T.~<(`>c_([ (Q/dSej4h e0P>\?YieTR.\h+O@x郢I?{q"" .nE0@G:0-ZOn{o (UPaB`LtyEõg|Ka-BL_|1[-`C[nm@(eѵ4`Y;uG9[p{Νki^+qE%P ŋ[!駟VPk^ g}ִoޠLm̘1ԭ[7+EE(J~!iӓ\Iƃ̞{c'TG@1O>gjڨw TN>&FSv闥Ϭ5%nku]ZPSܓ|YҢ4(yGy|WpyoH+4?7'_|iwh"ݏ?/viFfyU͒~3wԩSTu&Ol<@#Qm￿y"kDOyU< :wU#P23<Be;+e^V^y~j< m\6;va .cy)Zk&(2qgli.4MFȸ-eιj-x2@s+_,Zd첫?2ݣ E4L9oMś(MGjAӟ{δ~i3?h-u g4K'tlFH̉t܂ SC8!@AC?3_i:ṳI? E@(KYNoy(+Nڶmkx_uU!󜼄|qt3&"+F&_{vd]Zgu .P /5]t1XF xLzlnWB`+u2GUEbwL"( |u(bZ0 wMxx+Y*acve8e;`3%*"PT7{4p֭[GŲ8q!*"P,'XlNq'|k"9,TrW.5o-xG("TpԑF+>Bkt`y`>fC;!]Ju۝le˖nE8;X{챇R8p4&WYe{%CR+,4{('4(GyTqSI@7gL]@@YB\wʫ.[E@P @@0:!x$N̻mq袋̄ |c{9C&rG B =lov˩v}w_4*@mD_}])eiah `瞸1+7lrK?L8}]FiN<I2/JaZE@PJ*K6Q.9l߶6(SN5 6?ث{q x?S~9ןKP_ȷ3|}}Q.]Ӳ@"P1 wV䠑 o 64O<񄁿9p;ﴁ&nVP* VZL8ԭ[׎8Xn/6gh[o;&i"_-fϟӚPE@(T^s#/<⋾A PGJ^]ԦMZcDUrGxLgt!iӦEZ,˕B+0zYs5sOkP$쳏yen뮻UoNX Æ 3\pA"/x 'pm#\Gx?G?k"Ư6E@P!¿mkT&j!ZfZSiժP!ct \uUK۱ i8sAJϞ=oVXap|1\G>>hYE ,N҇_(J5X$XMG"g_xᅖ]=$P* ^, d buo>4>43NN᧟=LᾲxcdsGhhjAE@P@h.^Gkr7EC37nwHw otu9,fvaPh.PViB ~E(?CV[meW:5a_~e4)V򫭶Zb6>S~K*h㤩cx$z.?f]vIyoYQ<{a/^?_}M<jP`f~ak׮q wC/{)zk3ydzzO/CG @dgWܹs&{w4}iӦP%:- M2ţ#q>HFmd-c" Q:YeU(],Nÿ́  JǛ.]&'Qc힮`Mu6++XLr3z>ڎ9*XG ۦJuŎzPð}ifa_Ur|(o6[gqY/y>'yc|mx @vu!@,Ȩ z_$V\Nfҭ"TrK\Th avCav3͚41oLyVp5cO;r<|E6_}eo̕5" _N]vE~99}!M(@^~-u~*믻D.ԁ/UH@W֔_jo+ni'T)U%*9s"BgBmxp}Yyթ X|"5ud>}B->-sor%>sB RzY"|+;ˡlg^sі+EYL\%su(PuALR'P_q^YJ꠭TjO|%k7>⋾ aPzmM2_$vuW/Wl'?uSgyƶ` x C=ErYCOloM:SF8ʗP"A$g_5uDEP*Ƅ/7Gta 056@Ǩ%`("T!*+cNu5# w3ͬ(*A`_8!~<p!] A7&%\ ;BonOU8J L8$>,Oc:YWsw giBzښ1c.yՕKomGSOAfuH?j/5 N:)<4+vr.`&Xb-ڞV(E^4~-[4? :CrVU2X=\t: {鏛J^4TS:V_‚ <c=OX~9t9͚53p#Cz^"\Dx#P wV YI >_i~p@r WbXkc=J 6ؠX]vEt ٌٳ#RwQ- 7P>~7$R}M7OZ"(@P{ɠ׆Km[$42gsLzR*xw "V|r_*AdXy//BΪbYNMe&i@+ l2"D 8DsE =(0T)<XΟ?7°PdJUاMWߊxJ`fԨ(`@Jq04.C.B} s4+P9LB|gVy:A4S PN9p iP;|4T8c)*G$ZrsQ@.j0\@' z,w|w ^pcJ}vu 2tMUuQcb*CZbʲ^rOpMIrFkxVTErbe(܋2.{ EL3qK|cʣ)<sBˋSO >VXùW5X4eW?x݄Dx; ^|D+_JgĥYުfϝ[4nYvH\jF˃1Ptl߾2!sbقME2DK@ys1_sQ]dNJݝ*cac @c)o{;x{OuL)(u/`K!,Dǒj%I%3۶)_~As͛g }$>ˋbLݺuX7p*T=5C%r%8a HLk]Ţzl+"DA;m7}YӫW/3bĈ(w}|if ]wǝ_^DSp˽+jC/\dyt{S6E(KoimHP>< z<NNzLfÝ;wvvǍWP%d~%pcTw ԊMV[me9+wTH.}s TF K}"suxԩvU͐ǝӭ"/=Àx'UeMz紮]KɟDl*VuԱpJkסrAyÂ'ᨒvvBJqP{qJen$tJSC5X#N4,\[3V(Ce;(O=TɕXw;v,ᕟ5k#4eʽudF 2 wOdJ\rl(J+q &zLLb`y&Ѧ\V*H(Y-$[l$XW^g ~X$@EorS;Dܿ&Nh@{O?^kk owhyD_DS+tbuXq<R"mܸ+,^*'p8 "rW2S\Tha-ܮn W_mo'{6]Tw\5#p뭷o=,ǝpąsV3bϫWYe|A([̈́(gyf\\wysgYWW'3MJx;UE2 /p]P{E6qZ}xY".]}咋!x{`j"0z²{:f@E|f ^V_=my?}&[BD_ipCt~;|-zm3(+믿gZ44i!EƲ{yWZ# V(YmACV r%c.ۆ N4m~vR]^i:+EBu*/s}G@a[laSaR˱ϥJۭ Jp=zh'n;Np.]%(m\sӧO[)@io;ܠAl&^jXEUy': fcQVd-Qʠ/g_T"1: %kgK{(mIN1tۻ2Ws9S̳sōz+`:oqv2wMqX۟qE6H˼ +}jn㫯;\6Mݭ]\b3l}"A(˻m0;.&Tкa[[-{f֭(& žjl۷^M0|kl zs|@%JNUj/U| WNR}.'|Bz"uʹn.(HΕ]A%mXG>1i9E@0Q;sT"нf#yG,?'` |yrPhB+f]y?\g5mcKZ|bTQSg2.jn^s,ͤǦVM,yl+dX|SdرqvMP"@-ܹA+CK(hfϞv\26ZԩS{f ,r飖U!wmZliPjt(ZO,(k,bQ9&t_COt ÂEl2@ t2N 'f=owxu2M(cwnV {Z3熍v@LVM\ڷirO{9W 3?%> ngmNkX_گ4)>A5`c7lB^k~)%_`,V@_a^z Kc( xu5Ҷ3EƚRxf 3tvfuv{7?w>]DUHX5d-uP g͡{mۥA\kCP=.{E@PE w:z^,;vk*"8VcwqX!n" .콗}g]wvXr? Luʫ9ե(!{{v(P>eV࣏r}iӪ;t{^{ *hL|A{ eucMqʱrjռ˿M7ȴE֝XK|v?i&Jf}59;b!㯿P0X~ښzA@U~x"*)}s_[jY~(xvwn63w:[$n_ye?(s$ ,"4m`=ZL};A{%tOge>fMS:횰?(3'lm]Eu;a `FB!&E1q)95[Ԭis0ur0 wZqq]#T"T܁Ewc޽BvrX܃A!={ҥK @{ҳEs#/iV$H#I|>댩Ĩ:BȨ4$: * <䈢I'S~Aݞ'kG 0tuN:}r^_,2:1. eFUNB=m댆\˒U~}E&eMtikl/rtS/$ޣ4`dӞ0/d$0EFCr [P$2V#tfugы#D᙭rК5k= D8ڶW^6ըQlBјMD@I;4F1A@]d/su,q߾WSG>ɞJ)@ί3펝9k xO"DihS.:ݚ H}"h:ui+pڍ&srkGj=`-hi*1'dj",B@yc-OgϦ qlZi1{bZP~ | MzߤsVH۷ɯ>H*iy[k^00VoFyeF˽_,} i̜te}*ߡ6͓*(k[8WNcaߡmJ6Xpz;~qbyew+ Xzʍ?{ib_wEcm5s*h_"%"ޕm0@#$I}9#DG|SrTArTD A@B1cҺu(w܊#)Wʁ,`Y.$0w"" q߶t%N+im5̉OA79]!ȓ5+%I@x{U*^̦_*`'tYԺ1SLiN,ZYق~'}З,=:D< J0_f#+ta:?w/Bj-3E {>8qcRFo#a"EiqTLլX9" lUJ1'=s2%_.E~ A ذk7!g'ݛ5 3X{6m1^HqΜ9l i*UD?M DߦNvA@ =4P1Adfӧ8AV(# ߬=FD8"!oޕ[F 9˕5nELx%Z(kt/1?nM˘ޙ'OR}s?[ƌtx *#P0 \ZשcnKWs|u\=jT:{)T\];w˖~늁۳AJ렣+=^=a߾ ߱Ȫ!)jצlp1ߑ`eLtXy]X!Yl^P>"3#:0C&2<F`ĬƐ}M$dĜ9sZjEMʂS*\H*v'I 8!>j pBjh pO> ^4pAjҥKCllHSN5^Ĕ3ݽo> ԋ| dA?CiKw!}-m0q'B=uL$/A fw/kl6)CY#ukOw$K:MJIJWB?$d̙Tp!e1W1+`-#9uUuTɒ*gs-똌d0o !1= Zx@[P/eN6$n3]~^)Ŋ0OSR׀VAz@)M@u`c4;PJj@<(WMu{G6uSX@nXuف'hzU <f5pbB|- $s!gPq|#b U#˩"/ (Dr A@,X;!'҂B_&S&_n+QݻSo͜P ^0(=XzEmϜ^oo*U1: }ش,U`վAi>$" ϋ}xu5B]ic(md+!@KNDg E\1 *(Ҁ9Ei{akղ.6v!0]`tq`6v`ZblwC@ ,B 0/[t NZe :[Wc:Kzg4 f#)|͛iРAƾ 9w&3Ua㊾fKcWJD Im\RG:ʹt"q &?^Eqn#pG6mtyfD`֬Y4ӴѼysG9 ޽3e˖W\v :Zli''$'rD0@@ aB ŋSvtxYQ@Ae_ԁ; ?؆_he -*Kf4)+x7W^ 8tLA?=PyA 3G(9vn*=_p`􈽲!ذA8;NٱcG:p }ϟؗ{DU6u*l &A@O!n?e އ@.]bECҎ;OQ4!h={  3!"1!fI,Β\%eA@'ڸǗ$!|ǍK<`E Fϙctj\AhTJ%.\HSN56d|3qӧS5HSpjQ_EJiS0HT "s2jfIjFCʂ@#М J^x4l 8G{ #MVoF7UgC@&u'OR˖- eF0,"0x`w!-%J={Pl|O^ 3bpw#sK!=zt0)2C?NUT #u3 !,Hj裏>l&:g*@ hAO:) `%&ZAgFWȞ](y)^hܗ'+S2r8k~uʛ7/1=FbSÆ N_QHܭwEcA@lʕʠ)߾}[%BR!+ȗ R UGϙK/9T/<6K#PT)K/ H8u[L4(8~W$͛7cر!Y0߼y6f̘{n*[Q'A@<1{Փ #1czn߾;c =5hQ(5_ >U~2g6;{(KAA@"0b,KidƾG5jН;wTCD"Z$1PBSCDя?hCi =ɴA:J*Ç[\.}Z2Td)=xؗ{̉S>|HOسJDA@"p=Ziz!صkWڳg 39ҥKӫW 8pަÇS:)  ^NZQݺu֭[^7l˺~'wwxK%0B/'mA@A@# ?M_,6-[F&L0Zծ]8#B4uTU_9s椃Rĉ:) -[!^@hhݺu5jT/7$zDJ;v0\a#]~ؗ{<8U  X!7Z[d`O?6mjts1\0`mX8 "bŊeehDwA!_?rImQ9tPݚ5kaz-r6A@@tl`ߪ/J *UTɒQ0밝~$N [l #0~z?n\_+<{*N #);XYgߢE 8p գM6QȑmeG#pq;w.;w.]>_v ϣGvp Aޅ  z9IWK*GI e4m4"nzoٷ:E1sxe<# A7՞Δ(Z:7jD|ޕRfM}j$+VI:ajDVVfM ApB³ 8F _|4vXo7M0Gnw4tv}C?E# wǸH nGXKΝĉzk_g21zQ7a']!;A@@bW=:Yõodڽ{ȑ#(?ZY~7*Q8|4#TǏO(spw}:od:ҫW/B;>UT1 "!zzgܑeYs\,M={;w9*U*Eh"i1] ,X|ȕ+B7Rƛ&^@Vr׮]Իwo!:_%ؒ8U#![A@A@pP̝g4mƕXN0F/<  6X: ("ma4 vخ) Mu7q^ݫLSexnٲ%v\p0:Ź.^wn0F7ƍwl  ,؎.xǂƍmFxŹ>@[K}W_tG}6+Fܼy9aFAS[f!ufeqP /@IDAT 0t <ܸ*>VƳ'<9/_Nɒ%s0*TȆV"qtAkDE7@ȝ3gN1C Q4-z1y@ɂGU02sLZx*wU+H vG/>}Ӡ_A~A9 JRJё#Ghڴi4߇SH4͛>;x=zQ~ā^sQh:AňR)2VҢ x- +?ɓ'ioזr_-?jI1*${^dBA@A@sYSb-q@1ciסCF:u(Zb+VP l<ӥKG;v0D m)ʔ)C{<_qN+,!B x8q,Az!X@"ɵ oZJT~a繀avз ^k9 t;8p6mZ}Hx8Sд-nƍ>G߀z1@U $I1;uTjժC|ļaoӦvRA$-AܺuT\+gWWM?IԁU>uxh  Ml((xѼfRmϜf)ϋVpZ#+mVF[lخ밅8f (@Uخ OpȯJWԩSj:0([D4R GV[Ș1ZLB"̘bp̥EA$Bô`E Katp7dtcQY$OnT"~@NڝMOOP,7"E跱׼}T&Uҥ ͟?_QT,&LpzZDduۀB tmW튎&% <s>|h3/^<ҋH6-{=lh9ρ>a„ ^˖-KzRH<9ChatmQwukɭ$MeA[ʕ+ ]0xA'&3SGjҫRkMԝnY č&JDW{-ɋBUA@A@,ؠbNis̱9ɵ ܉JLǣy9W~v-03F%uxԿH }~/uM69qtvС㷖E/΅dO&,2@ Pep.Pti /w,xO?Ν;U֑͑ iĉ ""s֭5 ,'0[… U2ҧOwŗM  2 aÇU6vC? ۷ .LiⶭfmpLYz:gBYa67:QNh׫k&4{w7LEA@jZ6*AgHǘFA/Xĭ5S5S8AVjժ&^]dɒʘUu*  9sG8ϲSލǮ"9M~˗OEwN?3zg;XX+ƍ5uXq#<]mÐL?~|"i+ҥK-[6'ҝCG_o<$7^ZI,2#C;V!C7.AiTnTe+} &CVF]~ ]FDA@Ӈl H?+:v^1c Cc]+{͂{#u͇,U.(+VBk"{c\e3JFӡ+MM1<Ayi8ztyjCLFA@89O?(̋ $ 1vX Jx` n$KLkckvv;(m;vl6ej޼2L߸qC:nbm:N̘1}W6^zUQ|8) w/ ߰ݠA:y6tFB:uZGGݎTDqoS%}zIx_FӥuoX4ʝ% m>-,N%A@蝽r K5|}X~>z]ngVcMV ϛ*DTsHa^ Ν;Fv|+0N*lgATmlnju@ʕ3y}cǎ6I˖-Kx!"7!rT#R'8B@ P:A@Aѷ\T uް߮-}!< 9 a5D_A@A@`dڄɋXt`H#V/bǟ*(GJMhԄ# fnFh)R$zP>x@%;/x{C# \(@ L+VD#TZjɓpOhx2zzoz2ۯ&}f{9,A@A@>S{~F6u'ogvJ?߾mxhJ,ͅ7Hy٨٭[7UmK"w֬YUrU$X՟%ה%!CQO7F N8zjD{?|H5ktrj8͛gѢv1? ^B+WXtA@|־*^fp˷ީ5S[֪I1GdҀI{ oGL/,x+F7o6h<'5jQ'A@# c&=A@hrEHe߾}zoxviPi:s{طJAZJ VG^u>?FΚbS UY!"`ԩ0צPi(P:d1?,Yx܍J)  1 6$g#вeK[ĸqhڵƾ7. ^\;3n7(=PpIcA@Acxܟ+3:j7R}ldTZpM׵=(d@%ooWRF1z4^2zN\&@wϹr2SOBX1bR$)Wf2O=IVϜ6-U}0=j?[6ӧ nΙn7QP@kJ/^yj6e*D۵50~xs=˗OqhHA@G@ !(!0`:r޽[ͽgϞ3gNلGuFkOuZcUO4G0;JA \u:pB?W6tlޜ֫kD9mHLВ4Q08k؜a'^=ep5v,@p38)XKT'7 wg:Ց6zy,PMbAxUO_%MJKƎq1P(ՋFa32m`x/ԩfZ`/D@_7qk$3A i8qbuWNϝ$ ~ U,.wR§qN>5wEg\qyʤOC'Ta#?>FxHw2MhS^;7l앫Okr^A@acp/YaÆ~M4u։}.Ud߾}xbɠ ՟aD##;#$Hƌj:ZyVU*RN/G vشf8#绷Z-ٳKgĩR@.'!x3i=B]HGV;RTs 6iKZUK}L&B!G~k&O2|rZ*ʼn'L'a: />Yx__/6TJRAȑ#M23󞯔8Al*UhA4~ᴠ<#$t`Q6Irr|𿖣JU#)~\O @=Q :AK֭Rbz불N-WJCՄ }J@ RHjEZZ+:~S۰:U*8GO1=+"ЬY3:t萍7tukƍj<7ĩ}$q_m/,8lLJjΕ`>? 곗wl/ss}z%ōR$NB9akHʝ\I'$# SY]8NʞUtW7ޥ Rl(pg!s_EO -0[(os妄_&y_{s5 %">eLʈr\ŋ5rd*U+]6Aa}4&)27\ }z!/_᷉:2Y^9UZ9'f1߹C0:{M>٬ߵ%N^ 01/?4o _ǵ];)_$_K/|v/\@έ-Xp3$rgB:"y*U?{G @&}M ^Ϙɋu lȋۂ9A[Oҹq#j^E}U%mݺ8G#kp0kWjX[$M-\9t6hyjGNJ/Yo lȻhہԪU&RՒ%QosQ>dCGތ{-T^0cPG`lCk֠z}PxkH$Ih)'GP|2߻wP'jԨX17uPLھ}@q'Mh4at^s$udjM6QFVH4 0@dsDpL;nٲEmۖ&OL}ג 8) رc`T=ڨ%Kɒ%3R8|0uޝpn0 QFs͗/<߿>ur"g]u%6'NPG,4 (:"ţ>ep+,  vZ¼TD(xto֔bxTsiRO(sF `@洶?}ƍ1swlaX/02 /ۼO?Tpalǂa9U&S^{n֜ݧ 61Οu~|'Ie ֩kclGϟOയ׵*bY_޽.cE7clǼ^CG^@4qJv;wvhlG;;NLlY;\>U"s$1hR6 O3uGUFN0o ւr֬YK.8nݺѵkTQ8`|Ƶĸƍ1שhѢt#eƌv M!uҥ*؎s * Xׯ]a-Ow&ڕ(ԢON~7cD&̜9SyK!},==  ɓ'>}c#Lqƾb~iK#3}2e(T}\]#=֛Tj|ɡG89sQY'L٫ّܺը[|x:JGݻʠ1uj/7;pM3gPʾ^0&n#G6 C@C՝T!*\>5pu/3*E1e9BΞ*yAv1B(XWԱyqYfG2m۶dCBs~1$}A@\GafYfϦ| F)x.ɺ9; ߳Dxܝ$LfC7o Jcעւ>{^y9wK<̉uU6m^/y_0>th_N4)+c\OƇ< 5JdbwӒ]ZFVgϔ)q f._a>'R{rzI'uf70@.YJo$5q0Pkizu9ylۡzR/wrUt;l؎>y~9s<痯~G71@pyh*(~xM 9R{L#B]aNp-^fc>_"~ X he],g<{sI^kp~-qW~B[~8GﳣVw+\eQ`"xTV=a=S pxE?UB] CcEA x:3w6xw:L?rصjvDgL7N؈=7/Wc;~m~} G5 pB@MA@E ?.2֨(XЮ:.^|[zNI{$rxEs`bً0n ]%'#{ Ʃ1 ~ ޥӹbng#?x|u [Uk#!)m0ͪW9Ձ%[#д*ٻ"/~ml G>r/rJtZ;q~,\iwvv9wkGg:J_}iߠ^^:Ls|OJME\P,͘q~omYD]_rE~SѽMCZ2hD b"pރ1xn&mvϕH 29D܃Ar|?S}S>u:NX,.DyQ{p=ZCxBL<ژ{]~!>)Ksj/S<ɒR)jơ:L [wml-<0'ׯ<֦^Nn-0܎9@Њ.`|5g旬zXi!Gȃ)XA!l8:y2JIw?q6>!qClRg/^E 5MzIT=o0 =S#~͋ WYzh!uRyt gymx-[`pGʜ|ڴi)}xm…!}ZZTy#ilGcwhd9}u.I$rL __(&$6Ԝ݉L'ֶmۀK9Un m ZI]`AHC(=5jDߧ9 jOҖ-[D  SLQ (5} 'L#f ~6O΋`?3ořS nݺtߣ,Py$ +W.s]ղ^|]9 Ih]BQFcA#ח;rHB"`|g˱Gz(w܄o޼Q#ybe q$=?9jN/*]"ُ#Ћ!Xq&X|],Yp !?Fj>Nӑ  d`]|5WnV% q'2^cO(e8O+O1u*#YRh'/GpNߗOn-dNjV$P%lhl宥ݠ'U2=zL&MV~'ϗ? ђ n\]T[3Gy!@7g2mAmc;}2׵5ӗYgVFm$|-# w;׺*0k !_JgL7Wv]pypOFQ$޻HjbxKQOi{0ۣ1]Yi%Ko'x/^nSlЬzkk0|987~@?E0~°A_SN'F@ҟ5ΛzRPgya^7TqC{-kצY∕s*+]tpl*zϧLQ\AG-ᵾӧO#!f„ MH|Kx^(Db%T 2N>sCX$` 0ayRb'a+5Br<c@?QC ~0g7o$(9Yt~9.^jCe5Wƞoߦ=zкSw,mԤZUrj5hTJ?uCg˘` =7ߣEA pS6qDu+WHF$ЏSgZ*|j1\.W_ ö8)+zS^*O摏ܣ\ZsY5f/ɋ3:Yr½"{bc j5Ա}/ew>]ع!p?w`0tzD*l_'RN]N\E&\gE70&"\yLO\AS6`(Sj{*S!W@p%8<][8{Tjӆ^ 3h`D;rrGh*H[fMe U0$+Wtih\k,(GFdd\_.M#,\pA}aERxm?$v5 l!}6e1Ŋc~0{#4>fg̘nj7 U{˜=i$";p01ѾK.|rL``20&̂-~h#~zzǼHX95o#[A@\:};W #fd#D T8oب( Z6=R& ZݿrL#cJ0 Eel09$ a#꒯&U !eD38n>f. ZvmlG=Hdc6Fq$vvUB p09282A ob>5+{[ [|i> M7 V̰н{wE mݚJ|pѣY/Qdž.2w#o1Ҥ1(!o RE΋a6=)S5E`"sZ`OBrSG@_Ch-牪O\VDŽ  3nƅj|㵂Haw*"iAf;tbEk׮*0xGx rpთ}'Ȓm}In0ɜx*,޻5:tٳӾKrA L}m~VuᄁΗ P2Ʌ!ٺo?m\5E;Q";XG〆{?|򔓬Ơ$➆ٮgQ#G6rëqM9RDˤ<_y(kcH!y?nM 4$1on(B PK8qB% m!!Gڇ7"E { l_fL ~NRh4"y_bHU")R$E+mpG&jmTI$!dȊ^Y^0܃/ Cu`B፾(5O;8N/'1mln !DpnpҷjJq-eA@\Շ$yP`lP)O.saͮ#F)N,Al`q؎s`'fz 0v:8&:d[hJpypE͚:FF+0,rp_reZISDjX "p\s~h)U,o߾XFwGo,js]m͛7SժU!35 B`tw$Q8$w>xA@p$̴IJ!q;Mx4,L6zBb)TTpC$YZp^iӦ9|'7)Hj~H]D<{g.w{Dxۏ22ގSpj4mgPosT, " ʜ  FLX2թǭNzcϞ=SԳhqx(OVks_8ty6uz'W\&LWׯ_O9a;]VB4 ,'>+sDpu6S n݌;}ǎQ!N& M#xSEA >"%EK y\CL$VC^F2sب݌}O- Z%TKxA+i[ulA@<1u/Jv;Zh G9 dY H>G $-y&j0 01~ͣK*H}COp@IDAT.Rѣؚ5^q7CܕJ+.(AD:4hA3  v;HjOry%zʡY.4N Z^F ell$7OAg 2d;|Hy"8Цݣ;SLXtRǙp7xp-[PN|37>$/^L0(gq0ܧO޿&rL<L/5cG !0l ӺGi5{wSsi֭TtidA+c6_DA@F-;UnӖ~IJ$I=`СCT\9zc޶m(K,[:  t{62ހ=`aF8qP…)Ydla /^8ōרR0v`ѿbŊ"E =~˔)c'N@KS~}eo0ЧgϞ DeNdӥK^o"E"MJQRzlAexPK?a[)W&LGzwA@A@4{k9x0epg͛7Hp0‹]_i  ={^(QNu?~Zqu#n>PoqK.ׯy3f̀.\Py 1$ {A1c(]fpj}_CWnjl*MAZ;ө a"C:u w(kʕԺNOy ^|BkA@A@h(ף*X?>5k̠ǁ0ól" އ OhKڴi aGD@ \;e ^ChkG?.[1ӰQh֊bJ*x=YҲ^x!IS# T GvOVI. 'ˈgY2rHn}vA49ɓ'j@" Љ'Y8;f˖MMٳgԩS':}z> ptܐ[j:pD0 yy1w@AUֆﮙ`$ "$ Ar%JNJΒsA9'A%$EAtuzfyuƯ{{w"ERkk$z-U%̬GչG+$vl\JiCkׯIqE@PE@Hu_lq2]w^^̙3Ok:v=Z>P9{n1c;N(Xc7E yNUV]&QdExܹ1ESZu&vdgWyN/mÆ 3 G%E^$D,~\p!^u#1tRA8-2Lc;_~E/^lQYVZl˖-3gh% (7{҂"<=~m <}_?[nى>[ ڵL7wҿChʟ%bFQ{-Jd^ahR*"(@G?p=ۙʿxa7T6;M޼y}_ !i&޽{L=3duqضm[ c4:ydzxb^L̢\QbD։ vveCΥKb\ַxLP/"(@!>,WlrzyES=}dՒ&Mа=ft4|d19buT;wA?)'_/}z)QDz癜J'绫sSE x2)|#ERa{YrNdC8qZ.f2%?~ y'KU'8E@PE #cǧҾc#(Z#I~e7| PpD]tI&^ [nMŋaN=Ӂ$-/HPA1,xIM?}.C֭[13W^Gcqt>yqu  C {4Dkf€wPBf_vǏyH(6.,mժW{v턗RN-'ON2k1eͳkc2qDf!8?tP^ !)l0^*T0DeH׏>}N~&iÆ & _Tpd;!KGNl3~mYf8vYbƅ5m4Cve%Kԁʡ%) qa14Qf ZL>7봽uVs\&,^ug ? ,!۹&|ɒ%׻Ñ]"^cQk+佖J';SE 5bwd}硜WɓbCtRLzqwJPhBPE@P <8l!hz-Or Yŋa ]BTx`[mve G$c ŐbŊody4!Ɔ.;vio&M2~ƌ:2He`+W74,s!ˋt oxNcp2}t3ND˜>Pƻ,x]$o۶(x|fw1v8wfϞ^-H+VB 146,fիg}iN,6Q}$}0]3x"0X#/3̳ժUK{=bQF& qΝ32vX7&ggμ>Cal7 | :'C]lv Eo&\>g ֈh"y)x͑:y"(@"#:u*Yhz% uK/mP˗tT4goW$ z\x"чʵk9_Ŀq0犀"("S_v}3}/1LIEk ro̘1'GI={ Ck"z1Nx;[@''OsHθ0xƌEOk˗w_3B,իE Ko ג 6d{\bil￝cTz AQϖ-_zT^˝;wzs1'ܽXpZ|^Ki%Sy+"͛KN$ u0ٷ~֠nRKf{EJC}w-7;{.!sm'+%PE@P 0qһ9φ+ήuyRƌ'Ġo dȦ;'_O00$5Iu[[#=*83Anɨԥ?X ѵ\8v{%"pk'N0Zhcy=~T jGp QbgH9v;u$bkaU ^<3Nv*r))n\E@Aխ"h݌ iCpVRefx=zpoPoDc"p%ܹ6Qq("(@@f8R1קѽK>} %#ӧO,c6`! ۶}_;,3 $\Gؠ G$~-Zȼ`G`0-~wi<G`f{͎?NoM3kx#'Ȑw!S6AܶLTxbW\,8 xCMvm`o16hܟ4Q>7<3jC@ bK ֦MȮآSOm3alAMP1Mxצmy)@B^L' (.|aݽٚI=gYOZhg|Uo-tL(9[Lu3ƯWO"("<X,)"U@9] W J:ޒzUVk.[N=6i$[IFw΢QstҫV*2d0xFɓ'KŊdɒ9&*U2]JFoL1;J +)$-2 ,0`L#. ;{6Ay L RdfNd8+%#<|;%n^nݺb;a Mm*|ǧobe"lWV->Ӷ_Nh#M>>%ewd+[\֮?dΜ9/s4_w϶һWx*.;wI/f7o#-]h["(" 'N,Y2eҠ5Aؕcpٲert܏ɢEF!5:}wѐwY aDymq2p8Miްaà-֭)řΝ8fɒ;|馛xo &<,d)O$حuK6mZ9/5W:vRZ=܃ܭpg's;$xWl@ Xc3<40QYbEVxcXOoGV IX= |YM75ډcoYM+/ n{V,_ў/˗tC-qɚ\&1}BmwQ@K", V>GWgg,$[LRBiɗh8pKY|=;9{ܕ65<켪W{NXپS]w2wcQYL }oXP&ZfrL0o;|SvQ"/OB`YҮCHbygIH&f>WxY9g}&,PqdZ0J.w>XIHAm#cV)hcǎ5]6`ZQl j׮[Qzq Š66ۣ1;v70ylzY0[._|f.̟p'* A9"2> X۴)d#e ZX}nժU^谡ƪ"D"/P8L0MT{,7,=fҾc#_f<:S v][U*V Կ{ݱkPk)#o *]$g]q:h4v!O {c 4 wϟfj"ePd;#8ݳe8DD9+̢o>a Zr9Bhٻ'X@dHu]7K*󊖨>vA&1(?'o|x#R^[7!hlڼEMPC7 E ,_ɍ,F;kQN/5jr-]`5("22#Iy BG;4 ! E`<Çw-[֭l2CyM%K=&Vq~뭷D/jʻ; l˖-[dܸqADqЇbm)7-Ziqo=H8YPSDȮO>3?ȘѬKӏ˫Ԯ$FLubKBv!Br`@׬Q!@)JH6leK&Cj˔er'/v(#оwW=%Vt=S м|S q0d`ozbzX2z5G4{@{SKm֌x~> ]hՋ&ߑU3|dZVE@H{T!!K.P!!1 BzѢEF=%Z74j7H}mD!e!͋#ANѧgAֿ@ 6[nuY E@Hb {)wԯ,Tƭx,D2 { j H 8.X kڤfǻL йx:Yl+svX[##Eő,9ˌر?^ޓ-' |]5#^tcGLf놻đ#gq׬bN޲;$;1VG1cNзFv…?p;g#SF._m+u;tDl3b }7DTr@l駟,*mSf!Cv>֮d{TnQ"A{NXF煳jӦM#A/+@_m7$;]vȿD v #RF@Ts/|ƫՎ /4Oo߾CG௿ QFnq2sPz0or7+yom?APV[7#QSrZuZ$28;߮ziKnޜ+ܶO1h~ŤZF;L_/IHY;^ 4^XH=Ȍ1k ?or_j /Ϝ9m:t~2%?8Qx߸< /#uT>HPw=gIy\נTLd{&Ci6YbNL{m{Ul}\Uɝ93?. {4̕{"\w.VJW}$ ]Z&Nz7\y2q4XReJeڌ^l w:Hqɜyp >Yf9c=@E`)l9z:{Ȑ|JV eޜ1r<4(Gy|\t^PU]k(MmիRre#w:1? IR;v,AM -]tnx6xfv Hw4oGvd+5cv  Y ~l AY6:mwˢ}{"`ǚdȐ|רB< f͢!_ZsCB7Z{4(D`͚5Bl_~%-i!o~ @![juܻٶmX_~ңG)_pAM1V:w,'Niɓ2bl$w2f(h[kݺpX̰F@vE"$M/xEu @"Byx^YtR^#K/P6Xxwr>c|?pL4Htb Z}pA.ݽ>Đ`'l75~ڴmHnCiS:>^RNtb/RqW4ޓzd;D=)+Wxn- o ,ݻtW AM(R^|CΟYmܞϜ)e4V"ș3RSRJH|ԆX&̙3;C IRJ!Cj*Gl߾݌}>}4)Qy hѢz 'H޽67xC G:y<8 4LׅGV-'sJp7޲Ey,@-ZSglaw|0](^,$`,|k!1w'ɁLQQ | #]' Gڴ)2˔)=cl؈cDνo;%˿%jl2:o-J[s,9Xm@^xYNb%N6fT/4Xp0gÂGhUkKd͚ɞ#R5H dɜA)<_y3 P&koHyƴ}#6d۶&0.`ܹ}MÂq2?k,C]O{bZ2-Glƌ ٗ^{H*x.;׋M/]zmڴ174DzZ N`Ui犀"̜:ܠQ*gC!59H'&?pܨz7pj' G~s^cQga# "V1lm56Cpf;~fuL m1`6})ڽk-dĬ%!RI#xCc'OU#<;w-3E f@8\rm_.Lh^?-Y%莎#^ݶAj:FB=v[oݭ {2iJͳ;Œp<ڙ} HBE\'iZOdxG!/U-Ƹ?H;JC[|Ƶ"("A&k /׫Z*UOm<Աg}Vdb/ޓ' 28,8,f"Xx뵈TF-AͦW]ld Uqz%KUK|i{ws?5x,>W̽Vn]9r=+E[F#6:.G{½|!)mwCZ~wO"HdϞٽ ዜBl9/ z9]f[vĦxOw;Wz^BLi /mE|i_mZ'Fd;|8 ~M{wߝV.$˖L6/aqiE 3I.߶oto%_z/dv>WK-^A۶.T=mWH[xq>O?4AF0)NJr+$ioA|AC嗆v!|O6OY Ļ%߽QMClϛ7O&OlՑG"xc^}Wq6qB<ƃl…ѳgO[x[믿63ge!~!s+9 i@ʳ31={;wn%ٽ@iZH& ARvk3#66o~{DH}bK8؈ӥz}8 +W[t!2$:7nlW ؼ醷+NT`ҴImSխC0m!N;ޭ;+ o:$sen`G<{2LN2՝Q`?E9Ɣۏ?v ݦN{y._,:vG=fGo<`DEՃf|173H"AlE NXn|Fbʻ Ac,4kOͱqwĻ@kQPRC 13z5a_cT܉q>zhж3 <## ۍnRJ%<̍ '5|sN H@5<΃yy /5E@HC2rt3w,7d_uٻiE@\E|lb؅Hae;s#Gw,7_=0lNBnrK3%UEaÆ,\gP=? ݳg)Ø n\nKE #0dPt~'tؖv瑧@bwy| iҾ+!9W4; O:7!Z QQ9"^>=BzXKTD dMt v4~1;5::zԼh:7mw0=ޮY`z9tNR)V]ng*!ڷ%>_urN>D"۱cSz(Vb!@bŜa;5!K,K&[3;=#ζn>ɶ{u/ӏ;`ƌei"(I</?Uё`$jn\)!2zLA= Cw!=h*矗#Gf,= y˹t)]kng` v5~!ɛ`?~|/yÆO5yݻ0re^/7&e-j\s,N<-ςx#gĶwuvl5; N8-4Ƶq6^/QiΏlg{s/6<֐@ <7 -ϣ9t5$Wl.zyG!y'#u\Y|p.}-QTF?C{9gAl#}#̸}.+3b$"CMHODS?SDSx`#s\hΝ  a@o߾pض\lֻe˖c\cƌmۆe.V~ctʘӹsa}'|bvg 纵&B̽>}B?o9qp*T.]mٳg4LJonJ=*ឌY${*CSSKjVtgYv31[#GNyb%N8rxhupp`( `yԮƐ6'%lܰz 93g?^7z E_t 3ūzR)sfi{~bmRsomʤ.d YskA\)vTGOv.&r G{Nd#cw dD޵[&e7]Zwo<63e"3/uZ2a{jkgbiOK"?h4W3 J4bcBU3yqe'ӧ qktYp Go:^SsL "gcq.ףe|L{ݺ/@P=C ҴiS#m_:ھ[C.=RC DdǓ22, 0x)R^2gyF.]*̋~(`#8}#>esϨQv/Ū.hBV^nfG.GQ˘ǦOO^L^l ѣG{1tP=r̜94A d`xGf˗/_->}zyI#k`r?|›C~Ɇ$ 25Í49I û,*p\-<aw |("(IS89q7jH(\1ydawC W7sd]+Bq{Ť69AS_{W}Ǻu{`|zl5A*o<_ھm/" LFPysF;;2y}R罻WK֍cGʧP9iS`za עy}{R\ %nִ'`̉%;?۳GZ(\#I(ov9be4zh#gv-7e>zz\c{Ox|ʑjF:gӆwu&|\z]ᕪb"V" )#FJHpg9evC"]qH ;2%ƣ <׭1ԗ^zY!իW7$/R(1`蒳M M]˔]ro/c3l1^ VICxɓG0g!E<ٽkDKʕCŶv)2xNX$B҆… 9obJ*%6m^{@\^3 !֯_/k֬1/Kaw|TE@P&@*_mWZ͖e Ę) bò`jAn&mʤAFzJG492GtYre3.w +2>xf<ͣb5kTp* cSf;?HI4+AO/\ɐ^1zȆp<^zGxw;0/o=ozޜ1΢oV,"ءC}Tבb%d5ުeC)ȓu+WVj|ɼ / :Ow 6۷kb^4jXMxEd,D;Ui^<ϟbBpMssّ-♻;•'#g[K1'!cƏkS\JuXy/2zciE "@^dQ [ Nbr x##  =!O?5@p{{}mH`A6!C[aÌ50[^N4%@аv%4Ǹʧ(;vv21еoJ Z HଔYBF'?whj'9)វEPE Uޞ1T;D;vF@Y`O=Ԩ.f<D13 v77Pf2@p A0ګMdGhiZFX""$cD-CQP{ o^ʳC>P٤wwG:TuVqqOxm+ѝW"Cg1dAjDEwx `Nji/yW?i ` ,O۷˩S HƤ܍ny@zr\6sݖ ^JS=q"()j;wzU Ӵ7z:n($f?'Yb )LBbȧ: 'mlq xCp'O0oLPE@p XE:ux-U)vD7n D42,-[4RH T?.H:$RЀQ@ cȏ$U+;Wv^xn羱7bٳgwRhQ#AfIK.9? V&%嫆{J:WE@PRt% 3if$boGt8H':\gIM` Hot7ݴ&E@PPFѵWvI|@/ݶm rhV#otU=j'θ`fHtDDrN`DgƍdֶnjayBݚw?6G@~&Ŧn6#ˏK"{}۶m}ׯ ҷo_g^F WIW= 6w܃lٳ2gΜSyw̙3࿨S*":oE@Pd* 捕' 9gkn%>]x<ݓsRA/Ӥo>ysI.A%G 3͌>0|xi_UE@P>%[?N Me$l'CYj!hh0+\fG=yzQNK xh>H9-qMX;vlaQI&ҡCG/iڠADAofɜ9i…F{1;￱ B6cƌe.K.C229 P#ʕ+7n``hc[5k|+Ar HP{Ζ-["AhҤI&l L{,{ȍC b~'>^sMa)@EJɒ%XWX!=Yx=;d93,x K1RXJB={쉂*壏>g3 [yy;#A!'|X~E@P_#Mѭق\Tm7MOMs" dw0tF6p]!!5wd'Ӎ/vP m%m+W~*rfg<a@Ҥ33"(@J@hsMNIGVtm+x3f0|K0o[dHԩc͛74`=z' o2Vϟ/ݺu3[xH]  bxΜ9U6E @zԈnD94{Z瞻LWIw[ϬIHGج92m}L@XB:xҭk !v@Ul黥SE@"qӶh'ɉc;lOӡNBx ܻw =zȝ:u2I@s2%۽pO6mOiyFn~ ZL2!mYH1N׭=S*W,xGf5҂W^cD*U̎ǘ>yxs"3ocŋufJYf_4C2:vTB߁,ujfj} [ $=MwV/d +Xe oy KC_~|@Adɒ%RZC[E~BTkKM&?[ڶk/n4ޗ,]'o|Q>f:T/A۲uO'\>rHWSE@PE (X#p J=5+塇¤T1=q _lADLW-9~QaH*^p $a=ᴢ4w!$YXŦnTV&Xk':_|iFD)>RD<[G5"[y# YAPB -ns>,^,h &>VVmY=*@ pm2gkSOW˗8J-䓝DF}@RA;7#ǝ/5]TI媯>TN&ޠֆE@PEi{MfȐNRS4xe>{Qϒp60#^xK>J-eW9z7:E T~Pm~э^';$}WŶ `D&^U T4i%K4.DZAFƆ P8'V;A^z׫Whu8K*eGb,}W^1Z`@pzGc=00FE;ȵx n| Εmdֈ mqj`KKW1.#@G&#:VK\XKo_)`[D"" 2h[ij7<1ǎuFG~L|xb7Hg.ME@PE@P|Xt0L^bҲj49vӫdz$'1֭k$N $; uf/\gbmذL|3BC?_J9q5x0zSfٲe=oy>hnj0@+7^޺uV35kx/ɴiLd<YmEzS6!!ĹEaa۷\ی=Ɓ= /E|. PGEB2'"T iGp3m{|pyq}3f m`m`=NԢv("xֻ(LGv]w$7mdOqv3ivO8O0Arʕ찈΄ fzkK(JYV?SCXlae~ر5A„XNaÆ{# -FZ[H fQ2x!{i׸>7Kӊ"Bl/oiFթ yuTlg! A7 Y` $Cu0&XC.fʕF7 Hπ%ܑAGMH\h\Yd!gr h]RؾJu?={;n3Ow}yv:>|DqђWS 8 \tE y$|OHN:N;:w;믏W36$;{OyN(SLҬY3yW]{]a KMP%Wgּ(x[Kܒ6clٌ d5m[kCKх͂ O0V\;wlcó?62qe]0"Ь{M@JBta$mާ_X .[Ax%]${jߊ"("DoO'xb!sҳ8-?-iE@PB %Cn$X [hfHv! &j#$~hO~AA&-N 2;j"pY/gMƇ%\ _~/Y:;u~횜;}+Y2f#g^W_~rϽwKLG("嗟/ g{("8v\s=I{" }Zwx\a9?ѣ*p˖-Ybulܹ@7'0nc.2ZYRdN}n?l͑1"A_@ƊO?)@!ٞhƕI,rʼUK|OEZGZF ("("(ui#6[Ŋ t$ݻJ+qE@H{<_~=rl۶ݧOgtK(Nx/wcGݶVPE@PE@P ~kO0A-Z$Gn M: XF3E@H(wo2b^ !нy>74w}>| dgϞ5+۷oAc[[H<ҭ[7ĉ5@`C.C9s1!I0cAauԑÇ M!!҃ 6!w)ʕ3 8*/^cZڵeȐ!Զ6=ydo >`mڴO>D}]E_T)w 3F;fڄH'@,d;+ł"h[1?ʃU)^N(WB[oMN_&{&aLE@PE@PDDYZu|˗-[ ;׽ƽ4(@r@@%e.>쳒%K{-[cz˖-.]:o,#;򶆧65eްaC׿e}ԁ}[߿ ,*PR[ !Ah ȹnCc2O7,r'"[jNΜ9ݚ4x{۲h/E q9[v;-GVƶɛJCmqpDOE@PE@P vwkN2d ͚5 HOtEow+ٞhL;VBVunfϞ-/;r':$>=x7c@<[ /{,AW8!?wAx0ƒTRqCv[?",˪86Qg;̖5"S7Q1;i'>x=bڮSB$X8̚d4uwexr Q闭{O% A\mhdl\ݵ?mY=*@ؠ Ѡå%ؠY2g#6,@rCME6Fmz·]W67o,N83\w~I/(@Ccޮ2IB8CJHûWJ%*CA:oZ?mڴ/FxÛ"$-*Uk %˼(!Nu"O?vOX\+[{ҺqeE& O䈓>E@PE@PE * ;;}t2e:u*`n4oݺϟ?`TE % 2).E@P"*u5x[Sr=o_Y@!)f8ք"("(" %+{lGuȑf;yGmE@H (QPE ^xAYvt;JnڲaE"V},=iZ5ڞ"("(˗/d{wpBAJk;RlڴHʶo^RN-iE@PR,J[WE@".k>RʿX{'}w7_Iڻ HU85E@PE@H7UV&*/7]tңGdsϥDtΊ"D£E@PRޟ^O/SnL g-*#,g돚YVE@PE %!v<ى7qD_APX1??~\oHp4CPE ASAPE@P P!Wl> 3&%& ?~-k'TE-Pؖ7tмϜa2N>p\!cɛ;|u@Ń|-lJ~[9_8:~T]U̔ޗ(SQrf()UKʡ_ʉNʵלŌU\ ɝ'WAsٽkW4h!7F"(I'NȴidԩrܹS;~y5E@P!{pR"()|hK*T+{crIIʿo; =_ ?7z岴j\IL>oM6-kehNx#gO, ?])g6s>nX/)]6HdSFOdӏ}=|ĠRBiҳdˑŧLb:n !oGp?|K_5}̝C|)"'[l1^V+t#͛7Fwf*"G@%ecWE@P@,eʜ2bArx4+W;eHs#g*H]w!- Bpw,!8 :5[IO<{:]]ާgS=FTJ=l4iT?de'rҾ\ ,kF 0iX4l.n԰V zmY)63R707oдiVjC{ݒl?ޞ1Q~ ڼKjӴ m߿/j`F b"py;w.^>lxRmRvQF`B!ǐ{`FDČ Bwo]DwmU$de\w;[%?DG0R'FNCg>zpV'k4}f=,a$Tv#ydϕF"o[/ 1?@dAQVJW/'Z#g4(YxqȝMk F`fƍR6ɓ?*[,uԉjժ (q&#0G%edFd2]?-_ ۿ|1yRAlZU}}m5P7ѢiٗΝҜ`" g 8͝7Kf%36~:p;, 'Ҿ]dߵ*:w>NPptnAG܏GA3#"<<<u2"ŋGtMڿ?խ[΍F0F=܍q\F`F0@@/+sx8+TOM dxbljNp37;hY?E4{-{k i)kpv@EǿA?qӨ_f-ro{͘9{FEsKWf%6ɱwT-(O56vK8^I}VԸ=EW7~ڭ<^Z4q({ޯPpfq iefк$[wЮUeyqM8YKŮ{ZJ+z JKQY|fF TÞ={7ݻ:B͛:vHM41b3F`BK.Ѽyݻ+J %IrM-[P.w zA.\0P`AZlY?O`ȄdeR@ȥ"(%WBTeUjɨ~Y3/Pk(}w6F 2#;o|᪄ jѠP.BVޥu/:ya3&͕sMiRC -ӥW8N8KsLꉯ|-jZF7dhe.RO*K;{}GwhV fs,r}/Kx_1])[0G˗v t<<Ӈ&}j KSn ,e!Z+_/, vϞCGMxqPrŃݗl9rr(UL1}1/_ҥK>;<ۍ,jԨdggG;wEUCO23wvZWS7i#(qD3ϟ><1OeG򫗯3rAFT;>>Sf. SMjCLQ3vr$UqE>}: 3ԠE/99lCp?oٙpWq'NׯOu˚ҥS6m(QD|0#`'zTrA)R'OǏݻwT@U=ҜVJƍ<:D/^470@DC@/+ .Qlɔ*_]l]Y&@2pO&=-ZJ]ZbݛX$1OjRf|7C/"Ӧ5΋u9n: WP,5mmLVDSȥ)KFUUnB>>E[UjTuN WhbČA[)딩RP?o/~]IyVҫ=(?cN7&MQBESb Awpl%/'rJjו&]juI\xH`aCYd],>L׮ܤ"h-["0z\2z rwy J9ae7 }~9/'JTqϺU4#UA& ԬuCuIWnҫ:v-S$:l'=+U+Gl҈]2Z_>>AY6k&3]uq3+F2HӬ^Lc% wL+|I3X@ZxjVw!Kհ,f=3CԳgOJ2%nڐlO0!ݛܹCp^:[1#{iׯ_ӽ{&[ʘ1 IlB0U߾]bxC 3fLx*2Yam_.FB Q(Q k?~mp›:ujy^ h>ϝ;'h |[h,^0aTG?GKҍ7&Kg>|^Ny=>}Z&/.1Su0@xD@/+Ye4Ϝ8yLj*SGGox$c,ׯ^t:꾛kM|޽nmM)w"Z>'Ȅ$cjG멷wFw,|M;W@r+U{fj4{BYUAK!O7*^>%ZVx5eW"Ur:{꼺4;:ʮ Mڳf:h٘)BV2; X01^س _e $RɓN=Gɺ/ݠKWnQ<ٴ`~%Xݹs'͞=o(EwyNAB/گ20#`fÇTR[x!H@n۶Mǹ}v<$= ܅,F+Wkɕ+G)7jH#֭KX2ގR-1cЄ 4R_AWʇw~…U$קLB'N$5wU'NIZ㾡SyfA6mZ=z45m+U`ooO[l2o%d`C}!7ӂŏD vrjdhmQq N/|fBRi)x{iZ8y`B@gYXbGGhVIBިUtI5fjZ47%g'}_uqT?=hֽr`ܮ* |2Dj 2nSF*K;%ڽu1zu6Jv+i#bѣқNdhd2e:H`=AΜ9CgϞ[޽{HΜ9%эXAq>`ʖ- 1WZ%ef0QL â4 )xO6N8!C)4 o׮$“&MJ#FK*kӧқ zA'O͛ExcWA2ed[` AI Q^E!Ú5k%K&MP%taɉj֬)_!yŊkwSq]>'7C{6F`#f2B=W398qQ9oѤQ}dѣǠu*jkh;x5%Nʟ >ܿAcL7 O~PF $:cfxδEQ#{XN:En9H>eO @$@ =cIFbwEN2heDr?'}Eiڼ (ϥ˕$wB1_ݵDLB^gg0^ uu̗Bj4nxOCZMΚZz3 @lJ;QBv rr6GůbjE'gx5n݇8ea*TڞMxT۞> pK{ěMu*[{BnνFwhƤf2{\RHO#R1{of8rA6P90#`&ܭcj% q<~X#;wl*T 777?;IKn嵾1FJɒZjI݊pCCFNbUtr [9dW?@hHЌG}l KM0 HO!Ο?/uUw k׮-t, 9([U偨q_re?"}ćw<^=; _Fee.;Ak:\s-4%NO"%Y⃏Ƀ$KdUwD o_""*.$3ER!}1sp߶y>mrѺI,C;W/cPlOm ਥea+vߙvmKOFТճ&6bLe_SZ!=s%Y:kիBVH>*&aЄ(u;IWc 6vl%In%q‘U˗)J)gݱR{9$('^Wlb T@WS~wn[reLGlY2<(0m ٥zNٞ#kF3;z{wr1LqՕxFhd{9+.YPæA|lw!5mO.0?[S:(qH|&q\ 9Q z SϦqO2ú5KΝK+V~kZXp^G;~1#DLp-гuO!2x+:uF<3d@F;vVb!30|a<]аj;LGYKÂ@#H*HnVs. @+W'; 0 ۣQ3A+i `cdR|9`p^VްvaUj ͘9ENDo^%ڔnt'~|M󦏖'!繖 F7G sқJukߗm_&=-o|4W.T$nд.mZU^oݸ4Oj:6vՅyjzZz;cvYb)=QSG9 V ;v n!Mqypمǵ+7Cll @㇏Ԫqg_iY֛/:aBԢ% @͐w!I$ҧKE/ ;ѫ׾qn8f ?p9VrԱuCCء)M8PmTjʐ$1q[.讟p[K1X+zR&MrOoxë[i_2Ɂb̡.ԢbEde/ O 3횘hG;V rwYFlPp^oqչIlGK!ƵҥMI%k JB>XܰbLUhQbu p{t3g75ex7rIJVF`?"\ya;vlnm8H:tȏ8nݺ -5́ Lh^/ zTYjQR8ޒ%K/CL]0+HgeJG]&dN3@DDR5 7-=s5ڢϩvԣ=X8@V̥уPcO9hѢS6B{:#eB'+XulكYN;$g*P)V|#{-Aþ 9&uZKm[ѾbLxlHiHTK@VU 5C}Q _I{GN}k2ʟʰsbه鯵c4"=>xD8SF4=K̄y-IڥӘ"$bv0=-QX v8&/!݁*+iѷs"ReWx>CK;,wsȬt6m=jY9.ׅӘ>Tz9yl:lW Xjnbg[%IxG qv\+;4aN>e1RR,hv/O6jloz>?|B[]09e2#6Br 0,Dt72x/;&O1#`j)R] <Ǐ/ !Ҷm[^֭[ValeаW/M6ɠ_dpܹ#ye̘Q  .gF <#PF}Z4{+V ;լA/ !wd yYI3]at-,59̐x{>#em2t?*Cg^Ϥ #˙';6Oф飨mt=)ӳ?u1X5GeFC'1#M/H=wiωy‹K^t1zp@ F b*_y?5T,N!A0O/T !A\AmHvto޹)t&?ӱM#ʑٰ8.8>k {yZH%dTz)DMP ^֬rrvkš~eiR'zOQؕbd ¨<޿h(8 + ;!? ^cnTF`"&L`%`z/и )AN{nd 7F )ׂ:mڴ Jpr R2/Њ?~̌4 xapwE <~Ez6 "i#05Lj:,M[9 䊴`XpGD?k9PV])Cx>8lߔJ YS »/Ȫנ&hה2fs %ۖ+$i OuvhӘ{w0 zz!$۷ 9O #ܬ7x o[vQͺUcⳢ0 5n%P5ZTj`G%7l(:Їׯd~fR$ht5Yc15j^ZQK'[{A@]|CJ,YE^#pQi%?tw $ bdא*|~ L?!,m֟ӧM 5FȰbdYmmC5o;rx9HBuh3a11F`~op[|Ç48.U+VLC"K-D1BDU褃1 { @ A+g9n^xAXmW]|t/e_ ҢE 23C~ )G.!9`Whc"8_iƀ~Cjժr\hCz_~tС0G3gHykժ<7Ezի"=ᎱK.-BmTBI,yyyO:% raB:n<,m2`F "W^`жA}4p z.=yAmO$%OFgXs,8s16oA =|B/n{bAXCO& Mt `~|Ardd)jFML#gC!pao`z ^'\?b{bxZ7<ౠ5j#ǎe;~)+cT3~xj@ɚ?-k塝RN1F"Q-0m%h8t`Z?΂ t[W_4vkNɓ',kF`&B;@^CD0݄ ҫWI6*-;v,޽[4HuHdΜO(W-wޕEAHƍZ ) o={PVZ OOO?8aA-7NhAC&L0+|͛72 0|R;2 |aqgX}ٲBzF)ݲ]h\3zv=y̬{, 6Rٖmkz]v%cߌf#0o/#7Ps疲-yxH@C Ú5lؐpg :x#Ç Q!Œ oӝ;wNJ`$ٕ7~l 5oޜp (+ܱKǿy8P=x8۷IǽFzB:Pc`F`F`@Ă0~ʴ{ItprCso!4j\k3S.q\rGEq濭?K'N k֨&ma=qժVVġ2wh-u9JWpj-bZ5KD^E~=ʜ)Vv;L{DXkN8,ρCJ~t>+4 q KWmjwv ۺH2>V `> aAee!n#ovH6mԏÖQ}cFňɎgH}zYpxl|J~ڰѢE% 0#0#DvUVjp?A}sIujbRItZF^"@NbW :2MYHؾyNZ+Qt$@p)Y܅gM!SLC9+ϓ>~JNK!zumk r`tUzWh,wPi#"\M2EkK/6m's/@-rtc >EW;pI9u|*]lЮ0#wk 𱳴lFc|<ֺ`ZT!3J&PAȑ#iȐ!|0#`=F`F`F+]G=~ YȖܲzѠN My-z\yŎmU n@7oQUBr@Uʛ7Et6F`F 0X#0#0#0@@sk#$2 A#84Gp am##0#0#0#"]F봥߆xslokQR%ڳg?Zŗ0+!ikWx~:u l &0#.xzuF |Q]d  v?ܐ\rC {8B3Tþ#)Tf'-P/^gϒ7%M/u W^GGY̘1)F/_͛7 TRQB(J(_OƄŎE&ۢ=ɕ+bcX}w^z7ecㄼQ@q44/P7n\úTq͍&LHe˖ .P)SLVAǏʕ+xgɒ'; p.#0!EӧCgF \"pe0G~cn;wo ȢGJ-8Vg':(7Mu l@dBDpiͤ|q\fMZ`$j-14hMn8yq~$TB}8G &hDR+ /]Dv풋ŋ3gΨ*ԢE }6?~\C"I$y{{{-ԲeKzgԮ =Vw)s)EOE9s攋(GdɒiWQ sM ̺f]tӧO͛gm`V9\0 2"#0EbČEE n7܎`pA7d٤ sI1[7n zlFX<1Icc#20l4 aALJV_$ÒFyHbXbt5PZ.w)Id=e˖r?yWjde}emŋqyxx!۵B')\,rQZuFF1?|rvv&,$KNk?Pd||ό#.Y4cs [q5F`"E%Q.mȮq1i%#5x-Bzᅳt"! R#[*UPhnӦ Н;whڵ3ԌKk1 ^:U\YU'ʕӮU"$m{쩑܅ ٲe^ǎUVI)yǎԻwo5)2tP*_\HF`F`F`F`'Qd;YM;<ߡ޹sgIϘ1CʸJׯObW;$QP? n[wŲjժ^tK.M 69rHĉ˺3gp| vʠ?_N3I8pVz <- d? :VX m [_u-*AcA 0"Բў~UXV]V`F`F`F`F $eϞ]F-;u$=a yJK[9AhiixvURԣGOEdSұ{F6037zzx3n&:XuÛd;`u-Er qB}ܟ9s <xСC?ehlyzH) #0#0#0#!yOe5g5GYG>bpx+ovܨz MGx]@Zw<ժUK\D P8&dy@z D%SR;dc+T0Gu#|"x_e9ɝ >+!e=Çe^ 얁Qm6Ih#!/HWހA /z8!CF4½nݺtm3muyiܸqǖWńWu@^xmF!5#ǩSd} 7n,$ljG_$kxCՎ(87n\ z;s !*ӧ͛K| :v 5+ VZi 6_0#0#0#0#)ڵ#p ~k͚5 ~ެ;+ܕ*pss#i /mڴF{-g P \ָI&| CyΝ;cBڵ_~ra~\3xK"CFakpl#^Ql^ӧg4$AJ *z8VxFUT1F-`)<SfϞ]j?EJ=/pHX3h#a oZWWW?f"^z?|Z)>$$*4o`z3b\`a? ЦN:0WރsgȐ!iHg؆Y`5|ׯo޼ի'llbʔ)@$x^X̌2baBH*ό#0#0#0#Gm.hnw87ծpCf{& ΛP l dkD/ I[5˗ɓ%qH[ <ؚv !Q5 XBZxzzJ1_esn(g`#@U޾}{adp{k\'3؏rF}D<&󧌕GE[@^EON:U#U]]GߡaМ Vʰ}ǚa[OH D;>`\=fyXq'wKYcq`ٲerEVm6+Vȩal#0#0#0C[z: ||wrǏgG,p=r-FBF.At〾2,d7R@CfGD0x`3F wc\"e.V`*U4kaF x Zoz.\\l=~, H#G<|[t7.cF`F`F "$طrw+]2Y_#|߿?kI^em /~xk1!A U+S?~$< [}َ@+l_zSr31!<رRh eTؼbDADl0`܁"d#*<F myb&5j4*Sls~zL/T'M./yѹGd:nTdyn߸BvmǞO?!N2f*5Rt\y,FwDءBmn-FT)Fe?|{ұC's餐&]jJ!-+qu~ef#l(Qb*Z\lXV,6mkoަ1UQ>Q_E(~x/ϝ@^O$IS l3Hܢ Ph\, ,Fp:u$!sy@Dk]ċZ3@FT׫>胀"X,b1E "krؒVCӊ)" Xӭ—ggg#$C29ZкԸk+PFʘ7H}l~dw{ҳPW%et`pxF_Q}c(Bo&8 śA,_Rr!-[vI .ԤQ?."'Hx1p^*0/h۶m( ;#oڢE W8PÃ̙#Xe?41.t_2}2Qa WE_VdodX/_/{YVQ^zCp`rR+лG9֭['"X,5a25jԠf͚Im/xVO4 lսfΜ6m*`BaIx~0@px4(^5g4W/k*wl^Iר۔M_38vL9YΜ{\/_YȒUtg 9^|(V?AX۶m圠lٮdR[a;kao~,P@ sEV#P_H@2 XtN8h;r`O}6F9 Rߐ"dn\@o_4c7[ndĥvh$gӥ'(-zЃ޾yE %ZS Q>WSi(KvВ./95y-^J[7,%v#AEJ(JW.֮رe<8r 4 YhRޔK=FL!Q'5BiPVjv=~ԷKI֫O4x$ǘ4m^k@YBpc8 @IDATmBei޷֐@@&Z-k^ZmW֯L?>^,}IO{W;ki&uNtʥ߾MK毤{{Z"RDP8\`]K{jۢ>m3g5sѡ=+d]¾oq2ޮ];.dn{<#g T8ňCJ(?{UG]p܆`ȄiF8@d5UREP*ό#0Ѵf,1qNcz;7^~*Aet5 gȎƲd3yy+~ɖ $AL7eSG4=YTThiY4wH6s P/L0z'~h;Weigx.jX0\Ug^]iz>E tMrq^cVW/Ӑm(][V8|\xMLIXc7_@u*V%< y>!N:dxM /~ň jFz̜%'M|MumTY\eGwojNBKFǏD[f]A70ZN+w{MϮ7BڴDcNwɣ3Hj!Xf.uqq 7F{׌@G Ud4I|wگ-ZI,^8*՜2eJyg2>>>~dnyJF]㌼>}wh#eyB_M8}r܎`A0? }ɀJRF?,ئKNiFRJ[~-q=W~}VM?y&Os=ٮn E7n'Ȯ,ޡUM_.;:!`Vzά Tv:fAM DR'܏Kr)H a Uɮq[|ުJϫ̔hإHlw}Ӛ0kۥ?ňK_EՓ(L*\P*Eܾ2޾ϰo?H/{4O_{1b{-+{ʥ)!V6+ > LZH }_M;oaFm^DŽ{`zB.Gر)m޶O,PhzjdĉC8ҤI`Β_5ǏAӨ?.@7.0[!0l0rqqϟ?AGCa7ry̘1fɩgĈdggo7&ȷ{#%tTPT=JS$/}8N&dܹuUCZo߼}d$@T~:_~36:f x!&m*CN)x2TUH03#0%] 0Bu%J$_~yhiH l^%cǎ9v̈TجZoN֙Gt[l A˗/ܹsi`[@ x#0ePTkF'}.0QGv@$iV_lU 5)MڵJĎm"=+-?G#z&R\D Yw +U$БV Qї Ow'+hx)s4 PPy•QXݷvÜ~9y^h"$ H.mz˺8w4C=j(4j`Vm }f"G&E)KO?YNI 5'NmHBbg;w0 -ZTrdF "ˤI#8s|PwIo{AFY|D8<==|pt$O"˗/5jժEJ :9 0p/p_σg0#LE]ѿ*u[z-YX*SItdz$9ԺS?}C'mv{q浯N4AV}.XlJ?%T&0woCB/~LZPS]W2]T}dCwW@܉;qwD7 q]-gi60^݇//)S{}`NV\1/׶wO!(R6>t7َ ˼ ?9 jjc')P-ʥTBI}4$_[37?G(S4as62И߳Ë׮lx3FW~'#8gͻ^Iy٫<*  ,Adĉ-o 7tFQpQbHT /h^Yd V_,9k]W\ߐ5ZG`'vc C^/`؅ߵ7ocΞ=;eȐbߨ6ll{;,wPOY4 FLX ϹTRr7nܠ .P|(]tAjCvz w;}<-F`ZàiO96+O"A._!>iM G dՋԿPtdT~ :<v_} q@۝ w#d8`Hő"E ˅,Q튨Ky}"-4o5ȑ#VݬWAD휅<ɒ%KjUMߪU+If ٵjբk}h?N{SN 43fX%cC[SNHo˜93}3gPǎlI7mڔƌcBC¦yBHh Ľj KD|?ŅПf͚۷ [DhlE8gϞ  $ @+WΟsv=I?~Xʖ-|R#Q/I$ZU|Ӥ1uH *c*LG'dF(()bBĞ ^Dy"=zp6y.[L:4FO?V6o"~ԯ17u7Pyq2j 1V^35.n7'ta~. 5~4~ z/1ŏp Uk7"ݾ sCX3#"Ρ` o=o7#FR[w1lGexԃrJ._\zuo޼5VOp,(ݥK9;w.mڴIܩS^:c׮Uyz„~ZD=p͙3G+Ȁ+C&QzC]*]:ŋHEGhٞ>c6*TLs_#t$)RV# [W*ɀ`ܰUԥumX͕rsw 5m fsJQwW/Zh RPj142 |jN8$uݪ=QlyųtBY2o;n6ݵB3yظmA 6H{1U(V%bLF 9dӭq*]MvXXTzy7nYv svlCǎ9Rwbwuu y0npo_˅w%G 7j- #M/Lhy„ N w Mzc ʰP :Pxq9[v)N{U{)S&L@S3v4FAvmf 2eH'"///b?7n,4 `9N/Ĉ+fB+҉'$I'O&x[2`jժћ7 ЦMjԨPhQ9 \-p'cFp cD/ZxzgZ߶@W*N{ YKlܒHMm16o~'^Cjo Mm^T.X4R'bǍGU=i΂6TNλ0?gEO,-Oz߿1CO;.CZ֤wnȣ{-νGB:#a4l*> ٽ~-1yͰf4X8O? \9t3GQÚ-b#Yǎm;FdfѢHCM=h5\-a |F`: ]]]%r''' =ZzCCVr75k֔ aв r%۷;Kzjիhu;A;eׯ_/"&MH), X߰aծ][.*-[VJ  ;O^ a?;ke\ |+(wV8@dca VX_Ual͚5V wDAcQ}̖}p[#XCX>rwA=[ 苵^ɲ,Y : ӝWM]x&ԩߚZwK %17y]-K~̱y Bq/vݖ 2z½vdnMډ Ih1t90nԸUW;&$A>yt_R^,Mݗ6]wsj'p-S8jc.h4rx:y x$Y?khӟ,϶YZ/'ڨvڅ~c[Ă'M?Ką%+.V %vd2k,:ːBYKB6g] Bgݿ iH)XpLRZ]x]l|ΛbQ8qM`1 7}MmO1}TZM 5[ۄk3$1ߴKf.D393hO=V~FC]+I,gJčcVt/"ll R3FԨx?c))n|CQ{] ӊEIcZOȊ!.0Ά̓'OdLlĉgS t fȦ(U9_|R>L"»Zz><vȞd͚U0E[ew -c=?-xl!rDK},&-?rI0!A`Ţik'znҬ귛L2^`469vnYaNZ#0Gfn _h/^rqaF '/g|*Z17C`Rq ھ1 pWd;TϟիӜ9s(  `HEkYI@tӦMl |!a~Z|̷`^VDr^4X%3o߾I)DDFɒ%Qq͛rW)rg0#~9f%τ(|iW$;umY@E5B+8|ӭUprF R#Pxq#&Hm+om0'Q nFOϟ/Ϫa%auPGoЊDoʕѣ"q{Z*!0)6--ӧOݻRNUa F͛7۷oVdvT1={P4iYܾ}"CThժUF`޽SE\c'%Gpq+h׃g>r~HXA;0Tty6o,1ĵ2,vڋ*g&I\4i`71cƔYx )K џ^]v%z;\AsssӲ޻wo:uꔌ( 43fx >HVAȑC.ÍMKз@W^mڴە2ժUyizx$ xcU@# BK +z;v}bqɒ%ůڀ'g"E3PyB(Q"u)XZ81#IR:~*ʔ50toNnM=k3 Ҩa1w0 =ͼ#5-ezN ?8ÎH2ۺ ͻAYDh1Q 8 Q [a\:wL 6KӦ4WĶMGZC,w~:@+|%ǣ2 !JyՅ}*U$~ R_o( )@(WN;v\ F8m׮]f͚Z6v? +NnHpַLSn׮-\а'8KկrL' +L9'A;v̰ >нR+WB֛k׮鳵4^\XFŧ'Q +hV{p8 Ps 1`o>BC@ EG-,C '憗)V|V2a/QUI@3#"PM3}g v;M[i|b DCLʜ-7-P~l#0CB2ܖ[[֧,lWk'_3靅*Y)}Eq»wז)eC.p8/ɳFr h3d@nݢ\rIQ ~i >X]v6x#*ة#U=q ˨_UN ǁ˜*k~Ƃ \oXO'&M[p:0aK<ܕ=0 8.8+WDGp2UBqk>dA*/^XI-[A#2c>  X)4_AK ` XԣG:|Խ0ߊ=<t$g6|lj"LivUiLCYRƮi N" \TTihzjJ>W^->ٳ|O0Aj\8Cb;s uԉmۦUK ȍ|<+ĂG0]DJ&{1Bk q2n _0F`ΔR\U1`)_2#`lٲpZ2)2[n-xPCJ iX%'# S\t1N+CrdF=88rB1Ⱦ`!$:~|y}`Z Tedp6w# Fkd%<׍Kj@/ЗW჉CX|RR;-Z"CE~ W+۷;*cM  XThD\VRuEnyEC!6 ~ڴiR"X׻%z"=xAw &ʰ?Dx) u70.6F قpWV\5jx*gF`F`@1|n٠)d[w|ҰR#C౎#$;0pD .Αanmax9HC5kZ=U$#Dr0wܑ|jذ_ e35t늨FI&HquanWH MxЦ"5pbaѦ߾}ΝKmڴWQFr7£GɟGV7UVT~:agY޽iܸq֊=F RJB%g:矿ų_.Ȟ3?ppPE0@8}0-gLG$ r}DTNzd˙'9f srpA [ C^MQႹ(.Q|(5kVVV-#0@Dp 0Rڒ%KPp<-`}A.\'FZjbUG,bBSG>⏶>۷wV޼y>WGx|kD wW}VNgqӷo_6 ܵM(Q"'}ᝯ jBqss3iK \#jBG(0x )٦SL@g\xrޤ__X1ŋ/E8 |0;;;;;w s>.|0#Hp!p=F !fSqCmgNC8poڠz 3 r3gd3 k){4#0vpg3 BW0@E2%K$th끫a>;7Rt$S\hüPO;dїcX}Ng%Ȗ}+özʒOfր8^FNS$mDJTlBkRݺu(ℭ_̉ 2UJFF`B IX 3#_>SW:'n|8ǓuF`F`lv/ v S[n8k/& 670#a`=< #0ZӣwddN[NISp`F`F0@Ff]Ă3hdkƍ e-[&%"y #06E wɍ1#&ў봦:tBEJ׮90#0#DT #rJQF&ݻwiӦ2K,a=>DNӦMO jժQΝiǎZ9DHg}Jhg9ϟ?j{'&+W #DBNEF^b-jآv F`F`F T d'OPv(M44k,\R%rʔ|ŋիW\]]UVrΝ;j~b$q eҥ̮R UPA -uɓ'.-[Pɒ%iذa'6<zG_ڵ\$R8Q, 0 :wWY.vxTh{{ӝ[_\s $եįLrJG2 Fxܿ_) wo"#/55h:IEi9T0@dFR3gΔ$#<,Xy+}R4#3<#l7nAEHkȬϟ_'0xC G$I}p=x[#ܱѫW/ bn(U~z\2xãAra`߾}a)`8@dpM`0F},4a'A@IDAT,@{2e0hpc ;wx:::J\B2eNcy!tl9z ZeKzժUuQ֭[7mENKtɒ%ό#`ݹV-ͮNTӵvmďBsA]tn4aJ|:4F O|qsBD_zz/ʒ=;Qwd}az4fHW{ wd`"&9?½gOӦwN=R,FL$MTb:vXw9iP XAR@BXBz0C^FO_9vp$mneE0 bNoXm~k˨CKS]FUό#x R ]V|4;^ -aRlňKmkGڜvmv1@@_o^MZt]iĄA(0}"#D]:uTׯc߾}ꕔP;ƌ3 4B@C[\Iw4$C7-SL2`((ۢEdPTAf%o޼m#Zhn b^ _/1V`ྀc@vT."ޏj3`(a;wvxΝ[AGPm"!1 [dcV>h|`"/ nurk|`h+ΐ~9>ɡ3ApRNzSyחzMw*&vIgϞΝ;'=!eSpaKZW0oxkU ygO6l3~g[W߮=pŧ-JV/ }I~h{|A 4!O":<^1!:5>cF2 xgxCZ[l#1Xrm^D\vLŚuX$2mنP櫑 ٙRBeȜ#P>  [7.ӣ)n=WUG(iԔ6CE!P#y]r$,d i E^6ѳY NOgΖ[Eݳt"؃gH'LҤL'T0ٓt2S8q 3c6=}=!}tEz깈!%ӯ4/k: ;S._<%#ČZmB{^|~ݲ\X;cF  $Y4R =zPǎeP7A?uxjKMx|F=H@"CEoOh8~ͭʥJWnBB^H{39ٕΝ;e Qpi/^qpVzs) | БGBE@>(mڴjAA\Z5fN0@W C#ҥKUS\9FRJɗ>Vi? xq |cFe6F8̕0u=2y+ϋgSEC/'SDD,.?͞@hCoģ:P. 9HQ'([2ݽ 2#~޲n) Ijޝ6lM;64![4H6:?aD/ڰz&탺$nPzpP+Nwo1bRi9!"b]ӑ;A*G W/A@'Ol2Wm`&n T,/Sz Gҩly?7?hcF 6\عe -7]>QSQE}hptXP{q/szdm}Ѳ8>UU>//E/dxRuL F <pr1ՋWb7SmG\t U8/^~*Jǩu8{Q/Z8/9& ڂoc/gōMeJ32 S=<k׮t}6Q֓j^/9|׬Y% {a(QBz2z]ϨyX'ȜlGxQΝ;Wz@!a5.`,#'֦.!vQ1kMi˭Skf `b5?9-ꕖ9َ?9SFPjGCD_xJMG@"io&2mN>c>z2F=mRdBcb߿}$[vp'\lĎM %HleUuU oJ[\Pe!r`wU[{}p8:~:N Fd; ce1؝PBn/~?d; >Z'2lVdjό@DBcCƋc\y\GhZNjcڿN_`;F.].\4#a> '̓-|Sx|߿G;HAI/ÇӇgk ,777NtvuhsrA+BRPVXQlp dzA@NpWw^;62;Ƣ.((+(798,Y,g'''0 Mx#/r37,0`/֜a s]u5<\8Vx+V4qTE{P0ףz. ϨQLjlC2V@#2ql‹wA~:a5 4 ^oҤ aK… m m8wuuVZɗ M`[qZ)OOO鑎.]ȾTmA^z/U|H: BÎ;&^]vM:.lAݻ74m2@(<}/+C P<[_Q/bPU3'kȯt1 )-4}$iȭ ӆڣ mk, 1o_Ь5xxzðЬM01]xvx.;rBTc87;;Ht2f%%F,M A6vj'sr!f,9nxdws bΝF?{ 78qS}(/9ݽu&#BFEtRc(9A"I-0,_0EhLw~_xFv*S&UՀr*(~ki֤arK(BtȷR݇P.aMyM`Q26y 9y|;ZJH|8H`x={ZR=ڏmE}: [iժ )DHFݽK{ىws(W2!">i$ޫ0pC >;>E 9mpP@["kYXHnd585xnc <ed -T+pd{ #/6Ffɒ%'Aj^8knQhs`'1Ƶ[&]!t޾O,kWhؤ_0!EcR2EiM۬W$hPVEmuj|T)Ck@ _y4;86D|3g 9AY/q_#2XH1LH b]lRd*/d ߱ڥvra; i#k߾yyb 1XXB@h#GD8pG2XX&Ο?o-(=昐 p)Q^x JR\V| $__vc]J.5i.i*:Zf(& ף'A+XU%s˗O4+MHwut@+OKgJu2RVtf_Qg0᱐%: Zoׯ~Ro>~w_e4iU#UjUY|.Z`Ϻwm]#U87lIjTږ XPN>3aKbZ&GnԣC=cԸ[X"ԭY^鳗Wڵybλ9urʟ7V&G̦qCB`B8qHr{;HbP70@B=qCİaK<@ٳgFEd?0f?rQ#an*-N9)ougF0E^@M8,!P cǚĢ0jl&(P:7mwSڮ^VfMAHvΜ8$UI^ǸN*ULudF(p9 wY*zsv ,!~ק˘ըXy.xvC2=IQ۵u<@~ƚKĥx9"ֵKv\.2d#Le$+ʰCo_>޿s)eiUZ;3Rq=x\PmE,ZD6GF#px v^VGmoqX? [jjVIܻ΋b:PrłDx4#een BɣR+KJn_-pO**\hn+C`߿Vm}'.t FEʺUIO[o&Rz}SA7KsAv3beӚb T6j12k޿- 9 RnzvX-6yYZQc3#ɟ2gh?SQL(8Ƌcr/["`$+cD#~l+ #_tV}tH͛7rp3f "kD"x#3LJq˖-֬4i"|Rѣ ~aO2#lһ[]#2qMdf5 W]jM6ԩSǏiժUh"iŪ,8}-ܹSjC=x@H,~Q< 1 a( /|ԃ#cQ UVƍKi ) xb8޽+i֬l0QGJ\{( kժM6b=nMY,7Lde{\+9$5sLti=J"}u_/l\C?=OU{iUVgSN*4go~3eE ]9B=!/ٙ 1SRJUpCÆi:t\#52 8,[qkcqD#'s3gΤvXq'!0#q`=M `H7 8fejUO>2 j ްҌ?| rq*_$WXA8 >Gv֬Yގ;$//~@Gsڵ*T andȼ۩rTti1K,5f̘3^<{L}:7ֲS;k-x^n8KWI?5zwx],RZ[K}ō@ʝ,ˍ $xsp,HlK+CV x>uoQ;iГ56U+n!Bǵ{, 1w4}ÚE Xu&c4#0p[\GM2% Q/WXrvJLp3vU*ERZpI\`?:m%vCNJ}ƴx H;Ť}F ߶m[ICꕍ`F?PɁ79<===e*UHoI&ѽ{51"ãVj7k nܲ{'mݰ\FwO`myY3#`/@VF]]ƭ^Z:[t)C:a;Am'] =c&dj>~V|3@KZhrhwwgcF0E=M+l^ŨC|1߼yѭ@! e`7oޤK.I$ Rܹs*"E :{6޹zɓ1QQH:S޽ZM+?vh7C͡,/i9Qr]  F%5ЎM RCP2btFqOL_>B~Y30+_aڷs,v\KrYp)۝<` 2+ԠJHqxVU-H{,H,,7QiZ:$ H)PΝUk&MX칵bV/ Ǥe՘~{wSоmZW(qRMp-vRnOAwm][6CVIs4i)V2Klʖ3(O푲k6WQμ{yzaҐe>fF|j ^N&h'i?a|tCqsl4;̙#e^x!W6G>l,Y,0@B 0Bk(3f$hI&%l#@.]|+mZq#FLny /FC2w6o"7WJ23O}z)b$U#0!@09s&Ί xwuC߿yBcu!pP`ׯ*MZwWRsRbG[iA^}žt}z_4ME)vx^)f7vPO&ӓGS IHl}K7uL8,Y.5*7A8,yJ'd H }AEAH VMzb=W4ɛ4ZQYRC@f4 (<1K zTvNAKDpW`3#\ZoJ8,Շ-|F \ʼzZv4leL[kݽz$HwXh޴Թ3_E&SNRJf4n8z䉜 gϞM .͛S~d|4?+#0!E "h؎0@h#իWvč7Q}ѽ׵[,%Q$LLE޶-- }qDTÂpD<K&]&ak-W~y /"%+ڹe -5^+X\.p2紖Fh_^Q;=ǟ ~\zؗ4۰{{?&8 gĉGyu][رc5vYh5kLNNQ{B3KxѺ?nr\r.\`Ԥ`F ;v ,k1c˛jYm ir,[U#y ѭzLB}lI/z!^@l~be90#`{2k7Y +T 'LZEw7:NOu_Ǐ5uWݦo@Ν;2jT~NDϗ;96F`>p#*TȚ5?/0<` 6P2erE3vqqUN_r`1BG6F9oԢl^PɼɩauV2 ȓv /xX4Y6z޴(#U+~SHd5h/6Si 7nq);wZju>{ݸC3筢Z }77nlrJKCtOktC:kTӪGz6F@!M6xGUG;@f&C ԢE {gF`&F *#0c W ӓLBk֬&`gPԭM]񅤊j\`N~cޯ^Х'ݛgQ@ig)F`PG_bQ2EMWxݚ/'Sda{Pɽ_,-])E29<@)7ZpIiRRJ6~=b;uJWmA+Q׾cJ?y2*(fc۷ Z/3Jxų1#`o0noO0QnvU/B/^]EѮO5gO5a*J]`2#8ZE>9vHw6ASomӌEl\`#A\kU)z4 !):ܱJ/!߶ndե)Q`29f^8Z/iF`^˖-֭[R=mڴ0ŷtRʔ)5iDnr`H?0o"xbÆ iƍؾk.eS h+>|<<3#i`=<*(#0~>}Z Cm۶͛7S̘1 (3zlhhu;fF`F`F@<(HcTǻޣĻ$իGW\c$xj#`o0noO0v֭[dɒ- ÇٳHh; 8& ןqIqFqqw#zwwww\rde؅elS(kL 0&`L 0ۡT_beI9sZjх {1p1|!&@ L6WN_x)# ks,0`@7т] NWFZ(YL 0&`L 0/x_jnZ}͚5;wnQ?^;{&ࠩVwKCL 0ٯ_?9r:'NZn(QBY2q}1bjT@* &-#hȉ<;0 guȵEI[:G/ 0&` X*-׮]KCUx/;jժ)O<,V[ %7߿ƍKi֭-sxjL 0B E_ aW\/@!{͚5E;bU޲}Æ J*R/_GU`U`۷oŅ}Z53u5Ls 0&ڝ~ Jʓc~I E`wy޹%M$l۴:IJ8&@pn5Aٳgե6mD*U$ P8 TL 03KŊڵktrdРXb`L :ɣK{I">E[ (cNR .)kꜥ= w(X`MndAŻHh˖-B 4h rtt pL 08hI,](TYfyf NeիWuj3fTiN0&`L 0&Ȝ:uJ[o۶ ,H˗cǎYCwL 3pg7obŊUg @s ^V-+Zxh`L 0&`L X9qtilѾc*\0-[9bM0& K0~yxL rqww_EIwAVZ)z, _x1E=/3&`L 0 }:AcŠ)M&/^sMndAa`پk.VtijhѢaƅ h̙tmzģDQΜ9iӦ2mɁ"lٷo%綘@" 0u;y0L 2XPرcիL2ZVLB˗/Wâ߯8`L 0&B@ܸv޼.9WmF_%](S eTħB|lD {ļ;ޭ{!l%Kŋ,qQϞ=1ם?N+TNyѣ?~|-۬=;p!&^ =y7`VCϟK,Q}J<\#G\ >ѣjTR4p@u &`L En3]XTzgq\"Gl%P`nZ6o,լ߉)?|L.ߐ+&"^ϑ-%NTγq0Z;R#e66(\ x[˗/S޽?lٲQdɓ'cz=˗ǐ$`XUfMeʈ1G U1pO#MxQݻWuL"E \7oސ+C^j `L 0&2M64xyz%Laʳ[GzA p7 .iԮP[ǽv%:w.aw72ؠ`jH.f &hV\sAB 1Ç"8qHL <`{x}{(s/8_zĈC qd4iR3ޙQe 9::Q}'>|HiҤ~'M$Oϸ 'OWKz>uYç+ CU+?>l~ĉt5*SLr0:˛7/=}T_}&]@wƍSٲeJp ]MzvxK%`$>X{Ջ-Z䯅V>{Mϧڵkݻw}c(NܣG>3?/^~ݍ m,'NCnGe˖I?py+oJxLl`bA/8^`,ݏ;F8@]WeL  ZjXdoƌԲeˀ6/mڴ% rE&`L 8|sWO嫷PnLh#GyNc >ERʕ#LTYEPЛKr Q ¥tCJmfN'\7D3(cWߡ[wPT)([>6UO7:qtRxAJ,tb͒T|C__)B(ԡ4y \@Y sMkeΜ9]nVm .,]@=x`iݱcGdDNVׯ'X˔)ST1 ={v٦ @IDATgҀ)X~[lvn]LM诧1\z0&|w[n-'6nHsΕVwf,"Π_z5ZJ+*-ǍG2/(H5 &Dp"@0éx̘1nݺKXBg|H`L 0hXT޲U}Uv=(F&O_L򶆍#:wG&{PN/\MvЄZ5Mnͻә^*,U[{14gjJiGWjҠ'n }t C(F3' 3tlt/жȽ=}GK挑i?.nߐu;vLںf*] ɯ~{2mG)'8g>ʼ(QP)u"`r<ʘ1#UREZ 6M3P~ߺ&KUPx_rE7m \h/_>G,0MnU~hѨbŊRa)ƥYfZ~7W:|Ca)\@a7:ցSM0a)+W,ciUNXh@ԩUGN0 ` rL 0 SŒ$IBS'1ѳgOBP M+W.0>?l?bH$YJJi@vq,3:sҫvqQ^V & `C;RI-rwo+}m fBҤ3( b\]3_ ul/tizEgNIw%lؾy%,XGЅq P0[%/w6ʘ>5ݸu._-.G`Žq>9D(KBQރ>˧_hˎTDmڰb˓Gd<~WhD:‡?Rvt]Z*& Լ]14TyouԮP"d,2ouhU(}Sg5)YRoز|j~պZjVMH4m[;6>khʍԡMݥo2 Xx k,ֳd"OJ*+P\Ќ?@ٮʗ//7_R]Hu ơ~t:aZx1L (Xz\ 0&D <,4ɜ93m۶Cv>Xz 5EaumJ?y-6B%+Sۮ(Kv`CW/m&k5 Iҕgd-HW2g9g rѢǠrSiV|.6B d=1cY\ ͝S9r {jnлUCuScN6'91P=BV/Mn̉#{~2Di'pep\F(tD<  aSeaX+ܷ8@PC֪HuGqJٞMwA93 +iRI𒕚н˻(AmkQ f~TB y}s׳e^$֬@;6ĉөe{WXӎ:V;NV^qZu˖*Lm[֣9210wZ#C t9HAg2Ie{r.V$Qř`;i>SVE'.h фkW'd%dJ'&/2U =Ɖ(3n|5o%ܜ:mD1c02g1CPMˬYd`O}G'O&(Zzݼo{X=B/bMfo\,8z)y{d!d$AS 1&#_+q|ȑW߹s4i _zVٿ{35Qn^d]~}ΞG |x+EJuڿjұlU,mY=U*M)Qs$j=CndߺGujTgҲy=?z,b\Vۦ .vvqPE lbXkM` &4Iܹd7>ϴac~:9ed@`v8 Sm1&9݃*XpB0*Lo߾I+{DĊKm/А]ȱpI'ܧ޿ݿ#}toN+Q6"7 ҤtYr*X1 w\M~7". dRX^)MLo:QxTOF{PGOPZ˄O<+Us>nx^`ĠδxF,$["lX?c8ET5 3pWdФQsQZB"GdBc72s14W@RǞ#;&,ש0I`ɒ%԰aCi>{lS5%;{BY +k- kѢE{ߩSN28ȴmV6x_cI?aR Ǐ+Q-I>}AUQFY~z,MI`8 `L@իڵ+!r!GUΝ;_2dP![ܚz5.b %RȾr{- тl_/1=fOE~/3Mv Mx &4)R-r>d TZZm1%u(!VmQ [зߖY+޿xޔ">$f9PS ktM廱ĈmXDѤcn׎{(;;I2ȇxS VJXَ򘐀ÇkiѸNmaqoD l"2ߒ: 03~l ׮ITƦg’.8G(ߗ(wAQfӈɥVLv-)uwww8߳ӧOÇ@H{.]hԨQtUʚ5+ 8PL &N:%MݐWƍݻtAYׄkM`Uxq*\eGPE,ɓ%KF/_$XXBuX߻wOl P~CSN-ɓw^y(S&h_ժU3p3~\V*W `vΝ)mڴw3ϟ?תl3@ =`Le˖CӔ)SȜ jϟV-ierk);"~R2W{nQi(}lRKEo^HOݗ\4^|怒~];ʼnKgC{¿&˯^6Z;(߼y =yHdCXڥLSauۧKg)ir{ʔ5WZ/-N>2X̏~P_<B~$VI X UQу{p޼ n }3Gl36.c7ܩRCf}^P>+SX?w"=JEg5˔8IB!- UHr$KL![`E Ԯ^:)]xJkԯ]EM%` &*j}Q_6Rx}:iy^eo(ӬaMw,\KIpZփ~dرcpaߩmC2jXqZNnޥn@4ʣ+,uᘀ~0r ,;vv#GThРARqܺukjG4zwG+0l0PU;J, ߓ&Mj(ұgׯȮY  Ǐywrr2P|c ĦMϒštҘMB&N8bPT#NVWeL ;z}F/_.gB*}%СEK8iP{v>?vd)KNo|4 NnXM`\:Zym=ZҺ^Ь%){./7+Z9K[C+&̛"^t}{wn>]<{'+&4H{-eE^ %k|)Tv V;*+b-y lioh.G+aISST~kKc%sKuAd,NF͔ fu" _fƭRCfw]'SFF~'/+F8%\@Cr L<@&mK/_(a|GfҟҕKT\t_(Y/ڵkӦMdL."^ࢩAz=M4S2 vXu#kn9s放۴?dɒvZiqP*U%Q`\vw7w+^9~ &`Iܸq7 ٔ@_R%?O<9={a\7ȸ=>f! P:L 0<$Q5͛2=-/$Kv`ߪU 5drmWuu~֯T8F~'@y [ّAP˲Ҙ2G ^Jwm[Kmy+P<SQgp6J7^|g6n]xTѥ]1O)͕wyLV ߁n߸B}L$!ؼڤJ6J@έjPϟШA]C4~J Y١>ٿCgpV[ő]/&Zjje56B'Z]poݠm{$[)cG@<^L+o4zV1݄r)u?F˖{[x',s4Kw5jڊv(VزaПrOs} %i=M~wmG=7 @?~爉ʟXOY/ձeO:A>}R2uhΒ?)Gl>pm(!<7ӳ/UQXd'O'Vw\~[\4:x[W?8-}}g_M_ *Ӻ{rE[\bH3Ky3mťE][P =6"|۳OP|?~ qƄ lGoz;PxTAPWXbZޞdɢk 0T0~ˀ՗;D,qr=.L`)*0,o˖-Fi۶mǜ DM/ߡx[t)Hp_pt{t!M߼vmZ%,ӽn!(l"έkBZ)}Qn#wWhyRae:\d(\x eTUk6:PE ҝF(Ocw)p NǁDp~U 9tTUi$:6 \4sIp..>~%-W.)[XǏl{#pgu>-_ :`hJk}FP>bX|FH+M;}4S)e*,SQ&q:e߲nnZW찤x.KZԟU%viӚ%~cOia4xLʒ#/9|65ѳ(A$b",Z`jwbP#pn^VKJ#LNA *AZu錿W~x@َjQg.'x͞H;XdcU "%._Jxmx%J;I*?ͻs:YS>Cfv`ߕ= >_kW5W/~:~0HU{p!unK*QM*.Nl:!,G/-ʗ_I1cŤރLO>EFBNX'5auۥMo(K72GlUҵ :5w"9F+)e{LGN}Y4wmXYZ׭֔)\sť9e86"|z5Ƀ z '@ %N_ [ҽ+""GDp9c p?~ꂯ wXRJ CWlb߾ 3gZOɋ5Z:XvMK쳈脸wPްSKҫWTŊ cŊEʕ tTĻe =}>|# wp>`L/^aGA00ayzzHlٲ8L#l~I qPQ(O8,!@-_:[t`.`l8BIyK67 \`a??ȝ%ٓSau%PXcA [X҃@0^zpb H^H.ip#XНjn_Wlj7-S[̀c܋S(ȃڥs[L`+UčmW90gPraO6[- EE(K+zW3!ܮ NF5VCńP^*SM. fb4VD.i̔;ɒк(ouUPgi뷔{2sׄC:aݻq*r&P-[oP}B!YXE¯}j}fAq"T fMaG۷w& `A&ߧ .f0.p97n D+kԨ!Aj mf/ ^VuMCVc ^Q;7^Ȧ\jC!>. K]Q,METH)e;] F(۵q@JBQ ;$TVXqkS VRx92ǚV4mM)۵<[u#9BY2o^ܫ(b@8{[#VPK%TDraP3u[dϟ-[xpytA> rrTyOsچ 3.TvE bRȱd]Z#,Ac)b%8uT;1el_$&!X#OiTA'D?WR`"xZ&Z&PۊsvS>;>}C{ѮkKڕ;$z+l|T@u3:;$;\@P/{2d LOl~ɌIC+\EZ Uǒ猃b̐[7.$MfOgOQjr㪗6VFsC[CPP%N:Z|&6Tܭ뗴"RX` 3t2 |N6 -`# {[K9Q37 GƮtWn:g+;eiܦWUHjƓȿ3dJk;HHD_vMH*VΜ vv޿W085rwoym9k-i_U/lP}\1b;be/`bf"&&z;rհ*U˘<KxPä}]H<Ƨtܤ <Ϡ51kkTL7E M{РA4uTiN`e D@ 0k! wk&l\4lPc. sg/P!5(u_ `&I&,M4ݪJw(%_z+tMJ@MRm ,5sL޿TI&hKMHžvܿz\5;^柼VAP|on_`ٵ_L \#EVMHK+W?tc񳠙'Ɣh{o!޽}M<-kd+lt>Qz:$1%5%79柼dtByhn5oUͺ71o}T qf͵2]j:sh58/YK?}1W-{µQHKɩPjCXܟVAa{ɟ7͘8r>U1_:qk"?],ZTP pO*&pە/wtEZ{xxPFLNFZL ̙3+]臂^RcaBr'L X=I&Q׮]r̠/Yj֬};8a.olQPC ~E,/џ}կs6TG9&CukF!`t~BDoY?A"\ 'ӪKKJ)V"@`Xq>~?A_~L_ϔ5d$YJaϞ|FZC1cz@bE_jt0=Ҧ{wH7P$j\d=>ze!i,  6I7&UJf|?JiVX; FfRa 9/=j(*]t3 0&&1L 00Aׯ!YllѣGBcrssVetͳyScH8vԻϸ >~h:XomMQF8.䦤G;7j^YnG0U.]@AYḏt){F g&8¥'gF"{rCZ͐2g-^ ?{wx)TֱH)}q?zwm2YȁԴv)ɻkQ( itcxɺܼvϟ?}-g-',q:L0;}T53dvD)j ">ʾpI뙕owb0'x_s-:>,H'wt[5]1͝U; S ʘN7oޤƍ9}49;;SR 0`+|0xzo ӱc(Coka_{6^k׮|F"̚5kV6* 3m]=i=W ,07-Fod̒+dȔA~̉CtA5Wy`U ~e~vb|cL rH oپ7EY5.cV?}꜖5Jz h2aDoRj]Ko{53nj,sQYKWR0kpyT߉fPN80VXYD|b{o3+4KtyMjF#c(D@PKJnZ R:>LQf>7굽cRL?KL~QeԹMoٿK-Ueaᅀd9(K{xMc-<{{{Z`]t\\\ ƻo>*X4zkA>`L 00A]ʄȃ`Ln߾M*T 5R XG7N+RnݺIv=̞KiY~?i6ܽyA{?oI>C&SMtFב{JTq[i}eZ|ת6U)pT1QZR<Yɥ%K`E=nXO*TR{[gF wJְ>q ;̨_|58~ ~Ei MFڡ1 -Ƒ!ݦQDԃ^V`PQg6pPZ=/X_~J>yHC!'V.I'M`aN]A\niV)H{]^ ءݴ|]U:pE"WsmDP—-u2F8q)c":[֔le&eݙ ?/bbB$Vl;5(CS ̺ԚmKIN~X(&;Ԕ@]{SݪMd]ѽlҔ a<:q4^=x,gʒz&$\Tȩ;|ܺG֣nS\YgthQ=mRvMTZK$JP|wOZ\wB2Z3pA 6a,3\̜93[N:E{{]V *b1JfK`L ,6s8`L ?~\&m۶)S(ZիWoժ5i$B?>M:iEPG( Pb3%{P18X$:H*ġh\d Ґ}=ΐ9;5i]>7c.Z CpK+&\2Y ANw,LjlEMZ7'H +KmS>K<8vh^]sxF+0hLUk3cϡRm\h*V[PC,qdCA'{u}*&W~ϙDa~/Kx_;D~# ȑ#я?뗯4HلǨAVESW4ft)3s\f,)3L& et[r޴Kt׉L)ѝJ:v$lqڙ,kad6JkEJe> LPy| kC4X!gG*Qb_lS߾})QDf`Lz xq>rϘ`!FcFFS݈#hڴiVlu5om+W./2 Ӆb Z(aS6-KDJvP)TqB*Uw3.bq4iՖSTWDjc}l\`|u=5U.&Zuk\5TwL7K`((֒ "V>$L9hު=zY_݆TH,1MkL`DƟY<ÝVm^H3Q3J(ԤU}Z}O|Hd]73Kfɖf.LڻQDveI~>:LJj:Sz­];0ɓXkCG'O%KAI76έP+O2 ːP6W sJ*-AhJkn{XNEz)%KZD1b q;tE36v@jbJ}pnD]ޫBe/9/}}y&>y|'HLyaʖ#Pkk,񤫜qSGQ-*ߜI۷#:űY9pr z%HLq1,вeGtM=d_WzCq+~ e-W [ʽ[G({t@qr>U%*4GNӪU5À3<<<ÇǏ/ݱ6j] &-_;l C _ 0&ݫW/;vE8q_ŋ|:`L 0&`I f̘Қw(dn޼Ikצ|Ν;msk&@8# pvyL xw.\>TP6lHÇWWODX.]ZYS*nnnS٭ȑ#'ŋϚ}1s'iPfbL 0&`LoիW/)uԪ0VƎK_~U8`!G!ǚ@x/_.]#Gi={vgm XٳGukҤIT@u ![^>G? `L 0&"P&Mƍ4ydJ(j͛7ԳgOJ._jլ* &VpV8`EÇDS]pvvCQU%fԨQ[u%wwwu !WF4gONv:=eL 0& X wޥ!C7V̶nݚ2gL+W$<0&@`{3+0&Ο?O +W+W֭[ ۭU0IРA Gg϶r!0oXڿ{* wL 0&`&#F 0`Tw֍Fpm!OiܸqԥK9o>{o.`L 0&B@H}tm>|ӳgϨm۶)S&Zl+:C|=&``>"|{y?~GMpb [ruu߿ŠdŊ# #8[ 1&`L 1bHw%5EMyק\rі-[T>'`L'Vd9L X!#GRѣӺu {kK5nܘݻ' @e~障k?~DZ52klT\ 0&`L  č`֭[SĈUo.^(]abCT>'`L+ܽYp 0+$حZZ%JD*UhY6ǤMT_q w2,GòoӺuV}3&`L <H,͜9]Fu5XU|*VUTI_ ϜxL 0cp7&L X ϟ?SժUi٪O2dǏSU-$8@T]dA1'l!ҩeqa&`L 0k ˗/sQŊ uVʓ'IxlUFYfcG7Z;%K4K ?uꔿRX[bed!2uA!r9`L<ϟ?K`T"Eƍ)^xZM1XZ&MZhM ;4ڛ2EoѧqT BXq8YL 0&`L ̙So+>}!x΅Bӓ7o. `Xj;vl-=^*{άF˗/( #6$(uk;%VLa{HPk0& XˉzjբŋSԨQU-$e;(Q8tkƣnr#rTڷiup7peO_/A"1QbZ&4OzE[|K8fw̺vH3EFfBL 0& EÇ͛_~<Ϛ5KuЁzesR+ܱcG)Ynݺ;$|cr0aB>}:Akz%a^#%VL`{Hk1&/ a&;w &ؤE8,?;SNyjC} Ni:O5tYS 쑃KxhZ=+<6?;iR\yy6sVˇTΊ ?֯pi(>?|&@x$Prepyׯ4f|ٳ'uԉbĈbt}&N8j x,QL'(ui?,%VL{Rca{H0&/+WRƍC GA>`fRǍޠAjٲ:D"pl5zj:wt=sdV '1C;6S\ͺF g `L <'Ҝ9shС3p )SЀUV <Fa94|˗O/V@~>6cǎѽ{x%ܳǏTL+ :M &+>}dpʯm +'I$;wn?I >7#S̘1)UTҠ?0.ﱹI& $dL @1 <AEFK,5jtW,r=#6JIK]5BtLUnx!,^X@x P / ?{7*=}p_" o^߽M{u,IR~$/ն&!=2&l vXk_xA۷Ǔկ_,e9-xO{YP46`n=RJ޽{e:zJ…Seg׮]xd/o>}*kX^pwƟ? ]af/oiKq|r[/2e5}_K]Wg}GvMIwXҦM+NjH@>eL`i.@?XO6M]1A2LBT-%&V^MxPc nߤ5oJg(؈*&X@x nPw|hwcSM)Q&ˇTf,٨E!u]K]'$:#Kv`Lwxݻ7iF*'OL_$;FI57#GgϟO-ZP6'% w(ۆ.[#mhJ9Ж-[6@M@ܕ*U P=.<ц ԰1;m6J>ʳPXZYvK-jpEツ &`@*)Smzc s`D_>Aϟ<+JȤHPnU/a"J.b H`L |#FH*={R&_|W|"w(1qҒpKkso!Xk.]T3n0u>$iҤ2+& b7͘1gN tXp55*}Kֲ~˖-gƍ_aM *e;,bIYjC(jժ\P>kL(M4.\ 3f$l#GSfdѢEU3' w:"5 [@?OB9:CK|8K{GrmV!1g'Zן2/cay`^xWutxR,{zWϝ]v_ 6\dȞS+nX *[?Us8г䕎FGvmQ]ҫ^~xG-XIQћSL)sr,XP&]bHz[1ϥ]jɾlZ@s62LAJT~c&jfĀVKO`^r=eʔtYwqqZ\BM/&VGiY4h iq (A'{)k"Eйsic'OSeރm{XT]h֭&:w5<W)_<9;;K_pst%&8>ar +&#VXrӧow}t`>m"_4xJj:f#nII"E?|X0$%LXWؾ}lZَ=,˹Ǝ>8mROWe[>+ Y6/ ˅h-o`ݽޓOQ\ܢ1;p;gu^{7ՒTJ"yY'~ka6)L䀱f,mX?5NRv-V⎥W3kJ$ (-ǯ>5҃k3cɵtx`% ct]xQZ޽ j}7r X9cJ]?p7d|4._lǻ/~)۵zmP'OzD Xm߰~!XM}lz]}݀-X%we0_| zS}Ϥ)*M4y0SNMvvv,ik۶QMV2#*.T+(ܖ=UvD֤]vr|\۪_Z?>===2/[<oVWr!X0K.^&)&qrY9k#qM }~L!ҳMj_hwco=v{äav*V\*X]J;=Z; }z?uk[-q ۛ;=gp߫o;}Hjo"W,-'E3Kԅ3Hء|nnB^xC=)bAvǟlpTP_1\S ]uDòXY?VP E3M+KoԤߨ. @ %F'?[>E&R3F0U0= )-tKGO1- &OZus?>c]l1rE֯-lh W&ɘ1NAXSpa;ʕ+W cވf'\QF#H!͛ٳkDpcSѣGbu >iO`YzGl٤JZ?h0*)ڷ׀QL6bޱ]fO[-uEg 6l0Dn~Y?Γ[(#KW߇r=%dץVCFci(1=}l>ϡ~3Z̢d[%OT\Jψ7)>CzMy|m~nhH$@Gi^=ǢAFjjD0Sֻp'8ءG'ƀ߈}Y8&}Y;|SL p glk7z%y饗t/+0_P\Lp7C)Vv2"[j%ƍ *#vf 747PHD?0`ő7o^?~RdoaxI`3ZrpG{^4!A#H@D5m\B־ٜ&0.1eZsI.p\jHcצ,)栕Tsfk3'|e7Wݵ}lZJ+V`sԴlǦ%///}Gm]W{NXw 6&y78p>CsDO>#LļF>IRBW]w`sݲWUwwH$@1@@~ !AU%KH޽mTo|#Fx GAB;wj'ۑpo> >RL.`rMmXFDc ,פr< ? 6̪ 6LufN3Y",m_ʔ)A $)l3RBv|Q5fnD*nsIH!{˛Ϥ3mX$j|i;p.8\^r/)dcۛKuzr ڸ^;N W3s9c4 {WYp @plB?|l1"9I>ZZ"o(Xm]>O>$@$@CQp6#ؽ7ҕ+WѼGizB'g.B=}p oRܷl=lhscM6ԟۼNG`}M/gD}cUt;tDFH]=wP:sȡ%m=^wXѰ@5kzs5t x_6JKv, ffeEi'զK'["/fޜ#3ȶI `&6mdk8s%0"RtM$ 0M U24$9֯\S "F z :%/B+u^CF`*BVZ]u jAUvyf_=^tIA)~k+k{4"6֙(~7oڣH 7֭'x:$ϖcG a"u7S>8 {G8z,X@w$]NOYy 4j;(\to}XP, hzXQЧOw Q`A H.YHj dXnV)Z|\o [=yM3~1p09~ܹfƽ0DnZk&ƆŕLH@4ԛ=䡇Ay|3+hacF@G^^5̖qC򽗞;۶1ɉ` P܎t眣vplaϙu:YG]VCYgm9:/"l,x޾Yrfyw8]Ŵ15.IWWVjZDϚΒOm,{M:gɲne{lx~ |: %g^S @X8eCieG\#^,>+WNcp`g1x}ɤIikyfĸ:v5LX:)*T`.HIMXvvcus iz 0,ĊZr8Šmz+}⯞kV7N;&V,Oto}4nsGr\ef0=uuy!8~-od67lY2~0΢$6m)]Jv?ϧ⍳7Ny`eduAeD1ln}zK| DϾ]ʐ/;GݓB..)`bs/~|[ VXqykw~7KCކj\zdLkjԖW9^ZEK_(vyT7?{xßrGKgb_^^#    @bZRtR`]c];v02gh3zC i@¥M6vlPt9ۿk0`}v~i߾=g"y Jriv5i&H x=7 TGo'ErrYMҎ+|7ï݌/ȐIޖ]0j16m@&|+} P\xęRJIxdjZ?򔌘Xr-cn32``T,}sgڧFk>KG VHHHHH g(' Y, +Wh-[V;.袈weʔ)ҤI9{g_?_NsGHo]fϞm|W= V0eR= Y.\(!m֬YRvm)QY6ч+9 kp}u_[P1STU*ʶdvsv9s72`ҟZ-2.ƥ7y*1haՎ(ꜹs|%NG1SFJ=IK.-7Tyo6r2/BTg#$] nj%o`" @pNm*2pC70ƍ~2sL`֬Y3g;6l=z̓8W[[nz>쯿;:WmOg{"=˼)r2֨Cg{$&a]>h#I9y.z{S+7T ?62d @F I ( ӦMK.^^t4mT o Њ7Hw:$]v{2qDI4g;N:y裏L2spKTא\H d]{)]]V&]ef     Cqp%KHwDm۶zc-s-/[z_i$@"r2ֳgORuXΝsc/};ֳi&H ~A*y885=b^     Hq%)oV;!~{=w֠Ӑɸ[eΝ 'L 9\4 3Zj*UTBbYzyRe}$@$@$@$@$@$@1@ wm| kݗ2=ݽ{w=Qd? "<͛u>[_zW Iełvlo(9ٲI+*پyuqCRZM?Ivn"[֯r* dw cnݺYN=iѢ/Jvd/'ONX ;J[2k,;"LfotUM2q4֯_?Yv9KY}z˽A,5Vҿ7nV|%):r+9TM^yضa榞co:$hleL d8h͞=[_J(!}] M !>+ 0@tlu$k!ZhJ*K<_]m8S38zVPA *$vkƣ H[,0>|w`رŘӏ?_+h$s|yQqBng:s >}Z˘^I#d, &HHHH D3XoȐ!zu`y[l,X Z3ݨ e*qo~ M̙3}ЫW/ V|ETsZtypf3gNG$ ;wp Ț_z X~`^zBӚ &j"ԩS:o"EdȑI `niDz m _?3$EKTլHHHH X m!FCi~oWӬYj7Px=xz||guFIhje޼yRlY;BhwӞ#kp2_%GՕcm&5k֔E Y/\{G=W^x\12~i8pv:uȌ3tpB?Z ܹsKΝ gh<ؾ)>87^9po߾VHvƨq%hI뮻 %YHau0,-{gt80!d쫯sذʕ+M5>GѣGy81H'8`_X8~S&3lby~'l&H H[ק跗 K1 @F7->!0wMoXbRJ2:AYɓ'`Q,Ҽ`7ʲeˤ`ڧ(/>CyoH XOY~ӒD`.7o>sBu<V*4&s3(3?t&Z W1xw "Z=M6Iwgҥ=yǎeذa7h<٣|ey7'l6|8Ɍ4l 6m4y-͛ 6])i[)'ca0Q[78KohcHHHH/H8QuE۷'gRms\oA֦M ۦN}dn{>};N:ibŊ2qDg쑇@XwT;|sx +Fq]w/k%W_}Um_~fՠAe]T_Jo&7>Gc#Fn`PUx9;j1$OE;;^nÛ"iK믿޽{7xQ`ҍcu"J77% 0,?L8 dx,YD;"!۴mz`sƍ &"'Lf!۷3mL[oey!_/Y`> i&;w 8LZd7ͥ $%GG>/)YpKȡ3/helH a w9ZnJ&&,4Aitc8!~;`q>th?oW']2)/vQ&܆y8ˁgHvip$v,6l 6(-@-mᴋzࣁ!]v:َ`Y~AOrTP]5H@E&/_ Mvw^xB?ޔs>$t{M9mwR }]95o{6;y]G{:r{\d֬Y%j15j*IW2*{OpԇkӨQ#G;jIj-V xC>p<& lV8"\MJ8ٳ簜)dB&| 5_| 5@dY&ҼQyrl;|Cu G9zo#;@Er;ǏmC磏>mp6[li=H0:JQNzG9p;Gĉ{G#<}Lm<ŋA'{ϖE">ßL{*QW1ݎ\e+QڵkDwT*{2CmNWmVۅA9յPB_*W7isbƉ uT& % {|qKFcp0DbU>3vr戴@7'K°(f}c{_x^ʡ0m" W\Wo@F˖R3Hx/so jO6 00ew?^ٳ[Kʓ~aIq=)cOa,Dy`$(Vj76֣ D }&U@y-VI5{5a1 $ ޘnҤvS z -.lpM^y6DM]Uyh// `]\Vڛol70{pu-eVsYgFxpwӉtR8ԭ[WAo=^m]S3o=̢bS Ac ha h#j : @=k]H6Ht_`.4h%DHHHH2@fa5j 2ݟ!_,5Þy8#ٍ5JzƜtĂ1(f2h!?oJLc v.8#4wbW۶mu +5Zケn> j9%y'Y (C;t,4E&H BC$@$@$@$}|ozH ]#MC|E | $KhI/ksjmWti{ZjZ"^L斏R{EjYu"8 +}/}K,їwrӒx=ݶmی o0v$RcNvNΨ}Z/iӦ}i|&ƐOme !oEm8"s٠cgS6Cm„ e=0'|"XZ˗/\wuB e+zި1&H b"i =0ڸD?C]cBQ~޼yَLT-cX<>ΐ ӹ'mÇhH )S[k@F=Ȳ^   @fGaVK{~A^ZvIgܹO֩Sv`i\TA&0λiH `>w"4 RzI<"B0~';܆ѝ:uҗr0GA*y\C- 힯x\Psve/CQx\WqqPt3|iS>z 2*8w`>78yg.6+FAS:zgkSPSyZŽǓ?~'awyv"@@Ml׌/U/RMf H)+VHGIf% &&vCQ%ʕ+ {:J]?u!m# e'OllӦvT#xA+`CgΝr>Ց<|*QױeGQx:jCG:J9h"G7:c~/@HWYi;*іA">{TNQlݶR6pƢ XtTV; 6ߓSNZl_ CGɱ8pQ-ڟn(O>.P:*Km&Ѥr{3>1f |q}<,a|*"݁\Rھ!^j^[ۢ3fp& pABjT׍7zm\ڵ&bB (;f5;v8T) Q#X3"Oj5}=;'@=G3 @{FPe$@@ 7 5XOI8W]u>~뭷z8=S޷O#(ܶw-i0LF]SkVAw_sJ^z4O;8 V{9j>w1 \1bXnQˠ$pѳ ڎ$=zg9JQkO|JN)U@D*$+ vZC$H%@{YH Y ^o_XW!~>i%?j(37orԇJps6@IDATiSXi{#cx[(}w`)"86%?@J]P$.RZ LH#Z}~3wU"O:=ӨQ#G&; ~+x"gB# $oFM} ;R#DF|Ǝ4j@Vuy쳑N ''OF$ebŊ?hH"Lb={ a_50pK)aM%/(/Zwɛ7oJ RЃWQz;W\9QgA -K$L.%yǎ_JltÆ 6Ƌ6`,vQ/4͈ n7^ ؟=ZB8.rqԄ~98Hnu=_A~َ/ltC SC"$@$@$@$ nųz8T{TFՏ]II LLj(iPe'|G$f)ҽ{w;69L@jԾZiL@F=#N    |7Qȟ:uW6e~؃ # CˊI2u]6]Psv8'v(FcgL@ ?jM3A$@$@$@$@$  HcjZW>˴NbX'GCIOzZ1f6cʅ^l 8pC3gΨ/# ̕(6nh'~UtDA$@$@$@$@IM7s t#IHR o\{)"4L0@Aj_lF&H D\fW3ÃG    g]vm=wlet:᯺z:9Hn&M~Ytر=g!0er2DՒ @ (XAK.I監%%c=s8#vJ2dHŽk֬[m`25J"tU@$@$@$@$@$@Ez>9HJtyʕKƍ'sNJt;[c$DF=Ȳ^     tgwJ$AO>,Yֈ+ž3ApSN&Xj*LnٲE -:cI@?[:u֭[s&H XX!xbw "qF9}=[lRt jՒ D"vH"N`Cz߿=gCr2.Mmg^H7Lٲe%kVen,@$@$@$@$@$@1F{B ?~\N9v.7o^;vdϞ= o64Hn{3)M$@$@$@$@$@$%tG 4!,:Ⱥutp1B/FX[x)'cQ0p@HHHHH2͒ N`РAg u&tZL@`r!]2GRVjaH:NjIHHHHH H$` ,]T:wlի=gB![ʕ+jX&pIٲeW4$@$@$@$@$@$@K}sH:͛ U0ll9zhɒ%Kұ#K`ɶƍ4$Q6nh7KΨX/ @ ElH tHvd8l/P@蕲$ (6l8?իW$y$ #)[,'34+&     = @+nרQ#XR_W*UEd2^| jՒ D&HM`֬YңG[I& w!ɲ!@{zh1/ :bOI ) ݻWZjeK(!Ç:+)ypБ%pY`Lp熩 Փ @ ElH }iٲƂƍ|8^w)UOIHHHHHAtbV ߥEJ:j(9l.eMKDiQk֬G*V(KN-ٹs@*GІ15.b{lY\z)dh<; .tDO!U6׋+ 5)Qm|,X#ij GMBg#|MZCڨڶ䖜/7x0y>>}1qN 9ƪ&4'xQLONɒ%=ʡ,O?ԝ'ÇϻywZSξ}|{FlHHHHqHLॗ^p?=`sJ{WvL`9'O_~g0eo6rph+rqxg8?y'N[.-{}qnVۆad0#F8SNujԨ#TQjF^Dv#]IhҸf,V(zwuJ*pGy"%S+;Bs䁒)sǎ} j뮻%Y(Gm(I[6>"{iFs;4id`< ݻ<9u#Ǫ %c *&Lŋ}p;vda}8ν#Ƈj' (իrȐ!ϱ|(e oѣXqD.{+Rη =0Y OY1' @$(iGiE['^D=Anv=8֯_q#|9tuӚ,Β%KH`Lps=g!c=M7#vD቉X(e~y4w`m¤'NuF՝w)[is5Ik.jI=D%{& qVot8eu?j"믿n7NU;\%$    Hd1pWx6Sn> j%r7u֭֕[˫***ÇN ئม@$ ( QJ8ܡ@|wxl#Ym(ڵS %#jT+iLE̛ڽBL=*JG׎@dt1I9r|Q+ D隋ҿ?ދcC0yicz0nmVJ6۴wBIKNSVx]AN ]G!    H|YcmJoFoD6DԪ%ҢtqmQj#y78?\ njzKeL雚$~!u!3$W_x1 Dҟx/lܸqj~5UZѺa!(-v=QH^8i0"6Y|-ӏX=vL`5͓O>7 >KJD)wYv`"]IWyj]pb?.F֭v$NF^Ԧde̩ϗJ*d8eM]8q6.JI_Zm $(pEi^єNcq霞:B͋>v# JSIe׸tJC>rX#]f]D_MDwHgs鱰> Cvv DiӴڄ#6\ g2m۶Nw?U '٪t/gY'jcYD+z>|9rC=# ƘPWj|\t1DwGMn }Is+={S ]/&Q'YfR6*܌#dd~ 4w܂2p \>裏<,n 4p`HHHHH \;4`/w[XcDViDCսET68Xf̙3'#Ѵ|9dm D?8:i v D[NNs9'7A nom$/ 1tPT|q"ߤIR,7E>D0j)|6SU?p`֬YG&:Տ>v2C:S^Z^~e駟9@O!b m۬R&shgiҥz%KƸyf6}jX1qeʔc@͓8D~'cCm'[fzU ?wv8ew߭'#0c^s2P_dB͊ TUhaÆս4;ƈ > =CM&Zz٠2L} );s1=ӹsgݻ?h$@$@$@$@$@IB@9cҔtXe)ǬƎDs3Qz[9u%egOT4 3f8ʁ`*QKuʩQ7~9/v܌~`;*JQN  Ԧ@sB9oըQCGiG'X)'GNBs8Qj}! S%v:Ǟ$圳Ec?rC޷~[gwٴՆl.]*Va9iImTywy*7ydv}|PNc}P3:6<'J+iGsXZie?ý Z;r`;ʩ݄=mR1n$&qlH(My<.s||P>M5+Vow&9 D\`_j3*m    LDV0w<3~MئMmoS}K}D86e碋.hKmd֪UzŊkر)^G6qdEr&7~):TdS`AGqG8ՒitTDt=}GZjyaÆyT_ltH:˖-E8 1"*U8apȣ"āה7G(d{so?+USD {ߔsUD'Ii8,?S*ڧmKO(fGE%|FELԪ,GE:TIx%͚5s6v>)\6tX/{65D{b߲ev) |3sfRojժ9JBG7i.hƬ"ӝtO>ѯM^hhz+g K $P9yơHm5 H>jDW &Mm1g-16ܹSg~ѢE^+n jYAK)ǵlqHd h|7Y(RC.K#X#H#9/GHHHH2 :Wxః^,4Uh%*K;P/jL~5壣p@ BZ?4 :tD[sNEca5t]DEb,Zc=v:Ǟ$}yȘ]6p `|=W^N0{1BBrwãnݺڜU%#\/_^=NV9:js5šH L  @hL\D{Hqoݻ|GrH0D}ׂ%eĄA:utSJT/Gr ژˣ &=[l6gNp]mW jCie⃀Ҭ1,V2(=gM`„ >#u(_|b]&Gz!J]V323%Ԧ~G'xB!.I1CVRKHmIHHHH; FVnɬ6շQt~C?hF撨L9s[ Bݶf}tJq3BsFuf>hbOcXүVzSI Sn z4' r9ɸ߄{MdNysHw~ $&p?x`v6Ow_~dv&iglv# oNz=>.% -Z()WU[`$:xωLZxƳ O6~/.owŋ{_Ns%g(m۶ @+h4 $'w+=LgΝ}SlT}8~pCn@ipk#G?x:t0Fp?~\Un+WY+ycsH[}v##wN!IlJk.}ccǎ2f̘'*11YMi)2j5\J D"z$ƳQ @9;"Ǐ# .D!uZ^ɓ' ,uo?Ϟ=i?búPBh\ԧ68mԳxb Ƕ~Ft!M-3s!oe֭6ጄ1f͚r7K6nGHƠ/;v2e/]T몚>ڷo_ٶmo^/_.M6… k( dB_4i"`sC>>袋Ƽ;->q 9rg: 6Fy7jժ ,xo3Ԥ L>IHHHHH Y;vLvd2Ǚ[mN%_$Cp5J{.r>;J7`ڵkf]*Q,r:;/;uf.]*%Kg~ʪH|gӦMeϟHʛ5h@סV]N9Q= 1L( R2)#PD TB$@$@IC .+VHqs$@$@#гgOyaE<$+ƌbb1 J9sNɟ?SF /@zb?pFE#=e-YDAaSPl$ hlȈC8)ZvcC,c-[uױ[ɒ%L0:8!K U1yplOϑsO:%ZpF3gNYhg YpܱER[=t @ȓ'>|F<+VLs$@$@ #h➏6OGIf%_-/(+z7Cr\9r\{ U(K/T gs?hW^]?9plOϑs*8+M/b[qƱ-HHHHHHH H"L`ر;Zz!Q: lGD{F2KlHHHHHHH I$O4I 6۪*W,L@,@t;` :> $:ܓII N8!͚5G hwlL#X"-'MSi$@$@$@$@$@$@$@$ tG2  бcGq1 04!>}ZOnGDv        (=  ;!CȈ#0~iiҤ=gbٳСC;9rڵkJ       $ @{<" C`ŊO*jԨ!/=gb[Nnݺ3gXB$@$@$@$@$@$@$pO'#p R;KWS@=zd͚5jY2Ilݍ5i&HHHHHHHH peAqJ{[gɒE>s)X`Nt7nM6a6h        h= !^{MƏo{޻woa[L" &nUTI)bϙ        h.D4( 3sΕݻ^7lP}Y{ "~;dbbHHHHHH 0Mv f8'kԥRxA9cHN'`}#9RERp~s~Tzߜ2w9|nlҸ5Y|L^q9=:lwdSc:c`oH ۷OZl)O})^|rYgezDò`A:u     Mg/.ٹ\9sHrťa*t&>=~!f*CիO:_f]~Fɿk\^=d9[v"_xuGx 9pǭ ? p` 'iOEgϖzI#ޝ&V;7,+WuJ֭W_~-t7gI5,s~ɒ% jxVdϞ=zٲecJ޼yvXbrF3Ͽ2fޗ}g,\l 1C "܇ "SN rJ/_pJw7xCv*mkt>x! 2sL;ȷzKKX&ooܸqXF$@$@$@$@$@$p@vz׶?Oʆd;eݖ;lR藥P m~uS/Uv/VA\gVw7Bp\VO~s/S|'ܟH\;vvb * $HwHHHHHH@ rW>-kl8t{<5*Ib7i[:ujҪM6˰q?tZ*J,OgމV'ew|<1ѣGz۶mRX1%O߸q,[L ,(5k+[Ȗ-[tYJҥmx.RK˺ujLM"E 7ܠˠ|j;1S8͛5?#G9 3m5K.D*Uq)oju㽿[7o.x/x|;;ڵk|O      {uh*?*`1I^}U`>~nl~9zO7uW7jS}kMwD̟^xmԔekF}7Ouon9wy1&\7'Nhi… =UNBw9ȣAMJ=䓒?~=dGx0`v‘sK6mN:RJٽ{-/H%tо>}ڣ>lgg*:6S-Z\wuzJD] w"U]O(i8g`kSjU)Y9ңJD1n_pzCYw&8ʠ18C۶mu=@t6Vp_%5o7:tfc[r2s$@$@@1w\HHySPVfMy[ˊڴ;rBjW-/\W \Gq T5JYٚ6*|;c).7^bw8mpCQn[zHw_ $^܆hiwt;5JGC&mN1u4q޽i&F}&؝8{tYw;MҢE ٺ̌)yweDoohxpO|_D{ۆ aÆzZ{NO>]IʙCL u,r2܎䌹xw֨ )Yw)隬jHZkc}s=#o` wD?csR{Y| !6D`#zm<\yZ׷$s9ҡCuʰa禛nґK.oFo%y 5PkID`R?oƩOԟpʚ:B9~Ci86E9GEHzJxުU+$6D9<ݻwz%3f̐_]5gԍhv3y<ժUӎ9shpʿҤI}ό/ԑ(G8) 树@<{5G  HVX%HH%*؎Ĵ L?dmdɿ5~>|6I߲SoΑ;njntjiD[^U)gJVз!ӷKIeݍkc@\8!0~xG9$]yy״#%p=\A#ߍ4éO; Ae̘1Z*pNIn9;ww8y28_-{:xeJƍu$9$kp[8e'rR7܀Jnv=^>]wi;"1f`ٯJ|&)\oӦMr1xN'N(Ёن zM;&X5f}}~mEqfG^gcǎN$#o$HHHHHH y\;XȹQTf.Z+xfE ^$$u/\oP\p^[s 6^İq3r,R@ntb_^<"a($\YJ6mK7'& ļ {qoHkԨ!+VHO=h}8Or_yyw#{#pʺ4u͛oiw^=n La+?SsK tI={ fvҭ}ATHzUvZOۀW"ϯW~v#Á#F͈3c &հŘy5< $6B ƟfH#ҥq;ȧS~n"crǏa$%r}ԄyKߧ[zf`"f ļgDlٲi-o܃n{F-ܢ#3~S/O<}̙3O>Z2.,K$`eu5st@$`vғ7|РAV8c܌ws}nf-ۍuMkޛsI ^`s0L<^/cb?IHHHHH')yi_9rş#m ۥ3hEweg/~?l/ Uǰq?ЅXz7J֬gd?u.6zbJ6M|upʺ 6uc7nԲ2}4QԸVR%{8qgϞ\ =zyn 3Ơ,!vȸsڴy̭Gl@h&X*:*毒*^scRgLசSuK$LkȐ"ЭD$R" ɘ(If5]g?<9{y9zgYkg7\=z-^#̙gY5eup:NE8qS1c<\N(m}{|(Q3?Xϙ3G@#ӷo__[nаaxĻv*Cp'N|L]HGyo@ؤUI9|o?a/=B"("(" ^}} eC=l޽lE˵^+r_P) G5ƍK&4iEM=p?:g͚%V7tԩW_}eMf,Y tn8}y6Lb<{OQk4eik?wr,W;†a+b)5o)KJe]D/SYWk !(LL^D @IDAT iSTbmӦkԩm۶0P@?|r90w\q̙3Mz4TWlXs=yTQJqGzJEPE@PE@PEn /L GiCۄp .7=ȆyɛBǛ/͐W tD(hoP|viivŠgD.J(abӧOF~wÜ^q6-Z9М8}xp? e˖$ҥKeC VFM<3-xҿ.+sN8zn]q_R8Yq '=&E.񮎸fo;H=3PΨjq X)nZzBcTppB{D3a@Z5j$ԾEǿb>QFQ,+$r12._P|oᆲ)+]Ə8 o,}ISK\tCި` T/>2vtMu;Ce¬oJT\8q(ۉK;Q.`QQJ_E@PE@PE@PA{tޣgC;0;8{Da{ޝ+Ea(H9 1,8ȃg0| S/EruQ)=ⵕ8#8 4o8ͅc0(iiҤCwI{()I*:t0|z,8_z%GYfdϦOS3|r>U?kv»dT?W_}a^-Z$A6ov~TCxKu,oE8v{ו׫"(@~@E/,}4Tb*Æ 3wudDhΝs-*%4zh^6C |4mԴk.{y^̊"("P-[’2Yҭp愮m+˖sZ#oI9Y*\۵ܤprڤ'жTYnʚ,] T3*@)ܳAExUyx}hz1ݳI[ӷ4ei'.o M\ZuqamOuQ.q2GG_N6|s%"T>+4{8?묳*\$4Еx_T,=w=A|'Ɉ#f-KR_N)]"(JJ&fۚ-mXVn!Ss0<<'A|v?hs--|Ɩ2FrWdbCdXPE:P:@U,wx99RQbӬR/N,ܟ~ig=|[j%F+ǧScʕ+('7n/)ZKi~6gw*"&zdJ~鎼i lK-o;'G2pM6ߤle5P{a-)"D~*ST7OZ w}uRs>qk, 6!V9c$\A#dTt+b7Z,ZY^MSE@PE@pRE@(rPtG+aijEo1?"2jP+6inm9(ˇz *kE }f=qM6?|ÅoMQc)Ic-:lY[`ió &kBc=3Ǹ:+M._ NjO͓~ℓ .y·'!j+,D&Yi+"(@# ҟC"(uӧc=̪k@P2_dSjܹaT]wU:ıW\a~ge%Ng̘!Oh/bW^8tGBEa>}oPcǎ[n1=i6 P%fFzL4k,?B3\sMCm8awnO kSNƦPϞ=FI¥8i#sN惍n <ؠtG`{1gm΢ļ92a>.d:K6ҌE@PE(s八FPExZN&B@,(J .nsQ\c>t v|B N|o7Qe;8ڵ9좌KgI&e(imڴw gI*8GI-]ݥUu:je<ǭ96xPRGT=zX&'/Mg6)0*ۉgo!=2qDsUP믿ޣ> _Aҵv~4("(兀Z|hE@([,X*ih߾}َU,uxgرc /`C |{iذms쥗^8G}T(4YpҬu}Ы"("/A KG("Pn\|梋.aݎLEPrCc5vp&UB)6H>(MNJҭZxQ{<t5(cwPb Q.O}+8TPSDo4'|D_RY? 9METSYg6Pʸbwb\rj2iݺ()3eÚ+lJP#w9,å89KèSO=\}B%FV/;ͼ_߼r*"|i$ͳPvn,zUE཈Nb?浽2Cךyތ2ӷߠ2]Yi29E@Pj]djs&F`;EunXˣܹθD*ɰfA~{ #َ\8۷m$ z8>ǵk\)Qc+d>/TY~}dA9RR.˦۽+vL; -i 9-[ 픇^ƗXUg4("(兀Rʔ|hE@(K嘺U;$TX" 4xVc~:N0G F67( b4cdc;cd)QC+h6V}UⰭ*_u>3NϪ-&9teqWWXUuƢ"("P½͢E@!F.Cp?~ 7fF(E?lgJ'BE g8|NЪ̚5gp|sY1 {eE9{ҥKXԣ>*͚53kYhq3u]wE}}ׯ(ԡ: <={E?c?4$i՗K|㭭9eƌ1Aa- PtFkMY8]Yė:;찃̛7<M'_ם#<̱!lz!\?%KJ?XR_3n^.8ׅC>k%:.\(h\L0+x$ͳfm^E@PE<L shGյkWyɛ3ghc[L>}¢8sad=Sa.'rx%G0kB UMPmbXӦM?JÊ@Q"5 }[kd((;R7|be]: (e<˗/W^*mˎ-D hѢIgB 4iOR>gk3$㭍9BڡC\$y8O6M~Óla+׳sI'fwȐ!~TpP*#>j .?N;#mNW6u{g4iIP@O;mkEmxtBfԵP+JkHNJUb͈ry:0qHz Fr⋁ HVZg_B5 U5vUbZ ZvG֞( ףUR\(;ChREࡇ 6dyr*XZn br`W(GA{*,gü%J`СA݃C9$8p``aIly܃nݺ_;a/*s|Z֢8䮤!*߱]蕲v(J԰zkTڮn X9<)FŽFOJw?vȪ(^@ @98u9]>@@G 7tn?Ɛ\8/سX75(8E$6lXpcŚZ V>X\H2GIpY8Ճ nM49yS$_}ځg~;#@IV\%).uvֈ)jGC;,| jZ~n>i/xksҌ6Bȧ67i{cZ4Bm$xiE@P"w&Y*U#f0~lW]Bsirh*˩X[PG2*VA#GYIXK' O: v*\틢HUTL+o߾}rZwHq2ۗ }N*N5  h׵tXr\֭\)eT(+پ;3`GyF چ4Wed~!/n$tmnvh^zi8VIX|)@vcXDdI:*ϳŚѣ^֭[szi/["kUf;ZI)9 umi*Ų:8kgE@PRAY]tQtYY;½()e,*̢EZ6ZwXGXcn|:4W_}ر! 9#gN|y%=JJ;%.4sƜǎk솑Po:dcr?w\syiOxzyx OqΓ845O<ꪫ%? ={>TuxzF_ay̴i mZ[XkN>`ԫ"!tA'審UYZMT6 zl+Kh"("ajG_y=S"}':֕|KVb PFz[Erx;TJ'ayb.Z}{Yw2 3fezĻYy0oUXz۱Ѐ8'X#tAu)<.޿&ʯ#p][R9rjμ;!Xj7J֏H0ZnݺIܙg3bI ^ 1T[\/ BnȄh9UK[E"TzsM)9 umi*Ų:8kgE@PR@@-Kak>o<#Mph66NB_uvǑ 7Gm]o^Af2D"("<EI)oao!3~ko5:j${xFY_hj_{5=*(ڶmk~a 1B^:5I'卦G =ѣX5ek4l][#(`TFɇ~B˄Z'蟬|ԍ7h/^,q(ĝj~]}\h74 /TZl ("("("("P½y!np}@\plϯ*cƌ.l%ھ*}X[ [>[-u6ie9-w[o%[|˖-:kumm8\jz^wOK+n3'o X"n_^36 5a(Y,믥kFxdWE@PE@PE@P:@Q*q*_\^ziK  wA{'#q.1뮻$zgq2j(vmM7*^'f„ 樣P˗/6pRi `㏥7`LT`#{ouamK!(W|]>駟Dg.+42L8MB'1?lvf6lhJ"S 8uRuu鵴>}z8=("("("(@#P <дi#=uT`9 Xx {ܹf9s(d6lXgyg;0 Ԝ{͛o)ֶpS8qNDiŤI!Ji,9z~衇.qqֱ X3&CNw7 =}cL,Ye5ƍ3f͒{24m4LV%Bh,|`)2*%:f-Q ,-Nlh*'Ks90hO̩U-(ߤʯp][i*s_֔.!C,?`ga-ZuaAxH:vS!Ba9]\xogg"("("(ez 6ǪL<],E)V.F;k&N(wYh+\^g;v(8r{oРҥ2U93VAl t8u 6ULV2ڵ3Vg5[68~СCF<7OOW2c)$VXر}ǮX&cь{6IUzH3G̷<ŭ Z;! uV9%F3[̻:+쳏"C;N ͇ˣWE@PE@PE@PE@P?/dǏ?huAV>meDz&M ٔiۢ<8BPWK/ e]QdEaVXյQ9rs_pC?X-==>?(p،@"("("("P[|Bl-kn#U R{("P,p/R:|ǺhCPCggy9묳jamQ[Iu~d"u"!p/I).½"WJqvV{("PxᇍSC".<?~|B{^-ڨvD: E@PE@T^ k"(tP(ňVw->jj&ܵzsךjW۩ܼkʹ("("*z("xGQ(@"7|q 2#P>olXV[MO?$m4hD|gm5-ZO>f 0|~wk4? ?ˉ ?ܴll62^߿)׃^{|嗦UVf- 'HU!ֆ\Åh7jM\Ұ&'iS>}bl>Xb~{E@PE@P!TE@PExgp-L ( .: vmHUh*49 ZOS{\_xvi|z ZnG=*{*"iוqWkQt-XuU3]eU;,㲆׃>8Z 7o̞=;,iʺ:͛ ݬ|.kk 6*t$Bx &{:XCcУGuMs4eYW+"(Q+h+>6F("g}vPGE@HJZk>QEbZUg4ZTEybo'F|[k[ou|J¿/q{ e]%7pCk#ҥK]k#@;BI>nn\\Nbȷ~%i滮 UǮaE@PE *ܳ!# wpo*"(n-7kCE@H@Fdرf]w:9rd[o| 6hZy7xC⬥:uYhy'%\"t'*а@rWhB;C~ko^z%sW(iѣG;SOr_}kF]3g ={J_fŊro͋/hwrK^kmlZ 'MY[ix4hi[oen6s|N')Vi׆߇|iwZgqݻ҆uZc!>`&3e$-d]ƭWE@PE@P ?("([-9koھ"$Gco[NXy=СCvPw3 [,TE*˫./LNӮUQFy=܌,4l7)2Ҹ~%M6Ғwq *]~4|M34k2:FM`71"HO?=MӔM U("( I>jn_RUE@PiӦ\f{ (@AgN:quM75CkΝ;>cc8n6l357b9ŋ%_k?ЌUFJ<M{bgoi>I}>e-gy嗥lݴ0>kn^`hOD9 +,@-5IY s5o}XLNp:K>'-v]O_^Ê"("6v}E@PE9蠃E@@4h@`8묳 lfK2nmGI;i-AcúuyFjBQX|SUpυQUmKzƛfRK.mG>'-v]+E@PE@(T^ }PE#="34(e@ƍe $b)PL8Q?wQiڵOwމ6q?:b)b2kI&Rl:s -[ *okk 뮻i׮i߾aQo$I>'-v]+E@PE@(T^ }PE#裏YȼsX!ѡ+eFŮw$͛7(C1|ps绬WM2ԫWOfSNo6M6oV7n+m$Mm۶'83f9Nw-TsI L+bcV(er4Xm˦ nƛ8&8Erde+Wk{~s~„ yr)yEW^c:lǩS"C oX+p<9XXI: fgC8rٳi8J=|.̗:{ؗ78X|y8%i}CG̙3̚5+UJvS1^*#F>c,2y_^ky4MYt&M p*+v3Ce>*]~4|M34kRkfMmu~)f]* ;~Y +"(!NS+CG<i*Fe/W]uU½nݺU;(6Qg>,ˏlu"VSMb\blꯅ'|2G(Kk "oFk}?RTYQi`5JY_ag(6\: ~=H.孕~bߵwes7ts΍6`pJq9s*:pEŶ9+&4X W65IIǛfMZ꣜͓BTlup6d~VE@P! lh|p"ڪUQ+#⮾6}O>9A2=+XׄЦQTHo.x=3A>}~:k6~ =4O"`9hC֝|(@y!&l>N93oi]z_4G}t`+_`РAv%XdI0tР{! 80 8,F뭷^Fy?;s%ÄX-yT;1)ᩧM ~ʼr)Nir}H6\4$&o5y-=\n~5k`(㒤Ϯ|IUZ\&_E@P8T ?`_VFiN=T2rXU?2*ZUp7OZ|yB4&q v)]_&3NWj!T痱'9[ qepF 76s7WW{"@dʕM7$Yʼnn\뮫PP=ze5Pw`CE@P aew(5VrJs*".p]KuQ%Ga's' Xu%puZ墱P??uRj%:uNR*$<f֭8vmg`0>|y%=JrKӿxw5\c{1I:1fz(7F-kF|>[eX` #ðE@hɠk"P*[wR2<*K+qjE@{槟~*@`vRNϱRt/C7aAaWURQX䷼9 ^5ֱi@-p` 1cFXW^.:J;㏇ypOg=hx\߬Op)/ gf7Gmd}U^dXK^ׯ/J(x\=\?)W5\3\|ת("h`Μ9.iZNHQSrJs*"PJX#o:b"Pvщlg+]-\%E]w%p>ڣmlZ,b=?r ;@}V3_}lO8q/I#9i{*WJ˘kg+rEڷooSNttU{̺Jaq"O?Νژ7o^ǫ9#_] FلL2Ep̖GvS4'TE@H!FZN(8&sT+ͩ(@)!+ i\34b)ǪdŬ*֌RWhVyWK}(EZkAnGIB)ha\E) Mѣ͏?(+V0|KQ_{Bʼk$u3ēf$IMx̯lM hWP;(]PPC Gpo4/(N~m70Pdh*Sg+N,jHEPE@PE@(u0lB`OYe o7o[F\&Mh]z:Wgu7}h2<^səzW0^ᎂu _}̘1]04~K\~b w[o%[_{˖-bCJ;n߫pZ:$IY*U8c^K+n3O'o \tEĚ[^3͛7<}@.y>+pHEPE@PE@(!:ht} a%nfu׭V56@mHPJV_z饡GM-t(qވ8`dd„ gڠзqcql ,Qqǘ?CGu+^ɿ|P}ð'XDsP'PW&8d ½T7 XE.ecQdc+gσ38CȠAdHn~l22k.T68 RWg+eQ%k|"?8p`T("("PG,F⊮iM0& &~AioBk+ /NݩÇݻg4///6Y;ce nh|arXG3yt3̍w=jzƴǶ0#)Ze >+Zٿi ^vY(/\ ,o;v(vb9E7ht%T4ޡ#zŚ'zJo (D}e;Z\?&5kaaL8DяU/i׃>JlJ4Ju~̜1wUIRX£_TbsM7 E /AQ2NCY`{m-߿`i.sʑI^Fɺ-]~KJΧ9-DzUE@PE@P!{6>-׮];9NP87GQo5iDN裏6VЙxw u090!}AinГONl#C|AHNnXJ39sL6'shYQɏKP{@K/$O%{.S8qdoK?QWåib*Mٴi ^P!&Tuˏ?(Nu$)&o>v S4eʉjRxvmSj[净߮7:θ.\(%MjgvA jBjkmV5iP ֧/6~(9Bߌ>ƿSgyPm&iP?[NV]P@Do0 xЩB{TҶO~P@;x?{f>QA@yE@P#Nc15"6XŹ_ui { jb6jݚm(VFTSj|!Z~ % UW]5LBSNBlszYhLG)a袋]w%qVEW&m?`856"WWo>,|T}I~1S壔39æS iQE@PdpZ".r |XQs?G}$Nr'X#wۗ_~)ʼn!Gorɵh`? 7o\eȃoF^~e_ڪU+ӸqcHNS6$el.k~pq+B|^vs]X3^֧1>sؤζP yU=i*D]W~>GS{$ Ii?!+~V bK66I;@ӂw!+VF3_kC_LѺܿ ;wN0\~TçXD)ge&ʼEEPE@v-Rc2U{{ڀ"T&LYgvvJ;#/?hcu]u&eǏX'a^+'8s{F+C[͍78ʊh`̙}u};{>'*˻\2|~c)v$.KggXưM74:ujbm.DBGy$c}%\XpЦM yVF@VIXY`2RCz行& Vit]Y<ЁiM Xb75 ҫ"P06kYAO0`@?z^pرAKM={vl}V=ߞz &uX G oW/M"m7kE^O<ߧ~Ǖ+M|=|.<=,,I-"(@">"X(jr!Vq1X8dO:$q VoUp#7 ]v͸f֬Y**SO̷];q{£>Z,O=TWD,iu;<@^,r-.:Uٰ5#}z .ڈk/6 &JVaȐ!b}ă bh"?[EU+2'4}NHq9r*mƕ>˗/7g"PnwݜguU"/ ab7q| psBKx(fB`q'|o8<󌜜"_l?"/UIVUᄏ~yV5_{BqW5y4FK/?_!/: nay6̨(Dh Y֥("D@13's΍f{E@(!8 NҎ>J #0Z/Iؓh?^{5sWS8|I$ISV9ܙ;kAɑy{ 7RZ8yxnd8gx'|%Ӭ |iM6 &cN(@7n#9ӦMe1 )k&3fHvyPAC-9IU>t_PһwoQ&BM?/]|w=ẫWE oPNO>=\ޞzzit94ei2ӽ{wI&h]v2|(Q7baGW^awg ǩ<38-Vor1b?EO?$6pC @7N] UolwbOɻBǎ͆f:̷ݿ/RL2݌۽}+]Kz5kcogjgWEey{ţ&˻ŨQ`DE3k-^TE@PjE*£֊ZEHrs}`݃{.\TNP{^;ca|vzq49ʃNJ*>[y$뿤Y t7>`_ʆ0-߲v hr{ E{{h>PLZ.ZY IM6V5ws*sP.ֹh&[}Q$Փo]9ww]y,sݜLq 2FEH(w/iRy睗SV4a'xbN&A`O{ҫz&ϴRe~C9mRi76y2V^-4-z~khf?2XjK)R(L-}_*"(Ռ=Maͣ(uΑ]xUFbiopjK\uUBwVX!I۵jk&<Ͻ,Hk߾XbbKÉhzҔus. 7A][b3elI'PX͸be+nXWVgF ڀBEH27|HBݞ$'$;7rh[۲e$v^i*p˰APulM6ll9%_Qj-no-߅[Y8v+KJ)9,½\fRǡ(@"ŌnwHUX /:( 3'b:ek4JwyXZhrI)o,le;Ky 3olb7"ܸKfmwMe 7o<,7;\TUG^W뮻\~bw;,6xAQٲeXiƮeF#Jn> kN魪Vm7m{Z1mL{~Jz)gT^γcSEc=B!PNPX T!Qȏ™3g':Ŋ'(ջuKr1!ruR Uq$I.V8;isU,eIګ+6qXGo̘1~Tl JDn^>k2Z6{6A68 FU :s O0CXc7l ` 撊"(@#POס{]ZmV)+oyNO[o=ӪU+F::k gͅ:2X *#P[y~KE1V֟/r( +e}ݷlƦQ8U2;e:Qr}ҤI>sXQja-R; u%{&;bn7y{mYcW_m>l˩;.`q ` Ώe~8Ѹù… r+Bb\6h^6TѤ#(8eq:&E."гgOqRZ9taÆUVDPұa}G=((5h@sGK8i'ɀW\Zܣ}3b7 |(ӔAN8j`r:̈́\]vGPsQIM6IN~نQFe$÷.g$8͢9ͺJ,`*VQE@H@:S6}O_PP\9HtMO>IxעrW_ɓ'1KS, xOӧw<@"+ɄPh@(+.'m( Q:cE-ܒU]9epZ{̩*zXc{8e{v/Bq{#NcW^*Ig y+.}W)KXEb@A:' '99Q賳wqdmiIڭ5٨Q#+,q`NAqӿúNpM8e;e*MӬB.]^=K6T4NPE@PE-G)?fΜ9b /d}I*, V>z[o6rgծJ  na?ü(5(%(uiҤ A@m !P̬%=&N)sЗjw|uNr+g]|Bs J|%ȷIwmjM?8ǡ^YBóoUV_*BȖOK/$;i֬l9NWE@G@-q)Xp xsjk3aZ%iذ&VN ͛7NQP6BƏ '/ݤ ,(C k.k\~ '/q$}e{R}SwK.Ms\YpGŋkKcUGvڎ 8(o_՜>A'CQ2~ h۷n;UjO>?jD[v JK˖- dmmIM6 |=>I% VI*ԺJ>{gZFPE@P(Z;\h J<8=ZCUq u$s,/X0F]GE,|+F;B}.+Gqr"~=V'3GLqN 3ΐ:#܇BJu~\pZdIFuLKQG㨲sO?8)Yq˭U}7pq\y,#pӌ>vaB-z%kKp?KskE@(uV o6:QQj]5("(@i PL*lpX||A|W*>,KBt LW7? ?s<̝;=[h|>c+ۣ{0BI)/cOsZn6dTN8;͈#f8D(g:!պV)oH?'3\AN-&@TW~]wI}x6xT B̞=;$#4(@ఴEcNNo^\pJ/ˣZͪ$F@db贠"("(e@Z|ݻwS !C~I&%N._|̟?L:U,8z 7/?p D|SXp>|x'#p8:/N=ٲ Vfz(7|jB6!MQr` .f< \b ](+MsƎkN8Q&ϺE& sif8ѯ_?9Mh)Fy'ON~arF1ǘopűlA=i?R<@[ }٧ڝU)Dq8p(!9D!"!Xg-|k͟x@p|l94c z`3j=, r NN" xfweɋvСa9 |F#QFTlq 1>* tMچ"("(@*܁]5ȡjPY(byE`lDġP?/p^~R&(G{!a i6Wb4 6p~QqJW`4+W ]J2 oܬeݝwZĺ駟8 A 㞃0'?PCD?(З)ST'[Y/-p&owHUP m>*@ kXfB("(@q!P늲/KZl$JDg[hHQJ~E/X< h>|qֽ8ıjOzuH>WG?򩳔׍q-*]AYN B>#f\(7xbC!~v'p}ʆYe h~/]̙n ٶm\PE@PE@PE@P<(j wƁS,5/ &UVCquי/\,wn $[koٲXӱ__;ȟ#Hr$}NX !4X;Z8am:~]PE]$AݡKb^x.93Nmυ7L6mL{::E@PE@PE@PE@P~G4(@bah/V^6۬-QzqǙ1c<\N8ᅲ@1r :J?}~TA´~>) qe4L/2B/n8+E9N]`9hd+Q(6}' AC֬YM+8ޯJ!+d;<~TpKEPE@PE@PE@ ^}}s/7[f>+fo`6onW{ k7̜k˯YW>6x}E -FJ~0?06fF$Ϝe+>'h6K#[JX.NU JFQC7HK:}ڴifɒ%ghFtw]Xd]֍Zqkonxl$c;p?/.ƕEp/)믿8l}^j/q"Ea>d?J,p(X5A1zi 4 h,4)|=9Cڜh`i /QIV`(ұ|Fَ5/248w%toQ́W@9 % 7}n ` w^Xr -ZҜĽ[Gω ˖-9 E+9  \s,GGi; m8*"("("(D3]θ,]iբLz}fͳ/6ZV-tm>m3iji@X1N[ofPbWLpc!B4JcCGve(0o֬Y2X*ؗ4}gϞ!lJŁfZv$ښ483gP/a T/RxtMb;Ce~.XANFm$~ g'Q&N(v5jHֵo1O|Tpg}z_(LOv_PE@PE@PB G*תZ?6Kw0<1~g׭Q_Ns~;?6s9=uf4 ycZՇ5PԈ,/g,GɞlYZC64}v}âGߤI{m+] yMUi>\k*fѢEB/Dfa#"P|w?8}Y9Pփ)"("("T@a) {WH/Tľ='.f-皿oil'^t垙aڃ74?xtH)s͠_xdYKwd-Xaff1#gm-@ӮΠ1)e\'g(ɓ@EsL!c$o.5?_jR\ʒR̛QPCoɮ~())"("("P6 yo8vM>.U0Mъ/Y |+HeZT@E@=x1cF؁?zfЀ"("("("E9K#m=2|e+2szSP =:zE@P y̧^G @I" nh1&b,)j>;[gEAT,X@ocپ-;;?̲?GbUêDD@D@D@D@D@Dj ̘([7EA{vg<ܵD@D@&uwEv:Jx-ڜ4. ?};WϨ8^.A7yqu u " "P6iӦu>D@D@D@D@D@D@D~wI\ek8WzW>ۼwwa)(LM+"  A`ԨQGnСnĈ фOg׎1˖?^nh78б@IfϞ]RYbt.v7kq.o.vO<'J}wJv%·jյku(`TBC͋5@D1[[ g_n9J|t}O3.&8l;w{/auf$gak?|!]!eaGO`;}i\r/?g;xqҎ[mW ['nܔ9q}~e7^V$Wj" " " " " " " " "PS;PGE 0yf{o˙P4sxInGğ{RUfD@{3 uED@D@D@D@D@D@D@D@D ?;c3ٗ0}mr?4mj'x ?"M톟|Qwmv{ǟuPýzN=%pq9LmKnw<0}{p_݇,s/On+7w[e M؛?a<`S7owf&|=_n7~`WUU@`UTW^y]qn֬Ys۷wl8p;ꨣqrL|qi>,>|o#L6|!Xm{?_;x]}ן26X]WN_ss=l7s"_K>q7J6FSgd>W) U:qTN=TWWWW[nM846~:u?dɒdrs=MM<ꫭ~rPwI]۵Zg5ćM:&7wŭ~ApO p:myZ -g.5(o~/jcsn…n?t{j{_PGvxExbSOTND C/p-oDa[fvkqlbګkǼ!d>wCK:ꪭ Fxb7k[n΂]6k];]:]R*Լ Hpoމ@œzcgymm~a=Ygܷm Cle]i|#Gݍ7uξ8cp@9sTٳJOwgY)9F|[/駟O> ꪫ mk>MǎݠAr2nȾT{wyC=ԋa"$$7 v5*" _+#<0DN,?Gs=~ඔ9J8( 7N8/S1.Rgԏ%\RoQ&@ ?p{l-t>bx뮻|9s愧]׮]uQ^u/ϛ7/N{}!C3e?ӱzF }X|; =6u{gxZx뭷yB~P6yoq<%p駻O<{T%8p@ׯ_JT6E;܌32[J29T%f7C4)֤N'7 K;Evγq)C?C$v̟?~^p̓؞LψOڋ/XOlOg{笳rIMk/mZ}cǎu⠃7z!#M@{n>J%зo_ib'#GH* ]HYK6'4'xqMo}{#TwMB9;=c/6uT/f^r8Vf]}裏v_}p W\^}UΆ5/\>/9Gue9+f~uS98YD@D@DeXs5}{bc=ܽV[m5W^?+/Q;i0 Et{o6l?XGu~ߍ?އ čvuW&QGaCx-Xw!7 _?㏧zROyp*z衇|CkLqƹ~o#FI&ZϷb >b.rPRW$k" ͎e㡣G!=^c#<$N,%7^կڵkṆ'L+J .1"_㷡m!.R_oVXp1;"~CGr' Q4h~/=7*{g$~'dy8%$9׿w}}wr; )S 1T b(OZG3gO9Kc^S=AZ&v+3هR>W1>zic&ТE19y|v}b;袋ȑ#Xϊ߰b6A" " " Kdxi{--\N{+03 yь߳jg7nZ~mVT~򷕬8aoWr@ 1@ #tbΏhb*y^=#3<5@DSf?Iǟg^<%{ǁd#7|{2 ^Hycgq?~c=zvZ9Kiؘ[nޛ}РA\n \27xɛ2mnkƢw$|r@S$u3 L)zw}w,w~1 %\'ZHYflK9#XD@D@Dv H0|¯kN;4c£:iV8f؟y09>>S~Q|lOHj24SNC;md Ӌ9N{V+ծLwccmT .p?T~I'y bA1}p%3 ;Tk#샎E@D@D@jþ '=vab;]w] \=̇W;"7!W½y8jP6V~]w-hw<5#^Ρ}X&|/CLJ~{ ZgYˍhSmC q;wv/[7 [viؘbޫy{1?Ta?bq*|;֍3&Ffe*z s˺5c~-cmX[zD5Æ kN]jT[l_ HT152fX)o%5"" "P^lG\fCo0?4%!06fݴؘ<~9B`+'؈W^>/Qq'4en޽`l#:/lc E@D@D@D/#GNB𛔧 MJEJj~wv߽V]5<ٗA4ώ.t6<'GxJEN;'f+'v"KHYѣGWvqN'+bccZ_}96 GcNv-cG<`bE@D ý']Chyff…>a߾}}y1`M.]~eie8WLŌL{Me6Gi7n[h/]=G{!U˸MUL6cgMHG:ȁQ=)ZH'E{Dy7xwpo0XD@J'СCKĒ{[?/;WmsDHh +g~)cU" " " G=ب}pBwLE@DX#Cؘվ|r3?dLq( TayD@D@D@ր@UHni1<yN6MCU1F噪FhҸ@#Xc5!hJhq6Zܔj@" " " " " "P^S@mPlښbFQ -hH(Ƭ(UFN FD@D@D@D@D@BTԩ" " " " " " " " " " ͉4@^S4'ܛl/" " " " " " " " " " UK@{N:." " " " " " " " " "МHpoNT- U;u@չ#GA?\Vӟβ*7viM/\Hqiz)s{> k]w-j(\k㭆9QE@D@D@D@D@DAj" "Y_?\ESb m{k4M5Ҫh SD ;^z+=Ϟ6 8Зh߾}%K:zh{/^n,C;O ?qx`c5ݢ9݆nxB7({Z_p}:`0(" " " " "@obl+kF%4) Y  $uV5@6m۷-~U #.O>d|Py]׮]}\sO~PMdnDZF4M\퉀@Oc=/_^i#hӦwV[m:&ֲVZ<ܛӧOa8>s7+/*"yk.ƍs3g^={tnzϝ;׏wqkѷzk*d-Jk{{;kر+, |}]?V_}u7a/FS' :wv!… ݔ)Slqޭ[7?y|򉯋v̹hݺo a0&|ōƻ:l*e̪jSߛb~Î08gv\l~яzs`eKol" " " " "P\sM>Tw(@LD@D "<_/"a/l;uTw}7޸^CO;ѣG:"aoL+R\%.yKODjݯ~(tI\nZwEn.Z.<q9;ɺ:.!V6۬. cYG}n֋pu ?n$E-[,.<6r-3Yn;?gy\?y]$N]wݺ+NQtc!Nn0tAqz9Qt_o$6׍5n׎۱>r!u؟w+O淓M1g}VKݶm:Fex/uэz\;0id," " " " " "PFz40e" "P7va3ϝ|q`z =|nΜ9̟?~K.V >hR=^xa|3l֬Y.z #c9gzd1. e}GD@D@D@D@D@D1CQ&" "C瞫%2\DBoD$B‘6$ j1.E؉H4Cc )cexȣ.mYŎuEy58ۋ6MHK q~Dnly]G#dLh'/y>䏥"φI1&fQtӟ=,6W?>_H# PҢ'⶯dr9ڴ( \=Ʊnz '"K%mG7|5 m6OtK]$Mԕ3*j&Ch/k$ q<h^'tûj6,_]^uxޒHp^ޑ[/_)'Ǚg/" BT4x@&'O||5iN:sd yHIS<~yOpvk73͑Cw57pC[mUZeR7 =I($oSY.`ҥ~Xv(yUWeKXof̉ya}3u]aR,O'O'<^,?녣ؠOvdZf+?~>7>׿u&H<+q3&lq?Kfw0;7-%?m&lRW^>Oط\Ͼq#" " " " " "HpovS4gys=qцnmqgqC$dԩSyl>SbWׇz{~WΝ 3s=c'x GG<ڬ!41<ڜWᙋY>*ݻwC!ܹ#| I8 X.r6ȗ0 6 SﭷcÐ1]vŷwzo='lʢEI<&<Έ{?Ew%vmqݏ:(/#3p3%^<#7U1c Z޽ݡꮿzwwH?}_¼#2b؛EzÇe7rC%6c Ɯ9s,/!6u?4nѩo^Lj#u5vX7f/)_xY;wܤI|ڵs3c-7cFO^ ռ."  tu\}<q0ZpYډDҸDuhE{$-gcqQ؎-آ1lذ|xp'.Ţu;}HW&Q͈zƗaWN;zy5(kMRxq=!Yby?#~e;KF͢n8s7}kt6Or;gGvGqDoy'8pyb8'w$\fp 51LjmuM,wana |vhx,_yqH0cv`CH4񚑯ᅬ>ޣg>7`wAiĉuw۷xNKdƚ_I`^b3Ǔ[Z} _D@D@D@D@D@D@J6" "xaH<{l1*BpCЉNĨn۶m6M8¨tEҁ x!P'&@V}`ie9[$!f`L&L>ſ/Gj1aS[8.Kq0Cmѣ sskV9t`?wo}jwJ99uT馛:狱""ۭ:;3µQfTV懏>M6ͭn-(+xb; Ù3g6mڸ7^'li…n5t۷51wyǭڮm۶eްsWXg9H%KҥKJ+Ǻ[a\۷oj[OXW<+AQu@ >S/*|WE4h[yx /0mn7.̌Lc_ҋ;bm%=s+uWw} kɵm~Umokcǎ?cXeUf\3$ܹs,#sW{)XA{Jrܘ@oݺuFOqѢE&=zKK[oV_}dRy>>+sR@~x"Z`' цj6!SN!S e9ZFje1\深дjKTl7qUW]b*cBZ0oK:&dO`57fYܘ?څ7[͛vaSܨyi܄‹?ѯ|O}1H{cPV" " " " " " " 9 |^i׮Q̜/=?^zˢ*!6z~8p`vj4q=\={}d)>vXdytӹ?O 7EčKsP'B)䉮6ȷBτ?jnYs#" " " " " " 5Fx"JT "jc)o 9a-/ fscYciO6:f΅3 u3BSHs\o#{1 ؗb\&@i0x #b+.In+vmBg4 ˱rJ9s]+a]&iY60c f =߱#3\~w8l!Y UD@D@D@D@D@D@D|o4jBD<?~nb(a~GxW8`HJa,dLPEl^6 EIdB {-D1ޤ`K\ 6u֧BQ+fomѯ4|Z-/"sB>R L#cGt/dBN6͋ΜcKFFXr-mWs&܄ᦆ vM[|7h0v1#5fu$׎1rŎ~s \V'm!ҖPƝ+yM=B7sr V-R>! ءFx0aO3>ƚ^wu79(}溥gKY k6N]k/3;ws\ʚlsF٤򽛬>4F-Eo;i|WY^KC-yMiZ:&竣1%7e!" " " " " " 9 v!Ӌ^dEĜ8qDLCq;f_o'ElmUE6#LzR'!h~^(J" pؖf ˖N_0b0b;|YS-3DbU,kfРA"6lqy,L|mm 'J̺Y&3–v6 K 0kvKW/1-di"$\+|H7e+283Bi)kkL+7q'7)XǴaKR l]{K ˇ|faT,iauny|ީ_1dKd>SU@^x=Bnu apI a 1oBQ rg8?dBKyS\bB46~狱Cx-ir')ܚX*F. oPlg;s`oif!#YhMxM׭{Rn w%L aIћpB,b+8X&,SGk7[-KȘzx. ͌\됱pӆ~0'WX{h/SC`IrH.+ 6&&'xb2=u(vCdʛlXxFX3c^k1ד vP%\?!ccM,1P'"t6Ӝk>eJ~sm 5^3m~Ylasal֗Rּ9 e릒߻av_F9nƍ4a'[Mٟ|m_J)]D@D@D@D@D@D@DbB(;Nks87*Byh ! %v\x"jX̛+\iɶ +B5%`^sY]axg ,/XQ+MT@0!*[W-=)~!`#ZB"^[ċ=erPKkDŽr.(}_KOUZ~9adZ9IgcOZ-wkz 5_^g! &&>x9mQ'=לkҭ W:+emc27!9>umc5_9 ฒ߻52G޽{\ 0}HZK2|c|a 1({cPV" " " " " " " 1ːa!v[/ !Xl\C&%ޛiS@fMm> 6i<\s1;y<V )Yl=_HkU8EPeMq V#r>R" 5O|6xxЛ9@?ytde5fw[lٚ1LXg2ŬMڴM)Pnyg^`l n/Ʊbe %9. ,R7O\$7M6\iie<9O,s%" " " " " " "P-K8}; a3f{gcq/,W=obaM Ajyf JacI+B˴|v~;*e ] QnؼĹBFZ;xׇ]#3gxO3IkXDKM8b㭍>!jh YB&L>=cV>virx?TXu֬YYcqYi o4 Pnyc=1xO0b]};67k~7;c/vmR8X_YRJ笔]cݾn_xdrg`G<7cY| nuڸ3*)qvsj1 2S64GhB21,x# rqD g@MWPCCl$/!,]BOgC޽rc 0O0/[oo !Zr_\!VB L  >/z"^G\$ a=ڄy/yDDÛpxW5\18GbFZ?*17b߿02Oi2a=1Czhܴ ~lV~R=:d>?#-ؾJ]j )Rn. AyD@D@D@D@D@D@D@ʓ'O҈z"!Sw/^V6_K{GD E_QeDbBn]sX AlYhx|žBO!ɅZz)gmT@ᅘCBI([K ޭ Uwe/JY[J{)k9kuTvg>KYG3cYĤf8344jz ‹0+<_z%/ IMo~~ }Χ&8j" " " " " " " MA x`Dwn}!^x&tX@% Hp$M%" " " " " " "hLj<Ʉ  aAbE+AM& _5x͍caJ6dJw^18m@P +TD@D@D@D@D@D@D P g.)ID@DvhڙkTD@D@D@D@D@D@D@D@D@D x}l@U@^3S4$ IWu  53@CސtU@^3S4$ IWu  53@ChU!P" " " " " " " " " " " Hgoቀ4"Љ!TIENDB`bpfcc-0.12.0/images/bcc_tracing_tools_2019.png000066400000000000000000021430761357404205000207700ustar00rootroot00000000000000PNG  IHDR ` pHYs+IDATx@ݥ(`"bk݊PPiQin?v5pw{<p84@ @ @ @C@z  @ @ @r ~@ @ @ j$a5l  @ @ @b @ @ @@5@M6 @ @ @ @ @ @ @ @X&C @ @ @ @ @ @ P FB @ @ @B @ @ @FVP!@ @ @ !@ @ @ T#dc @ @ @@ @ @ @h1T@ @ @  @} @ @ @Hj4* @ @ @ >@ @ @ j$a5l  @ @ @b @ @ @@5@M6 @ @ @ @ @ @ @ @X&C @ @ @ @ @ @ P FB @ @ @B @ @ @FVP!@ @ @ !@ @ @ T#dc @ @ @@ @?x~iɆm|1D^]z'MmO.VDݍ\H;uz5d+*+Ixo( @!@ @/Ab{Y EYl\/`2ee--n?ޢT:NBtDZvxKr-nMPe1 @R @X  @.pzQG^ZﲻjZC?{>uZ+ȐUSת_a̻Yd(Sܠa6-#kXa 3n{ c,++B/[e)bl7ےtd/Io%u-;g؅*崡 @E*O@ @z dqOյl\vNvBl$h4ڍ'Yj4=bJzNVFTxc{j`͢5JZC @R5 @ ll,i$ )+\jj)S03gnթ]X=YI\s]/ڶ.V+*+, @ViC!@@eۦ6+(888=-Zbױ-]wK6W~_GX7g K,a@ @x @ J% vpwYUw~g1dd&' 好ݍYdE԰vt4T*nNL[D;+#-&OϷ_O6Gc_f(ixxk+*EQdUFz VP8ƪ~-|Ss*ը U߹!@*@*M@ @-X6UY϶5065>~]K=I֙+oJ%A3U~nyu 'r$pȏǵI*i3 @ @!v@ @ 8;:dª~1iE?7?yL;ӇqdU9̫.nmsFω;3%|vqx @ WJ @ PyZYY}Yx ޺NCæ,anN6/po~[7nmJC׏g`r8t&PA @@j کA @@Uxq;9NZ6E.reJ,2RIytڃ<Op߸̬~&&-zjk!@Uxr14@ ]{M~o֮+CW1H]o`_d~4(ۗx8شv͊=g6N+SH͛o[uRw6oDnn%@463ym"^hvO1cBLt%;YrIXiVv~J?WIjRwwa{aϞкnq~sw_eܭrg/ fa%ç/߻u|uJB,^rF]- ٽ 04442@Hc2;Tgбk=/9lZ[6Jk:M4?23v?c3 K̼gN֋kGn)NI#|={uݲVcܳѤvq*٫[%$Y&c?8;7oΥ4N566{mo"¸/[׮>{E#1@;;.mi;GvkV̷:/iQ;׭֐~NSΞKFi4Ƭ]h7d0YAvZjN]㸠)5@a˃(Ls|c;ۇ](7WVHB,'91?:H7߹+]Y@+ܟU"%IKM7,f=w>JE4ǫ9>llDJ+F+2U ?ĽD^1GgW('&$wRJ3 ?3jd~]\?ؾv-Yq;f*J/uX~A<#Ҋ(_<~pg @JVX @@.2:(ѢY:2ب߾$' \Y?cZՠTRIƤ-g;mugmaTX7tM5h<+rQ" y9 S^yK3CtVdȯ/^i"^xn_7*ov{"A08kؤQ=ueԄh/ؔH 8gO7o4x=$88@e%aU~4/¦:'󏌌g8'˹e^Nz~jNZZk߅#:IdoݯaeM[5i`LɌ Sx|~miw uCtBmP2=m`k蠲v=,ӲC:p ˅rmTM)AҒ|{ym);Sҡu]LNBL=Od3+!%r˯fviMkmlfLlVzJDXǗϡ1)dU~nQgǒ"@ P>\Vꑠ @UL){P2z^NCmNAdg&={ҝQ-rv95JPO;fּ_(BfY+HJ1|~Ot r:sb5l9zp ZjL};%h/瞼Y')Ք ̀i7iވ/JqqI b}'ZB%^!⌀W2VM JyeF,3nhJXq1 6jji$򬐈<|NZis~XYikvP űvl[1~ ;;K6<5s.{d rګ([%'{Ǣߣ/(Vr <Bit9i׎lZxl&4g'%Bam1cU_ol~t4zEyY|+$MNdo˯fQ29-A66Vm ͳ|.6!x" @sB J*}Ťr;s܃ ~Oo}p({,8 O]n(]=8⹓]:h:Fw]iԠהd\5¥6s, ;ܲpί$ 9}7vM:!s];bO27q:@_G$~tBu϶:B̶?ȳNoL.S"O+&*,ѕS%_,#-ӝscXLCZuGNص+ۯ_Z=`yJ߉^wy۝xo3Xe3m&^0Qoo5[d'>ڱCwzbQV]d'0"6A ^^_b mhczc' {S6R-+$}p۝,k>mMQ#3jPcϽx۝r}$ @T,@ P肇cbb*Fj7~ՠps8)ޮv$ wyO]]r{(Ԧy+yE &X,M; F;Oܺ9.\mJvO~AiwGɵzѬׂt~GE9. MqiP\UDԯOɁҪbWOU꺋4Jm:FKYQm Fɫ ¦4c7/zMշعtO\|Ԛ~d?|jqjnM[%湳趷޸@tJc˩7{vq0=|"?Jz~[ٸbη.䕡Zo9pnNVۡ˯NVM_B \xOyw0w !Cs'Ygb=qY @ */ab  T^#×XCxvKƇrhۊD똪/\r2 h沞.kHJ9{!Yq=.Z_):Xe qeXdv!+ sYʩk2K֮Q31uDW$pNh=iy+ ybʪǻ\;$ҊNIJ ㇏?r!)pm7Z $u;Ԋ.vefZ9ZRRr>KVnIеSDdO\bU"a#F,:O>shߡhW5y[%;jKuiYK[I]sܹJ&Ҩ>S}͚W&OP>wF;f*C?r'5ڏiD.NЕMC#G$^!/;dWf7yʼn_HHrWH hjP(i̦Ϙ|=5@O풿,6ew`%ĭd^ܩ#j%S> e @Y K 7@ j Pv⌒$Kŏfj^Rީwr#9rs(QLjظuNd /044_(UJr{s @@E SEmA @@Qjc%O%ֻw}c~M4W̪uLȓ4y߿',fx܆CxҽRa߲M\%ݫv;3* QB..Ԑ*,EM?dN J\aXd~ᙜ/78:+.%E)KH26::ZMdddT"Bƴk*~jԬI}O_aY{|\q΅|''6=cfN]--ż|/f(@ P j3@ *@Wƣ)>v4=:/,55ŬYY%ba4DJ=tebi0Gr{Ziɒ@v+v:u^s+q1q͗%Kic&L9i>:a*%3c\rsEl<*9iJ*n(R$9Iɔjj"*)Iɔ򫹈^實+.;V7M" y߿#;Tk?d tmo"@>[V p!@*V7o OSgm\n+JC4y&++p8'w#8YYEL?GH¼n^|"oջWy$U+?)v{2s@c%_Δ؊`+(ioDLWdgCR۵ewxgFRif̟.:#0cqWSeڜ->P\qyl\nmJFN"S~5d kN9}?J'Gyv?׺=4$, @@%(JxhS`TP/_vJEm @VJ;u8 @ S͘#|Nfr>k#33HyjJ8iŬb0)-{,u g0&`??Yr<DumJtP^՛,ۜ^o'F^%Nz:~X.%'?)#nFKK^FcrK*) \Nj.[U3r񠱳~||ŋ/_M؂uw\m" @V @$$C=ߨX:Zu/!=_zg}wO_[.>LȏpDCK,֦/&&FkD)iZhld7YYQ#d~y$5УxN +(GB%WNe׫gDmϏFLdr~$r4d3Ny Fg :IBfͨW3IuS>˦~qz^]_ @S~4@ /7֢@VbwswȳV9gV3Q&Nn8x%pib.vxiT6_zP*glќHۦ}m&-=߮B9udug w]!ķ:o(^tAttVC|:Ӈ@eu k%T#de Ʌ7n{ra$WP7knʤWg7""?5H pCw$OOshzEΕ uhהZ{LmEi4['o`h&Go 1 @-P{ @J(I"00FkC){sM5 TH~Nm7#V՚HdWݹ\8sUK˖KҾFk"t/Ըv"qbv͵.q31n1)v}vu2߰r2- 9{YYU`s.huh\tD,-@ ̢w$gGn}vTe%WSGi+|xG?s\SaN':'Yn4/}v= SN2bwk O.JiNl_+͵Pn[ݠ1p^aP o k @ؿ1 @  54$B.n?i;I۵m_`%-SgL^ÿjNsSBtY玽h;"{9+jޤIoNGҁ=p?@5DEpq#.o:wGŝjJr<&SwEVȊt{-_ۿjGM-t ujGDhYc1dǯo~`(lNVDj1]Ut]8Z >vƮb:ʖoB ' '9}WQ/[#Gto˫f~_ {`Y[M_ xYȆPLw]]WW{z!u  @ @Xi @(YMXӍXS*> NSZl֙ӞMdW:צZaG׌&wXGSm%.H)N!dԓ\ؾ mA%Xɿn?MɤOP'=&-l·8 &/Oٜqpr~=6w$iZwՓ%+⋌~&*Ԇ)I}pj{S("O*QDLh8<ݘI9c߲:e~E"ЈZ%W{orIڽt΃[VK#%NYfTT2Ґs.|!z{c֏0[Wg]zO+7\2sH&ʯf F͍c%Eo:yuڶ#.)@ j#PM9 @ P ĘҜYzdj߫éYas&wy|@UcNv m̢ч=2uAVxA6|"%AN=ǖo1Li^ikknznV c9I%Sg5WԁrF=]|*9WAbT @sݝqUn5JelBW>cµ__gΘҵ,.~ëyʁ]#7a(Grc&O_CO&erFG=rsqSLusFLjݷppϜ8K{3Z:2A8w,Z5QJY{yIS~5 7.zϡNRAOΛ>zwҼIZ5dr2#|8=~qLJ:&/J\X@ P rSA CbgI صv.rPͣzq(G999BN֮އYz\{B 2sh ;r7Is8gͅ5{]1I_۳,nLegtxۘFتo>7YO盝kߐ\4BG)" =އs3uz4PAI5}LR0+hxt<04Jq2.\FoԢ0p'zrJku^6s΄R˾WS*$c3O_oraݴc'0R bpk mrO ;o)-Y|[ ] \^\ XOSXQ @ T*䆡@ Tzպmݿݿ Lt)ڊ7fkɒQr v7cj49{rM,򇮨ܾ7]nѤ~>[S-r[8rƼUt庺JDǻپϮm/~KC}67 ظ]1j>n7fC^<*Y PʭSIqS/[hf%̲Uv܀US[/Q)#[[~fY`*,[Ar07k_29@жV:+l꠾nفw#g2d 4 /)^-T4,2:X[=GpxTZfav5_gEk%xf4xsӘ?|m?QßZM?otY|wֳ߳/\=)F7c- ׉ @UUª: @/fLUZ:N;vS0]|g~|9sYːkʺ7nw∷OߨԌl9ŚzuLZ6fA:rJu:yR)VA)9Sė)^[|_u2㣛o@dL|i}utT n">ǨUCz8xD4A'T~-۴l TBNJ6vi_([dU--~&]?Zn7,ݻwk^w؛dY^C$? %.s.$((1=GYEMR -^MiVJ*=Q:y!tPt2)Y Ҧ,$r˸ ]m~˱[dơ30l̤?YKVvp&(Nt9 GNF_￑щI)Y9,9yu u43mR_t*NP @R @X)  @(O\cNģ<ېe5vC<$inUHWrZG=P@A]עP%:]FnyN/bQzЭU(IlmW~5jRX @ P} sC @$-S~ mF:HA @!tz@ @2Ԅ_~?~?bq]e]Nq3MX3 @ @ @O8 @ @@Z>n<ٻ7uGv!K'3 &8NϷ4DC\HA @)aEj-@ @@h?l\%R9<<|}- }}oсGcj͙9*)@ @@j @G @ @$iaɘ_恙~]f&ɑglxjJN~vi4S_F  @f*@ @t 'l<덏~q2XƦMi(f%|Nh-YK{5  @% @ @]QO_0 }L541@ H2 @ @@ t:}d]'Ǥnù+]0FM;k!@ @" @ @ e/;fZ9:W`Hl|Bf6)+]Qm{Xԧ<.,Zy= @.V8!@ @e`*h>xl @ H0  @ @ @*H F3 @ @ @a@ @ @ Tf @ @ @ J, @ @ @ +@ @ @ @@ Y@ @ @ @ PAV4 @ @ @4 @( >@ @ @ @ h4@ @ @ i@Pf} @ @ @@ @XAh @ @ @ 4@ @ @ @  @ @ @ABi @ @ @$aA@ @ @ H0  @ @ @*H F3 @ @ @a@ @ @ Tf @ @ @ J, @ @ @ +@ @ @ @@ Y@ @ @ @ PAV4 @ @ @4 @( >@ @ @ @ h4@ @ @ i@Pf} @ @ @@ @XAh @ @ @ 4@ @ @ @  @ @ @ABi @ @ן,Py\[+YEvJҩMN'77tr&*@ad P |sC @ T/Nk=2uO\MHo",G @ @X# @ @dL%%9| ęJ&#Vz @e@솨 @ @)@W~Zƚ_Crr/1L &}sبA\kaiG@ @: @Xfc @ @mqD=^yw#$W*A\̂t[VYnT•= 'a՛S @ @ @ @X( V@ @ 뽃oE}:6Kag&}ׯLinV]A+!2j2hܬK6rGΟ7IKB{PXDZ&KY]a0بʺ ĢԪӢU+mV[%%#* Dl7Olb ]FF:ښ6IB})50oס~4ZFMzm\:P*X Gs @ @Q^/~/-wIKI+j|zT+;Xق[h?<>ZڑBu9,? >?yz'[:j"wbc>#BW.up TtG @/aśE@ @ف/|̡%wyJ_,7LS1i;|۹ˣX%W<@$^g% 2:<%?WnӚXNON.$&cgp+Oo|@1y̌t"F D<{U -\]dfNJD*IۭƭK,D_G~%)#8"+ 0ܲñ{/1do>pFjPX"+66f7 }\vɡk Jv^.+{%_X;/8}4x (_`e @!ITTWT b =IX%)\ Kr'+.m~gjC2o.>t#2gsvX+i2F=w,bsQJV~0)Ԣc}hݤ'_8!F9\qum8d榵t4h9~A{&'}ӔaAy͚i*&F{usq!*|c1tA=hjzzY66җd{'* ]De$0SEx%XI Fʝ{ dnHC߰nu초#~ܿVn`W-@ːߢu,y{jM_;ͩbtI&0%6L8Њ[3_-'/ظ{$=M?o=F-oOpOw>9W`\:oN#@ @ _l߹R~`oEo+%qUZ~ $*Jxn ,-I4el-]Vs3 v=lU['wrkSp{;iߏ4|%*"\ACrDJyqVOEE:?h.uK7֫~~5l݃AnK DB*D\$w 07k^CMOoߚk94^9w?:u8y׸ܦ*!os-v*RoA ڨ%iw6_Z)L 1vt`@[n mGyms˩YvYcnV$xt vrwŅ3 ֻw"y~;jnOm/9㗓S:],,&=)ӝݢ|#@Ʈ f5 H.5 @>?۴a YðCR=ƛUcc:w:tW` :&Vub|%+9xҥ_ML8V\&f޾/( ,[ٿ u6z=7l5SCn^//r;02E3T4jZtpj1(btEU}ܵB'aeOިeN2+%ءr:&ӪרuoVS3]s#<]چS.Y1b.ltО3nz Đ36` Qz"78pܧLv-^9sTĄJIL؇ݿ5J|kێwyӗ{qI,=V[ Bh4NV[?;:\'5xV7!/ [)H09p$/1{Tv۶O/eIbG]e5c;֨r~Fwlܛ7n?~]]UXFJ*7ˉFőK:llT>x]4 S*DmLizD@ٰ>q˾̼ܫU[]Ipqt/?̯aaѕ$%&%~. X@"h2@ T)NsgoB-0[¸ sXnMWuM{Bu14bԸs T,oqnqj`* y?aZSϜ?NzCaG.8e(?ϹA?a(C|~wnxrﱔL+Tsb>;FS39__Zs9e4qsw]q%[&6US/=sƐKs<(Y.<<޼}[:_DR6$9,? >?yz'[P'L܇)7/=z<^Dc'/ܸ>ȼn` 2}Z|[hMNzܽ[^?ҾkЙ/?r&Jq;}/VVgіjְ5%f h-YO,|8E=)M^yX@1M22Ÿp=n},`=G3c{s b> pދ9}LL2vq 1ʱ?}$7)fO\2]:=qrLq2S'cdE&b\Zt5 [ 2АFɫ۹#T7b6,*˔q┩̄%e{MK2\,b{7Hd&Btdl߆V=B^9Bt_Qü[3ʷi4OJn,4Yx ' ` @m7ͧF7Ec#YZB_o;m˩theOpnDvYu-!p-Fl{ǐ$CN&-[6iPvME@n7L֩CG7ND2d:}CAQ,vH]YV?c:f09u޺#>D1^ Й5#s~NGl\? Wf oYzj߷|d|!YI Fʝ{ dnHC߰초#~ܿXG'FUk˲ '++'?QM ~96Ԫl@ަՕRb|}|!SraC=˽li6S:wZ\@X ;[en k0Y|]^:p_4vjC~\W_\V]}{j'Ft=[.;ǵmVg¢Io.CіU,9%yp}.Ǐ|6?o?G߫w'ߌt:t',[Ǭ ލ o:37LVQyh׆M\_-Ժ Owܙ~ʻw|׏9Lus 6ˎu@D$,S晒[mvGQ!;sڬMN׷PvJK?W~Ai޳~F 4?r6ȋ`~!8o>}[cO@F  @S{yr :]eۿ@&oرɌ;nD1]:asCNm:RT{_5 5EݼfqgCI^~sZG]Ʈ>v5q8pe9g;Nwr?Ư{t*D]CPhvZ= ''Vwwv5W֌>7l!B.l=߂fwi9DbG7[s8.W+9p>.'㯜-K">ۉmƨSnJs|q"Qɂ 3eT 6Db#gܜ`(N9|ltu4Av=\yWcn۸V)*;aa,[ZmסaVǂ #ݞXkoݸ@OB_xsY}݇_hʥYѱyoY~c,nL?mvfMUӒb=^={~Ւ~63mѹ'Yb|#mv26Cp`JX%)#*hȭKϹ=i7wwKpjwk#- oHQC2qlZ2D:6mtP٥NȴIG_lv'n@؍Zfcڮ12/3Aw.$hpj\[ox75i1W8֋4 T!0@ @|5uV/]FimO?̻T(uSleseX;ϼfq&XwbQhԠ`0ָсO~'e*53bs/&JI&KB׌k>8*O>]b@cruncK~v &&97^37/]fܢ+܏;DS«/?=Klv6 |e56l ooIP^7w-rjoleTc"?gjxjE}ϲ*s]"'P^8͔Voz68Nt!>f遦 t _7%wO[]_UairZg~\g13/{nO"/P$:#uT~dʚG7"3[6̨$zYth( n9'SiB. %8i)䫒8u~65ӻgsZjJ̴?]}Iln蘍<krR8$2Jsr8?rrN~#wFMWљLTC)7#]Փ/;;[sӯ -1Wzċ*8哻O*֭;7/+2%MKLxϖL$!L v | ߞkF-;ҫGI1S;~ XXt=>YH ŠiNg2 uЧ1Tw?hu\$/2VӯثOs&5섘?_?}c&+6"k}}Cf尉fP9emv}[ v @&KSO@ @rp`Fw]"ܡUuĢA^MǛls)6hEzMhxuaӳRn9NYjYFO{}1V5l\U>mwY1}|J6NI?DR8Mm+;SKGAiYf~+4=jc w-oAK7Nk^NNܵ=ƾEか6r [t{^dpyDŰYc(d)^Я%Gwœ(wO3:ʙz C/ri}9Lfø"P6WA ;ST응! --!ӫGS t@ge$pw3nz Ǵ8Ylݱ4nSmzTj>]D̏孊 =-Th2pVնyr*dn,[Nwi'\]m=> ͳ{я[2ӷ/Nxnܐ.w|ܶOR~,ɲddr[LIJ}ڷ- ';͓C$C[x&w$nCx#.!^cD>]޴=;uj[4Wޝڎ}pg_䕮ݸHt_XQ4?@ܼpo)DNzXXXb^*8DHsP,BZJ&݀ @@yѕ ;05[\ MW9qRa'ғӈy?':8:Yt{9]+!OI6>x_);̘f~Aܓ?8`&I۷&,PԹ DaV.|2 !dLyQ5bIcsN'qjr//OW$ G zR*InтwM| JLCxi4_?_MDPFF!:<3 K3',Gs[qͬ>"[l4%'G\iz98x=(ʉ340E?=_ IJLA1r Vf.,v TɅ2-$:#u’gl,~g:?G\!NcC:CG Lk/ `ʚYQGdOX1pG~SĿ-Y(Ti^{4,<+t #*jjqU99964-?HKuؕ_)o6${{7WuPW_!D:#mWt}VlQٜQ"2뾏/[et ZL]%,wt_|CFZ:F ۴mU)С(QX{bY[r:~#8ΐ52iѶSs˪O]rrt~+:>!gضclUpK/1 ͺwiIʼnk"הfJyb4ek9*%&7T~]Vm[95d֟2Mۛ+n:$6/iڡN)Y3lX;'A2`N FLLt5in>DC. Tf"̃C!@t ?{A?Y‡Zqmmq/JP(J~ݳO^Y92]=&ZX4q`-E \⩧+ѡݐܱQ+"]^xiPc_KO߸YXCȟ>'kN"LwEȑMWlh̏X bOۨKr-6W#>;sڬMN׷rG6ϕ?PzӴN;sϿw|׏9LuȵDvcw9d{[rU>-D(M%L|~dV)`з!aU jVCΔHO圜oSl5V-$]}P0R@ϣ @BJJđ۷I n$5~"G[S1A/QSݹ(U3'vm'ss7g gܺ2efddj& |"x Oʳ+T[>ny9G NMnJ>ff~JrP+rtۓvsO|g{ v6Ңtyk'Kıjei[c{tnT"+СSo-ό=kDǴ]wcd^fك\>IGI`n7>zMںl<fkrɢ9},;ӯ`euzvЁad-XRC;RrH^^ 핌zٳuaK@ PUD|*C8 @t fЬܣԿ1 %D*)Y4'u% ]F^GGKJNJHIEj?H[ t𦙖 Np}eۭmth^JM Û_q0wiEdUHgf4Sv۶[7QmNVZXЯ_q)tIvx2ڜ=92fٰ3Aa [?#w }ۋ]O?uzީië2+uY/߷j<Լv.awoϵi|K[IKG+S;ӏ rͫ'_v۟lM.\l^MҲ|rw#g+? ?]nݡ쿡}Uw?hu\$$s_;W>Mjj 1=~+ =}Xʁj?qdG1o;ĺYʈw't~s]VJrxxɎݵk_v=uoUdEK-؎o9v+8*Iq~yN&ڱPCNPam]ӧT↽XWY4٦Sb،~% {?jǷSɈ VAUBi  j `fA@#]Nu?KfH%EQW焼}17[N]^0"aNjB̟?ک"C0Iܽ]tۆљxIek[6|1ćxYpcC]oks5U]>qO"dSDNw&'s>id&齠^5!zzj+d 4+c6;< o{.ǡ}{r pS}rR‹DpW"mk;ş}7&ʧLJ=q辐"biJ QWKLqY94%5 F-Z%*}NwiRpb~} MHJ̖WRm`d֦}+oG`ߤL9 47ҩ\M"WTeS**)Iw'61iuL[57m({&iU7<|DOsx!Pj C͞?GKD; 1U} {ODPVY[AgO}iK0P4e*B婋* -aLy63D˯?"+LzC-_mPAUsr[o2`)NsNLG\uD+u~\kEu pfS.^6gR;n]w^\dP#Y['5}y8HЬďV?q(2 L8hԘq]L7]Vrtw]_퍗 KϦi406ڣCh*R;1رws)*uhAW2dzÎ"P<e#dI;#1N&.+:9(9j^my(QPձ:sfT T9!@@  I@ @(D +ki_ML?)CoO/"lwt?~gS3zY?mڰ,6YS~BASpw{7ބ*PˋWof/$FaGY֩IQ`{tCgB vKVYDz]nĐ35 )X,9@׿OfgS老YjW VnY:TS I&|uw?;چS.Y1boo~;02b=wP_Sk촫{םK?Ry :CY]r+Ĝ&PL>/goش,:SVEM]~C㦦M)V|$7DQl'*!UNAYSG׸YsuhczV8}ΗxrO{R #rɖ KQ2[მ_@P\r*SVIW.omR80˪O]rrt~+:>!gضclUpK/0 ͺwiI}SU=fߌջ79-#^M:5 531Prk6hx}4C~R~ƃ;XlXK-̃O:r$e䕛ڮvJamE XoD<*R'csl$̫Ӱ%ڡ'#2@@ @X1h v-9-(;ׅnɼ}DőYF|ح{afP_33PVjQ]ndf$>,^'";aec^c+HICOFjmbcs$awDթ_$ @ @@r*9K@:ZltĈDUw<|gx6''2,sۺnO2 opF ޽{;b1zf#w?$u4i´Ik(g%wίSnA)?(bҐ~'JچChnZKG7藋G$/. BJ|',ǜn^d! & ZW-qJZ>f5:HUke's]MVFooO%pNX0ri0 8}_IĿ -:]7wΠjM_;ͩbL9.M`Jnd^=2g1"ۯԿ}3 @ T?ߜc #@4{ZEyQqNrs<) ^r?yfNWoomH6n{ی@>eAN!#kj(/ l^3̸Jn>L純t[h ml1z}r RzIv |zNOS,pO2Fn~c~d[f};˼6C$Jytü5rXgYrR97D1`ιϊowMlC$k52)w#v^|"goyҘG_ͳ?eH}R[q9q9i4:]Xw# P^^^w`D_ԕg҈;/֬c<{}[R7)Tu!@ PP‚&ȁ @j |5<;6f_w5R-8TҴ~虜ra*) SAqc5bO~'e*53bJINФ>@OIݸnǬ1͚PSkN4W Y'd/mV>.:ff [xtvrwŅ3 Q˽A"?5Ӡ"^6T2hKVSnZsSkzcZTv䢉^ }oHp M30>T @ 1*@繽}^/wPcWl O& :jX %듽G9}ȋp76k-0ƂO惓qaJ: L5mBԤu}^x<^ܦOm,$;bҍS/=皗ag05cnK"gzNūd'[cLz5uX4;1RpwUiu.EF. $2Q;~#U @  @o @UZ ˓GVbKfJ#'N:0;)fژwuSl RRVS{Ypf15U@nwPM[ΐkb-ƒ^Hzp>vJҩM&ь/doJڿx~qyqZ)qnk]nw^}l𧁖y\[[A[ kY- @Gϻ(gTG^|יJ5'YZ +%ڪM*yϼoZ.GHacI;i?خjaŐOHN&4zE/KřWErRCM-u݆%SXYtraaѕsY o\Z=Ǯt^x?9w6K+o:vW跞p?~-{~w?=&U3!u9׮3B H?EuA@,'#8Q4dXaMhf},:הbC}oY-bǤ0åԟ_;:G?wѾF=%>i2aaԀ!D ݄KҲw;ٻ@%K7i}WllѾv2Ȁ HbU1 v`̔zGtݑn63:ÎIUg8T&O.*a@NES\wpO͏wyEUv%N5.7q/ W )(-; ]KbD pAR ZROL՚ghAS+Ϣ~MrR".6hS؇}gNL p_=w9LeddMprRrtx O>E\ҬPAёi/G);:%?K/P%2ej.]>#GlyFZ&YInp"2qlZ2D:6mt݆NQ]LtVr[I܀!@ rx넕?{X4˂ӐS%07_@? -ԯ|RP3h 5jYOs8t0=~B[ro d ݶy~'Ϗs5i傓.\=WݚnڦK׎h*9ԤРߟ>y/i s'\V߬}۶yqF43J 7n?2?ZCFU[TswJy9ZjG~N6寢3ek 13%R A 5SOwhS>{67LG?ln{"єDQjg9iAȾGylD.v]FAŤ]A[ '!v.awsm{eVz54rҒ~~{?e7f:CAe,,y{[A֓4!p^fBAԒHCE ĢԪӢU+mRvW\RS^IWiŬG`ߤL9 47ҩ\M",f)Ɖ 70086!)=3[^IYU[z6n7Olb ]FF:ښ6)B}RTQ70j`ޮC#}oM!+9^n޸MΪeԤWކʅEv PPif4{Z%8K}*5]KթQP% WUb\ %^jƳYGѷ줿WO*f__L&ܽ]tƿ #'3SIes(6l럹CyHg1QNV];<.Yev ^8G35 !Ȋ1q_[ulcR!SNٛlܳ_̬//^u wX3|Y: F!,Uc" v[竻v!=~ M]bD/h?^w`d6b=wP_S vսNuNLes3u,ھZ!X6#3B`@g5oʙz n/ywcܵ^Z]#dx]9Ã3+l{/Oc(h[E5EHDOES=|756iȰfq_:ȡ2׮i}B>-{Je s)TBbiZʖL;]T˔qԾ'z- pv9 om]nV]x%љ׎̠3~>(sceg ,3\zƤG柫'޼EL9}/0y'_[\)#/ Wܢ@oشBhY"͗{o`t)PenN/t{`PRD7d5۾dބ^_UTtvڒ@e[2]6[礄okSaػjꅳg֐W ;z !aibp/֒( |Yץ}xuhaǏ'HpX~n}~WO'/ [5/WңW̞tu{nTܪj'89|R3U56m٢LC 飔i2}fdu iJKYȭ9"Md]B$^gW}#rhJjuZjK\,JU.֑-Ҥ濛AmBY)HEyrQwh %ߵmԿm'ZtXmB$!p)N2+%n癇 ߱rޞRik7-f(7:%ޙ~rXJc;VRϔ)uE- 1r+giއ _g0V{nf-"QlKk9ϧhWJ{U7<|DOsx!FLWawȴGhIT{{[7=B&d4}Ǜ&k3TMY>ygn}H$TER*Ij}{~U~. vv"g*>pU(Y2O7]nO.n_Z 0ZrFNcׁ/ z5Mxٜ : )wL,{>~Du#ո[CF@NES\wpO$s?Sߞ^D.V'ѫN'#%UΒ [CFjPX_3+66t_A3BS^>.Wt{еJlW![cE~hf.Nn'X%Ң~ٳԥ;A1&:F-g,\rfa&V?3uN]};tG)%}IxzC=nٲ(j 8 u7X.:ׇ.ԯR/^9Mw=g6la!K'MMMݳ'^U7ބοۏ J nG=z+wH8yvP$Y&jҮݛj+;JL):ZJE*Bhⴗ_\7zl$?0?KϜ8yAud_{Y?7l֪);=e#Qd;CTX tSUdYfJ_Eå#f.?xΖW}~w9eF&潺wR˻؍7y@]3g|Nz/"b2ohԴi#EYJ,R!]Fu=nwT9eM]&ffr;d#y~]>|KN _h󡮑IOJ$FOf7*%-ƐU1j`ܦm=-ebza1/Yyfmkj TBrdŌ斣l-GĄ|ʔUկKiI=HȬ?g?/Hi{t^Z0M;_TUȉ?h}=_$^_{모"zLف/|@_q]*FݢJث4Iw!A՚zll/ OTri4nӵk{ZZɱ^_;?\)?GŸ:lNIpaMkhrrك<@ ef(k 1)،VM5EHd܉?/޼O[qmDUA-L4n_|VZr{sg:ttw$ۙn!UŦ 6e%-3*w5&+#Ϸ'v}"hsfZ4<+`n|Md}L{WQQk'9uULR9tI&0VyఙVf65,㓧 ?ba]DG_wPTNvFh/N|O<Uk5lݴ&îQY2j5W]?OO4 V1c\:E0y!AzE~kJy(d5 MVFXK ;G/?¯\M#,^yw̒=Gd^>w )aq}횼/;sne2zW_RC~G7u9kfw; 8tNKJ@)Er]KTÕr}sN?wby=m~FC7np\V8{Ztčw5ɊowMlCk5N`K[/>zet< 3gY!q m˂ 2^:wֲS"Z|畴B8WIޓIωO{N$F5:Rᕡ+ _EUst͞1yڕ>_j , !=x\'Nvff;7,G)%{? eQF7fMޭ;וJ~5DGN ,Гte"H4'=UQ fNVfi^0=9@؏[E: i;#5e~!"O\ ]ZTݫo={7 -{t`_ ҙ<>{P;URKTqr%8f oYzj߷|ώ+s7,7J$Zg;2V"2-q$O.je$q#w2#09>$ @@@WNXX$Va +Wk#..."6Q)qGؔD*Jjvwlͧ3_GƅEĈ$-#$-sPnHߋ? kgm|rjoleb署Mzz`lqg[J}: ݬGO/oUUEq|71ks56v0j,Ms噩ָQuj|'O~c㓲tUzzh1d$VJ\:TJ18I&'ͪ9bsnclX۰yGC^_~Nyvr!g6a{/߆|c4hW>ܒ4%cr3<8c[< Eʓmj0}i?q(hڋnxҭZeTl_)r˪R }NO'TX mlwQG)%{ID PW#=1ܒw>e#yb;3bvU>]_AB_Rkqߏ4|7QEu$?,YʚQwvl!'e#Q;Sh+tvGb?e)~SZFb$y-eGJaP 5EMM?L-ζ ?پs'=Mޗ voEo+,mea~vv+vg?Lӿ gOɣkm'${b6la[y9b*nP=tУCbNz+u_ e^e_#m7$~2LD/<­e}s#oT\MKzLЬ߯{%s 04MϟX0:HnbҍS/=皗A)|z'knFW.9a:8).shf+ȼ#Ƣ)%@ty|a6K؏ R~-9߬(wOŹtY;.& t  b(XX؈* bwbbH)Ҩtt}ݲ- 87o޼r;of?tH߇^?#DtƐ{^63Q[0&eH 1Uף؎5ABG{lŐ-t#L{ߺ%N QiNoòE\{pУԤo@Pqϔ! d9xG3|umEjFB#mȠ_zunt!dz~ jyz" #}$CTA3 nL]vBB<Ӽ v9=3wޜv 2-ǫ U7n q99G:]]AyGf?Ű)T 4ܸaP'5Vd9.AaӒn%͡dKm ο=yV.'FB|<ІcFM¥RwkH>A#V(3|.Z#0(t}?̪׿+ٔkkosP?%atmZi O/O_ `^?3]_acm [m~RZoeI\m=z_z#ލG7(RMJg 7NR zwud@bxOGR*"<˝{{{1^gaw PS(`{Agխ%-ݺCT@`"jg8lqmfIc~ۯcm :o"vA+DIi ꑩ1ñc`SAntqӝSyUvX{Xaܿ~$ h.#+L,˴DkeXctrZN#W$gbMi^tdt*<Lo%}#CÕ榾 ]Є:tN?9{d߻kiJcM_V{6Z >㼅_4&D R\ 2pIRB u)#k|򏒊"B_rbJgݺ3Y)%]sSjj*үH[QU@IE.Z_ đJJq8ƛS]\vk奜kwgOe3ZIUY&"][t7D8 Κ**BO++7EM SjV~~鯆T}i\}`PN`b2NQTW/ٞ;:1 mie.9nQj-N{GRV!಩!ƚjh97vD3X7$@ν Aw7!6:5W.ltg䄁L߸x-{>K(k>A * o(rhwC7F$YrH;q,Bh>e2-@%$p= ]==giv#'g0)L0!vT~vL0VVvX,. 4׷z'f6 /#Ui-4܁FW_m%%4nM {{Y?HdKHLt-w=YxDN:җm'!knhg@K <2Jػd)NÒ $>QFPs?q?QlM'+{y_SJQ?GO^aɌ,1] gunb:, zg] mO6yJhLJQB|PN]lƑ;VN Pܙ\AUb~떙teS=VanUDE[OѐNHA GLz #lu٘NUzEd:|us)p K\f?^7K#:÷wρ vnBZE 'Q͓CCLi ߿g` Ӣ*zOi NNj*GLU%^j-6` gQ*~'ņ~j|N,ϋjIG.Ыƛ9Q$;v眺It7?}*ijAQ>q 8v&߽}sBZƘC1R^"5 2e3d h2>FCǬ|enӂjDwjы ]sq# GTpSG@R usRSRZOcFPPQjLe ((p=,X8{G+n,ŵ7_wwߐ @YvT&&fMQQe2%&̴F}D0IOab჎t73\BOu͹ VVvOr3@G Pׯ5s:z3_g_xtIl%zV[SX#Uײ "L +w8yt.ų9+KYMcvi9#g?fZuy;h4:n<äz^c箄 U*6F}_=ʠ"4c=hM.X}_r +}* 3T"ȥ'W NW^TZaX=efzW4)<" VDjZqEOai+#}}A8ʤ^ٍU~)e&hwiof;`LWeW Ou~}zJ8]?b=8d@/\ x UΗ:&EFMA d:ƚMw}\yi {WlvC.TOSAJkv(ek-?v۫uڹË|QFMDq[f8ހ5}<:2y#Q^\ ݦ{  'R1ҡ?qnqp|R76 G&=Pt78.S]p.j!չs,. 0M.~Eb7j#o1ÕeA`^sfj|0osTGe 3z{ZAT0 0tA9 R]UZҗ~OsR䚂s)GLєD,y"10cJODHM4GϞb$+V]8  /n8`g8^ :=:UD;qV? 4390A{ȫ/Zh,YT*QY^Ƌ]?f^XUY^P\NWG}E9_p{~)T[J U9I0=zH]eq§u39KgLcr3 nb H&a6H% m(8sR'Hb4mլy9lvOnޤtl %74ː&GĶؘ lXTWoe3H^ΥzL! .G=&( 8XWWxHi.ie+Iz NG 7 FL/bbw61ϺQiM 7Tv^UNvQ6V#ѵ7?P:%Z(8l(勽CN-1hM(-;||]>RW[T?XZ$KU&eiNXo|Co߾г(BL綆c^{. 'R1ApA,7 e-gefmΜ r:m#bSam ;~wgid~:_>Y@i+%<Ñl Do|A`i={v1v\_~:'bRuۏ}y :Ƕ"t5F~-?܆%хwPlqti'o'jҖɶIܦSo\i',t:V Y&H;dӍYNJA*՛l{񴛁"G<+z:KNXfgR!!!ӷ=;b;2#ҝӗ6osdi6 ?BZR}xO P^۱VaS Sxm H,P/|ܷHs|w%Gvn̘\ z8XF[7K5tZbh-iy b؏j)7. 8b͇֩؜ðB7xRC/e)[.i} t>oزeδ Uyq)o?8Wv[}9:=ha旖J^ol&cKBw.: v%47C,Sj5d=jMORTT_S>((<3Nhh  cՖ"7&| I/*w'zpeoƜ0#m}k9*) 5?>DF[H2<"RӖ9^tcz#7 5pMi40:evu+ ܻ% OWњ=ǂCt$󗀋wFMfj #TS#ˋOSʑ +={qP(d0o"lh$'1cPQ6pژኈAZ rqօT-O ff:eƲ_Sc?~*x-3`۴ԐΞ Md(I_k_>J}t<^J$O]3]O}bȹĪy;[$Xtp߂yzhQ{:sNQ˒^dz]lR+g֬QZ)Adf/Yw8ٙ]߹~nh0GG`ϐa &R@! ~y3ScUEY.;-9#c-W]w @ŻD*lg8M&=vw7!)=Ϯ6[2z!=B}sH JGr_Hxu{E|cFQEG}x:$wXh0|: 7w}!2zGqrClKO>I+~GE8Bu\oDQk YnT8G0Y~{EeNOdcfmu!WoG!Z{$z;ˀa`޸~G*ffPp,I]K}si{5KK6.\4K8D TpVpG8ݻ|Lky"hf|%4pTpW&&>6]vu l2?逃6AB=IsAbHYrLE7cAuCyԠ<ҰrqVD您cil+-IÙyFuo_z{%%_  72`XR=m=A,ι}d Dj8eGvw]#l#l2e9? ,g{Q2*T>${{6MK5w)Gȯ0(dTH /|(H)m#WM^\N_R[}A p!eʧ_DSmn.W.H[P.\~.ٟ}*n>q~=k+Q r/W79v&N^v#*7u8q"lϽ;IiO_TO yd2#5~~ag{u{- )%<]Wsv|C*]pnKT9}C=|n"@_o\ryĪh4+&l&RQ"uJJZ2íaWGO=N:zBi*ڱo 2R^EG[בXp&=~%ݭY}9=R+7?\T,`vcuaeQu\I@V ,Dp4GI͒_sV[}@]0R~lPaϱt =װN{G#*CeS k+VN!]@B%\ s3kt1ka~EH.S?; U() MFb G{ _2|VW᪳\OABtdޔ7JJQu:rx?.Ą6S ]>O _rY2 0m-nػuveǎƫ. -89nlF)iVkLԕf>|1 2ԙ~8rQ}:( < d7%jD&K88,c;m904}Jg>K~3F Q #."+Js2>FF|Jiq|-}ʮNv'aIЋHɲfÞ@nz^@-&RWԠ$l-_]UDmD*JUrMI57T}K^u]BxqG瓎#LKz#@>bSav7sS蚹rMss+;^sp ql3Bɴ!hطelGZy˽aGX9i2LzpgrmYe܊v';D<#(^vb"2=R[q_<%whxߊ/̕j+kî}-B,3ǺIkҲ2b䘠8:$Nzi?Y-iAot?OWe$c9N:zUZ5Z/oNۖ;t^OI)H~صЈO exdnqusZ!|D*R}=}TZR_' LZFpKLz # Ըn;=PUs5Eŀ 0VVvZo- @kB_RK |got?3u}Vca/mWq=V/{@+D8 mz*ؼj=y "\H0wDݿF>9vՑ^OXm߆& &%VmS닃8RII9T s+w <زg|E~!9e#Qoߏ\E3ƎL*RP<=+n9c[}:(&. KNaL1C%MO]wz3qRsVb*jF b"bQ͹<算z#Π]Ť01 fґ05jܓss+;-9?"UxlZPPRÈ]Zu͋ߢD&aN3߲@k?qnqA{wH߼/?G򴵵4v0Q[J47#>i:DI}}A儒+~u&Z8Xm&;ȨVCA~ڬ1-**qs)Hi NE#YHgHs@@<x~IWyuM ]#if$h_\R2Tso. W̴b:+?7 @B.pQ&pO\ n1moݨW1dR/rNmjNKvS!&'VvN_r +TJ Ln @J@T~=Qkj>|ب00X_| ;Gx^u~sa6&#*7uۥuE,۵ 9z3ljON܈, ۻqo @- 8  Z Ŷ`'uNbd&9FHN󎢤 FLy=btӼ]###( FR]Gz< MP}4q$4Fx{5ÓcSYx :y㘊lFcD7# m ?x-%FARH v+"YOnޤtlx.%ӈ53+kVE.a,@@",ejW~?/Jӯݻ s:Wigž՟5  !#O:}<$$2$!c?J 33Bo3t,=3K^shѼӻ0}IXeWRe; #lqgpVi1jK 늋 RV Uy_;{ b4ᓏo/cđ Q*^=y5;8-Á wnwe\KEC;&M35Wo y=Mk Ϟ=(…Į#WR2g>ګvutcĨ5Qʼvl4ޝb=|-֐&Djmg&2@_k_>h}tR%TrHMee-(. F mN_47T}"3C$H,!6cn, 3'B3Qܜ(3  p쟆}Gg(䦒_Eu-|[m{W __=z^2A%_%xCFL]yp8Vs+gs߁}%" ' 7,GTLvv=e6G\۠1nT!< TE v-(47j?2!٥[\ݜVH1ϯkeG;Ru#9dઈڷnyeP EKGHc}`8147SUc{-Zca"ܙ d bGGt釻pl =ݯǾ[XcH)5!9##_Pw=ajꢠUݟ|| [ۈV=nܙJp4OSh{Ba}[˚Drxe$` _ZUF͚ 7͸Yh!& +M791}Kl. ӧN@% ɻ\}5wɃncS5+[fi˚҂c^x![#>H/3Vi>N]{iYߠrN!8´es`?n\>4KOiN bm)h5w;y3ty ?3՟{~i'W_ rzqcN6H8$99Y @O"@&\16O։wN(G % /J0U@ ݼLF < j;<\ZFTA[- 8Ɂ?7%Y$sy muDkJϰsl1~+~䡇VJ2$Z2'ǿ{~udm󰶔2[ȰP#"Ɗnn&"k6:,1p#2k}s[6a0B? NJeK^E%Tї@d>mKOkn-y'Xzxٛn'cN^YO:am+-DZwvČF:O@UkCI0"ePI%*nۍֶ(Ό;} /ܙaj>;b旬HT9'W޿_N7)!A(FFl|C P<#l"ng~4Xri/ K*d@!Dl 8sn᝛gt[En YI _x?ډuedqevJH%UiS3qTּj:A@ܶ3(E~f&߾oޭJOI \; ^=B\0w;Hv.''nD|nفTgNI,E:#&Ĝ+,4aCfpq^!#)л&$aMwjz[MLa@1x<Evݍ"kл)a`F@@=QQ(x%߬&1%c,9tI7k̴kp_Zˣ)/ύ.ˤf<$U͛gAOH[q+rX"whtRQ#R&NˤUUz&uխA*֦`!W9̟!:r}]-ErЪ{p;{̕^<}p$sg ѩ(Mv>iX_]r%᎒@@Iyo@"G׏>VUi=^Й%痒)M#Ñ*[x^0#'AJKIrn\`iʑ1S4%T "j=LO^R8SDP:z9Kd#t̢kn*f^{`%;O$8LɨBa>{XuI~//x\xq&\GдY+AI%ͩS&h*jaʬf,5JAsำc? +OQ-&i+ޅtA)gKj3?zoxE~~r}u*ZTDRVT9RK ȩhor9*`9a2oщ+p^%+oH{U26J̝a t`*wiUÀ@p`F@@N& @ ev}K{vvm6XF[7hIB5,f#o;*Mx@U @#wu*ihTzΤ3B ZNkOx~hl Ku8"5ZWa߆m?>80nJ*>hx9'o'j.ml{,nSo\w$ *4<"5GV'ez#SW$|VKLb \#"6WČ7ڰ/?G *in;yp;X__=z^2i%?ME1 q?v* ?T    @'(:@@ P*y/Y~{Eex'2qMu1Ss!WoG!C޽{145F 43N-pT$nB>I;2`cqÏ YC q!J~nI? ^a5'Zl *2?bw騋AC(U8:ɋc||Ǒ %8A #xi CDXRS eDrN\t$͎u'(d4cǔťdBIB*[,oz36 7,Gә쬌-S".t%Or޻E+pX P]-1[ 7ORb*N   V@+  M i*a::Ft-읗>NE%xxe 9 OgF 2߽H>a)"nǒF1G 53=.J$aeڸ'ZBPS5~xHiD{ c8 . ֔{+DG||ϕ XNdPgbiCAUg4?Xz?>MQԴ4 b4l݉ ':TC >>4Cb"4#Z1K&ZQ),\>Ĕ 8 M<| Jku]ZuSgquvۚ6.] mi^ZMqJϰs;a\mlMJN8GXD=XxBz~#7{[;}K˽iR~Ndؐw}@@3 @J`@"D$ =R[(a|MB|<4::@n9^CDͧH\*m֩NWYvơYxV:tU#m%Rqppu߿hH ~hŏ@de hˍ7>{nST Ջ+h,\q 9ν~nr\>s$@Tt>`31'/(ROQP*MT{|{E?$)(&;r h!H]8HG7Z#Pvkɇ=ovMȍ#$6՚(;w~o ĶP@'L׀  TV"ABo6eybԆVV4ZD@IEsh O/H%%8_ r/JEylYLe"&ҩ5~~i T}i\} 2C01͹7[2q߻֜)K9 +ҚrRU֢Fgqiǚ~ՎV;0˳+A%'ګڭ=]zA;^DAA! @T@ `/v 1)5 -7[.E]6/zc@ @'AnsrE pXCU%(UՈY>^(^x!aDaH15uceA,)&P7֛E SWOYs菟S32JPP%͚6uN&޺Q3\XEQG*/+=0jg@a}'!E%nvƖC37_U\[f^ߪs:)5-8:W" 5=j1Z픱'h9  t" Q%M-(WNHq0;HEK[ .Ӣ*4E E .vCqogض}11%?c^8|Aϐ1)E}t;\|r:m y|Z buQ*~'ņ~j|N,ϋjÔ~t5>IG.(BO^AQCSwo9!@P"\X}G|):2Dew$QT%C8\ڳ-s$-d_W#e z -@r O+]8 ',.9INjKuO@_ ГCkҿpҶF l @?FW143(v96!Z ~&EU@'l.1+t-&ӌM.Xr%0C%2SGc&>I |[\pp)OO[|)qߨol@ MOtQJ!_'|tp ^TZaX=efz֑#pmI$NCO%?yά9 4~|*{mgǮ[N Ru~  #KHWwX t&x.τ=i%Et&G`K`7%N ?D@VYɳ::%FARH vi4^ap$1ϺQD@ rC=oN^(ht͛ԕj>rqZ!܃67Ӳ7uC!Չ-*rX,-:$7,8($446)Ks}[7?j~ tJ1t/~B̈́@8' )'M|) bc&@dl3XTƳįa =f###( QHuo=44A@AMA-g]]BT]^$-G9WzWA?}[ʥ39 @%@iH/(Wg@C)5!9##_Pw=aj~9ؤ>EFfdW3P[oCqzc}׭],^rqr}T %88v1!]hy&ORj+E!e|%3$ ,GE`-:m׋Æ0BMKJH^TRAH+n@47m`&<|ա#Mtɵ\ڇMOuPE@S `ԸSl\J}Aݳ? r{A<"@@6UxּBR>cμ ?w/9R^EG[#'Ct$XKEC;&M35Woӧ(Sag[`%G>-i{E얎5"/%RWY)Sg|^q%47C{uĮ#WR2ч[{în{l5bIi*ڱo2.W*/:t`ff3PVJ On,U=5 ?zZficb2|mŢŒČ%CFیe Wd, KCnv/3ܫi6iF?IQAR}MANzpcm@x~baRbBƺ⢂Ԅ!!BUCN^ꧧ/NQ7i[[TMi!"$4*;R2<"RӖ9^tC?/Kg@@+{y_Sb/ѓltX2c$o1ҧ7o{MJ6U:ٷjλIP|yO`rP7AQEGjM ; ^?q! n}lcwL(l"\ ]G||~< \6 G+{ر?~cy1tMVZgS3u%=v7!ޜ 鍙a^߾TbF> CtEހ—-{PUO3aKc5{AK)=@ @'"h aO]'NZPTЄW쫢o4@މCAku5P]/:9US= /[IG@ҘA6V~@&KihPt߇ @Aj/W7ɪHHu??t9e|]t`ϳEeŠƪw7R {{6MK֦67c "䘀n r.)Um6%0h.-qfA(܏g=n @"vd_6ys9=X[ Bc+LBS1ZUD% Bez20iu Qxӆ@d+J%il+U0XoW\rg ogv ,,͎?Vh%M,ι}d D.A  gG 0ی(R+6_yW)6s݇Iuo%=6pw7x=\˴Rg)Xu/XA|UWdejp7"NSm9׷OVgőrsZr)(n]o$SXy>sZSVK%"FRJT3޹yFW~ΠÊsl62b- ~rfÕ3U,DV?Od ']RfWݷ|AgD ?\RML|/vK.MIOfv"^~" 5X/G {2&G=AHhza`g:ߖqߢ*1Zރݛv&EJcNYF7C[v?]^qBD + 5tAw|1!r; L4TR$Ffg}:p\4qבM2R:6o]g##x|ft4q74DJe@y*Gk+j-^~gǀD`!//c>itx,Vĝ;0ap+m ^{>;Y K73H*Hgd-(z᮹RH¬(uG565{eUoSj3jvaYCdx^T5[N4Ą<.OboUe\2VG #,_:(T[^g+gڠJxdl=yO ;@ 0tqw{ n. B9!~1Xf {H ex>'b_ۻ`>!GuneSiՕͅeѩxcA"7d 7*nۍ@43l_vK>wf)q#61VTi=6JnJAEO|綅AߖC bm) jE{k>vflIg’ жs'QH꫹Kt;MTːl?evY(+M-)aɞK;zb#5*O:vl^lǖ-/Am%"5z :pEڽ4oPQ9j88}FlOytOo=J6I/eb(_/Xb,eә>w(ZV>w ^DMk7lUAyS{ N[o}!Drxe$v _ZUF"H 9r{!ԘI=ۻ08~f " Kfi 3&k@Gӓm19KlJmɼF'~Ȯx_CMeFW~Ig=m7w!(((|̀3bx==`҅FNܸDbϦƺ"RTڢ("hɢAgS׍{XuI~/p /nLaON Lrz聯 v)ޱu%g9@:iOψv$sآC a;tC-b Evp;Y3s'oclĚ $݈_Z7r $Erme%rAm 2< V]Kd5b;!Gi2<2za !3p23gbRp< ;CO3D1`(l /zeZ-"W8]=+ظ̜ʚ:OXTLJZO_M--9qS'=9mP*?yad6# -  NV0MiB H(NcŪKjzfq#TW"H"?'KGcH j:j:UiC2zs滛,Uchb;j0 }P( 8I=UgAky"hf|%4Zk)sS= (.1GFqiE#/,"&G I"U,Ba?,yUiˈ xnIBc5]'#^׹ވh =:4|?~ǯ[=%#uӑыdDO߭0Ǩ|+,fDWG=3w2 +ozp&g|=UFى\3tC>I;2'zRc?33M=P;86nwHԧ^O0P{ԝ9w\c~~v38_EW)7 a1ws.RܴZa3D SB^"ҦV$ݑ@/cπ Sy>1u$T#mP%х ,cӅ0_դo@ֻKyge9xG3Qܒ0lT}'_ Mx@U @#wu7d Uf^3)nk#+%<Em Do|Aj\>uTg+FJdR'ƴA.Ygeh Q 옣TG 5##p+:&U" f|ћ7Z{+9 E/8Ȏ ~6|m0}zj_d\_x8; iZ̡26/ i^7B ݞ@;/};A@!^ wq^s߻}~7QУ8 6ϦEgЉM,|~t1<6'GR*-3*<˝{{={#iT%L 6Rq=p3n-}<-5ZNz;b117Ɂ~!%У&7j*Onˤ @cMɋ'؟ '3|@@@AfxUDͧH\tNWY؇kr\>ֲxRaV*WXr.rBmױ>6'eIP$ܮ HKo\9l+єc rI8̼ sXY;oN7 96=T v *]JBDhexj:|bg~l1o7z Rޙ{%Dt'?zr{8"A N @@`4uoeg e?y\2J4~s]hV_Avmnlpxa͕ Fo89a 1"5!]@B%l s3ktFy#TI+4&FZЉ;L}VCca/mN{z5 ^x~'zAԔT9& .\cߊfdZ^qe%)ValQVyOBeE B 3|H$*rKVUU^ H``1%>w 66@`%@$rpZHC1-**qBw1ܰtZJ}40:1Z54uNae^5?bi@oYJůĸ/_)y>_uE544pxU1>8\rhh1߲[%ĪzКE-ŲE\/n@IW\]:]vN.6SZ{Xp{q͔:2hE{wZxxo2J]][SCr2mFt rrD=(?ss EEaeLR5EEM]HOĐ4H\c ZUvi~%]9p|No6I/GIYL 2¤ClɴRjJZP!Lq0ewoAG3Lsr [&U|)A16_k5h/  ]듛7+[;9E}m4ޫk3+kiN]]"4U4I3>~4'YW*hE 'DoB' L[EU-mLeϛom8jʼK f.  MҜxߖ>0!a6H%]CǙ3?4@ҴUfF'23eCzzBXZ"Vρ>棤I5~&7j܃'iɵZdtOl~gC׀ >XO]$ pݾl]̮SB?2r#>:E06v:؏Z|e" ['WDY&Mm ۚ)g2Y[n\O._jo},O\ [n1 Ncbn[ +[W PrfX6<8s&Cajkk?|0f {  toMM-= ;#f~i A)-Ÿ-dty)Ow.:>ݒ(iR[?=}qۘfom=GS7&7LJШzzYZ)rh)i/>TU#=jMORTT_S>((<cx%J}>QɊ p?F9i(&Փ1_#a!!8-wo!=8r}$~}k$hc`E9M9xhBx!-O ff:eƲ_Sc?NEkۦ bB/e)[.d|ްe˜iBhMOyljļ[lU-|߾z͜,OhS>= NCUg|@ے'ʫh͞c!#H[uJ\II@pkzm%kdžKOѿIy!FC9n1)H@[U0YZ[|de]}:1cF_{i5RR@ aw53 q1uq⪝cs&LŸ8Xa G|O:`{jC(@輧y)on\oa᫳0.%.ᛛrXT/ٔTPOCuٷoU5k Y{HYT#IGۑScS)? WXE/r㕼~cCɷF|> {`^o@`2uJIX9`lYU)"&Éw3p|hd8jA}ERuEiNfЈO)+8mrXQCt$Q_.5ixSMŏ//>MkϞ=(Lw¥01b?K^ѼӻO@rIΟ?2 ?~(++i`@@!|#]s |3 .&3uG+I쭪,KmW1(8B$`Ш!*4,Eΐ"ABD&3uPWOQH˪X/%eںzXKF<T4DTюa89Ct5Kd\?"-#z5?z56!c z @;| ]1C|sQ 'e͟!nJs=R;V[o{ pVKg/Cs"la7c'վ{!65dk7iZU ~Qi3honHanp?u]c9N:zUZ5:/oN[vН ;(#?EsҘ!zaM4KsMFD|Lw/*vX{Il4 # 1Q ٌ:8/*[r/Y`eggϛ7<$2;m`M 7͸^TaҴqCxǽzMR"L:klj .Ss~^"@|h8IoĦdoϣ3||Q:}aBGTr@hN/r9f>]E4f!Fukw&USRoh.FbC<=l9tG~ zHnv/<TT’ sŘ<澧 C@uS{V)eE]FIuqсkm|Ρ4d$9w%C%X-d1-*|-G Ck4܈O 6g5_:8:{F$fb08_^w~"[' .ּ^v}1uHE~8',쫦I F^sEnu0 uxvko黶ot >>3TX11` v-(47jzv'*u3iE{iAP1 $mo,BX̝~0-BC ,aͦM?ģ:1ih*4,R N| V%YoN \7[T[SLb5u Ѕ4{QbA:5{2(AԲWvddT̒j" 4UGh*z"v2L0UiM ?}`oo=zΝmWWXӮ Mi.*iȕa6" *~ŕVH8)iϏ6A    u阽Jj^mF%\b߈V֩ѵ;sgX%LoUuX=fJE:T/mS ǔ4 BhmqrlGYQ ٵ pڎ@ ȶ''nDY|nVyĆ;\d:SAai Ӕ&0I#Yg'COӹxbbbׯ_jjkkg͚E N$M7.(:cڀ q^v@drlL@@QBXW s"aUtFBo'\e- hXm":G-uܯ.t>wP'$$kddTZZ Ȱz%DZ:~} t(#[8P;2zA+lMҎ@@44gyVQ   =>-U\9T `jjjnݚ6m2M;wtss233B -)))(.Aj/t2iJ?x~A\mгj    @ =9ݢ{ DYZP]-1[ 7ORbC\f<&*ȅېv=eQhrrryҥK 6IG  xyyіf $ ~~~m۶@@]@]  (&2r!A!|C&"*<䤤$nUUլYŻnc'@n1:9-+GP_RVQޅF)lW8a$e)!N6)/Qdԓ >@QS)xڃ_-g #2xņmVҚr:/~`abz˽{\G6KBqX__؝ϷEXM09@@@(((9sfTT , @N ="kлG4&eՎnw RLv.W7jpM-76({6nت5m;rߛSDL]Q݈4dSm9׷OVgm>$n_Ml(u'}#(#џӢP1/ K*dS $R6X93%>v>k'G=sm֣M_ͳٜRDv)-rCleO 3sQ麟 ,Fgd _~ nF1lAL=tu/̵^fͯ/ܼW>)r37.9 AT(حv\E%ku|i;BGVS QX   tsӧOFQ]]e˖o"@! @ Kݻw"ٷo$G$ э f# 1  +# ОBci^yfԜVGݕcy#SG*13HcX^!͹G1 ߂XSI7GHym26 Ҝ 8yh&Z*o=pE$a W9̟!:T 7 fC=# Jc=~ϔsV{>aE7'( b@$ؾ.:p @~Ǐ-Z=o]tرCK@"(@@pvvVB=R39~̼𱪲M"Μ/q8=Li*-d@ۯʪVæD$eEx|̀sbx==`VnNNܸoSc]A~⣔f&ȫ/Zh,YԟxqeE<|9z1 D vW  qÇ;991XԩS6-z@h!-,@ @g8xM##߿-J8k,h ~x~gҞo풩c4k $!Q#&UridcpSo\;$,t:V Y&H;dӍ9۴ۼoPJ#Ud! N[vn%Gvn̘\ Ի_ZFk0H=/6aVC7j ><N* HU(,.fJ=U͝P[-e0SgƜz/zb'*rYx05>.NjH֕dpiö7ow%Q }1bu5RMGt37Y"' 1䞗*LTp90JM6dMڈ| QӢ~Z/s%+>f@@@qWZu5tˈDѣG7mڄ4 L@ 8@ =zdll쭑x ZІKOn ':u7"gH~몋_kVOm ++8ztD5FLQ\ZH *(*${*@ c2ʛtZ^;_ )OrԨ9;[@d]W͟j-#*@ su2s$ mv1=}V&>&s5 w>4CrFUVA?6:[3G"z盀HL'h~ |zD9%5-A 7[wh ]0}do֛@ GVFxa͕ Fo89asH@@@tpp 窜;wۄn6zzFcol|Ͼbi>Mԫ T[wcr jxxE 5da3@11&BL0# ]\nh}CCÌ Rhp!!.4p hpa ٨|z"ttPFpj[3cEY8/'{j"{-YuSĮ(R%7"xʲo_EURɼ(9hܵqKv [^>^z 2Pj9sEHW2>^ @4/ @SSӧO47 466]… CGC[(6rAiiJΝ){Z0nOʻ[6>e66ԕޅ_c߹vlZx_?V(LBc+J@N' %%=}pĈׯ_mmm}||KN*^ˉ?VW8f3{0xsc>7+(jhꎝ49xeee.jfg:ngƭ+e[O0zMP*~'ņ~j|N,ϋjÔ~t?KD~jg:5~/GI897<*xJkg@7\tiTHFA+@qq1, t˅oܸ1sLK'.a=y|QA[Sv _khbbm_Fg j@u .Ds玑=|A|mRC;.ԦkӖ,L+J~K u|&5 |[f,2#}} OeuM r^F?u =BGCcj,It;M,".<~\)t:棃 P‹J+  l޲LO30#p 36? &5dYOi(v= R%caiq݋Lr~ ɕ3ek֨Ij, @&PUUw^ гǞ>}bŊ'N@tV@@rrb%%%???h-^ߪs:)5\__D͔7x~iiI >4֑bo #M&nO@: Gzyy!5Ao@ƍC$ ѕ *+C)VS')76jTm*8z }~=TYB ~Q  o&pdϛoz2\y9,dsW]9a-R]]T]^$-`4'YW*qDlKtQs̙K\ /M[5kfd^Sq䐔&>|zbہR[lLOO,j YBƈA.ܟ{}*nYK\Qg ב~ξ鋖JZF4L>hM~~ag{u{- )%3E@ih|f]"kKK˲2a?~,//ieC,VWD$,.9Iw/.E);ɇ"Z~׳ϛ .P~[T=|8*01N$0әۀ _&pȑw~@K͛G꾈7P}6Uxּ\R>cμ ?w/9mb=|A 83X9˜CĄȍuE _CCB +`Nߝ>+}c$~8rQ}aJCū'cFEnnGo1VLjmgz~}}t((<14߰ kݺ`kw'lٱc%K>ps mwA4]&L~3F Q #."+Js2>FF|Ji#ʍ~ވfj t{v£G"BhQ$@8}ƍGbee-, t&KH+*$lג! 2^H\F[z\ &F8Wr cAE n޽{999+~3gNhh(}Ӟ R۶xjȿyMIu??t9e|]azۼb56 uo_z{ϔFM(AkdǼǻfX҂rEwq7b:Pq+Gx.s,JˆOTh\@QVFڂrLn FV~ȹ~X[wFH?q8ZPyo|.͎?Rn-I,ι}d Da% G|V'p=К : NYxk'VqVDVmA,,νiW?<'nϺ{u/ݑFݎʢ4&}ޜm 4Y@O&ܓ{'m1`N}R5" @͛OCGC:aʐI$@H]<zP;  ' >|p̘1+\LL̚5k^'uuCnQ__~GdnEܹ3-%o?k]A mdr#$c~,"''1D[TKCа9C>}LmPڴ }=*֋7|)g*3jvkԔ@<9ߌBKpш? j ZSQ*(8ުZlK gc"Jz0KBJBd~򡕿F^sc9y!  t;)))CnMPիWV B#sE bPv|BN)BҦCEAw.7gfv<-|RWQ U9#Wz}p×0 NO::V7^߾Tbu!F=Y^yo`?ȈwZUHnFF"Rӧ*'%{ػt_%?@:tSyH׮]344\#ky# '^ 0<3]hol-s ]T6/F|jNZ=i㝗̛gBZA#gϝ|R 7͸[HtV6nH/2c^A` 4!n\>468D>.;[ h8IoĦdo ϣ3||Q:}"}/DڷșULxL]ޅY{倨n;_,D=#C3g`K]t[LibгEt޴M~F^סӓ^c߾ܐHu'xoF:v)kqYJ`ѵ%[Rb^"l^:}UXyH:W3np#br !u<쏀N 8XWGFLع_+FzH 7_^Zsc) 衃 f&.JZrY)_?eWAEaQ1)i>}U49ԂrxO!1_~U-֫qL,<زg|E~!9e#Qoߏ\E ƎLO]wz4&}NHJYUS#IJi*Ho;+Ź痴;pu||^OQQ$*!kd6mՌqBlh`قbS,kyN_cΠ&ՀC <<i/ܲe r } &@LhLOd򭾮d:Xac9=Ww$CRuݼyI!I |iLQƀ)I`@|{mE Хvg$ N &j>Kռ݆b&L8yuFLYsrľwCl M̡w- $_a :Aܶ?ZZ+9dnMO19D;۲C)ii_|I<bCFt@ ( Z pmЍ6EK@#/Ah̙suhQE߻ (v"{FcFm-rmܚjƧ$-vdsgabzA#=@{4J@iz/P3 x*GA@Ǐ߿v( rnDD"Acǎ1k.9fäSUGFJ2Lqܰm]I )Qd}KgtO~pD-ѾlymN7,m}8-m3D[IƮ#̈́wZQYy5-˽w,+`bu?I$mP @"ǎˋܰaùs纐@74Ֆnr7i[h% `LHD0 _!8::>x vh.Nh?{-(\`ZE""'d]W͟j-#*@ su2s$ mvt9'Vvvsg$z;ˠi#ͮy7zQ!gff 'fՎ6Mۍ ( 8#G@Zvy###0 -REMOþT#SJ)䦒_EuHp'ke$l@_&ەl/h; (`kfΜ t;h=zdllv *3hH|zD9%5-A 7[wh nceC옣To 53'~w& 5 !0 Н YÇW\An:aÆ!@+vBIU|RohI> L lMxBRHY; 칸•w9b&3[F%ϒ[<  }>}:..ǏӿCyy) td½N\t j @YG-,,DF t/7nXb4v{nF ^o;Lewq٭Wrr;<ڷeWR>VvP&;Rt$ N @NP@ @500(**;o޼7oހzlm..ihqO2uּ˭{d %@jkFBG r 7 xxx @FW^!@-B[ xҥKJ#n:z;/spߋ[o%` m0Usr3 "@ f ꢭ;,--:88hkk@ -E **իx(={Ch-~=}/_suu=z4Qɽ25{C/Z@,!8t,ij>q؂B@KSv8BȈ @!qF#>b333 A@ -BOy@yZF 1cFJJ т :b)A/*yfMy QBzQJ\DzAU& /Na#rcf@ @@`P .2|ʕlFFF(jk㆑ m`! ߿!>N:ӟzf(hII ɓ'#$Q2V#0yײ)l&"#!B)-?QD=z˸8cV'(:zᯏxB}lmȼՍfȋi3gv//wm{1p~sI("<3"7:,:n=_rhP^q)TZQ|0[Z-~TIOHL}xVjG@ 䞹O@ deeaǛ$ܰa[ ^x584Y]X$N s P>tMOrH4ghaM7F1SV=u9*}|mR͊S U}slP4=cܢ p*T1R'Pp > @9=LBR1^|}vC!y @ɩ^z?{Y@ NO>˗/A@ Dh{~;88^l-2o݄IQg\爝/_v86JR!9 ( N+-)+ʙxz-/6nY%ʻHBac &&M:x wرcZZZ5 VJǏ7o޼~ZZZnP]Zm @E&hvvvoQ[1beii7lE2Y-U2R\UczmB$z3zóZs 2n3dANl|ؔ,*!($.;jJX}޵4࢛oz._%(/5Pc0r'n/D:dNa \B\2@pkkO>u-.6\S׃Z7n?eÈ#Z[ T:@p?QzLL ȑ# Cf?FDzDM&O[{`n_(/ VPTLᗔSV(#Z8`D~! )OAY};5O]"`}׽]c"}Տ JUZU5og1jb 6d2둑o3gD[ԟSV(.Oj @@9ѦeN Q2IL 7~xХXV4GvTSЈhhg٠"N , >Joƨ]2G԰ '– 8{%&'\jUϞ=7p#v?TRRv 7!7\M`׮]>}BE1/j%Kӧo߾\78j!п55ǵT5@ @ @_v 퀰{n @hh桪 ƅK@s l'  Nd}6ڌ0&&gdd(!!PpB糷ٳM 헀MEE>-[t?n#兒=zQ 2@K@K&@&򁸻2s4**jBup#@ re8 Њ  x:h޽{#|:uA@@ҥKC1ׯ_]sttD  pR% ^g0Q (K=i #0w\ ρB' @(//GO%;xD$  Douѯ_?d w gy8|$kx >~A1 I}d`jl(#[ǰ;~U iY m'R)LiБ|NxyyQEGGϟXivv̙3?|Y4@k lƎ+CTu@Flt[h?&p߿㝎5 !4@C=7A99>%?/,Q*Y%I,L1J--L|GϨXJ"A[!*88}8p p @ HKKO2ѣG(Sn\\\ݹmp m@rr2??HaP@@;_|I_DDKd @ V1Mdk%PݣU xkݼ R0'#.ؤSfKյՕ5g RuUfT|mI;8ւu7B*g݂y \9___]E K 8M!n߾ӝ= hܒG]Iֶ}={A@e AцѕWΝA[d@\FnQv.Lc,*%1+[h#M޽{(hBB6Latm+&&VPPm 630 @~:Į]ChY>>>s"CEEEd @+"V4Yj#@%lV,,"LT{L ",&YYUkОJ2uT#.3epEsfF./SlBJGJVz:w?5t=]]]GURRyG}o޼ {p4'u@pf̘qN(YG(@ 8aJIݺukChA/_^v-}5SD%@EkV@ Wծw_c JʩexJhOΝ@jjrHWmHl6ssD?%.۟kwdރ'Xlܺj8AxZYzJ|-s5ooI 3<|]zٞ]2-BjkW#'^W0i2ei.3ߴe/CWn+˜i<Є[guf&¬@l B?ҥKq h\_De!2 @'v2~'C О }'$mܸ?"7o>}4wF9`,p@#V7e0s_M/+X,Ջђٮ7(fie72)%#SwAD>[o¥w.Së{@nFH8`t|'@#aEA^Ev}yطZ8hllLT h @J'nC*JK)(.U#@MC8Nj.>GJaulOXXH&ƻJUI#ݡg^SRJ j{ٺe1f/eE|ݨo}HHȻw0(7h FJe%gϾx"o\\\@@aZ&@ Z7V?V@lC@'OCCpP:v62dQ 2@a;K ,|UgᅥjK騲h뱡ʑE؟i瘤d54&/W*btPڴ4{H dD|~<9jc~Ǚ}|Ur\# MszSXZ]P,.AB}^>e{ERYgel@EGL;\4gdǣ2Ϻ9>MUvsz?2*ò5^ *ǍMIR7>E-L65#B~@A" ̢YfyFPP.'Ue\>e@ 6m߾8;;;>>_GD2P_###< =Rw_{ \p4Z0^ Ht+67;YCϵ AT4VDWkYÕ.J[S%SD<^buvݿp/.{ ;6O^a y㹗5gGL^qVwY\c9~:2HefGJɓWZuM$n.(xuaQKs]]T&=hyVZk` @NÿK> z*@ 9}jq6vEpg g \@ԓJfPϚP %p?~>3F__?OƍWƿc̘1bbb | @|l2h rC^~+}1*5/ͮR[n` ϟ6 !VYC5<]gU"dq'z)%ONlW WC>>oގE_eH#(iliWUaѶz_]ϿIĺvM-:s挙Nڵk:::p!Ʊc0WI&q@ԇR#~}A -}}M= [A@@JA,n۶ mӎ4 @ g>#X7gCkƣQB_:U<8sZk7gX;ikԌVXYny՟)xb"EXD ^topԔ<{m^ `!b?~DqA\~~AFIeCe9S@ f8Dzz:>9s <?I`((ScZkjjJT h3 @f@Xxn *""cH jH<c؊0e=4;e!E"AN`ܹs((Fe&={vpp0A%0p>}DEʗ C Z5XԪo'OGϏ(  / $$$;G_FAT hK @ؖf8NhMi˶|<>HRXKiLtE~^^B_RJHv3e `K=cꪥU@_Ν+M@ ZDg̓\ooYfqc@4\4 4A]w׬YӣG  gأZMؽ{wd @#660 YdQ1F[PH/qqqČ&%'cTΐ$$j (#"*uDe@m:w2a„ λw,--Ϟ=?ŋڂ(mh[' EZȑ#h%%%n-E+Rr9RFptt\|9$6mKHH  9Nݻ7YTmpq6NV5u˥Gß|˲/SLJ3_YSPq3g'eiJw޸K!2\A`̘1Ǐ߸q#^FO&=׃@ ŝW@ /5@K4ĉV_LL ,jmnST,mڴ =+[6, * qoǃ.`n7Yp+3͵׌OS{ (JDBv*c&kH zJ [i{wyϟх$3&V5a,'~~cb}fLTccT@x!JBt@ @褤$TJx렅ϟ_bQ 2@ hMB>MMM|Ѐ0tΛ<' OZ,'ӣ0+靟oF&MXs]i @[0D{;?~mb?MHdqK5~zppt~G)aYs!H&;Va#Q=NkoڰFOwXwC}.:Kۏ0xoLO%:)++<  66vO>~:v1@aOww?*(>$-? o0v`EkhϾ>A%&z_B}lmȼՍfȋ ps'"_ݳ=4t$Ss2R~|%<}*ջw ;9o91?1޾yU(Hvk+/ܱI$ yvUI*KUFGX +SC})⩽ #~# E7O^'$O,RRP8)@=c񀭧fCԒWҊxHCx\)9z%N z;֚cᎧ]ؖ2|yEC݇}qCI}-6'O\wf]6-AZ> P?}j`AK8} @(+/qž8*;++ Z#24u^&N"%%ET hZM}p{yyWan`-/6ByF0:Y6JRa!ƻ;(_>$1ޙd&[aRHw?s"JCC>Q6'NPTK8 cWeu)Q1슣 e#y3yV̎;hm_)#æ9@=L0[n%օx!CHOo}撓gv @UohG\rrMHMu_INtd @ >_#ag7h~?ĚGO1txثYf4îG313 3 d>US ٕR;Ik>nm$fG9˗./dp+qs<8dg2FTʀe7Y(G̹J"(rgGGWo>|T\NS>jܤsftfi@P&X"0>))$*z^@HJ˗_IIJJ\" $CPN+(8pb!^%P\\b GGGYDALd2@ wT!h{;G[xg}5}#)R3<>AQ5QvfVWh5/ $ũ")(Cΐ#FwZis|# ߂' IHw %BlVVT׽tj`L\BnA B'Ӕg(GCw܂? yEjޝ5Tm5hcN ׮] QYk\]e$9#`Wu0@vI+YYN@3@U͜93 h%EwQrQd @{#:(767endQz3F5oT4 G@g a7XRݧ4Ubr]&wP4@`{fK˗avq""9 @خO<@ dInش ?f"bhh@H:ѣgHL,@>y/,O@h"\ݧNlk@-N@TXp8<zϞ-8$o%[P &&]  =zpB]eԋdeeG h]ZGEX!(@ к <×.iijMĢC ЂL !r{ Nt h(*&P#ۗin ;fccr\ٳD%@vKEy,|uk>_RQkw%wl7#D`>**j=z4TG`ȑRRt^7nD[i6_` mcPv9ii 8K!/@!Mڗ@+ŀCV~@PS7.\=5ObܘGp57B#K7w_`ԇg{hԧ-@n#W6'O;"XIAa!@p@zzYP9Y GGiӦ  @+ ¹ @cKJdoJ|Lɀ:`cLlu?]= (( D"B6#@px.H9>z8}6 5  )qqqDʞD%@ p{9Ue& wJvwOPYd>E~N쓄jp9=֮Cw 0s>>ϝ߳n- dI%ǏOlCyl  @pych(>Z^LgA@P:cc㼼<#F  F蓽lAQX@ @#M}3Vg(prٳZc@K@YM;q: Ç?/@ ZDXVD N8uꔕB!455| Q 2@=@6(=dg hlׯklh5"-Az*+Am~u mfra @m(N  b];d1cCkע@ zd2$" DFYFf@u@O9~lٱ rg[|".*:^{w Wm;3 #@@wK``@@WΟǝEQ#[  YYYg%ZsΌ3J@&>~H h @:Ғn;s>Ǐel\Μ[l1u8+'/7(5n&  'pl켩S55Ch:h߿MucD%@ W/ eMIIԩ۱ @k!ٻCL6mv}+֫V@0'//D@@ @+"'͛,/q=@4˗/̙C0dȐvؑ j#B_ۻ61=2faaQw5(@ Mv5'O9׀=b%u_XoD@@@4@43KJVkW?&p+**vLLL]&,,LT u_b,u. @"px׈W>`n`oݭ{έk m#`ѭ/^,<ph iy[aw$).c B---FП9[[]v;"%¿YF)YN7x< '_PLBRAs~c&q*;w <&*Q/ t=adx\J9+1_`Q6as8h2lGbkMQS7nj LuQl9:c4+>) BVnN"d+66Zj}(FF 610 @Yl;v=ꇛ޶z4~&;o޼gϞ-%7nܘ;w.Q 2@ԇ_^;2|ӵr]^V~R}z12dZ;(倩*g %g?M׵~L퍨At(cҪؐU;w>~ tq ́ߧgeȴ?/@ o@ׯquzSS M 66 22hAQQm:CT $z>!L ;eџk԰ϿdBc""$wI\ +і)V~#o7FaFth-jpشd c(( P<}ft'PZ^^PN@P}7 5  _5kVff&A=z B \ g~Ql޽KIIԩSC wmDdh2| GiB/-~sLbYEe jiaJF7W9y?M?q7pQ8@+184md}}u 5 I`޺}+*N @/C_@1B7!@)\4{Z4}:~#p5ss껝޼ySTTq6@  ~Ql QQWW׵k׶! xkݼ R؄ s2~MJ/>eT]{ X]I]sA,%[WeFnTc-DhgM8bl QS7nj UuQlf5*`l,hּ ݺb\!JKM5O** @ ee;O mKԀ @P(kk'Nھ}yx{9 Fޅ !ⰽs'2rL\sddQ)y^ MD%iv'Gpp:#[t  ~~{O4JJM:DZ]!At,x-L+j@p+^Qp܀_mEG6tʘ!@4@~~󽼼 т   @pc L;2JPpJ"„ n;:$sH|⟢RdgeUC{*ɴT̙&2((?};%T@HDc>Fז(utAb?-N [w9PGK?0"LHiၯduT3anm䩇rr:.XFjaCE֘ G @ qw& ."y!2|d Jׯ_Ć Æ #*A@F/ /ddiטrjY!3Ӿs+ē+Ir弑lnY?+>Vm뉚 SK]?{5ȼOظuq͟9#d88S\B#a֓ ǜ"L+qt;|rxbvMwEt D~cJ4.԰׮vGN(`5f%Ms-kME$g#~S>]uPiFk;α\}s y2[060QcUUǨ0UD xxy"qyew0+xV% ]w½U[SÁWU9 N(?!3}KPVQ^\RJ AB"@n?B1_H#.gfMA@AgΜFlկ_?nݺ  @Sp]E,w[_s9yIIy, VKhIոٮ7ZP.$Kis&<]`udU=.hgM]΅~Jb8x!2)!é8DSs)%i ͱŒX45m y">4͌=f0.k/|iod 2[2_wwNxueɶի:1ˀQ9@dvL68=fn,e($yeЗAO$bm[rа+no 5 CX_$r7wIDtô4;I*Sc{~i9{<+cbt/:b4Z)%?#C=E%`R-T5o'jOP߈c;ȴoa8|מSVX댼d^LĚs0™׶=VQ^'%{I!&ޱע%vqx?O>.g;84%%=}o[~#IW]14tp~h*@m1qܔAQQA b6L/|>>_^:vzEƳzѷ>HI0_@= ¦nnn.\XlQ 2@pwQt1h|Q\_qlM\۰{{MCoE@ Iyű+tfh֭ɩw})|LK Rm:|_fqW=s)iLq0[!L(cBdbZuWʧÊ)Bp{x&zN,A±Sv[l?UFC_fkvD]%uM0ݾrUԖkLQɫlA6MƓ7hV%:HzDNv7}<Ye(K`YoL2Z +df+۱- }STN MIA+>6|x^@07 ޏv? fOL@&5H * ~=6-]e 1mǎgq2VA@> |B,'':zhd   6:(FrrC^~#^WQ/š7JlAOϟ6 Sf֫2=w%,.&;LˉڳjiZ4%zwO-~"{|<o:swW9܎# o>Ki-H77>خ@ȫ1|h7 ,:6 Ey$YTppUe{b6m q'hs m@7R[~uc䤭ll\hnui$ҽOY[C5 :fzjNRϚP Cߢ؎,OaJ)^jN ;"kL!['&y&VW@`xX) !D)l]w&:^;2~9f ?&v?;)bѾ:uPRG 5*bMcbӷ!}YNat3Ǘ3fCF'Lyet@$?8wVgQFv6#ϟy6>ZI @ hkh;ٳ{v 0mڌ݃i `?lDN^X͝2a ށ`C 4, b~fXB@Mh Z8PP̌NS/Qnjs˝Ħ,*&hoi$gm+枖 L֞o_=}9"*W|BV>ĮjPQ3Xt(‡b$%'!aZR*PLBPOWw1_?QcJh7/| 3Ns&Dꦨ|޲eJUxzd2ZD9켼go.@H@@p[魢RP7I3yׯ2]^IAhovhA qhxZA2@ pK>+2 ݛD )6wAX7iI,䈛40eD~}a9>9s[EMEr.߲lsr"5ԉ|dlC!6ฉ_Ƞ~bkV# q0xg"0nP-V[7ojkj,045 #`? ^= @|2@ u" FG7JvabU+%a6L˗/1OT(?};%T@HDc>Fז(utAb }f"AgrR~'=$"!U@Qô]Qjdu(;9( (G\fnOPŚ:Z=ZNy?d1FSEE=U(n r$(6`2Z:c& s-ڎk'VO4IGF>~h&K)~nڼAoO!ʘB+LQs–q/Gp ACI ]i蘞Bh6="<2,K8I38$\484c\j.h@߾ LTSG6>7@3 fm#Q!#Mo b_x޽]zv j#h"IXtqq!*Anhe9vO]O7= S*}f:=\r(TT^d!QɁFGYw^B{tx5I!#@0Hj3D3rܧ'9? ('TV2Dr~;SeB05쵫ݑn/+Vא|Ӗ"ZTt7_rN?Hhlry.IK9H2,sփ=e7 󲞝)qUPQe_6 YjGˏOO~ e+ljYs˕F HrBmwDHO9#^~khbM$s0e8$@i<#bG7WY))bM@s@!+X&@nh Xqzx]f uݺԃ@StÁgnr9s=\[C4nǎH hA~~}$-b4_ 0oՍh6ꟷ&s&B~sb0LAIk%n\Uoyj;hGj3JVz[L$;- ҴrU$5_dwۧU2CY/~5G:Lr4K1-{͟`GCo&7oT:QI+t8ϊq(_`0jw"->~3x 6?6M:t`%D Ϥ["tw(G47;!Zg_Oς_B}lmȼՍf0=#<Ν|:j~j)})W{j==->d%Ը1_ -gZ[{VM!hGVm!^vm£6K?)'=%GLÛ <+cbt/:b4Z)%?#C=E%` Ei f_pg2#g,~,?RԤT2S5iɎ3Y6> , b4UK¬wO`w3BN\y| v"iNs K s3 B$qUADS^>e{2M5^B3hW7Q?0Mˋ#!Q޽@ `m>MTQ8bknIdU!2h-%rSv85SM'#jq0p0o݄IQ' g\2ԈPR*X,4 FYڨ>"){6`aW'1G]31Zcd>US ٕxq)g8eS34 NF)œd~Ǐn8xGTeHU^rKzm(^#B6yE;|Β_x"ʀe7Y(Wc7c%mwt yS~ojn@ABSaЮgH<. t飵xQ{^>elh@.#@`h[l;yyC0I  ̞נἩSnjM[TTS;֙.jh Z;//2zhWWW999vE@\L/-'jCi Аfڳ=l%t {Rkvx *XPnFt0x&# g_O.źQ{(1D9pң0{^!=,ZKxheu^+^c0jFܒK ++4 ZoX{*"NFL-@fvg}5}#)ZROPTM{̿='ZDG /k ]MA+|Ǐ!~"qW~N+mv$![Ĕt_LBRVNsWe5uu)b\3CLal QSS9eC֞D? Q_#cS Ps~I9e:ڊ2MgGDGCw܂? yEjޝ\W|>5IɣjѪF>xբߙUECTxO\a1@7 n9326+((*2X@JB*8bׯ1h5zs=% )G +l4 7l@{E3fo0N/e|/ ҥK/^( ՝,|WƂU^C"*tQUW>Hg3ԎlE/>C+ՈR?-AX9 ӧr=vlXwd1U Fv1pUgUtikԌ2Bg`gU,mo Zb"AzO2r<$,@H*+W\"FakFQz3F5wT4 G@g aSpfz܍%SmiF)œSMCy G?Ֆ'q~D~L2~>{P,ÎUժZCzMT!f`<~/N#B˚; l  O3ӧlKYC 誨ȢC X|n B!AAS' Ǐg1@(++377vqk#G6o IwXڧqcם+ţj~|>°H)^jN C|rl5>V1m&cvDUQr9LExXtelR%/Nbn>jHy[]@VEkmv nv߮f Awin4}o?gnX݁M N/,$T\Rp!@!0k^=s!ht|i=|!h M@ 9k֬׌,#ؠ޽6#lcQ4Yr+Cf-'9=^ *ζц=Ϻɨ<57?_Ylׁz, (+*(GQ K,"dQLxT_}ѺI  [ w@A=|sxki#88K37c었? _ L%>Q[}/Z"4>2ʾUmMT:@M400e|k׮kCA5Y{J˾yoP爨_ Yӣ|gα}K]WJvtۘ'lmLSH`=AVR_uֱF}u" bGmaRr_~iIIx2O1 ?_~ȁ@%{֭bB Ml t}aX4<@r]{O턀ѣܟ3e#j)5Uaw*@ΝK$0l0wwwC䈛402R"B{qLxTϡ[r^2T"~;fd?\eWf~+Zbhp,ܹ)6;j`°U]:#O]eg2HE u ZWwSuׂҶNm}a|@ n?6xl!6t+EhW;-%!u兢԰ȿ@}  ̞<9_aOe]捻(@{ pٍ7R(`,XpU!!!N|وË;[d 9šcM7 PR?j gXo 콓{ڱ;q2jXҔa3 6伟 ɽ.ȶcJ..[FUD@ed {_9aW8#!i?B`CzNsf="=1YS4>19=66[ @eh۹+(*<]bm6-3%mWAI?x_õ5 Vu\I@X!'پSD" зkW@@yy .\@2۷7P"Jb˰ zyzfS=a^HXJ\DzAuLbYz$Vfd~]pN>YW[S.Z*{#L 3f2cNrڏm+{47EF,GZsl]G^xd  Բ<=O2ŸJ#MMw$ArnPBzgwFnWSQ|帺~~ab+ mQJ?6\XK߉}h 'B3@ ZF^]-)t ׀8E@"N^ [?*`ҨQ^dNGyJAN[t4.-x=Ww5(m@vv6J+KEDDnݺ5k,d `+tH-L޵l)[5I5zHPJ$-_=dn5?}Wl^1E3} mW鮻ُz8Z~_푣it,%.BQ 91q$ va$3v/瑓u T(ʋ yU@uxK G1JbTV7} uUe ]\FPJASY/XicEmw7Fo.I_H9m5OHn ;Tw>$&i` YIQjyIfoo߼K*$;|D5! @܄>@!m]<ȉ]NTWhy6Al%=c񀭧fWj%_{u/){PM3a3@ ZMGxsJ.@kזwѿt MBoj[6YD=擛*%@mrP veflKK08r1g"X[[EgV$Խ0)rm[}^}wC{7 R*X,~CvR*^@'W!C:ԃߢY45LL| 5р}r劅}89sܸq%*A,hqʐ䖰ԩ<zz>7d#=0k((|BM'u_iϝO]n S ^P\~׿xE;|Β_ִAe2,-0rYo[O( ItRea =YLFQͬHCt͇MɢBⲣ43m|sx/wIk>nm$fG9˗./Jlrsas@ Z۹#T]sFGرoɉis}T%oldO9 Էyn9X orXxm@"0[Om򋶭y4#t\ff7mk)Ai[%@PN:2ݻwٳv`=~ Q_#cS I<2rʪue("ItUp}p2ނ[z *s}OZfQI_LBRR}5{E$\Gݹg((u3H;}@vhYtdA5WXzӷQɞZt $=C`}׽]gQz{8܂>a9N*=z)/)\kK(hN%i6@ {EV[y?ܼl4Gfl|Y޾37f9ZaϏĚ!OF٘ *ۜ,УGO=Y 177$O@t<򰋊X W[j95@@HtT޼ Em@^^Ǐ~: zV4Գ~" quTf[Ag%~vTO\ ]&Գv-xEutZZRݧnZ @V4P@X`hvM|DێWmܰ D&Bd MD I y,yj6mҖvq{UODS-qw?ݟձ9(QaCyKtdܰaRD @F ,:mJ(zeg-%h É#رÇD( ZEƮ]Uv@/Er@n  h#Zo:( cqGwn|Z zի"1؃g{@+r!6H~Ad sG|aѦI]jjm1P=:@b])sw/5/f{}巠`36+G5C'-i͜{\5S\LK^o~ڮؾw h(cAYx]VNT BC P7Z+:tDx%wGʔOޱ[zmD+PyĄiI>|QTNTQ1\k wZcCG-((bl#&&R-:JYQhXȷq)YTL^hH 5+aiaߩٹ%e~A!99=zk!\}Max{EQ~YXZ޳n WEւD'$ t4@ͮ &'#sxTlbJf^A*$$"/ߡjA}TEAHgM_a;$ ԡó4zj(`@`…((K/^LT %/;4*#¿t@ \GVk&bt'(''GW?J,0p,ߦg"))Dj[6tqLq3E`Yږ{64Z2<{j޼F[@n]W=|6nbeExt=Lj iW<56n#C2H~ / , JL_`giK}rfZsJ!S+Ec>֒ #s>O+e1/"mhhbBhAώ\ݗr6cD:62ٵz4,?n~,/ "7.ZyǞGR]?MTi^ʵ[/y}Kb:?e]ejVi(IFN .a9m?{Q5O#=PTo{mf솫Xj O㷝}C*XzU!Sgn]Lkf81a^尠m7BPK7C͡¨@ CBBFIT 1.cO6|%=E$;nگjk z @Mx Gӫwֺy3VgllRz)ԥ~F{F)#e\٪tg( - tPv ְb ݔj sLx񕳗Y!JiY4cfyQ~?{%nQ:ӌEDQ`*-^?6c룪RF){r_S!qj߿qeu06i۷x}LJMs;<̬echLGQ9': 2fuϏWQ #?ͱG|W*!ėsJ^CsV xxdSV/xs{WHC]WgRybBlPďRz,/y%Gar:^H\n=D 3CC$͐2~,XJ"x} |.1(_0$Dˊ K,䁵ŧoH{+P9\W^R:}fztGڽK d|UզrA^B" )ř;VɹkPQ yw w Q{]v{''e˖0͚:u*KHH€@/  >u}E{4JiO_N+*@ytGcR?]Z,!q>EI* HH)uU7Pg0-qr3}/٧~eh߸@jd'ży 2ТđsǑӏ#F8Ċ-<ׯ̜rANhPԞOmf(F8viyyMր!_+\n !ze2x ]\*o_fWHBzW슒+g[^t'}l49kjx+M 5bLjGN[~aBJMۭ?UݲJɃ~L?muģ|sV8~1iA{95^>ևH͇\(9xcxtPuC;G2]fw`qO3J>etPG^ȼ+j׻1%Բʼ=+Ӿs+w2L'US}FV'Ԡ8:>u9<1Et D߷cJLY}zb{Ӏ2!DV2Dr~;ዓ8a]펜t{P\cn7mYk*/zTt7_r>G:*#T4oIϤ _y`xA)5:5OMSN{jlAEM7!V<|]zٞ]2kFsőӯF8=S/]︸?y2wFIV=yZ޿zp3D3ս"tjvA{55VΝk@ 0}aAR,Bra#(B^ *-Ml:r(~@x`MXY&7(TN( 1xX(Xq53Pn5fOpDtp u_?;a|(;#&*ϊwكsq1q-C =MUߞZ3\ zީg#$4MSn+ v$*|5r#+{:vq+pc^Lu4vX z0k^==c\ܓ9e:gQJvP;qlޡQ4;{hR5krPQN(m) ж FGӻs8$h cKhɒ%';wnʕD%@ f@LeBM`UrM&sO6Z6TDM^pśLH?"C{2i9n}ՏNQEq^RR>wՒz1ZRo%i b(X 3^f`h&2Z݃f{%V$;- s‚b iZbL* /Scˮ_|yN7뻧o= $jXeZ ?UƑnoBur =NT//'b\u~;C0DVGof:vNE.>Р8r5gg*ᑡ3wyX^O3?g-߽{舾Tߎ%Wk ~~{׀J@BLlر>~ իrC@} P.U~fjI1=Bl(K>9WlX5YN -[ ɫ|ЌV1pYk zdy kBrjBG-y=s4Yb?^#/5W#:XUAso瞄WCӴwW`U)67L遻O&=AFg fY9cWPi1)TNDE EʏZk/dowVRRVR&Ĉ*ڀ p @5ffLL8 @rrӃU$̃ƎKT ml6m,lfhlY!ʻV!{ڊR CXd1RZR\ ,SUwX=1+n2ikoՉFCϺ\EpOXDVZܹD)L67O;9~PRsqʑӯiF8;S?~c3 :x(q9 r#tkk7n{z#FX5bes{BcbCCee׮4"4$> DYRUCW51aԅDAI9)iY\4## OmjFКY:qsd{݋BW~$$Hx!/$1cLQ4:,O`RRX>}xxxٳ!.@21V>AJvwO[pt))z~g"P5Xefkn>*tͳBFLU tTY'd\vn4sfG9)1%>嗢 F 2qGރm£6K?)'=%GLcnG8Yd~ M))(*)kC-n~OSd0ͰE6o= .K0/\β:#%JHMJŨQdǙ,yԆO1ۥdaVʻnB*9qmFl9=),-.J'.AB*w˧qWiGN&LJy$.}ӫRb‚eEbV@NS{Lr֙&4geeaF6ovDSƌ8yBD9̈管٘J!ʣj_ӂomRZM޸q|5 a= 'HJɡ7Iz#B ѣ&Tga!'*i2 tSuII9&VYVTH()ȓBC9p}._@ Z#xb_ĉ]\\+6 @6ImrZ7(HmL5Shж[1+-y/RWi3;7sg/֭fh\Y*Y`,Ku.'ne[545GUUvY[L%)jX=VQʣS!7;??n-e x5 u5\FL~ϢXm5zlpe@AI1^Z=i;)3ڥCT:@ IP @<{n?NEfڔ#/_):kǗ i8;~ i$fO m'Rʙ/P|Mv*Nagt@@ȶfc\.?ýn8mq@5IA*@k+Vܽ{-,,J.z!s9aVxdǎ?7"4h C4Zo#{:7?GZ~v)%Fyw N86'7XJ^؋~Hd^.j}} [{&}Zܠ={d ֖,jߠ#&S|测{jFXYny՟)xb"AzO2rh,@H*+o4M4U 'N?i"XৎG<}>̥o߾vvvh5k|Ռݰxqph'ٮ={:x@vD[a5y?>e U}/ 5NJ: Z#o0Jw|ס;CṥSnr\9̊kn,%_r.1 <@HH9j ӊ3#>؋,.U{0EB~iᛠ6t=jAVfCAhHHJ1$jnfTkjY@ڥ++V?JeTF~:~.xtN;}~e @@9xf̘@HH\DiE'MbnAmzp[Ƌ{MvSv !†jGuyl}[D|ᶝFUyZA@qH|]}ݹR].hԄ֣b6Z Zr,:s*}~wE.!>F4&Ր<c؊0{-wZru݈qpѹ#@ɶIy^r>`'':Hk.''x?^| [U.?~DFbZ4W δ |sLAKQ1o~렠̜ٖ ЊYhvz?)w'>cW.kjd`j#,H*LFZMWo "~NoZ!@UA*+a:E㕢>rytCܒ>(/;ہC'Z[ҳSx%@J!!! D=zӧQ 28;OgObxO7H:xnßc**QK S2ro&4chDf(Iq! k[oGTqr^.2X9OH: ۾ZGMֳpxfߵ}}gwgF+f<£EHk,Yx|I_;(),EXyq  C|UL aQpn˙9sqonddk@haasgufeb 㓒L6nzr*6 SP &x‚wa\rp N'ZW=2yL]i;sE[2L6?~H+|v{Wf$%'!{iIIs4T pչm #Fή[ګVԁDLNLuzuxz#. ѓ腾`=lٲ֭[a7ܽsgǓ'XAV>|9azkuӽ\@6;|qуA}|Ơ8Z^FJD׷~/n]铃?¨;ȑbS+5$aK'uQSGUr$Ÿ|˲d6CX5sK93EG\r!"88dCI?v y R{z;iK[Q/_,blfv˗V0 &0i䈃l[vM[Cc޴ ?{@ BXsj1F%zEBi#$QkJ<2u8kkr"X Z~zN9^Ͷ 톣c1E^,%x55tT cW$[ܜl,ᕒmTc=l@*NzjwfDZfSgLYӱ!@(--Eq&G$*A@K(?};%T@HDc>Fז(utAbL\cNy?d1FSEA~e;u٣Aq>쯟?&Qydz٭C]σ-A`!!@ n"¼Hw^>s}pіݪ a_TGF^Dq?YBNqX3u z|;$b3lؐ~'Z{Cc\>BP 5kTP 15$!|~Zi?]ACI 퇂k232э ¸%k;/}s'/+OLND?iC9rqyoL zHs%}68=$a*/] N0iucX|/VoƘ5;O1Ig>=88:?#eFey.{,e|*45hpnPB'j;O#7L+۩(sb[x N j7`˭ oÅؗixsN -~pjSfdVQ[T!'' ;1Kcx섦8^t7oF ###1+'OnݘL&_;FbmiIFDKDĮS^{_^QN]35~AԂw`WV3W"[wBLl,)IFẳ_/0Zw =ߝDXˆ[vLaOww?蹏`<ܕgkUv'$>x؁,KhEX ա&Z6唭ޤUd$D(EU=zXQK*u0]kj5LKkg)q2R38 kIAI/ܱIսgW{9;lTEQ^lTcOϘ\?XܥQ(?+ ՜`,{KCI~1h">>y蟿>޵(%i9)ZN{?^؁yˉ֐d ͛+nxɾ|O/@Mͺ?9k oYwC,@H-ܨ_;fMA'ҕ";+"^gW/ \c I_Z+a/ʳx!CꪋkPW^{k }*+*B_aA; hVfބLpT@]5CxTV}t5x'_۞qؿicI@LTÐY ֬ ;.k1ITZj0ÍwtmU-_C}3Ve Uvyʨ3i$siwZc1^ǡ)*Bң~/n>&ړ7JU3K!`W)rMU47o=G7#)tF$K)M{QFffqDWƎ@]^];$Pib_e_bk/ʻHBNݻ *3|kDY?+v9>|d*;vڵŋZ3&N5 s}UU:hqS9V+54o{y1W2Żt{:sfm;0[`ZҌҊ6w| SYUgUb֘mu+Pk'z]&޻؃98*U8t*q=:iQ'D`?K+-P3_|5ѡ[cZrn2G˳"Z;Ntd1jPXA6+~_,a0Z 3곚%;~t;*CFzx<[܌~+=V)uKv;W<-a|;g((.?ffkgcW,ype`MT,3߸yBfrYo[OtA. d4/_`b2(㬁HCt͇MwEz!qQWZzҀnxxڤ5nq~ga4xE Uns#G!7exŻ^y4.H- E2I+̖y.K`vye ZcLMkMtk׮Mk_wTD‘4Fia)<kV.GU1M[$0w_nb^K[Dcj"w.BGԲ^${!u+4NiUKGլOG2#?]%Q%j㉥xKWu!>J0ZcX;yz+GTYo.\}i7S~~޾oE~l}!?`wR>}`BN)7 8 hM o׸e7 )ymמf#;)a'kcn QvY_ H1V@lygSqw_foe6DzMjYCKfGUu+jFtM@M ++kΜ9>oXeQQ۷oϜ9Pچ .X%eܹs/^(--͢ѯJ;U7`e)\Nq1q|=;65@C^B'k~dڲ(ҽgk}LL5|7r=~_D#1mE˳?q G uzuw8`ީjFܒK ++Tv#L~Wxv|/BAIcK #нzMX%~ph1y5pdϾ\bOPTM{U֞D?X Q_#cS I<2rʪue"j-8O1?ey$$:)WoOa~̎huԝ[; 0G|V~!/RẂ:Իdn _aOߢs D%{kM;\BҁVx G?%Yþ-dTT׽tj`L\BnA B'Ӕil<И8rqOdy|[dZNBhH UQ0TuZL-&633qFpp0f%GnWޙCE|"TʒJTTE6K$)R 6Ii=KhC$|<KLk=s13)ʙ{37 F^Aų˗$<PEnwO_g"X'RfD\dڝhk\C+@} ޯG !?Bm1i9ܵawE!YM=v̜+*?1gb7ʊZJW)xbOrٮ {- sM6l>Hi)#CJgݷk[?zμ+kX~HW?afo>ZqRr;z=ZtCEVlxք#G@a^Vlܛo $wϮj4' X#Wteރ`D9;oU;IK RERS}=e|lIV^@p@BB·^ڿC-3PQ ]LLs͎̞D謾rU:U3zΤʍѣʡI"STF aƢgH-&8~?[g% Yg= )mn)M([{yȕW`S]7p8-0;h2ooU۷p޽{sfMR\'^X~]li#'C_!ʹ<\ eACbv* [m]&'ڐN} vg@-OWK(݉AN2#, W)S5dꔩ$R|B< XX6 @?sxiEcjܙ wY-)-ԒWع,vA^Y61I)A"bV?6T)w{feImW7>qpO kVf0{b56<\gUϓ'ܸ6P'Nt^0 mZ%m1EɓmMҥK+V1vvv1\<QZ^'Er<뱒_YJלf.]}uPq(A|'y/Z*T.]la B9o޽X j1*HH+@hMр?~`.\`hhZk8&N_b7a{/jjSGj6a#0f]]/A) k@ט=pC6[1T;f,؎ q ": O~IYTNTVV>@o.G17ˋ7hk؄:enqݴ]̻_ҳrQN\Br5 V yω.%${siH EHf6IM𷓔FklRo'HuѺ|7"ad2DgyE>j;3qPRkmwڬْçoY4Aa9Nj*hab$ Dzl^ ]Rm޳mcŗ >-,3Dded{(лݪ} J xyy+۶mۺu+%bi;2ʈXYY0Gȝ;w>s̘1c8So_7n]EwjZO- !wâ^OLJN/pg|\'g~sǢ8]"G=ٌT|V+F8[BeF~\\TnbX"$.P@#}j6 MH]vʢxk׮6m$&hm*.ǘ[ۨ=vWp^k#`|ٳ7Cm`eߞ [7 nENh?"w{v\j)hCD'U矜+ ,ңznS|CGL:摿֐_x'PPCk,  Λ7۸ hʠZnx!ͱ% G5O>^,XFݱ^ucŝ &˪N9`zZdeEy~Io9>-۩Yո|J!5C/^ZB%bG==&Pj;H">xSWWejaM&@OĉX$߿߼y3ǘdsvy<6h_3xQLD9<6yLqq֛)%寜@ 铎lsvW^E[o ժ!{ݲeK9KQQQwwwSSSޜQ:b^p}Gח:#Gߚ?LUPгN~):պ.Ɉ0i,!#?x zN]jR?_ FH t5Y]btrt ;$8W_53bemY)VådKYVM$5Ad(!;( @ ~q-+HK{>`B} YA 6Yݪ꩷7|CwhWQ)kӪq;, 0\1R_RVJ 7'bFN@VK-+z…d,¨(ʕ+[mM_DǸ:խÆ8ų~}9 ?Հ@ ,4u6nܸcˁ- mllн`O>=~x}/4wTd~ae3NX}m# JE<؅5o?oOܚ7<#mءP ʈ~%6Jm鸮Q} s>Ľgmиͺ۫y,AD+鰩3 UjЃ"{Uwn'vpGd\1f$%rC6Xf!]Y(*i ;9sL<@tՄU:7߇={Y@Wm<T6VFc旱bv?:[Zp @Qׯ_'/((x  ^222A T Gf(2޼ySau\0f!zu%Q*ԝWP;u*L˥p=eNEhIx=(-s\^QΖd@yr6>mthrwR1#,cWGk<.fⵎI ـN8 @1cڶڵkXlvvv>>>0&o씴4_9-,]jrwA^$ $(6#[**@ Z-4&F   N1+ X/{ ȓwՕB&C'N})Da5'#u;//"Mх3Wcmh+S|-BLYo4e +Yu,9`ڵO}JϢ 1s, ^i+,~v80g.$IO_b?duCL*qϝ.;9݋,oU dk[I9g-|}BF|HJ-.'IH(;Q,E)ab}! z! h $ТЮ /5jTkc -pk|ÒR̗qq+l9gO^476~Ν۫|A@ ^Rj@ mӧOuuu322khh]v%*An MLL}MSSWUUg!\7aWńjû+ 1OкenQt7!c${='' 6A)܆Ad<=1G}-=DᗔQRV=P^Z):гUj'Ew5xjc\B'0uԨMVCE$'GYrJtlL段F   _ׁflܹsA'ӧOG qqft nK.$FZ[[޽-9KLV8־ܮjT)/#in 17i p6H@Jk׮={bccߏm6}P&z/?7m٧Ap m6Kȷo>yR $2Aȵkkn!^$^ |@0Bˮe$la]ẸP rԂbk#F=tG`;vDL4CE o @𷈠@xhǎGh``ХK\BCnKLn;6,j\ u=@JZ4b  \LTq`` YtmCK,!*An ޾}khhiN0@C[W/+FƯ_s,, f&vedTV^_\#d*f  Z#Ç%Fk}6sL5pFET }R<<<6l̬޽{*h}VlbC9z%}H/@6EJPeUVnv1f4k֬4">}\vMYYŋCBB8"@PsZ+㥡iS0$[ '^,v<$-Z׮]WF뎊B`H[6ں 7r+ p maī׾׮ šL@@ /Z5W?^ŋmFƍh9 := 4_سgA@04oj@>:p  Z+W:u%k/_wlpP.Et4 >XWEq m=m||h8@ @@+I:::n߾ İ,--..J+KJJ8f@{ Ѿ<~` ^6KΠ>q2O^WGI>؀7 |4 -JE;>$4f޽e0IC#@ Km5~~~SL!*An|#G0vڅpkd ܛk$` Z RRR0_;Vti:+\psbjRasrOgoޔ6n3` @47$#ݺuv횚Q r+&ijjzj~X{>1#FtЁwIZZG˖-swwG)8P@4H6[ -YO~ul;;; pEht[pmڬڣf޸L@R\|zLb@ h& ŋ:$((غc0eee7o޷oZ_DZZf%*A@4H6dh xghh(?(~ɒ%#Gi[shNq>$&b6 n'ҢH@ @Y lj^朝mmmѵQrk%`hh+njBtܙCE @a@&@  M6a^|fff_i[s(xdahGTjK@ @_̙N*&&1cQ rk%n28qں#cǎu֡l1Q2@@PCC@ ֮]{xqG׮g]XNc~x~]kmZf@5@ @NSGGǏ]t $*An/_gϞ(I͡"@) @)iC[@ Ж7n͛p \!0u(Ֆ()[}uٓ&@ -;wEeNN1_rENN 366Gh[4CE @a@G ;@ -٭+Wr (+k@@ @\h* .9RBBBD%ȭ@yy֭[ك/)%%uȑsʨ!( ZH.@޸q#77k ((MXUh=.̍cPPXgfpYR\R@ h PfdGGG=z[%>=##GM`",P@ @C@ [:vsNKKKܭիW¸ =gJU(`]ba988lذJ!. Z"H^@Z %ϟvXa}\g[X2 õa;zohT t @ Z*4olƌ^ET* dgg\ߟ#:eez( $  !h4Z fȐ!>hπ 𐗭ŕY&w:x1cp @ @'fee]8p  J=BRRR8366򒐐C  A > 4h>TVVfaaq+1:a嫸[`l….TRj&@ @;fnnv$Z;w.Z/JTTTTl߾}4CϟOT ) g@'eE3220oݻwEpu9l۷w9X-su#.X! @ ZZn;G`[lA{XZ_Kbdd3І v9%@HX;@ kbbbcc3e4׀-R/,.l}dd@  a7o$Z:q℡!Q r$Rh(zC`QzxӦM||pd x|NbO@ .\.>|yuVfwU:n'5xto9vl @ ˌ3bbb.u1((hD%ȭ@nn/Ghh z( x$y_+ hfhwm$H~@ lDEEѾj$ӡC\ HIa05m|͛ۇ}bC`  @7hS^Y,2P30h +W8z Q r#pzJGz-$[JO@ <f͚5mڴ7n`geeNjo@p4[{ƚEp @  c- *$$,^Rd(0xϸA- :ue"o޼!F}ӉPZgiiyfhQ#FphA A؂: \@!ubyĒ%K2:cY/cΞśXﲧ>׀@Ȉr&Iv|#"mmٓCE @ % q6@4)AA?o]%(**K^a_DG;l?ۉ 5 @ N3OH9OQQ^3awOL/(Ǹƅβu}~lh&`3xK 4E]tx˗ 2WXMB١ @t l= @7n2X{hh@@@5&OSk]S5WI4@ Ќ|rs v+Y{9g_7(z_'A --m̙/^Tmy---RGcǎm b2ɓw̙3cƌC @ MG͛X6h:^Ks_Dn˩JaZp @ hbbz:r<}ДBf/^Z?~=h@sϓih8B/_DTbl***׮]%LZ>p={ѣGQz  H ԩӎ;V^`iiF Ncw XsW~(/+ @ ULq۲١[`^D|*goeFEtfcd*pO^NHI-*Q]:J7阹:nD?à}Nx]\jfߡCT`?IZY|H/鬠4T{`w:ݩS,()ԧbJ0Ulo ht!!d{* ,jE"166FFMKII t:}޽[lAĠDEEMMMd C/! hfff>>>^ݻkZ&.yyj}Ģ\ag )@ k ,F>SQzT_;[𝙇 T2 wsʲ@LK;|‹d|ݎ}ODHDeꕢӆ r3r1dsaeܪ~VIM=f_0E_*͟nowmD9ar!V"ߓ7Si M{r;6}C4zw %G.!Ey_m.OU^[?~FDDxIJ[ B GDOFrh A*5Uj w" Kx-^2xr47L``ϟ?1SC !`Et v^5  @|MD( $ۘ B2/+n}ޱy|m[9,`&*r<X:g2ьhGw.Zy*M DJ pRHY`wdIGѪ{M ZC<`K@eHh];ZO$4/vkR-)Vbh*:KG L߿Z$$ SZ >ս|ĤaUA=-4D"f|uL˳ŽG%fMEx݌3Q(++_vO hh##xΩ3g<~LS ׄ&^TJ쬤5hpO?zs&'c'~UVAhEoC=w},62[.E|5=T۱so~Çl'PDZ[J}vjb~ wmаQd촨gQ- v褨_[K_m#xo^^Fk/ַgF!e\ EٽAeTd'JҜb~;UYysg֡gIKK=-Z $SN5 41/cc1ee-,_ʶóM @Mv& 젦m#d ^-=p뀙O9wfOXgu+}HJ|:o#:2U|'O`- yѝؒ1kVd O,2R%A'}K)픆#Ź`nΟqģG0iii[nETp>ةu2~~}S|Vf@ ː#{nU捰|ݙK8 C=8UfVH|N')vk1|2޶k!@;yX[SXgCRj)}ec?$TU? Ґ]kfoZͼtt]銕hB)J)W5_BkBU-P:hZntuu]~=<ҥKѮ\ ?w5==ݻhjjpԁb!6hy{ۇ,;wG=]Bٕ^v.ށDϰX::"ʊ߱5%x71+@g`erf#$;?0SD%2ĈJ2tF%F(f O#}\%T'&=4xWX|[CN~8XM(HnbwǎݕdVߍX/ eGࢫRd~tx\[i~ڛ!k$[M*❴fku S+2pg3_E['gV%EtTT)I9' L" c+Wʳ?̟8.6l(3у[5IGr0J ٧=VgwbǧF 9v=|Z' EHB/Jt}"p7:c"P#+,δFy=Lbj7e jjjk֬AאX(_eff?ڔpC[g쌷q@ u3p @ + uPV}|Lȃ)9Ÿqhc;w}Q*aQzGhw r"A"k!yANhFt +wտĞa߯¢jS$%..M>u(..F{tgҤIz(Vy¦.'fUM]RT '37櫓S\*j],?|Sz6ŎsIVj({=wNLb5eg0Q Ux93&Y3=_`Skf/]g(ӎTQ)<$Z5s^r^NU47Uqz?f]ۗ9v o_CÒ3 ´g~>^gHl׀c@ lٲ/%%ٳghK˗[k/zw[zD@@ ='q:9Lc^f ϯޫ{}Ȃ• 4:Sr,ҭS](huʉ $0~w a }zUG ->,Rv M\4p/..' XcF}c x{t{t$lzY[{\/zm5AOZjC/!O^;~ni4֒8o]DNu3WVnq/Yq^\،k$yGONǰ޽O3$"NR#h {Xldn;w=߅_'%+.-Yԕ;gn8S݋Ir _jwu:%kWo}/* IvVR4cΓ0h_=|&kz^QH{>t`;R_/.&ҿXm׺>4ʩxp 7 jo&eE=B{x- ʊ4z&7^~JI/SڵSQ۳,J-5QM111ޞnp A-siWǏ~5[< l߮]c6@ @$',pD}Sd1)z"S74Y\Z5 K}+LRVDrX_ɤ\"YCg-ӿp?'dZC(?mԨC*)wjۢ!!!(""!m&=<<6lP/) LHHh޽1DQ+GCXMR,ս brvҎKyO0+ ^ӅО=tF Rqӟ\tܺH}ksљAǟU}?I^2BIf\d>vz *""+.V}#'~fӴwx=c#4);3Úph{ ̦'ZYhHF3\0V%vq<.ғ ֺR.9C C*O㊑JC>%q"(i`GYnކogB~B/p߇//o9lu yV1R1F#00 {5A{u/Э{ _3OC nnݱBwo veFYwGGcg[CV G{TAͭ|r޽hlN;$A"RsYe?c/unQc~9-)|{ttX46 ,7m 876{n<pvqs}W%fkYw =cLgnN3ws1TMVXڬ^+t {x5 휊6GCϣҲpڳdRS篱uʣ7'@VM@T^&Ce|*wyZe+{57(b?^gQY=H:vnmY% t8qX銛?Xg?鉞ITA1mSF9a\Μ7YᵅD c/\رc\ ?p/F)ah(ZKV]]CVO 5,5"Z}@tA3( H۷Qw^Z6L6Exzb'6i(sP_oib5" vH~KnsA%r?Dfgd%IU_WmGO*ڧ7GmI?W{bOԞcƙdVJˠ!*vl>&S4NV3;Wf O;C"+FV‚ 5x͙3O z_)On~7Hf4+F]f+, `gWrb Zğ$R88m0oj .F|&*\Qx14coC/za ZBo:ܡ b쇫K%qF:ٲ0nX&vBS4|&~Ǚj[J zEFr;VT[β]g /rm@8sÙjkf{s+ Ҷ-t>{O_{/9y _]!X/ Mc?\p`6s~uJd~tԼ>smcdɒÇ׏]wsl9&ڜfu7h  hN3OHodV:y\-lAvHQZRŅhzlWt&05m/{١ ׳{g49իW>|2=k.4 ͛(;A uŞ={ B6B &6+'xwBEDRai&OzYYJCd7OU9SPe,;kч2TsߊEp8^1h?R~UZ;y˭6.mH^\W׸8<>Y_]!Qy l?&IĠ 8WSMSNJH $$q8 4FQ+QFYiDhFi7= r1.9SmDzt%.&GΞ<&,߀!XeMhُWއ>4;'VbJ%.poED8*',̶3vw!0Ko©K4{]aqĽ1_| ުIj,E̙z܃5̏Ogrz=kmvM7mڴyf˗/ݷV#vX[mrs#s츖)Sp @ h ]LF/>^iQeVk>Ek[]`㣢 zRcԪMMo߻uν'3 2$F[w}9yT ք/6EEEϜ9C*xK誜ZB-!*AnKyyov_cbf2~Waeey%EA5eU65L yc%M[Qpߥ뷯"^W I<~_. xb_>:+Fji,(&}X j1cdzy,pDu f#͙1iT/Bqo_ 3F[k¯[F^=:Ɋ '% tSxp]LMFڴcʵ+<<$7=.e؟xVާˉAiEiS'jt(Ɍ;i9USS\*j{Q¾Ew;DKL;41=SxHе7(2`B]DffT}KJs~P|rJg7Nj`7qיj&qm@8XrV$Mfwl tA2F,'E+)9Xsw}rteS3il?ױ$cGЁJ+%><4,9۔^r,χu\{|={lk?h4pڵ&خ"aŊ11AwDo죬ޫ  #@^htHd+ǂo+CqְB"շ(Ÿ>)5"Rg'w<Ēꑍ!Ϊ۹R5::::;wߟ-@LL!ɧNر#mYTLe=>%/:wcpN 5xę+)qB ,9Ǐw%%h_ef ,| z*.>1)9%+9ng|\'gc +7Cnb zfIHsу+FXV J*N1..Wey6賍+ <]6\i {e "#~J!е]m{*l߸ʰĂ?ռ>\t54u[xǁ4AzEc7`d_ i,0J-'T;ou?bc2E}kǝ>[Ĝ+ms9ZOiF$Ffnˋ˻ےϴ#YxB廑iɦn19hpb?ŷM o gbw?;<~M{;kukkwE=;5V>h_&ywH4c-ET>n:s^pzYq^\LOZq988̛7]|OO& -.hF,@ PDl2Z劍w~)LvFE~E-YH(StVnG0r7@ p#ΊTz%8鸪ѤG5e6KYs&}NK4rs@u B:ʊ9x6G@EEDVC!=H[ӧV,A6g6(=Z]>ATߩfV3dΊ2O?*SF lO;LU31s(d`eEy~Io9>-۩Yո|/wY!/a"0\1R[HfEw hyN-bSHAǟa)$>iիBjݕ'tN#WcٺT%$BzUT-N{"Sүr:YL\RV^[7Ҕ%M" u?cTYX8~|JٹW|@`٨Z>V<ݥUg?yÜlM0rmvZa0Mz a"L,tjnK4q,7\oC7S׏\?ƍ? 0J:$ mzo<ڮzRb@ E˜D/Xarε]S(.Gk=ƕ B+10ӲH]:4*1)&]dtf[%n,^vQ vxFpd=4 %Knܨ)뫩3#I`̤ɂحԇ'60p5Րp)˿Tњat9f#kWLM/a?l‡7'c3!Cfo߲p :N]jPUгN~):Ɵ r2"!KS׬Ow14" B&K\CsbpaT*Iwܶz* Й\12|^RYN_1>$XYTNFI _J̷s5;#.7CT]3S࠵3 QP(TQ>_?}HU TL#]1MTv[$:.pűF%FIA<@m۶[L2"V ߾}{ѢE߿eժUC<@{?W䧹5"W)J}ѣ7STǛXktcDa9-Ycwi(h/BUSWEOf^KB[d(Xu.:3c{1G>;7j2,^3>Fm03=ueE}SDkvz_k߱d GVy11O, qI BC5iyYa~$v* [m]&'Zÿk7&ͷMp"fk\qW.e|OOؘSKI2urUDO'x4AVٱc8daa6B18FfAZjHk@@ @C\7me`' 3P&YX2 ~~/b(ĸY)P2t-!ecj"Š7W+[Vuu fIJEs$qYQE'5x!!!D8 tGbȜ8qbƌ-=@(NCFfo3=I4z˶$s2|ױYo޾ۡLhms;{=2m1B^@ XPDDI~<8Ґ\vj/@3-K|cw>սKv"d0/[EJ(gHz򥜤.PymY2e꤉UKKJ$,i4s+Sh#`FD潧>',wg Sfv O\ 3jvzC|n쿽}|`/_COoo^;@zGW9wP 7'b܈ {(>.}JGL]ʦ:F\J)5W_%F^n^}=V۹I^4mw{nGGeL,;E\F  hd_1.l;]rP[ֈ,Amy*>K{`Z+JqƛZ5Ɯz-5 SǢIon27O{/yt >x#fOUJa4!9(fk'Us5nf'.ttt∞AW\4hQ rK'{CC7op2at=./_)Plk_0y\Vv8;k~E"U&i–X$Sr/6aB-)oV/=U{%)V@yŭs`fQ&l_dg 2~+ͽa+%8g)qtkq]q% f"\Q)|{!β۠qu{#ͧm\V~ZɧMMaWJMwmXdL(/xt=Ej2D%rETc`> "Qjfנ#Ǐw&MpǷ {Bbv+M' GJ11q},vSr")$+i§.qݰSQ!UƇ5~rTYF+m6zunm[4Zc+qqqa"ﺌTc#?..eR:Wx$%쇨TCJWfܳgς z쾵ЎgE~HNEuG]cl F @\nlaˈ&s"OE.g:+.p^nӗb!LU@Zqcj·"sKN%]0 EN\]B^JYMHZQV^eo7YoNC4̬)c1_YJQ+FR$O=\yv1UDeut޸LZH9%pgE'\zȔ!Deuڏ{ObQ<ݪiA"uWPQ;f#Lrə)***$R WC!iO;LU31s(deeEy~Io9>-[nʝnb+\zp [蒢2nĆ˲׬/NxVQ QOO"u|{>x˼)˖-C-{h(hj'!}p\Q]>-^/肦h @' o@K(,R_oy8yQGyQUrY0 v˯{5n S' ddpodb7qGD^O+oaD{Zlk2n]X-1jC BxH1c2++< rWf?j XezY#+\:*|*^xZAtʕeeU1tuuϞ=+*y,Jp(90`@&ձ&p>~猚*5d>eAS΢*;sŦ~).T[LT WK}` =џ+ ;~!~#gm[5gX6*_%zE>|qnIm+IPè3=m Y=Ήvo258**4ptKFSr !KvSҳAۏlf[?;3FŤ/ݟGUv;s]vrv Y3&Y]@FÝj߹b$"b啯_ÈI$ )ec'ϝ(%L2}!eȐÃݷoY8l=CP=ZSҩKVRה3j{-1ȉ;pZIJ><鄁(cLҳN~):u7\qa19uZIT>hO#"IKt)/+OLF_ǚDR7lRn_yp w 5{n&K\C91KW8ܿCx ۤwܶz ЙȒkypsm{VհVl?תd޳&bTNNN?{?lzdֳI}U+q @ hʊ5}y ]aeSwFZ8,wFN3݆U(%VQRCld\:& e\zOد 6*?/qi'%';_w ?>~G-_y33T %A(IKMx{tsr>MX#Wt-svn=U(w򿥦<{>v;U#-<[eU3| ڈn޽. Ν;a2]wIZZGhx;d9@❖[%%݋7J(T1 2:wQRUSk'R2aAkJvH-;aL՚dtfQEqW/?~UTRF:)Pѧp[Y'fPyq|3 I~Ii%erAѱ؇Nqɂ'o8PS_ٳȄYT~9.uqVS\1fYVylZ-Rci~X_΍L?w|\2>Fm03=u7=}}£%%% ܢ  7I8N>;+ΰiЫyڛO?RR'?}[\XY>z}xk 9Vڷ{1Xkvz_k h.q3lC8S50{I.nag W(˻s5gϕwvާ9hΘ(NCFo3=I4z˶$s2|ױUݼ}Co-= 0m˒)T'M]ZBVZۗ'aaOXPm!қDi{Lr _?'ƾ{ְǂw/NZV74Hm+{vnǬ}|`/_COoo^Vңu=|I =բRО罙8ym8|ĺdC5^~y t9+.+cBTQQTI/+{%/w3YuT[_$q8t"hEK֬{nyΪ1'Oq˯Zm?q5KFcgq $''Ϙ1ݻwfdeeЖD%-@BBW83f̙3g:w̡"@9H9+ޯI1{"tIQyzz)3z& v֖ /SttQFo2oB-tU vhD,-s CO.cVyUB H$ՑDJyW|>L^-58;k~D5eڏ_$RL8qjHjӭMuT e s$҉KVĔ=* qB6$4|3;MwmXdngpGdj=RIJv3GP{Ң_QQsE>h T*M[ovҷoX{ٹzO/B/4E@@ x@qiKp~wͱA/hrAaki)!;h؀S(.Yfx(=bTw#"F)(ItWֻ3U]lBm֙?ytnBv kOkOP)R.Y:5ËRZvRJ]iiud)6ٶfd-Dded{(лh"""?~ 6DTr 'N.,,$FFڱcǺu` Y"@wO*\ϱKJўav>>o$0Ǥwn|} WlʳET&ֳIO"H!R6TVdol*F_?qjn]ID]=2eNu J?1N{+hS{WWˉոM9Sm*=:Ãa>mt(+^{rwRq-Etأ5npm'*xe҂uEU-I]]}͚5~h{UV:ul޴o.a KJ@L7musCV:@b/]^%$,*TF8D:b` L) ]UгMQPCφځ.]ZZZJlp龾D%-@VV8Bٳ'zhkks.3v^qI3Yz.T/rdM6 AE~ɮAw<y[R۽vIPè3=VcT"U˱QgUd>eASq,wk]D7\|{nj H 3fєl٪HWq߭g >Z,]dح> g8j`!oi.i3<ƻME݂EoU dD;rJDN'f+__ЇRIR2*j#Nԟ;KQꏆ1򎀖+ҳg|||xǽI>}ܱv7jh]׀@ @ i puڵ...h=[(X+Q,^SL#~ X/ MA55%RP?G k4!H6&mL՚dtfQEqW/?~UTRFIHuRGOOW _gm>|}FNJ~%*6(v"y$݂?BףurC_'|-UU8bPwZfow/D'K/*P2:wQRUSkWi艙=T^:}7>LQ1ZDRZFIYe@yi|'xQ^m_>L[P' ,#ש{^}5:H֗f71j3xDmjp^|wcY_ٳȄYT~9.u{ o t={6eь/c>7a}Tk@@ @ pdjB ysOo۶ {RRRG;w.Q 2@0Kjj]xvm +Au_',9`X~с'iP%GMUd~4ji2KY  rݦv7&jCm[6& 0[LFqtEnDӛ:u͛71@ Yo:(iܫM @ @ o-A9jon5!4>|`ddsFy.]ph,Tg]f%gЈ)|}7:J(H6[  xyyŬU 4>>k}k33;{ţ BMOS  @VAEtd΅s5eeV"ABqXZZY8 6D,  ox7C7-@;@@{h?¶,(۾FaN]u@ m[P\ZJ0qċ/k׎^r?:C E" v@ĆsKΚNG6EJPeUVnv1GZT%;;s%&&^WTpuh8`q#+WհX׀@ /`0;z;xy!菅;[FTB %=;v5 4%sf>|Zݚ{8׀@ @(.)Yqn..YDCfo߾{nC<1IIC͟? @IVk ` 4ѡݒ?s]t˳%*|ݿyӻ_cєWT[m<(PANUA @Mϟfo C[[4-ϟ={a;JJJz( @ch1 B@F'@Upei\rΙ$Snpuluq7o4o<|8yHM@@e5=3seعsM4@ @]޼?s媯uU}K'Ru1Jݲe˦M`ؖ޿?-@I *@M<|w7]߸q ,wq]XPGcuE91 1מyz#;ySp @!W[hSH%533pMDYC4}CE hT lT` '3gRR\uXmYʍL`7m4w؎sŁq @ @4 x؞c7l,7kGӸ4O.XKD7z@ \ڮpOJ;)(6x @ - Q:=a&U/cN-vWE?MM\@ MlŖ-gR({7l^dBT TTTڵkǎ4迸.\HT N4ҙ9v4|`o6:@t1lx 2kzqa[Lbh9σ;Ԭ  @ NgVœ/%.OH9)) M| >|9P@o <<$"3 ~0n}Hݾȁ@wyҭSxǥ6≐^h"tW n2ꮏ?a @"X*7:w>rXgOH… VB[R[nk."HNNfZD!$G h…ŸmNG@dq MC@S'.Z\Z=ni @ 6 `hcWP@ VJ[v..4}md|!GGA.6@ddѧO8?|@J   Aě=zW%&Bʯ4󍽰U3K#5 H`,&2a,]a;fs&z}zsgL_DG^W_uzϞp @ @4@yyvDS2v:SSؚ4B\xqn o#_M&,&YIYk m}2Vճȗo tعzC(rHban"-)?~^Aع۠a4_QvZԳ_~;tR语уopP^O)etJr*j}{vY A=Є8q) ZSىF:34pnַ-O9S@#ޫXhhK3G|)e qt؏߾ER\Z:"* V7j]  @_99s-WGF9oƸqD%-@JJ… >|Ṷ{¦\L%8wNTAͭ 0Ɠ_%sbR'/=n#A=ڿno :C9詩YgUƆY`\RzYEXU];^9䲟ˌ^}:xh;2AJOEɩ1r|w?-/*3zHUb9:.Ylv'2j9ٙ9qFǾ1dz̃gϊ+`wXbfq&wFP9z%(z"u!mNgdWElmf_5Ҽ?_RK`πHedkr- VI`_W*xS2XJDX8m]\456NミL<{@ @ +?&'}ҩÇa%"(_tiŊDW;;;GGG~~~d \R߆_`"oБ~U򲧫_(Q~n;^SG$ZYq;V[1VWѬLV>XS~pLHdM}"#9n+u-*Ɉ3ҙT,/̼x ͬDq |xnnҳIw$*r{o:V*EEAvgﳡ$wǎݕpZw#VKCD.ju0fd~tT;w={;ׯ5Gp-KX>3ASùvw=A! * o4~߾Y 8&u>;4"mz&@ 'OYYcfwGC PPP`eeuIs;w|̙1cpScu}+`V3 p!U@Txo$ bdQZ>uF}juWf~|:kԐc×+yY!/}#KY:t"oB.Qw 8QnF9vw@ M8e[eXВ;=^Z&f>W ~w^gl3+msqt`;R_/.&҂l5T]l|RRʯrAN O]S4P:Uwwvjb~ wmаQӢE+'ءFm-zfySJzҮZߞ]e9 ݻt_b',0spxXs@cDױ'w~s,klaqETe)Ifw@ -ZP-+%:,"$2gdE۷o&&&p{*0 @n!Jy_{lTNɭyK;:,?yÜw)"rNB{\ɏ" Rqӟ\tܺHrNtjq96;0O0KO8gy%ФT Y:_{ VghenOf1#9A#e乹[Bog"dqFeNMBPbt8H4q,7\oC7S׏Q!GWbIV{-@VחgSSVGge%Œ1!hߣ)/5e9^λM8J=H^X$M,:GP\3vҫ_Uvc>%2bpL{u/Э{ _3wv+nnݱBwh>aoa݈AK݉-(Eoǰ9ٙ9q}g]=Qլz̃gϊ+8}W%fkYwDh$6􌝣W\ҏU(R̍pt&NvqU *&+,mVo/\?D@nUЬsW?$%aQE'$x9kdq  |J?Җӑ)@ @zvY)Fv5j}FOVޙT5ʡI"STF <ꪡY{gvyUZFBR $RcP4NV3;7Wf O;C"+FV‚ 5x͙3O z_)On~7HfWoC3^7AK¯OyXK*.OSKSS1]NLTb!C-O*Ɉ3ҙV兙w21 OѤgB"ĠWd$\cNZ]qㆽ˛zno.]gmPt7b-GnҳIw$*r{o:V*EEAvgwǎݵV;nxih{䂋V^[d&D9,<}.G& ضu%x^St׀Дz*)۷oʕhB'yz\)݀@ @K$9r5a輖ڕC3<@n! mll=᯼ӧǏϡ"11'XYMk&TĽO$LVV6q|SgβRdAKvXWdC5iy%#%=\m'`fer +շ׸8<>Y_]!ڔy l?&7Ġ7>W7SMSNJH $$q8 4>vM0ܜ˞,yJvo>(+PYzzzU%CED8*',̶42Ui7= r1]g"Fjsǯ>4yOOG̶.S>>=@U84}cqF}8Z(f~|:kԐc×qmaMzM,؂"m!vތqژ,-<=qv>;a @/;8Wݢ3yZYTDX2[ ׯ_ǣ[l3g?~\FFM  CyyNv_cbf2~l)AI^4mw{#j|jV^r^NU^heS3i諩Cؑ*tJ} KάΠ=Ka]:M^P<n=| q9$$#GHZǛVZ&\d}[K@ fgS7neEoP!t:}ƍeeeDVX%AgƳF qJAJxH8mY%^?U\|bRrJV>R?tG?9#%qM0iH%#5$&!U抑Zz2U[-X8Hj')<+4Bl1FohOO/i{`uƌ^wUYlS90>عWRCNЕ᢭N{O-wMO($D?4#JM[m]cmgf{;+T6znd_Cg=}Lrj* g7;a{3vvǁ; 3H&v a^[7zd1'\}!QwN'/|74kI}U."qәCYmC\-6V-/ϊXfMշȈtܟ4hуw8+?-o0YVpzГu2#/3=.ͣgN|iQ'8NΪƵWEU5e eY_~6y抑Bb4.q3U Vvۅ!_-[ȏ >u%,TN#E2Qx 57Vk̯?M'z^ZnDnqj 6*"¾qXYmVLAB.y3lJPw's3[2=ANɨd乹^s3[Zw^ #u2C C*{+Fjb>̖"(i`GYnކoS9Km޼E)9QՖm RS y!ϸ  @$یbNyܯQ r "tLhݻa$+ $XYZ Hup-'+ LX=Jڎ$2UNQYMOo,<dih/B|y]v Hwk12Qe($,AH*+f-vl>F"qAl[ds") c:t!:#x+DaךAL[뙧v-On~CJ0lZ3gڵ@HU{8<Ǣ:M{54f*@ fgdeۻwC]J[ k:t;L4CE d3|+uwۚȈԒ)J}ѣ7STǛXk>/{UqZ.J%)P_(l6x!xWO0x,aԏ#Ơkb Go-^L6VigƓ6H_~ۅ?9+$mlW]2qkd](U;죵ᰉH0xЎUMsHaЃ4ƒX4zH7Y yŤ'XnDyQ B 6632".]YyNV$T$Κ:W%56a$urxL˃#%=\,WYj29F|7/5pO,5l]_dd}u)jJ7S1ˍ+FjkWpԱquU B)> BN[ӹscaYlwu8ЌW}5GI'/-||V@L-k7o6@4⒒B+o#Wz))mٿ+n;` }АviN<)++vq!# `iǷ߿mIZeK9?|L|O嬛7o[nzOf kUR/ ^JWVR,("L"U$C7uPtiI.n{rЂM~nc>>G/ߡ]EQާ7]KLQĺ>D\d+FIZ-˦b/(hL~J0[/lVTee%\R胤4lcI*(X *"` JdXI~ǃbNZ~߾:8@+HY8aŨ.lVS$N(WCVrOM)\1RKdA1=T $$ j6BvmݻUw?2wʔ6>ohm*.ǘ{t:}mTeɛ^#΀@7 Dyxg(_.aN,))BdaÆRB]!!{øUp#MO@^{neeSW/r2AHbW&15Zs~ɰ jI?-x:U{%)V@yŭs&.4Xm=>Ǒ=ą4Y& Q;z_&CС7YD>I <89됺 ֿ]#@nC*`hufP%EU~>ʅasͣo{Bbv+M' GJ11q}ϚBW8a4iH@z7#4ȏKԭC,qt^;II^"ڹ[tF_r((&5zzbn6cD_W Kb,輠bŖ|&"^@~ssFW}a旟9? 7PԙZwNw+qNTڒsn{|i`: }fM5wN%kŠ67p<7d?j$?M^c8D;;]Wh<߃B<֭OWSQRڶӀ@Co @  D|mhݧPCP0 Q>}#AAa{(Iciiy!(k8+AL!69zۑ#G͛4!kuVS?E~J^P\JbWFAQQ/kDv}͠R6{mS0N3$c/5E$K(Nb]c)E ۯ=3_ OHL+,e%;`аBM$ћjHEI\ ?ӳ K$!19yU5U)"{M;dIsΞӌKiIJ 4f WR#8{ͻFnR%]5o#~U>-ό?uSEi:燥 >._-z wMN>5DMq R Y_# ??W3gj[^EWa[KOUX7`bu۶,NgRUΥ]o=0tj'7kQ^q`>gQσgC5tLu[VMV{^wPy2>dW{:J`PڸCڭ09Z-kCTǏQۻ/RU3{dⱕS#^&Vj&.R hy9rQڮ5Zv 3OQ*+24UT91ao,/?@M=|uZ)) +JHٳڸ >9\潉Z_v$BnM 5 2(ũ޼~<<~59m84|r!&mqBw<ݔ^;]O®7?mP̀Je[OUiԔFj-8]ۡe;6_rcIAէ3BJGeqwoJHƑ=ȏK0Hc':U+EziE>;n Eݟջ~fdPC}ktY;Bg C¨nV[ ݛ@ @+ e{ܩV[Cf]<=IOWÓJE;ѻ^OOϫg+5L1R":`˯'}B JӥTſC@F e)h{LB@{\pWMIK;7W˼9Gǟ @ W{ŋJڵko߾M筂߰apBǏ...'O,)[o x #a=AGMzH9| G *_!Wy|S/ӨE\+gp&`ߏ>C -rO6&tmb겑 j ]]M rX Hʜqq\;n;Cj69~^2ڤSJ˩ʊJl*] R5%=d 6ZxQkj so Et#̒Ά>BHI=kkI6lNf:opo՜V ~%]ptt~""/ǖWy]gz @Hd*BB׼&ͼ~R YjUmM6'+^kK@|漵u'/$Azf# |؂gܠlܪflݾTKߜV\0}ۈ!6ۭ/ _"q Qx*|uj$6'B"xrkf!])s 6mI -L"I@Djj m%ڤ4\83V;nHWss3qIinW2kkk([11SNJ@[-Qjʕ+UU'8sh%իW[ZZrي&3RHI22ZpSRa [$[Ƌk9:xW>+'')Om5FȒ=F~ԇ_D6KXZY؄YKˉlBQ2?,cw;RQ>sCHDLFN^UMUJ>d)E ۯ=3_ OHL+,e%;`аBMUK,2}Or8$($]EX<=LCktMc|FAsΞM—4i,,5OAt_j#lPfz%Fn5rs=z[//j=t/ghgF%3:ڑQQ/^ؿؐӀ@ :@QI}{99bT+Y_p1W== ¿Dhև蜚:uٳgpZGm+DZ۰a9\r :,= KtOj<½ۊ D5GhͰ9JȱݜM쯥_J8T'jw#-Vřwxn|e%b}3ek3Q8-!m_jG3|>aU-uۅoRWVV.2лgv @{@vvڭ=w:zlWfx܉vl Тyر[ɉ9HO"1iC]ӧO XeX@UI;{;MΪ¯wW.%7u~EFV~-Ձ@tn.C3VZ8SKZRӀ)^f.c~89;% @ |~ Ҝv}Ȁ[WƗ6wDlM/+Af}7oޤsU^^o$l]A[NeQj˗ [oٲPf3J#@T_biI,wC@ #0mܸ3g^{ZhOꃾ奱lYyEӈO9:@ A&[ڽ,OfI[ B=gW>|rʴ4:W׭[{!h2; {maaRp1J й Aعw -G,x&edIWF@J߸sn?n\=}R ^bzuK`^vmܽkFg*+oXӀ@ :o@H_@˗1%&Dݸ19^>Io[ǎ êwyA!{+""r d RhsK.999-۾}+V%N~  #rيjg"$ggR)IF&\ n i G-(jof&bՍ|} -@i@,/:y}Cnji@@ IwN.t=s7l ~MN`o-,*ןv0^ 2o8kҤIΝCXp 8p TTT^h;lV(!˜ pDRQZFK@zCR@|&'?~ĸHG{O?Q]@+Ꙙ @kvS3@ K`윜'?>m6dvv4_y͂>vhmdV&NS6o\D[x-Ǟ={,--I$+2tQ?~MKmh-mUUUye|Hb(@@0*2}HbjG2?e ݢ/^-رs +׽tt2S=LZhb9xš @ @7&3v49RQQihgwu|M|m[׬&?_޿I8pŋX{p >|#;;Ǐ߱c/C>Iy^+ :OKz?@KV]ڥ,KM{$gSֻg+V;*![[Vp|@ Gla tO^޽ EWٓ' 8Pϟ?\z???@s Qv0//a3f J2,%L|v7 pfqCl/ii6 :ڦj}X>TW`@ @NHkǎ'-t<Y@EEV>v옞z2T(ZS޾TEѲEv~Bim]ȗ\c뚕~`!̴~tc@@h;H!X@ D{pfa`QӜ0 4 t"FGqNi Ue%L@ h'h=އZaVsXBTmIҥKԟ<0a)^A]Qٳeee acc[xbR0@읓Bb &@ 8P@ =s-_I253ֶeFSݳ':XCee @ @%yᄁy' tټyjf}|}}MLL 񞰳YYYD^2+X''K.UUU5ɹr۷5,  pp&ZS@H2: ]Hps:.lJzb!gN;?D@ I=QK;%!~d=d[ʭlܸիt5N@io߾utt dq)//u,,,zݴ(e Z=B`4$2 @.C`ngP=FBC;gPЭ-} L֧@ @g(*.6ۻgZeݳZ;4iϟ/[,%%Պ+p `udddyf @9DC4LJ6qx={0"111333cc=z0J*n>!n>$HvN W寔B f.N4 t.k"޽f܀aC`1X @ L v?CG ]:wn[@v"K7n܅ dddp AAA(5ɰVNʊ~KI/'zJ+O s[ĈRsL_b= 1h)(JՏ>&(.-#"-qeJEL`$S0 u>snf9$na}]nf< RsєA5Dp(//?wo"͐!C* +tU%9lGi @,j}8.E uq#/_җ}RL7މ-D?uMJ-Z%#;Oy򤋋ϟ?銨#F!bؤ+))eEUSv\EIIw L@ h+@+6$*>~V@ðEc[VDġUU >|xЂ(04о +t%[7=55QGRv IUL{(496E.Jۨ;_q}y ںyز]Bjݲjw-UASj f&?}81L *[lHY3+ qf}|sڙ)o-~/_2Im^Y鿨(9ɟs|!KG=_%\͎z"4k+ob=%ߞ eWL @3gx&Z0wA29c?w2,  AȚ@;wzxx3cƌAz-JTЬ:d[sZ,6[O/=--iIILBٸ]t̍1OV[Y S0Ӏ@ ZDE3-jte6ix t=No)2@RR8K:sGF& dgg޹x=&O6 ="4W jw[V\"x]1)' GeyWxY}OvK;ٰf5 XLGj9NbCx>Bov`TdyA!gL'zv?u; PQyy ܶ8V.2)+NY01)6 uLkKٸnˀF{%i=N*ISTZ0yѮzK2/(J^7sd?SnGSKQO AR+|TżG (cR䲂mVFSV0mY{EE}MWHdGyο`ryNj£ϾȨ$rqe%br Q> !Gz~q'7xރUYhWsjcP:M̲U|x[Jz9CTR^q~͸nuܹW/Fݺu M%TTTlM  zZS.(*2wtUDk?z^x u/ׅ#@ s TTV٢ubo^(/ZWS VtҦMփh*ݻa3 t܎=ZTTDW9sرkXOiE={>e]$.[݆IX$>I:ŔWALЙ;[:2SpA2;׽AE8w6a[OMsrKnwo9|x$[-G\^8k_u#8۝R$.CNT]`s=kͱ*ayKC}A<*j]D2uay6y٭:y;;yӰOL?(2 uZܚRṆs{c2fӓ/E|y99%箦G &[7.w0:Ǐ-,,?[@ژIp ~]wo86v@0NBRҊm}|A@ `ag|Av69',CpFmiք4119w}^patz84gΜ)--Ŕzzzh֠2eV a,6;/s"=ѧI+T6 ݥffApm˭~{b0MB+zr ?BIHy_q?qb>zQq!2<7DGћ@d#0d0oH3~z54ļ(!T'P/K,f/oz:=n`3  $;;zm7 ͞~qm܇Ҥ?{cֽGܜ1e^X!"WBear靺(kN8~<~XӋñC%g$lb.*T~ˀgyIa3F;+khcXߝ2+9vWLEKbΙEB^zM!1h{PxwZ}FJ;LkտFW\0NIpܺϑӗ/QKѢǎ600HMM엕YZZ޼ywq@ ڙ;3j_&{>޾ ) Cu޷=ckb΁@ "oCΌ$*& JJQUAҮ_}n_CC 95mϡCmWo_=:=bڶ@xxҥK}Fgj…hBps5nO ..=Vj,rbŊ۷8a鿪Қ9ٴfj& 8i ˜hEK6J"f'[T4Ι/L^bM@hF$=>-sQy&=`Te$TzH/[ob$_󂪌@Ӟ?I&DCkC/,[L $dxPȢ؛ubl˥ Ҋ/KOW2 }( | k|y+f| 3L/;)~~}IN>pnc>CɎI0 @ @%9?ťUdN.=Ȫ ,ӓ u%9i 7DN7+>'`ooQII+Ԟ6`BQHD lRUU@nVTH[ӴAɡ5gɌ?edeQbqa}mk׷҂f% K$;Xq a{F5M?dgx!7닝/E)SVK$%/#x%m,PY%\hx糧eA [vk/?\ט1I@gҰߦeyp3q9/S*b|v7n,*%J x'f\j.,8Dov3Dfr=B" 2~uID#X8}M!S*3S3ljWǯ"S5IaL>BE_xo`EGwV!*ܢﳀ $6vIEI{eG\=)xES$=ۓ[Dz[m&,B[ݿ}ޓ_SP"KRv IUL{(49Z\Qw78۠+ lذׯh5vccc4ԩS}!4v}lvK̕u mMM}:_-_> k9  :ާϜ(%/%*,[uv=hgznOJ`륞0}8@)A=zT Fe [||1ƈORRR/_3М0E^zx@ЪZhK$ R2Sbc!$D͖qrbS(hǽ&,׮DڢEFjP7RjuOfEЈwq I) o ~ŅjEm,0\एJ+޻l>>aechXڜY9ÇdyԼGϦ[,V[i=N*A.0ya]5Kt¶Emv;Q,+TϿCվx%=i0o ~/,a{c5+ow;7n޸qǃ랞hJ'r&0v5_FQZVfwݓ'tPy6tu%P+(12z}_w?P(rcf(1)N]:7OcXG_#(':uYO8 H$r$v88aJ5TT\lw_֛VE7 p|n]ڵkIENN#79eϞ=hqHd ƕ>zCCCAhIːMI9eKV\t]UIIVOb~5ϟ0m諗/cC+jMPB4Ղ_a b%W8r"zc1gF}xӾ?s,fRw1·jW)c4e]tCۜʺ@fj_RVff%MjBNws=Q[E{vF)"bswAդ<`$vt#mL̬;+9=]עX[JnN g W鉪 l.g9V%,oiX1lMP:M̲S?0vAϞ7%x9IušYIcWBB"00&Drss1VZО0 BW'ƣsr?{9 8tx…P_OmٹphJuۮE[ R8 XZ\9**z+P]7/=vesW o˂r.",Ƃv1ܼ|nWwHe {NIKk/[P!iuڕZtܹs`:.!J ޺u ^ aMܸq#>Ȱ(x-tFsMZ?x4|6cǍ#Sjv4GY>_|:vWAMɵǿtS;N-v5>O>9gt BR?DA1ѓ{s'ROSիZyAƌ$3$]#lk6m㦅ĺ dsjHlG>HߒӐj$aSGmf1džj:pW5Ҡcc!-0t&"&L">(/hklДٗT;{&$)* 8Rm c_^A141]ˬ\f:YlwvߊG&PJ;F@ wh|hQ1^2D[l3ر05 "SN7o 1 @knVSm67Z{>P {ϣ^Aa º-Vna%(a>k@}}6(Ͽx,Taܗ ]=ʉ'$0:=h)kӧO 9i۶m[f taP2"@r7T~l 1%HU?=5ccz̼j=(ό?uSEIe97hK+mI-Jbj;Ͼɬ}㜂4w'b 2GER_< }) SW5_t%խ0:;^, !V6&5wjAm]I>2^A /d}248 )φ|~n+ N3+t4 B%a&= -Z+ØhԴdKfvn=u1grfLZ̔~|!%lNu|m5N@P?fN;bӚY $N"p{?~mԌg8;;{…K,9t;sp Z{ջwTfd{{ꊱtoΜ}),̝#cJE!1Hnb#]6qy.>]7V>)a ,JLl{0J tEpdˮ]**#[j=swwGa)N?G@yy ߟЫAYYY-[ VeʤV=h"l]u_ᢤ7gꄌvvuED62:syk[i~K?溳VZHMt+6-[W{:J`W9˖Ep<<^?3भE۩3f("+r3&|x>[5@ y >Xr@Äg,l=+ry|{{cՐwgWax=UhvS1} oUYqĈppL02nɑ'TqO&(]>~l=x#Þ~_A<5ebA:O;]jKӝOzI5'!]:$GA~>ڄNwE #2,>XϹi \v;}j=N χ.^uLɹڴCV6mZ\.]ӓ'Ojii .GD$٪jTV.:{nrb-c??zEO@O l}߿Or@?eS! 9agxv4}67~$Q*9Y'C˷o+"$l<T4a)hXϟ?W\c:gFWo] JJJN:r)))  6zlls߬d/nڝ 򕮿(EącW}¨}fc( [bݸhg4 ~qA<@Ag  Fp: ]ȋ;d[ڒ_4ц޺|e\ Ī̬-AH)7]c!%tK3_d"_ӿSbNY*l0^Ü- ]νz~)MT|~ʃ3:AÀ'x] D53Es5ᗕ cxuOfEЈwq I) jgg:M̲\-P bcSf[PŽ>E:{xxܹn~ZZ̙3׭[@u[`X~ $=CCu@풻n2w<#W.*1L &Jg.'ha4D6q $W~_DahbL_X^E('?roS'N݂o?'dB9%$z Gګ a<\^|Mέ"r1l@ī ߼}KRvA1;ooijb,;.F(ad$O#G!j؆B.,j=AԜB>!hPYQ9yU)Q?ypo\>-}; mdibP{yj7v;^ lFZh ć|CZgΜA]ջwS33n|]+[1aaC/))+1.nΝcH2d5,[&%dMZΏ6 ]77&P*'Lĕ'kGU&=uˡQni$Զ\FOV'lNX.&gǜ=;kJ(vWg? .Ֆ-]E_*u>Tg٣G(_߅9~ea^{4)U~=v[7{e>;zıfF׌`%*& kȑR0)Jl]KWtHI\ߺiEƍ֊)9J?-2i+ڢR$AS6kU}@܍ px{Zd׆r85NlQ֫ s $\je Qѕ-_͡Z(**ںu+脮w))gN:N&<"c4&ORSL4 o].?zKRjIAPXLN^q f3@5G;lLϚ۷~]V6Nf$)LݻbBt)ɉ1P W{߳pnD"Z,\ܷ D]^~VTz8 Vw0Fzf# |؂gJƭ:iKT֠hAp |eOA_ҝz\5h;yXc.L'$YL5DUߐsn{|ioc|fssONܘٿ_z2P J٩ -}"O;/g٨9g$>_~((&5zzbn6cD W N241/!^K,],,,BOM3;[Vݧ}:gsRl5]WWIUnTK|}L1X5$稻\LN n]?ӫOi _xa5ޔRw^nTQP* Z_|9Eqnn׃|_hKuM3 WW|/{F  HSǎESNtFl ^__?.oy&bb t_@܎=6t 5kJ 3a)hB@Bn]#7iFq㞄},@f$vI)<(%9:xW>+'')Om5FȒ=FD/ԇa?/i%uژC:3%D{QPT[S}IqkZwDeG*J|NUX\B qɫJ6UgȦ2cK+K>{5#wqi9Z%=@~< r u&XQbgEN$@jo;L(<}tHS޶F6yD%c{Hgn(r!MuPm{z^S3J*}uH x:mH~F<Ρva<&W' 6H2!:f&f١n$p٦cKKJ|{|/;:5>d0a.I8ZM%P(Ν0=@ X9cx/>Ul۷ ︺|Q^c!o`Đ!Faݛ:N %O8)K f6Q{{R|_iu2C(-.N,%5 J~X/&~TZf?8.{&:aIAܘO.e]Z,٠6RP+J%X r OO`Iwo2jWd6)dÇrdg|UTOf#w}Rf:O3Ң ,*߿gW?sk3e-Zo~!z9hATWV׫ꨃź$"Zt$'{a﨨 ?_[3tp隁7IW:{ h0Ñ;6mZ@FZ_ +ƋvG ENyyA؋e6l+`LY!99(UF]^Çǔ t:IvFx{/^E`âRevI< #Ǣw կμyF;G gϗƦ%3x=Jghl^k8^O0IifW -۱r۽:CN>}ƘR=*}{VBZ#Fw#?Ľ8s{(qhEK)rSD^T=SSvZ)v%\w\B5.(Vn=BNO ԃ>[e򟒨Oϟ=ĶV(Lse5BjvS1} oUYqĈjƒRrLôue*'`AC=V[? +daĈo޼ٽ{7}~cǏ;  t{YKi']C{ <.VV>={9r7}{bI)  o@j,c W}< ou7Dv m>k,T 2K=۔ԫ?ժ%VUNJ^] r8l< +99vY}#So{J H}|ߪ^Sۙ;^uY0*0EJwaΰz :e$۲Jdj[UvȠ3tSl$˲]{$,BiѶoF;,;8%0_{@Sc7E9'x(+`g顦3#ů??Ǎ[K$4d kO?vvz7Wҷrfjf~|xCu-|[;PLN}xI|1!($:c؁ ! 9gSǡҜQhXoQДBj2cBkճȋ/XpC**fьՈ$O戴3锩Kw_ߵDlxuuA0 ~ vsvJfff(';tXUڵkX/k֬ٶm  (H5]"=`uKrYI& IDڕGΎG8G->laaCZϞ=}}}g̘A@xx8u9AFƍ[n`P!<:g;6)#+`ڗ#q~altˠ:D#i$'=ᅯ{hc>TڠzC"ILznhV OmLja22V]wtv |^Spebu$m'CU,xg {)r;EGD/"5{!! p57w:-BB@dp2U5xֆ]Îʣ}'ԳSג@Pm֗QwH\CT-^J8ѯDl6r';J]-(~F^i= 6nIMn_JA14'SN7o].?zKR*ڲWPXLN^q eO>>>oo磡[)))h;vh*!,a hQk1Wm==4%%1 CaCZs'o@R}L@ *S'@ @iAƼa($4BJ)E ۯ=3_ OHL+,e%;`аBM'ћjHEI\ ?ӳ K$!19yU5U)ŚV3NSs̲CCy͵G]cʔ)h-[9s 6,Dk]E %0cMT ̝/{ecYY EFG|#aTFbݕW~N/uaa N~z_EEkjZ3ZQ/ tƊ̮!wkm9^/P<[GftZ$iIqBT/JoTͺ-o'˗X}S9`}094fyXa}8i5uGbbb1A9UwFYh;֬uL B%cb6M:e*$b-?Ƥ^u;jT߻n=s^4N'&yzzUʰ$5>"]\\$8Ѕq]W.))8mڴIPPaPLVU?Y[źg gY:6#qiD6`fS!Zf0r,z Z4Q]aX?4p㲩/r?ƍQOV'fc9ݻkL7WܹS\|؇TzR0_$ n o rLz쥑 ŷ~?ԡStLPe 櫿RNfՁ \ "z`@L7%%K.cUUU(5au>}FhJ^^^@l;'["!&\L!0FlȆfsK@mO؋Hj6,'nLJQvf1:fb߻I  ˋ?'%g/)f!k~~#e3쫫u1XYYYx_SЁ B@EEZq*..a @M/_ɰ(J+tc2N/:[ Ht˗/A!(((Xn]``'0=@5 ,_~6 S|/I.'N26bMo+IU/)+VT߿ -Dv9v:40oF?{WBJyMD_g:>5J~[ҫԭz>]T;pNSԱe}~G l\j*&j̛6ehoZ|RJrKhS KGJ՟6v6.ٻbHk0Lj@)EA2L9^ߍgJjA'ޢtMoWW}/#c JKK---:D75BvG@ڞ%,\t@ h'~;@'ؤtڦ#}վ\lD;hߨ;-^xĉh۷oAўx=@}>vcNǎϝ#׷/ P ؘك9z鲪 0 @g [[RU"OZltO~ȱVOhı[ـ[&rewirY5K!R>!7<~\@j~9NUYaثml7bFzEbePL))*D~% Ơ?"qqpp6}(hdٛ3RYwK*ȷy]3=^0 @DGG{(:455QgϞtz8 ;ꚞ0R55;v̙3D%@݄9lGit_$p-L)W^AAAG͛A,4pҥhWB4ӃXؑ#WꞾ~XiYɞwO`5?ѲQhg^y C1 @kĹg\8c͡[ԟ$k~阌;uߌݤ֣v?0ڳoJS 7:[x搇MVQL!Rů.rqZa FӡFbtXY^I!_FHjAc _^J=Fñjb3N$OR?/;#vaX&ftX:{XDK<|0=MŅV433 /;AH===0N4N C>@ @C@TU?XiEj3Daf͚ɓ'm>M ={vqm!^2`),n>|;7Ug!!3f ZS4:h\$20@BT_ d ?Jĵ1&6v$/ e<} z{u5Ҕީ-)1^|lt5+<{>z+J>~|/_J(ESj_\W=ܷ:"7o$'Mi4IE>G\^MZQ^Vw'ˡѮS"Z㲃75'ŰAk Uծ rk@u^k׮[ORPP@ 6ufUW$.www4@<}Pjpܸq]1:$ 1!&`#69\wLHbjmѽ_222>DŬ`333͛j*!!!L`bN,؉q:?pqV'όff|}96Q,u XsY{QLOmm*^o>X&Cv7ڐ(3Buz[ sRo;8UHZZ[X-#JQ>rWA& Mݨ k7 DH 7 lZ!ϯ6L VA5*K/L;jA9 r!!!6?##M-2藟)f`ʕaaa~|}}=ztԩiӦ !FWuSA&{C.VY=p!RR=<^=@|f~;V @  W$9P,N{D AjRZC~03ի".߹9anŁG((`]3)ŽD|}/z c|NEr(9Yf{VtZϑ35exZ4c %b*FN§BK!2f,zOJMjz:%\(዗#?X*C[r bJn w}.?Lbh_]h`4N;vl֭t+Irrr:88 =sxk(&޼yxMXכKK`}n@a?>G@YY9""bϞ=aeh[XXsssѿK{iYT/޾ XaQCׯΖ уM .ͷC̨&5̇?#.=ŬԘXCFlU{o2 4v>Kɬ rGUjJQ^m;9M$yTAYc"ј,wDj[^Vל0^wƴ~ha̓Kr֩p84]},Mn'WPDH!N!72"?$ʧvS^?jqM7`NߋR 1 PZ;yyy???^K<| 266633or8Ơ@gX=p % C] d2ZhΝ;hzݬmoތy)S ՄaA'!59yyTRSlz 6Vs`@% 94,~̇̚)= .Хf 9r(jnq͕w[s8b|WX+I>J8~༾ߣ7~Çy[f#I`kN=)-ݱj╓\ +I [[ZX͇O_)C Ba%61(Mݼf>9dʕnrk/$NI{B_eȿXZEAzÍO*)EFGS+HG.fẄ lxъ+w]n;l 4,i A/^0te7olddr + %@K&p.I{ݎ;<==Qj!&&fHoccAH@OKq_czlNt +پ}/^ nn۶-T@.N/'SOU\;- :XN K~J\$ԭ_bʚ8]}N"swJDv2mJQQ2bt n\=Z=z=M:buO\3>.juⲍ'y^Kd>]zReO]ڸ-Oc5MdZn܅O.q8O=0|҂Q]Lf9.JUuYiAb@䝭1*;5K8sCq3A/X8J(hn=te4B~xoS1LhaP;w ~YG>A玝_@4mt!PJ|p[e(A6uu1{p9h#tt)t{^TT7ٳg%9"]@YYٳg]$h`ŋY:8@k?j]@tǏF;0(--EkS=Ϝ9#''U?+_ B >.\]qٟg;oԈ u#1B@%{37~w~Lʤw@aW??]VTT)aÆlܸ= :ZhtnOZZR$ x(yT&'/3:vcY|K@]VM[bZJ [HT^ddko(JaQq?kOhoRmwLlS%+!&1@n8[  ɼ(Qܛll]zsjʗ2GaU{ 6yQ 6԰ȋoogMֳnofU [Ԥlrb$4me€H 񫸂CVnИC8ٰjBa{xsQʩ D ;\Yޓ}Gv$WS,$%Q/u$o^]4[L^Ќ'P+V']h///~~~:=v>>>~1yC رcƌnX% ]/\nڴ 3 [344 DKӇY݁ R/ԼLm_Xhtݽv~Կ9he$jw>cf@w@!@5vTnW߹קCSu5t4zq/E[Fˉ'Lh!F@  A%N8 @w#;~ 6ܸqۃ/_׃ :ug،+wӛ:d;29S&22s0EyS`@Gr%9W,ˌ?\K@ҥK߼yC ?o߾tz8:([\L[?)E  3Hv  $$$|A~&7+!oݕ@=LL-?޾yX{:nc1w]ʕ+'OvZ4w.Y#Gx=@cX~600QBRˉ; ;wЎ$'SEbî]SZ  |_6&~o1X  rrr6nxUɲ/^=z44XggK.UV. ޾}zH#'@u CBB=m6!(Zh):ڹӃ:9cg7A_Qsda߶.z lNܷo[]?imh &~-Ҵ5갎\79@iϟ?_lYJJ ]5%B} x#ZnIjD9L޽n9@ AF M>ثWF+xhchhɓ'z@{2rԎJL}x{ N@qN M0S8=fi# xsU'*W*(z ?Lj FPk GM wrrG/$$Yd ^ r& w(Pp˖- C>@" tC@4999 رc~Y3g_,8׽8ov߹԰>}p+MM?s  ?E =1ʭt!I?3L6٥@FmA z oܸq.\4 t]h{ J >}afffh=z0J Fx`ihglsssMMM7۷o?~=sĉz@vXs'%e/_R=D,5.cٓ Ёڪ$ׁBW@ P (??7޵kWANPPJ yz@ y&k411qh`z@X@?Pa}eeE=swOG7O͜<#WW.AY]+Ld=v%i? *yyyh1I???h tz8rʱW^E+FGG3tkKKK 777 @ A/}%akk;gSzLL (V?{ADD"٫`ux=R{ҠA#e"=R_j6*>`?77);S@' =`ĺ#XO EիW˖-Cm ѦhA:=v-Νۿׯ_z>x`+++}}}aP $@.#GFFFL!}1GƎ~޽Ӄڃ!/=K5Fh=x2LA6ra2s ;wU׮4  @Dݳ%yك sF' p˗w`XO-4z4bNYY;鵻Ҳ^|{60pN:79QQ/jfuu”1ck @.B )) M|Iۊz/^4 t9h#IR.4gΜ Yȴa읓BxIX:gn`D0-WH+5N)ڿcLJerV @'~~BP( TUUGmN@z-̝|B@BTC3.&fn[b @N'to߾yp£G K@8p̙3 FCЀC6,M r87q$|n&cq"s!ZbQMO2n/:q@OH`f>dTw`A E%v dTk Aصx?H@HH>^~}f&TBtݸq׏O 4ed,׭{0"Bdkƴ>P{P|܍C-4  @.D-lggWYYwkx%]@||<;TR˗/߾};UN(|B|t:8@s@sC@6;w.ژmpuP4zmٍn`Sn<Bj[P}E+bt5 t#^e4l񪪘 @ %DѳgUSSC |ߟ.nnnm۶cY?~Oy%Wt_#ƏQସd`RQo)dRQIyšIt;JU1q_ɯ"3tĈޢu(Ue߅#=WgJǪ,vJklR mؕ^LlXa{ob⒒R~旔Up -3\u\.vV AL`  БĮ]6Z5zSx^zaz@ pqy5`fjf/f`@D;w|{=ՇEfo%%;+ @ްaCNNqrϞ=h^2xݻw)JCo-[ٳa)i޳۽rpX9E NRF^/?@M#9=ޱIUf$Kvx~N ~,QZ^m-S+f݊7+13 UP+9DdKS-8OezT=ucҩ+(h[6k)~Nc~S[qi'04ŖP&|zf!TwнKZ.gF;/USBߤǭlr ie5Ck]NG C.诼ːH`6]ȮX d{u߁(O. 6<"5ykMBM1@ khh[;sm%%Ç/^ք 3fP;9,40׼Uvgbz'g @(,,Dk>}y޽ϝ;7i$:=8GY?f览)XKI)`t!>WҜԫT]ntDBEH gX(~3bW3?9e #ǭˏ9}TO/z a#Jn3wIM} >hŖ_% *?< \Q* ]O)3x'AOA]ͿIYK HjbEQljW)yXppF)ʬ}n\1W{OleÛŔkw2͹lȿQ)߻;)Nroie7a}ߪ)?Z{GW*%c#wL==PnZ~.?aNC @s HIItSnZPP M+\dI@@nعNB݉ A]sgނcvw,$įy{O^l؇{ۻ{ @tuh J/_sĉCs_=D3ѣ  cMDO96n܈2rJJ=4*sR^V JჇZf}5Ѩ^SBCv^40ANAjśkwO\6I~BųuYVZRVKWᯇ<<>՗ s9 uUVVFL=;*&}ê^f#UM_B_v "g}H2#@t2 Zԩk֬AA뮠-НҜ9sz@ dc=o3  cG@karUUIiݢE @ jvswwGw辘ռ@ͯ_R>}bXm1E7xxxV`Me1>;3aւ3&AMg1🭏]( );l٪~&=y]HC.Jۨ;_q}y,|.$ /wNGpCTff6sKg.|vP@ԩd)婉O?N̪Π;kc˞w u 6Zт숫'Bb^~tǨ(A,IO$8V,""Կq˭.-*+)Z H/JLbcQlP s lt͠Hsٰvp5t!ss#R)&e& wϏ>ίWJdTT_j2Eռ'8 6Tyҽ$yʋ >\--:mb4o#@6cԩiXT[@LC{ tz<!k  222>.)'633sܹWFwMBBB1 m@}+W NH]NihX4KDFEyiw1 @ @eG'//RN3(//x񢳳s^A.]ն?jMx{̑j)ި> jw[D TP qeov`T)AiAX|#5ɨ9dX-z!!:#:!bH ~";L^ƴTo:_bIo? 5 B6~gqmRf>v0Ads5V~]anWTNp^䂘˯DUWD{o/eϢWk*y?L6艡3uo)z;:6ˏK(623I[ೃ< gB$i'%dG1hjj\2<<̙3heOÔ)Sz@+pM\@Q9sdi5hYF':PV^gb&xg @:'&)|Asss.I 9Dd!:-`666X\>䫴i" 7h3w>c7 jR%lM.28)DLou"'cUX ,u$ipt Xt!{Gq&xR%I UQTPRJnN g WY#.螵XaMVƂqt5SCcW7Cff.2/'n,;+: @(*)ç DJJٳh := Gedd0toԨQ;v={vW^$%ȖX:3R'+_:3t{< -rmj gy}5)G&PJ(IgӫOVW6~i{Wg @D}lҭq߿ј7oɓ'pj@BC&MRbʘ|)&^zg ފMN Қ9!:Mkfm@# BXt)~SLOv@瘣.L[ДRޏoR!%lNu|,h˦3* G/_&\|[;%G/WX]~55Hz'6=t͐{s1XKҘ۠@f6lXDDĞ={ДA 8ׯǏ߶mbŅA-" ."daanfiyy1 O@cԨ۷٘.\PUV^=Ӏ@ :h7zϔh<լ(vfGh#GA~(58f]OI)S($%/Yx%m0\|YEPPSXXm(9rhFdh2OYY{ĸV۵F/dYX}1si \v;}4Vqt5S (4QE,e6'OE9KwmT8w+q( >i40eW"RJ)r˷%v ,{j1 sۇXjU|R=];b+"nش{#1 @ @genK}(!;Ȑ (&hSNMD"tuurGÇoXڕ5SvJi)Ɣ()3e7A&66+!$mp{R 2mŵ~߿Y Zd/_?|zgvjfk]CCÇ]:D=4qT/Z$l[˩nh0ci vBU^ﴟ)_bOdL'LzD+WO2_~F;$jଫSJZ^a随tJ-uєcNf>>;%inwOHvS HcWNJJ¬w BS 0=@+ 1^9jJC['/fdjKC:rs.%ozIt3"N@ ݕ@Ii#~~t;r"8d>}rrrv6l777z aii)##w/4f('A5Aw-8'gDٴIN1_BX x`#?{]vZ<7d?jgϟVHh8}{J٩ L;n-5DQ(q2]'uETsj_A6]t\((&5zzbn6cD*W K2n1dr0 vlZ}͠~PH8_ !S{ս"ڀ!)*2yn/5.3Ӵpm]f?Ι07ޒqr]@d . @+ hhh'NTQQAn޼ _2+{3kӲ5GF RG K̵Uُ^vusnR~3@ ݌8_5KC㔓(J!+pppu~"1~~ 6zʻ0wjAkF]I>2^AaY_# ??·KvoBr7T~l 1%HU?=Uccp]K_?iQM~/>Bg3!X #}s 3GN!gF7O e|ߠ=?,>7g zt(jb=_uiOb(~ ה QJ_ݹt1EI^VD8gbWQiDŽ cN`6Ug̷SS#Wgq}r2{SlZq͠~аO8@a+EHys .3J-撛0Z_Sv$Bn͸功/ԔF*&{2ҳXoXh/w¢ydLz]2Ԕ&8}ΤwFFŤ5*AߊŎ!Ab'@t---K}>{lذa\~=܉0]4mjD?-+xd': ]͍xj~U"i@ ⵂ4@ HNMP?_7mS4({=4kիW زe WI2t9)߄w!֚;*ByōSG/+]QD-FOpBJž?dm0nCg4 ~qA< EVPvwqX_v]y^Q=paKl5UY[D_wJXCٛ k0YuᖞkGN=652C˦   a`]M|ղY0z*j4-E*.*r~DM+lR%-Z-Wھ 8j8 j*y:RxaXTwtJ$;;t ` """~~~h*!힝]7񿰰pƍ'Oݻ7Kڍy=VK (]:pJT8, h6 tЛNsif7,M hvl۶ T˰BW y&Rf&&5rvgIl^[.0ʰI6lNMk`q駔E5 9뢎IgzH9BƖ~z"Bzb#5_?O%dq^{ 6l@!]hq#0Uل.^mQ\âQs'n`j2aiەLlv͠p~PH apYkv]8>EN>9㸖fmˏÇ}}TET;6" M[RHv. &L6y&ޛeee//˗ چ9@ @$ tGCNNNqqw&p1uQ}_PJDEֲ !k҆"e)BBhBe/,IJ!mh_|yLSZi9c~s=3]?6l33_gU#AO^/cRxe3P贅]DNOw^|V^:cK)Fw_V:}\f1Y3.;_r!bhyecyȒOFnk!S}rQ7H14dbl O`H' l>ޤCocS3Kq&~AI)i%e%1\~pJ OL),`df0t؈1xZ}Ϯ63I]mPpꌢD Xَט2)"gZ䪭?.(ef)07GkN2E :wiOō _N1V~2z (w%p ͘!ݎo:fo&o҆=nP__ƌ֭fѣGhw .H{nzgXhNHPPBCҫx!k{#wo\_ $@ J rvu7Mt؞m[֢Җl=z lI]Kzw6ZF;ћf!*)r;&'0Ɏ+NYGduJѿKn3Pz@i(EEgHQURUGoy;ѻ$VHȵГ @ؓ  ggg/4Gɾ}fϞJ@  @$yZ) #.MMMΝk``H鈛ۛ7o\2o=GkaH<<\^.LDAqq? ֚= ~ @_:͆Ă|W.X1_9RHMME/_.+kI%^Qj@+DSLRfvFy|m, 8 i' 銪F!} p?{/0%ž8> 0,OO Tv=|7axx8Oh+/_"rH-7nyFEyE\gaJ:{#^;|&"yO^=ߖ @o=""Y3^?%6@F@BB‰'nܸQUUռvFFF---m $PMojui$ɯ$-,+sS'uQ8SWzd5iiY8<~O@NNÇG^;'&&N>}Gacc-&&&gê3Va!%OZhAph.3cǎZ0:&@  @/^03;ZV^AɄ騍َa- J,$mkk{ΝCm '@ٺZ?(Bv }_no$5R?T PСC .D11Mc?&ZC {CSǿJ`e}7j-/ض >h߷$@WoGha$@ m܋֜iP[/ ;vأGh}%Ύv133ˎď;5g][GŤF;u/n<jp1 7(wBhKBK;i$hpN}ֳo\2BC en?qϟ)$ 4x: n @$~3i5Zun?;&[))Z(HHH~_W*v#}E萷B#D%ef͛#!4b^ZoII)yE,\J*(M%5E 2~{56>3wU W|S'*r1Ws}|qkE|HN,e()8^e8E >GԩS/600@`CKx{{߼yS^^C ;dbógO^aeee$ǣp{yҫZ @@AΛ;mv5mb3i楡kKljm5PB茠 {YܶmU!@I7@Xmq2=xmVYM{UM6/.6W\͚WWҙ3箄}l*IJ2ݾg,Ճc )SDDDٳŅ˗/hÇR$hQj34 ׯz?$ Fʯt#hfa'LBI`h+klÜ๢@ i˯^x2]m6/sOhp(.J#ÿC{R(4[#ZGt׮]hMQV1- B oM6@b]MvrscFj7[~ͣ+ωm3&N 3lj = }խELgMY+ݏ[_+Aѿ^ikV͢oXuE;n.9髁>G9?M%\~}jj*>4bt޽O|8&D&&r>o3^eImm-5=rr#3^vUa_ϜbScfϚI @  |auy^UU=,EF^V )-m\dp&&Çv6бJ{#3ZΝ;111t:t(baaB o݋31#L,(rHfOTPM/i\^6yI ?~>wvp }LD V_ڹ9TMĪ* }QjI&fX)*,@Ys̉D될E)p $o(tdI+@uuo^߆E){o8~7o>P[[cIɁQ9@ 9D$qq ;wxX+Xn7Y>>Lh97n@?[,ڻzjFd. g`/B J+K sK*I瞋_E$Gm>-@w &8޺1&9"3#U#'7k;RpCP|v͌ ʎ?*IɌ-/3baN6$dꩰ7E03PC) l,_4Wu@V&\ANF̗>rM7 57PF.?G^z(sIAϞH/hVQsǃ9ZbdR@ht㡩ill4۸l˖-^^^׮]3텆tgd4 xzS_wY BK=J(%%I^^5]['v @vؼ@BB2eW̝;mָ`MƍD0  '^ dfnq"_"Mò] n?갖|`r3C`I)4Kڜ6>hldpj%եR+!%KX\N3-7(HO/X8YXs;l&"${Q?68pU5F!S/b- 1r x~~~G>{ڵka%~;r:s4Fsq՗/1ƛ,o\{8 @_psrxԼ-+8peVuuAk.-(|\XR@booC)SyZ. '^ u\XqxX,)ffA1rrJB\hSK.brHCV.D{~Hj{vNS.C}Ɋ.G֮ۍ[ 0IKⲲ[R9@ _##!@:n KFFoڭF7] ,F#n%bt+BР*\C 䡝޳щյT Pc|qw܀Zz6|_҅U Tm6۬ 8yc46 uH}t$KxN knN%M "9lyst'EqUY5١/& 6044lQW\I)t"Fw=v~UU k VCk,Z'6,y[X3f y"6rXl  @I-Q\\RoO`an\o>غͺJ?j^sc uD`\hsk(4<$@ طˏ -*ɏ_+4iC;~_{^4n݆0A|jл'8zyT]b^C)fMtT\:CsVMvK-g_O@J ܶ%;S&&6?a| Ї ?m{`ffV\\4//oժUhL~E`Ĉ{69v:U/xЫݩ_ԊU7 y2z*=}֭]\Rq| @Iz+v޽h4~~9v'NVV҉+tmaav`ZEHWX= ҕpV6]ֲd{6\B"m֖Ξz74S>~m bXZ.f>Rvl+f! ^D Tܰaٳփo߾ݻH‡ .Ckq_&r}^!Zt9 ҳH[r[T!VϜ8BlX )9}GUc-@ @o!v-6o~s5]M'Gkn.Y oFg}fff===ssaÆ5 gG/WX~OSWY[ᒓɵפ;YA!|=4]fl9֥ I)m46:QR\W[X#aVf22agXQQIg?NDi:@ 0d???GGGIyy9L ;<{,&D?!hhX{рe 0$/!**t?~ݼKDZ.Xw%nyA@ 7 q]kwIY19ZY;HӧOǎ򪫣T-3hРVŀ@wPxB!Yl)its{Dj|n>{6J%]7kN] nr6^EDĥeGO3Exq C҂BR&Γ$m맴4Ef߅;a[-@ ۷oWWWGcCCC)\~]6c J9k.ȗݻݲmmTQhؤioyh ڨ_"bؔH9qxA6VVL @C@u')޺qVRr`B DA݂&&&;v1t  Z$BZ4h+ZqlFNAvfkIU%tKd}Oo}ta Ol՝U).H]B3m$%Xny,V6(\wUP)VI-@c>\]]if͚yfΎ!=cfqɺu+gLއ۸h5ڽʵX#/kF L%Px+/$@ ;?Ͽum6;cgЁPPիW(4Om=5]8萷B#D%ef͛#!7OWf ItU凊oEq/_2*k9x-?bCweSyi _%%WVs (4aԀQWtj:"Zߍ8yJJ)W.Bu(B/lLLLkh~GAn>m.Ml7Gv`4OTTT`KskJ jN~jNӋZ a"ˋt |נ:󩩲b+E{M Dq.qq*0 W}X8 ;K$ xKl,}]Z#B 6FF}~5899'N! $v`Vs'MϷo7@5[hhUUs뗔j([Y@ D[XeTcT Qhf%vڵqFNN;'M$eaOyq2KKc2j]my}l:(O{mBl$ʉ6[8saY3y۰'NND&WST0hNN/qvϠukM^DT6nT9ӓW˨+3tXu_^=iuHiuSӐpeV;-t4zERcY<[HU&lOVC{^K ! lԜ|]¨6i0>x`&-NWwmQ jH6'"3g] I{ gn߳a "իPt=ܧ TY3ǏᨫɌ. 1PlM~6;hQj]U&CR2XFyqyc)- UK4LN}a?iոQ,>S*l_9hnpܹ2<ܬ_>}N&;➅)i >y$tSP 'o[{3*:޸vg/[5'D!wUgeztL]@ iJ:*+00(VMAvV;{FFoܳgڵkYat6|C}"7Y`rfЧ)CR%؏oЛ@SF},1Տ\=X8W}eڬ<+ɖ@(EaMBwA'N@{*ܸqcر}ڌUD򨿣ίZ4DrPnuh>7kNM fWWܽeAZ @ emF:csgN4I3F,%[n߿Sʱ4ڼB[[r!4,~S* 6+|bl: Nǿnc0AĎVd8ZÉFԋ֖֞ZQA\\?04ݾш@X(Ǐ?4dsm)W4mغwu]g>m_aZB7MApnS&={1UB2Q5gF;ܜYF#N#ݖ9rbm R**',.z&eͽG~ې?Wp  @ w=ڸԂx- W:uׯ_tb~Aaq) *'NR_:_m0O>#HOs#.?eIjREN +VZ9?s + T;}jÏpAR2cFˌ&>@8) ٤Xl{z*ؒ{ɺG#o)(,-/!-f E$Gm>-}O6]g#}%Y2:?Hv9Cy8K rE||EzA9{|\)`edH!@xNdl iinq`ڕ< e+h=xV+;ĭ֟S)Uk8&00QG#i2p :)Y߲&Lh{zH?Ӌ'pi5٤ڐbp⎃'/XT-^oyٌ1ͯ2dg}zʭ/?i+3JɏW_RK{<$[O vvvŋ[.99P]]mee[@9C8y|7?O=zbb8}}]h;_ѮE~CFfXJ2@ @{ $%ko)Iɩo9a[={6+w'NܷoזA{>S&K M6Vg]Ͷ\(c#Sx'-1=.U ijY]xh|zry`çijקd9cE9F;~Fc{w-=ݱJ^pׯ^Y,=s:WPp1!%rY]B!d@OI5BǓT>JH3rzxEaˉc("x6K'ˆgxƗkwܭΕLXϠnCn.{ago3^0K0rY8Y0ⵦ@۶>00;9^p:9n#\adMp%U m #FO\.O KH/sr "6OZL5"@ضztNG|~4as~k:M&NMGn eZ[Ἔ.`i>P@JC| QN+&%UuZxM7g,3;nJ r劦QFFRQQ=~Сt~c k]w}ԐtNǏv7߽JksfVNz' =(@qS[4ۮe$^k3g\p9 4Sp…(4vh ^J`n+F\a#HB\U%D߀Fنs><:HE]sx>5Q?j|g3~چ8Yh̾ݖso}q4ޅt,@ #]^R]g` lOJJB ^v mvt[b ѣ炤 O zd'ClBkV'IsNjYk9/$ݔ.JXugTM0 ݦKNBTtfg-51-m r @=<<,Ybbb[a7nB 19$cL69s%5 řL{Q+6y˳ra1 ]jf|*m"[\V YK |ܵA2&>e;?@Q+0OWxl3!:;~{u5BLLL:::{1bDv 7 p `k8+JJ\<<3pp[ڒS5v`O6ݦ+v:SÕ4wu @#+WN6mÆ ޔP||| k֬C8|hǃ22sH-z ۙwy>Gw=;h%bMe/ab~ed.*)%21pr6\JQy Hc0EsJ_~+wAqU W`9y)&Λ5EvixI"/Ny$V:n1y1~mUnfƏ?\ H4ܰ7--KP̟XcܹӸ8f6qرЍβ_~Fkil<xn)I& QQǏ߸qc텅XhX0&D u5:۰6]`&&DO#ϻg퇲fvɢO^j谅>Y0g՛w_&ݞJ)9ukvnZ6E%R8rQPI5iyyyaAArw~3s .ӱܽVN7\WK*3m/W5]Mv m\.N,1kYӦG3:i?>zgPy{9997mڴsNt<$@ u6k sn}/<5h,˟ [zjN] ë8is潾h B2(3mq(i]ؚ {{@K-,i``0c 4իW~юrHZ+^v_-(4ڞ47a3gLھQM*Nl;z'C&5r*+!n ncf(%ԔW2 h@dTc-f)CAA-" 3|???DBlU!@F`rv%5d偵C8y ?޻k JYrM%YS=}ٌ̄m9gmϝraeyJ 殺* o?r@ @I`/^pvv677/--Ų.]ˋ! '7k++M ݺqf* ci싗YY""1G+B.JÆr2piFn ŬV_]*>=ޅ[ny@0]CoɺTm37z`1vVʲOjJmM>G`oޣZ`.DuaIdj ̗ '@+uUTAfddT6~sAYh &DLA4_A"""h 8՗fm+;E@XB#mM}f$󨩶7(p؝/L5 4q#bmEгۧOyfX}QYFXeWYcfvj(Mw_bSdgb=w?T_. f@t 12553gP8d֭[o޼z*J -=t.I:6m b``ށ ?zTܦRky55wzoۺ,迟ﯟK2ȡ͋˱ߣSc?9?DcTWal[͕{RbpͿ0Ykޚ'_s07mٸPSn/&lLkÃΟp$u>.6:\<jBW{voa ) ݯ*MwPSITRn2 !Cp TW5V[{bUYZrB"+Q$ҧ 1r /\gq Rep$Y3ǏᨫɌ. 1PlM$Uݦs}5ov>SHdj4t?8k\Eya|]uAnV/>G'TsߗB2{B2]a*@$0|ӧO0<ѪǖQfJ |e5Q` N[X^ˆhޫuF6I(,| YJgG!OtsckpZ'h;$>C}"յ}=_ru_}}F~0V;Յݦ}nbIT6輈)~}Br, "0j(Ñ#G-55M׼?TSS۾};ʂ5[څg*/?c9 WXXlͳ=[9vvӧXp޶jAma"k,v -=}H-g0ݻǦR#%_+,Rw Ȃ_>&$gWY؅DEH= $}׈yIq)i9%L,lDGSԤX'gdiY&!c+)Ɋ7'^[Y1(K܂rF6*+ͭc$zM'^|>G̩! ɏSYblV9BG5x|[9%,L}Q5oL\~df6GxC:)k<_Lc(N}t(;gSxHKs;.O ,#'/]@{G] n>݆tdg}zʭ/?;_FHH gY.};dS"~K bU!٤[k' 4)U~z@Xy5V,۳Pq_S)צ]@%JrvsxW3nq}ͣĪbE6+*%^w2EM!1=N)ڙo[m_~I?fޘ$9nɿ+4Xgk9)@N[oU2?:}7h+uXbUh>s}SIUs8v~zkJ ҔqfQZ՞W=hl;OSE!덝ȉb#NNګ"[֮# fMuMQ{{]n@XCfn>6vؐ]E76H{}FsQ;@{hM1&.ޅbzhYS|A4OHC󝜜~MQhp̙˧o f]ߕmij,txyxr J9x)Κ>]bȗԕ46Foj!Eg[:3[-)">4drҨVtFIT4 \w\ৠ̬|"#F_HkInmbrjO?8d%'T02 :lĘBc2Fm6xIm{jlmh_(!yf@k׭8}{0jȟnR7Fq2ӗOm224OTd߿~JEʲ̬\ҢҢFYb\t-XgWQYͳH̄S{^}Ebtl^>وxritQiLh"o*=tlVt4$9ѴU{1(dӼ!6gwxm4\;z^[>(#Y~6;^Y[N%=>^6#]] ]xV6`N>D)}XkOW_ezG~ӥ.yN]y\Kku @`P#N.\0g͚HYYYgϞuvvFCaI,], <|hADJaBW9(wW9u[7 j}vFx2cѻt`?=M& @_ E,Y-15 -=zOhIRL^G`u7oyF|'y[ݙ+{Mz]C2{rھfJܼsp!i;)nOnD:6w@{*T2%ڶYG?#˃[iSb>u^6~1QC|DEX %ߣ߇Ĕ5+-ݯ^PrR9+9;bV?MEV0?g1~'F,E'#szee9DAVi%D ‚B n2=53՝[HA!2SUd9r3~.n'+ծjj{X,0_n%{KaL^^ibv!;F4i0 ƒШyeYh }4Ey YubRta%~I|u٬1 nNuEab!O:EW&kW""rbMn6iI4IHE+OtK_;<#먱GH .kR~AUm[CucXs: @eևхE&ZF֜ӧO_rh7/Xt)   @t g@VL:5""bh"e#533V,493{9-EC$d7m\hƽC)9t-E)?ԆœVحYdxLY3 O;`G[g"V{XXI83>|džwPw 5)<*H,kX\55r޽OvL=uuݼaYyŶO.X 1.enmj_YRl}x90L՞D<Ǚ;7ϕ+~Ö7ek8(uڒ5$(R$^wYJfԒluH'6^NmɓErs7h~c:IB.>)f_D}N `vhmCIN5j66 "A4rXvOA /Pj7ԺD/#q㵶FMMHJᦷ[ɔO391{`AX I HA%ݫmxcaaYv$u~D4l}Ӌȍݾ@戠ݡM| sODM']yvn*ɏ_+4iC;~L^J`Μ9.n֭nQ B&&&Q|o<'~*?ӧ?yvRm &g} 7G6@,IBTWYt 5kt S'+I'ǼZ٘6dL/1,ƃF-#>1<,[}X ,::& hI0]q\>b&js^#5Z,:ؘ)|ܩQo'-B.>)H*z0mteefC#c2pإ]t=RE.;ѵ_>۟6N6|⩻gA r@hhc0Si988w)&& !'ח]Gi=Tj¬ꀰn;XzIvXX]unk+p?{/GLJ!F>X@WE˿-;;kKii͛=ztcrH ǎdvófN`$u$$ğ?ivҴ9B4~r|~WIx+7ǝk}I z'}Fɠ R9׮_A///k 2vsr0;X~G'Y9Y*[fgWQ&⏟)8ܐWˍԙ=*G a7D KvuWSks7̕lʻD=}H%DZa9cΧ  #]M%@J(4KF-[ЀTAAxEhӟL/mza<)0ucDߐɍ#ӻ֮ 99l&- =22@L'Oi&OOOիWGoOJPЏ$SҎ=g{lOOj5[6gδ-[xz6/t}Ɏg ŧ^+ܲ:r)8>ys||,aI%,WiI.X6I\`Vʁ!ۯ)C'ư TQD0.< ZRM]?%E&bXϸ`uoC2U%MG"5>`PUmʣN4EKKI;FRkXVZpr9Xnx zKѦ΃s1V Up>>KJrv_W'8Kc6g+ h#.;7:UUUZq *13 SHt/1 hBt o߾Ԕ@$0b r .`цkAw @«3m9vi1\k謧NM9x^Po|ՑsQ7@!n>|xc"PҥK->!CE)L+# *˃mz0+;-7 gzUa~==/ !0;%o8LX~i#Y uڽ"zyyxX @o>|cOId%+VVgUqI)[ʂu)bEդ2_M015MWՉvW4JnHk𔋧Iy㕔?⫵MH B%7ngk' hz QhthfffddNW@?`4JFmo?6g%n*`a힑ͽ @%f555CCCJGb J9{2qFm29w&^ydО|eӧOܹE)K}5JRfY}&!@h``x`PZĚ'wu5<w???&DO&`c 3Cґ|HwfO|k;~Vi++x^찿'R{)"AL]ۀ`w3 L,nhq5E% ɫRCnN9oNRyE%7KsDX9'OTWxђu%7zUHM9u8NvIH|h/ZwE13Nn|B~կ1w^xFyҬn_</SM,Va%\<K`% 'OEڡYC{^Ug0FI-?$ 9i>&)؇̸Gv[y'io:cMvPkYwǓ.x8cuϞ c5f' ֙ݼd \K'/ݸ[ 0+L[uJ1*q7m$eTQ/F{^8mm^% 鸭:Lj^ A+,c:U9Fz|.,„(azjJA}4!44hUA0۰aìYFFF(|`J9{&'m۳v|<kyFF… ׯ_sc4]&!ڲny=ap9іV.4hGRZZ`veL"x%窻٘;4(\c1wiSC]kNyםhJ(#t3@zht3܉˴i}9s:Fc@| %jt;xB_5%JftwMfOTPMT ͖{u+`G`$%3f̈a9XʊzM^w: [Ww[;$gTWdfc1%]ݑ„3XIyK (|sgO"&P^͋&[X O~17op 5Km8#*-1_bvqZ#Vu.It$/՜(/*ȋ)HN~TO&Z̾"07d_vE֧l`CN`ȑh;Ç1h-[ڲ\fM@Zz.cIՙl %(߷v8\ZZ}}5uu'~LF֭TҠF&'/2HE{76vz9+M}tc#]3m74lB\/%EhgDvʌdSFmuĚ)̝/MsmLt9|S4c KXhB+vىƳ r6|sq臓[X+C}@jjӧveeg)jjjР"@ |%,.ܼX[nP(:{9q !Nnz81?XRhb 5uVs6p߷ڷ38iau!fǶ2QcWsc+#9mH!2 K0&GlaiU=8oJru͛ 508wnPa|TdDẇT {pt*bś|<$ba] ~a-y7H H6~~dd$V mxꪢBo8 J'IH^Gy&tSPywPVw*vjR~wيY']4W敟b<(+_ff}UIyޚGSpΖ+thh I+xaQ&"1IIixG,OF;j&WW>VL`Ԟ;w?oX3NKpE ݦ][h sor4N'E1n3JtU+U Df h85 ?q,|bn@7HHH8qč7EG(G W^maa!++K)4@<3{6:1|1GP}5a`1*O KH/sr "6_𤥶E`0rY8YPY}UbϺxi0]/qEyr8uJ;}hpf;FH LY!BN|rqra?Qk(!n6l:ˋ|{Lr@tc~h`)圉)Sٳe{|w v):X4~Dt~ܶɷ<'NZ0o8;.m07&Pq#'/?ֲkOثq׿ <-7jĚ:F}{͋E|I&A$S[(zj+r[xw4mIDS'մ>1dw.=x5Չ87R̯"wr !O//|3QZch 67 @P``gg @H'!@.JXu4_A s0ê5kZ~(_ԳVjTUBQtS&ZДX~>IyŤu6lof$-  8qϟ{9ɯ_?( Jڬ13^zWZѺ+MnA@N<:Eڛ9+nX1Ur?j-$p_r܎+H9? \xVG3]2 %~Ņfp|jjHKb7&/vnbz_DzwN`ʌ.[:W7ݞu7](5OwŮ=J ekàMbt|AWHAԑlZ' w=zA-<<<۷oju= @Cx֣+,m?ŧVHUfCɭ %) MDD{MAJJ.5Bb|r?AjC%}Kd+]k^r_-,H;wO}Ւȁ]N )E.Ydݺuɘj+++oo7oJKKcrHYۺ-??GkQb-D"0bY "QU|p%}[zW|a7&Q[u'f(@]# <cZ*%Y[cϕWYǛU^{"_e*񞁱I/F"peXL JS\(~ѫGB¦ܞbqդIn>87Gg=[|~գYcoNųWYo.IچQTnXn6TIQ~IɆ:)h 䏽wŮ= Qq%VZm7;0^} Q/ix^6Bkt>/^ m ///] ]J^h`YYbMo21)$>Ԫ67y.etPHJi\ՉŒ‚ԔGtisbII)bX^ָZ'vEbd;H5Kd3]WZ`?}^ ->)9wqS)Wη7+L#oI @3f̈رc)+ E;vl֭B)@ؾmM7Ϙb8s^Mrt髧'Ϳ#Vݷ;⶛է(JyD,N ~prr@G`;pV,AyK܎(B-@j[ Mos'4~![fd9j2a8mU㽷1lXޘ&w'n7]L}Wb,L|زTk+>t7~*>16{CX1=?1|'ru($Oۋ|pgS\2s`Q._=xS%݉Unj64ϾpW[ VSX< H]UC=B~+,p8zN$j?K6JqօTzc]|,/{]Ǿ>e !#.r۾~I3$(ck1 H~@Z/_DwmhYQ-C5E988* 4Dz~6+Եr;&guXtWLVG9A83 q21**rrr Ѐ0s-QK9 iNO"#p x~ԕJ nL|6dXyB,Ԥ䏡 K9L>IR0Yኾ-3}5eI?{-e"2IݹwZu޶ĆY+ :qw]UEzJw!$}kG.豌O/B:>G'flp,DoWG.$rɳgL=W+ûR1 ~d䀌o :i:++֕3n*Aq'z_~[zc]{YU]v$!5`ѤF-3PSTrM}jx1n?V;Kz81OH  >|Nm06B >butwyU2͑c_Yg8ddU"m7(krZfYe-G bh{P~  +]Ht()x+=T%姇~WXgd0H~7Y'H&"B>E$Cf()8^e8g}aRh`j䜛ۛ-RU=5h'NŸ}Azle"a:4ĪG۱z_&OƟ!)x?IC^SHZ;T8_?o|d77ck(~܂b*{]*%G})@X[ o(D_lFNAvf&p¨(ݻw)߾};f̘ӧO!OcFK`ڴ zKo$U[VVmc t=Y: ʏX[bRJ38PԢE4U#,KEUWNȧkM WbĘMG9 ,-n~A&:iʱHKs^&)QfiiCf\mK-MG`buzPU9Fz|.,#p_܆ DdBz5E $o2 [k"$4țK1=yջthq쬕vI;'CJ4E+ڹns/BFv6qSwUNm/E7CPmԫ7!J>7uZRg=_pLvެ'N-Cg1"6Dn |T֕ᛜӅ'p-xt.;=R]EKgΜi>@5~xI5&B\ Y\9aNmv>*1WWI5?:p p'`}捡z>{͢7IʉAwƏ޵w3 +Q?xa{Jz3CQ]-%3D WC;;ǘg߃llPXdk> )hM,M=Ȍ;t) -x%*)tM̟XVRJ.SWnN93GIf,LM |&(bt<Ӵi ?ep,?睳g%QWʌ. 1]%_mvD$riӦO6˟;wN[[F@  =dVD8sdv6L k *YDobQNfTĔ2tIdfa!3\zHWmi4iM&jm)t)o>|YRY=PbQ2L``nmgZB|ؗoɿJ+pL|RCG k+zU?˛K&_nsy%"F[آ/əEׯ$,,5bXvA )JLΨ3!$ Z D}'|69~o{olWw|22;E9d@E..ngmɓ7oHRAfOTPM/i.zlI,gdcgFwȇ?.Rԋ;U&V)~Tө)={)GYݾ VViu%fZ [Ҕ(G)m4 gFhFx\)Hy(pL J VZKnD]W_2'ÆI5 vO#HoѬI,?V*:8uY.œ!AI+I4UҐbT]XC$ wM". xDEyQA^\MyFr/XsxjWmet1yIEj+J~F<{[ZɃ-+Jϗ1 :(Mt?_ `sˏ!0m񺧇t<_Lc(je瘸s|*4HA2v\&ޡ^L:s[tb`C6_.cӭ 2^˓W(&N[f7f~RՅMswaCpW '484]mʕG$9ϛ {>pX{KıK*, #]јHuh!Ȧ#72hv%.Z&u=j_:rk56OPDz(Zs[+7{}Huژg79ꪳe=?kG+]th63rYy^y-!'!"&La16|iܷ/Uk "r3Ul>ޤ."?GƧfdLRXR&(2`cF `?|9 y@@6o}UUב響۴iɓ'98:⡗4w}.ˍI "鍑yQLfK @MDw򭣷kUf;7 4ozu ħ{25UM*j$i?9/t(:)[WayڬxyLRٰmGwE~`1QVkV2D FGE!IUԆU%&UIGKx\K5J67JY_Ttƨg-51?{vS D<Ɩ;2, @=ʢ_|D+슁aZ^RYY9YM. 3 tu3&%>6(G5c[[ڶtJlx@t/:"ЕϞ>u~~ssqn2پm ] ( DzhtMtEI)6o@OsÃ> DaN%ǘYؼES;,H;wO}ܴ*)Dq1ĉ aqqcdeCE F*  ?>8OemNMJ? AUu*9XQaam)K ч5/_4zTGΙ7O?=E@ 9r䈭-z8 ;waee 썞;VP_U8%weJJ{csg Ѕ2޽ ȇO/nm*M8@ ߿ V2w缀_'S :f{@ה?-Mx* g0Էo1Euւuqj;^iu?y&4S̷F9h0 !...Cq>JKO~u7NKfrrsSԦ\|D*71Jo\<9Q6Bus<g"66Y}N 1 -Jڷ}iĺ߹9Xp,kݜ$̇׏ŰErϟ?q㆒&_ ##kZT:&>zyvx)%[^Xp 9z\萍B(45 ~kdЎ9;0e :JHru1Jp?7L{YȖD=v~ij17<dbQnFLw/o^sJ- C=8d6(Y>ʡ(_ah{_[i-Yw,!7J"@F8JކGcgent$?$r4YNJ8iii./> H &='׽[rX6  :O@YY9<<|gL8q_Ë -D`;ORIG^abj@$0]gAs~NR!@?o=G қ@Hɡ0ovm4/0iY.'OiASKBrD|I:* ƽo؆>|)~wzeC}Z*p\}X8 ;Xo޽";Kf+-B}>Ҳ 5=ob -('p J@sz̽;@6mZDD٥K(***- Ƅ%%Kk,xO^(((m~, @A-1sΚ_9puf}4RH7]^^qڽvR~U3f^e.(//^**Jwb!>AČ:\]U&9T㿡R2XFyqyc)XyT!l~ϥ#&.V_FGYn(3*{׽{ 1eMP5N]촹6 S~&aFw Ol9bMޭWc{F *lҍK#V{]Eoʪے&H4j!ٹsf^[YMPd1iڴƉ5xƔT4-K>b2#j30J-]!Nx}<9_,~wY6qoMYQ· .^fTUzbBHˌY4;cLΪlP-rହsexuY?|➅IQ!@CNR.޹P zŋ555 0+++cWWWLn%`WѺ+TUH?@  jλr-͋`gfeQMTTZ5]0:?e`-~AAA((^hrUU#\lY5+n(SMM9BJ5YAW?{̍HUerc,H|pE&[Fj*-{̖!gהAvBkZFFs6kZQgdQIڐIPX\A)kJt8\^JǕ&f) gJЈCO;AdfEYGdtsGuiBIw7MY!MӅrsmH>A@gij YfFK(.J'sĔW?r>O`!9R[}7}_0{Yy8\CkVCI y"##nFul =FۙXb$Þ133aH @ clLB8} ,,tV]t܍B"XzƌI{MfLI @ $$$ o߈D:Q&8bm]S-**:1 mD_{KY}>QE$pYmuu-jWz++5-Xn03Z8cAq2!.zC'ciC$pGGcI+_=AU?K Q2x,csi56eI'PmC8PUk$k&~_)OVUmYց aImBVgzlOaO9@%|||nZtqvv viiRK{r[vl_36%_ٻgSO|@ @dtLjGr4\Esꅋ%%rRM\0ʄsA@{ ԠMP̏G^gsLj9>>ڋz9WfNe OHL~Kf2-Q8s>}rQ7H1~Qu5㘘h/ٻxĈق< Z&M 8ز.m8<%//iQ87W Ll&wZ!"JJ[/)8hAZWj!wDEg(3 -* Z  }}})+e`P9(e{B ^c;x~Zc̞@ -w?.\t۲@Qq)}%JJN=z#;o, @^J-moS-[ `c۩f?&PXXRP044y>>I&EAVV~ 74l}J"WEns2GU5m |v :޺kmR@W]UY߾?sA S@_ɏ_+4iC;~2 **JtzFuFN⤫Ž`` u\U&k6.dwHa3@ @&6;Xz V:vl[oln^:  ~6BoK$$%% :%M1/[pn{ԄY{5!h#iЂJ vWS,G^YQ_|5 +'Foad^h]N@LLXLL4Uim!Gڵf6Mpرmtf+OCKR/RH^[tu Q;5'P #0VAcgO]W9Eu233u(]O@{k$h6;'Gk@ <m`.i鋗$Be]b BϟڡB > 16-'}Nn llA*; (octR`baw-؉؅-`*ݵ{cml0B=ܽ{Ƕ޹3E_ܾŧey)0\_]UgŻ} + I(5635{r{|ռ5[kkb_YTPR*]Y&oԌ/j?ַ 5L1j㓱2dddwnwn60!6 G D-\q|xtd¶Z@ @K8smG:UVՎ ր@g#P__DBYYY{H$فڡ.h ~J6o[^]G өǼ9t y,UbW߂J*w( Xݟ/z",!8JGyt^$PI%6}#kFzt7T2 O;ra:ݱ&8%~~O-1UIʼn_pA|=-5(-\_?q'FhCҪKm6m[PVi$ݩuqCsXG`Zv`ǕU@AFAow>y=q!QL'۰m*"{@^[6-?v2ZD"۬ $( @tG_ڻt}= %P^^닄7QQQdA$(,,}]@f ?sJ32JYi[/5h|}pŗE)K)ڿaK`rL"_| aJ},MG1 iNx\y|80J̓#6n7᪋2@ b+(F"̑1՗gYp'_ RNXz GU}T[vu >7%C Gt +@f ]pzEVFrpfL9tusp(@ CG. ǐAO\~pܯ06o&ND B&r6M8}]@Amz55$d\C MXD%DA!dM9?>j2"f֬Q2v9nf}Lf,RjkjEU#Hϋ< Ѣx&_|^m)w*‚BBc\&0@ C>lrI3pȶuk0M+BBl'MYAS$X"11uFpW@Lj.ΠSY(ˉWfyx.Zb]9?'AO4RSeEz,(-&KbG*:ۊ1:"32Zťv{۞HMMWTUM/M雌 @ i ^xzi+`t@tP$4t6Mk4HZZ* z>oWFo3۶x<߇%=i팽Cͥ ֏b!V0ʉ6L7ŶEڴ}3wF3yפp%MJLܼ(1qT* ]8INtĘϽC?Э ,~Q_S_ؿfC!u #TlzOea,NE/(nZnl+ڵ}>8[&$EcY6=|gzϲ%3aN#gm?QYYVyْ&hgϵm !#H @tNd2umd!_瞬T5e55`_,@II DvjƬ$ DHtP@@Z~?;F$eՑF }~1{SO̜iG_ 15w"K4: Z#m^辋OAY׋ڸN5B ( (oOJ/Sgs7v9IMIX r0(곋hSDe /phV+m%+|@C~%}夋JhEdo< -޽4yxXܓi& 2CkXVvn]8'oCBT@ @^^ajZf{F҈tmޞP'#""i)))w Рl &c?|8 λ"2Ɖ ¶X1 rɣ`2SIUB3.b@!FpiD=13$R1YsfoG>TnǦ'ܱm5 9X;.*/8$ hIJEeUxu@ &P[[WPX6GlC-WG YDn 8 "qASSSqqq&tGT>^[O}N/t~ivؕ[Pݵz ®uRYXD,@PPSo՛Yu:WVq Z)*"PJK\H@H@`  @?[j^^^hP0 G,6Mo߾||Ȍ+xjgx\R Hj뵜M%:; v;ǝA"/vV)5Pmt(6C^-r32SEwӞ 4LVTV۶tܣ(!wTWUX衡f(d&KbYKD@YWNGˏ; !)Ѭ. ZG1QqH륳k]e"(,̌Ckzeܼ;09wY{ FEEaDpcgϞXPPKKoPp}H %qԀ%@xC2 *)rBro5&n2bSE|%a˓]}#?~נۦ_O|7őcp^eةVSٴ$$ЇQ#)!?{CdC@tdȡkB~zțϖ5oJM`a2ww@fn>D# 65~kLh킹NI>44 fffr&&GfbAAyyyfݾFf$U =9=jޚCz5o-( UK&HHkTғˊ2b}OˮvS1`|ow,anJAFBhDtrrjAqiUMhw%54GFL!Ex#"(=TOu\SψQf|-PHi1!!)ٕ5$Aq%5 &=Z2WXJe)$-:#FhM8߸gA~r{mcu/y5C>{`r iTe,Wk:O|R&8|*;ĈFBߜ9pˋDt{!5K"57u0("# @4C@M]wƮae̴^`y !!ŊvN#Z':@ @***|}}ѵC}||}7afbbn($.{x\i&`#ZlnC=Iʼn_pAn}OK zH]Srw 9u$z5C%cQ-9^QxѪuH 6,͋Xp٫oA%^y̅/z",!LB+Q*IQvJ.Q^<ɩG9yM%bbߑ3N鎹)6œUN .cQ60uUS5rw/29 ә,i_c*k8ZM.탓[8!37ܻz2e~&buLzI L˜ W=vp˪˘߰%0Q4Qvn[dK#yBZ>ӨW^-ݤ.h3az|ydʞf9RLnsk8HTڭ~Y12rd^%|"K?9ŘE&x>938ص:9O(I>_z7Y0#eޜ_h .2(_s\/_eE*$ꬅb\FLnqՙՉKtv~]D"S*SSag'( ;`հliY|HT$M2џ5o:65.ɓSOfo삭wS{VLb2֤;-f9d#NkR_s7qVf# zO[i4TiC*JBx (=%1w`L Iu8=>j10G|B q_ѐ,L*JW&D&ADʕ˫FýEn69b0 /;6"oI9ٓȫ<3إǣ.F?@c hC7.`ӜhU[nGwl]Zrb쑽뼿nA? @NP @2d366&)=d޽{չ m$@s<;c.Pjk\KMIW\6M(۞ر|]y>Ç*jғ=䗣eLi.u0 k@yYIvCxc *"Ss2rq:=# wyĤ3hqrY./^z4 C쯽kRSJ"\$VV#*kcKKS meYrl苧rsDrBd=E6/if4.FE_{JboYEeb,i< QKf,:ONcoW+.*Mp0e%ctPJYg|!,nX?e҂@W*„Fp7Sc9BKMHH斔if$Θ&vF~O҄x6A.<xqˏ q+/=$fELkR] GZܴeJMu-*&ь0w\.q?gIDw1|;bWBO5 sEFdc5L’<x$W@[}0r3|{7fm~#`p;gb*'n<0ݔi{g.BKaxgo>[C~ԝ崿痾eه8Ii=eCogĹm;CKnJ `bQfr1--1REQf_@LBrAQi/"*.У!]T^tCrTD4°% AGPfhJNIKG<|]a޴%^ՇHXmM 9_:s*Ckeo}?[ rx MZjC@ht E/vB_Xـ̟v矸;~34:OXahQH VkO#1,_wo9g>1)e5oLsv]f}, up̑-%#bqaY1qƬi l@ #`6lУ׿svb"s[`U{PhomOL KlA_Y:@ @tH,#jjAYYY$"LJ,@RYXD?5ezs:R<0ojbb 7܅K!.e/:[=JiIi}bK)*ĆAhh5oSM]uyNV 3; w@t4q 3nd<|Ueš=k؆H$ `nݸ ^PIYCuj:F#\8?o31Ri܅2===,]5,vm*œ[\B:u? |uSP16z̗HNN)޳UUUq8t)6XU^?~t)82Ҁ4@õ篟;CYi U${Ӓ/` dܨ%ŅHfOI4"q=4{8Lj"* g@ @- ___tPooﲲ2jl""">ڡ΂H,Kiط\ Hn]YzǹUIkbc U-NY7sgZŋwBxF fjݞ>,+*U51]6ݸG}y5suG9Xrk%pkUWWc(%2bLWN_Yںխv*ș̚m?m,UYc[1u m0Q:#2k[6=G xٹk"Z!c\d"G.= c?pK#@ e`bcciqqq7ݻ7:M s_4:VZv$P135RWQSHE)>߽c! Iu8f>mXDƳ.d4!.:R4Dּƺ|Ըp'O3)^ 1PP{VLb28vZ&.?sF6-R_s7q;[g:0>_dyu"0;ήMMw4+Y!T_ݰWLXiwi:j)) ߾հL$gE',u mxÙ¸kO 3w}jt=ۍ>Cٯk(3={ùK G;b67EH*n9ۑFࠀ:z߻~e}BO2^XE@y&p2Ze˴ #{Pe$|y|d&Ӡ8C*jN!,}~@P@ &PZZ#_e%s%$$ D"ƂͪCB |^5NGk6擱h>}*v-ܒ-^!nǎ)fELIuuLyDvH55PSc )JMu>~utJ-Vg:pXP0kYeMAƩs񼻮HαNy1g926)&[Np䚪jl\d줛%6wFx9vȨ'J}Z>{CtR NӇX _"@ :gfcvm\ѳgN׽9r榴ܹ{X9Y;/r-bkeC]-O#.^y(@ @"؆Ⱥ/7m(G 4>hxU~|p돞%Ԁ IˬNާBol7䌪:3gLV&mb뮳MS/x)di}{[ orVBV!၁٤6z_oϕy%Qi/δбV\Ir[x7?QC#roá.申T:jҷv=RxՒS'hY_1]d5Te-w#!g! r]d)d 63|/]əxfΞ;D_i,(z'申tN;{<'X7@O҇@tZ:N8^8x(2#*#-i2r5u-VȔM]0,j5 ݛ4k$@.Kp @G FFF"sѸ`rr2}@B؆r]yU%?#cs *k|wEM{ 2Z%o~cS矸;~34:OXah#9McF;K/w滃Lb0gG51у=|BʐX+/<(;ܘabcXZFVIEMGWW^BqW|sy' yE~ayUc!϶N.Jb^EMaCtF d8ɐ@  y 7.HP(伜,,LʹCNf#*Ef(([69?7υ= r @ @TWWЖ***XеCEDśLhG?N+ll19IGiX60aiOa|,rH:fWK J*^9ت5-d[i 촧@ {Z߱nSݤ7>L d;8  @(,,D`MM vdddi.h ?E hLJgd\4 ̹ 2AU}|OoYetҌEl@@ @RRv(h{3ZĂڰ#H ' dOTUg?GgUy*+(10mpD)\eoߔ>y6")ĺ"p7@X1o]iam]q{O6-!aן9㙦{5 @ "Lh @$ ѵCuAt_n?x!29;7%r*<8do69em'I7Il{s L-f,.GП/:~fkuμ$4M8gOp|+CB@ @%ٶ@'7`ѓV"x;Cס=k6N*).l>?@ @tBڡeee;)""bbb,uAn }ɬgnOKM^f+1xr@w!+tcdWQX 9?[2!ǖ9!@3@%ImvnϦO+Bg8m2eUMf"A7dٟo1 $@ rrrЈ PPPCFͦݻ#@t>} tF֕X$ZxZ@t0QaJطactO䗑"RHeU6|_3}i, 8>},06@/n  %p讔nn^=w~~Tu ͒I)c6 h@ @da\\v(? }`^hqA &Dx_8b\dJ. ;Ntz}++w)!RT'%%&&$dU ;MWT2 z +0lMKcH0uH o%Ko22,:T( ZHG m;.@ ]2/000)}OРAddd @3tJRKG-ݻx ^_T2@ "e%{,CFvHܿo SvNbqyRX]]-//6@ ""s `ee%055EƂM BLɤ͖Lj9S[|Zv=R[[qVШzA_c3S#Q'珹W[uH/9N]j}|'eV vSg8dP  g*k,o_ZJ^YTPR+]Y&s']YCWR0`SQwJ]~!ez3ZUZ@+'6egqʝ{jNkh8Cי;j‚ qK @ ڡHP044,E***؆zzz,N"3P@_Hd[yǖWבk+u/cޜQROꮱpͳ`Aڼ TRY)^y,[O%d1,̣<:/Un$k5hEĈɩG9yMec)$90XI???ŧa/^ M>PE-vp9Lj"L%xe,h|vEfH5 +e rO}ɧi%fӶ ex <>r129'Cwc'L%5q^}WRpaql_ |F.zxqLF G{59]IJ9 ^S+H>/>I$wYA1MH FӛOﴭ.ZWz[HHN/^YMm!@EO" @#@J66vϯB麾  _OL&GFFk"d@ :MP r_4@j ?sJ32JYi[/5h}}pŗE)K)ڿaK`rI/0K},MGDl,4jǕnˇk7YRGl,o8SEO^d~#~yV_%٦UrG^$M+OR_yf{nTvjқo8=d9o#<*"4`ϩ_-y) KՎ Q64u\B(5q!!щIyyEU$ _DLT[7U.tNU $еC`QQ!0 "qAdQd)Q&@I]_SCB6<ia5IB%j!@H$X-1I6f7eӓ:ӬϽ 6K.:{B3A{kYۛ+3z+jarՖV)԰U]8TPPIag&oa|mۺv ͽҫQS#裏S?tw+~jÜB{E)?\;&Uj}f[/ZHV݅ .dh5ۗ<{YT|e̟|$906O J@'PXX.D>2j D722C  @g'o9%loD}mO Rl?e"ݸ]Rd=vٱp"2>'#{Gr Ie6B4v8 e9,O/=BИR]~حd1:#8tZjJ⼬`Wϟ%rߓkIHHZu)Fd$pUY) .o܃XZ nm 9b$<1*Whu3&#Q:rv3 9?"S{ژ ZOTV{}5i Ow dJ)7Z_[]\8?: RLZJEALɭC"Mh7#<K;_D19iF H :\7 >$ FQ( 95wn^::bI@PWZ)sY>퇳. FjrSE'/=:㑛1{2/ɒ+عkD- n_Z\4qSJ0%E9}xg+s5%PSȻQi CuD[u㣡ګW/<(|@qb=L Fv޷+W7ݙvm[+|z7j6\GX? 1XV0ʉ6L7Ŗܴ}3wF~kCЉ*T&nn͘tӸ~ ps?zƮy+Nd3dbjYDX?)P xvd}e4d J,kwϽC?` ,~QiL$N+{|a%d{<.  m^1XW>Ze #鲈g(9 SmYOi<>VQj[[pJ^ݼh0BzAr~t{í$z| P6_h,˱PnpWƆ(y5)IPyfy(~1Tb i eebЅRjm>rߟ US.!.B$j*+r33£rcYFԺi)/&g i 9ߘ _qyWM TH$Rhh(v(FF!b3w}]@"@.Oۼt})(j&;KASwHʪ#ў3t0:{2?ˢe=~TbObp[n^:Ν ӟz#jx 붝tΡ}rSR*)8}qA׽2V$Jb (WeGy @`#ggCI1DՌGlݵjcVLͳn=Eǵ+w,mpO?İ].@?L z{{r DH ?K uYĞ= .xd{.ދ܇astlt !)I jkto)..`9ۏx_d%5ԍWW [yt~_]#PIWlF 6.q|݋&~A ҡ愣/_=J([3nQo~2ʊ~9[| Q<޾A#H-U (n|,L\+˰4*{M@<<"fۢ|dB!uZ׏{ūp_oҤNSLv.s6ZAl>air`1vN)u^Ƣ}&]=W0 |7A[ϻi힋@\eۥ;Oׁw v;?}s>Q[5)u9gN>>f>ݵr}eƊ7گה?4#TWg{ݺuE "9B @ ~AD=.(S<$dB.S_>~NwI6y}DxLCWS&j[xӫf,\<Gfzͤu0~QH9 ,=冽[W ?H,&7f$GYsfo>=GG*Xh bj"ɵg2]#;L:"͍얪9ܜp*OazȨX\KB"mhsGlRHV胬Ӣ+6g_ڢ_#;sϓEק7vZOjRS.+e80& ~peO0t& <6tCp-Mmbtd 9'9!2*1+/)#ֳg^*<-^TrmeTphtLrn~I/*)2+&uUug)EaauD~n{ʲY,\lfҦ𐘔 d~A!)9Z=u5x[Ͱ}  ._unM#فHP "w "qA%##}]@4O+㺣`'Ѿ kuR;G+;9i4V'F :w=耻v 쵓q'Ԟ[P|+<- t4Z>eƚw~ߔ($·223D]f1.FrK%'#4;W$Z3EKR>h 09ѻQHKPW}= 7;cǺWG-뱰tW~QE/ߛe5ʻ7郀p=/60 zv#q_,wRkk{-AVY{2$@2ӳ.qDrZE]EaT+>=(⇱$m'ѩMJv1iu $6T&]?{O9%WWQXr.VhNNq3Ư+@b#l7 ^DP#X^=t.b$Uq)N_UWUսp=h _m'`F5ߟ?vt|L*`8ɓ7,W`C e|eYZjid?AA__J=055E .h `#{Dawؑ J2Qro5Py5^Nw =#4?+2,ǧ7oL?Suػ.+pyZOsqH㇍2>=jnA8B߆KžG}OΑL/5#hÅC<.3oH @DNXfw4 2ad;CL$S-nS=ÑnGm-xJyoz)q-סk]@INfk،i<}aԾmxS @ɩym.6>97@NVSV^5tPdUQQA&{ {#g@WXJe)$pRZLo`HJzve IPD\IMhIOE)d$FD''V vWR3?PO{.>;S\޼beQ_t|RAI9/]Yf[gŻ} + I(5635{r{|ռ5[k_S ;|wl)57Ƣ;٦/yط,Hn]Yz@C!h٘!OÊG1|dL!ǸM[6X%H^޾_ S7'8q\lCbI9sFwE6 rU7" 99AHM"|HY?ƞ헛3a1Y N@DTlȁL+LD󬫭A :_c}|3g/^߳мEaoҗP5ѬGU:DAGOgA^N;%h9>_߿r=4Tcw+ibLڙr3gbl7Wۨ?ÞBueɱ^Ae5ԧ6U85pܗs-[ UpI ! u%vQ_2RUJ7^3کV_gMڐPHǣah4иҩҢohԓ0I /?<.󔪓+ː4z۫PQRUUO6%W^m#%t%.̓@@rAo iIʼn_pA7=.i‹NY$2:/("e2f[Z6B %{[SUEDqalƁTG=pם AL\D cȎN6ʍi֧0DkTwGa-)sxۏ޲RaYIy1O<ד PݼuE'b?Zsʉ~jRȵO.خPB;R8dAug 1D9m٢ 1 d¡HZdlwRug2ۺHFͧ0Q:#2X-K[@MiZȱxr?/dҋkԑ7H͘;mT0RfyԜU-_bJQܟREرhOL~^T-@K<ʨO Tl&t>F&glK%Hm6蠪SwВeVr~ޫ΁ҋ{M&V$=:7ȡZE1kIzLԕ]Qoi6Xvkh*9|ޭKkE$_J4>F*yA6y듂"gn_S . R]v;-8p<4͏ h_VS͵ ʲT'vaVXC@f߽VoZV_yf{nf)av,yӣIƪ̅ 9J̓#6n21Jiꢌ'/VPd Zdg;Zҫi&6݁T%:;M.B¼)'67Q$ a5oOj)EwoJE$YkS߽cMfHQQ*˱}6j9cX.%&DNOVKj6?>_dyug"fgX;uK=>! z֣!'XU,MyM,d+G|f΍pV Ip9.3k 5QdԤ G;b67VB*n9ۑFࠀ:4ýw$hoڞ Oʪ'ȵf}u%%T_c4qQ@C@HHb@\fw1u6OO[xvF5vIFLx W1c ¯._`m4NJΚFC >6 BDՋͶ_(I ?l/hҎ#Ivc8%/ ɪtt$/vAN*;u[&(N'N6ܮN[{5>gܨ=ST1җ(ǏWvc<䪋HBZݽS1ꇖP^=\f4L)Ma0-ɳ^P`SN^|qhn^Wjٴh7W5s/AJK"\gI4 @3/ܾgN DX 5()##3h 4(hdd=Ͻ)@#-?PϡAqkՊraQ yLCE+%>~-R’%?щE&,h:xkuEqsFxbłx&_|^mFRywX<R[Cy4`֣4,,EssʮBR!}Ė6q £gUs+6#q#;V|XuՕfݿ|>=Ttq&ROZpcT%^[Qǀh96'؜8ojRtNXe/HtI/f;a,~ZxOCNہ07Z'ot9tO=6}')2GR96A ;x:Ge?rtSVTRQPlsp6 5&]9wv "˫:bYRVA<-)KB޽~Q'Wf1A8)2Fy>\KMIW\-f },*dzsfp4!856@ ]~oȎ_9\lV].#Ցp (5 ud?iFda;d͸qCy$x;uqyPEs;0h֧725Q[ fnӖIgq̋J;}f;_8ҢY44Xm@mgPig.]' `ssN_MfSh}Hz VXx9vHSHRU[]C]ʸw7  @"I*hhf4`^JF=Hv8jk_Q<+,(' qnȍ: {'bFLL єoKM?ffi_*]RE*^,B]\ScH]),~q)) Iibd Mû/6l 1ΓSj#]aʚXM؍IɃ|> v^6;NK[6^\ B9@ Y 2E}Bc:8@fQEĢ "gfpC^CL={x}}}A@Wܴ!_{ vDvz߁ջ՚zY297QTi٫f^G 67?cuLeX+ )f'v=~o>ɩ.ٸe|Eux[U\7}n@n_t^0!xfΞ;Da>IŦ1rir/{-ށ@ 0Bk'2⠗.O۽܆ ZAqLت5R6߸&<H^|+t^}v1SJ(W!!&[)`H"/櫥{4u!R05 ˼B^kl/$쏎# 79M矸;~34:OXah"O$X=YFJsOvh~xk#l Eʘ+n3T@OCu/g2e$o޽~cZ^ [siO>zpԢ;l\VA Փo?©Y9f ;xS~sԆXi:]uxh{ n&TGEҫ9kj!_BRnL2gҠȾ^t{ߓ?]?t4gp4J ?j蔞.TDh7/Z=cbCMX{C :;緟7l?tXs#?ܼBZD0?0$t7y$8ش2Y2H$r"m  ~=ociO`;5:`8e)N^D`Ƅ7U˙b7>}ǀe4pg.qo:YM:tE_]#zIWlF1Vm8oF9-^JquqAdl2^Dg߂{fŒ6ʧ {atd,ggr*W> KGO  Jj|lO+ڱh0Ӈ UVml r&ggR$ѵԴ Jxѡa1%ǎC$@MM+𔖔ҳ߉RRJDܤ* qa"uF3]H>MLyq{ʋzG!s. 732 i ;v䭋G< MD&ˇ%soYB:D=aCA&@DD8y ʉ3fM gScúXQ8a]ie>FGTMyGu֋"#Щޘz< ̚3{u~HDܫi2yTv6O% W Xi R36"ؒ?Xw)>4 @7G@.zy,D?G{3ntx6AiKF~q)-1>A-k2YY /6ݚ틚71kJ^JO~0,Ԗ\ݲwc~XeEX( *½7^B\ đJ)n 7"l/(k kp:GŤT"!W!q­˜rZ9k{8&B<"Zj9~x/)ojk\}ʪ궹RV^ѻ뫃DC.h :JQa 5z:r]IbSHiqJtc]RyQfRmxNi6Uʰ NB7r8:P1@H,,_$()iA#$7f7IJdeZ-4ϘHQ @IV\|^z>eRRs{!| 4ϖ5eU{Cٳ)`W\uMjP:7fKCy1#?~.;Bq8JMM5_ ͽ^U=ryе1I-H(VBzpy/K+9Ruy/qv!]ڌy C1@MX@d4}6ᦒԵCM 0 :->>ƖފÉ6jue5f50mUf!9 &_ڶ1:(rKsS5E9"2 3=5>6<3 ;S߼(u!Ì $snF("bhFE_jM΋^{^̆𬿊X{? @t#t(B|²w#$~5qqw4Hp&[Kcnj_#g<io.{!:R\{ồ맴>X<{"{6x#|rS>e7=ed`&XS8r1Rm:!\{Cg`$APrN9}rH!Ru/w8TM4 Z$_mN/;CcJ݆!ˇ"qA=yx6u $*EsŴfSPN[G*w!x^ks zu nݓv+"2Jh7.OB wj&E~YWO/,K93|SMǨ3zecH> r#? 2هwu*]](@ *ys?ѝeGOP(ݢ//7wHun.Aj󍄈'+i=;D޿p,XbK);džE]vIDk|w$maPj - +@6Tbt`Z-s}= i(q#o_] 5>[!>r n`ZL$GL亪ȀO^Aq5hOufE=E:b#h 2|g JRs[ mg](@tmƟqAɽxj+}lN!S #_|$b 9GƷ{n̶_ndi|j:l`)ωKGNqn%|c"uVٹ=<$Fd&͙'ZwKiXܕk~Rū[x^!qZ:VCn0g] . @/!o ʩwt]^5tRH{.a<մY?١n&qg  <,<$aa|Sgr}QZBơ D$YGzK!KI4ި$]?‚:|c*lK{S,s:F8>RQVN ^A=S3ޅ,tyq(̴7[@ @[ kzru1RBBY-(@%-gˣ !q(x25:pc79ξFmڝ8҉׀@ w ͘88:>BqJ%<g`ğyMNJ+8+s!2wq^U2>y8?G=Y9,BRuَ4%'-'$E$ԡ8{OOW7+Aӊ=Ν;MG;2#%w߰zgșfDQi Kw]?aaq,C_ 6p07;"<o75ݩӬdx gg>DB\z4d Ӿ  !߼,7#^re%VZ쁪Euw!fF*JBx (=%1w` TCO{ \7;qZreƩ׬!/&8f4%FNa<-@ Og{œ?}Ng 8cނ?Rag²O6{yiD#?@Nq(nѳ9?iCTHy|gxXhLAi϶M*%Iʴ:xf<~1JXp$nL (]3NQG[1D:J ǍS'%%<$}S3TgUS͡ex*ߝWXa+ &B~$mM4Q@fħ^ <ϯ[bu[iT_ p)tYޢށF ,8gXDvV yyx6|'@%>s-ly۽BU ቬʏ.o2{ECK2'm܁ x]W_$Xp:C:LtqL 2NWBO5B2aHh^srl kL~]4«ƉlR6-B f uR2G^Z[YoxCw@ 9R!}t**9X[ o5su}A#Y]?S: 1&$M2e#Ȫw?;3P{2wwgc~Л}熝2ca6z(ㇱ1;t_<Ӂ b_iHE*Ri>?Z0;-FqJUgBQ/_~/815`5s!* q<*\F8r|N9VM Kmz[tGQ)%[0귿SCZ_p&D2o&m8~ {nng?VLɩ.ٸe|J14+#K^,ij/*k>f2ӆ+&~e_ d1 =^6b-H7ѹݺt.O @X->SJ};WITFXO4gʲm_-s,36Ee D~ b @%sP@HXdgfJK+5yuewΖ`w'L} bΒ45IQN8f gQhGZo~D5[\TgDL@QΜ"Ǫh%%1ҙ6hoNۚҙ;XQ /bѐmSwt%-Z8w۾5eux~ֶ@ƨiK/0OL"-nZrp#{pzx*ؠ. @WOM#cZY wnj  <}ٓOãJ ƦC&NmW\"Ŏ<Æa!]b;OƄgUVxDĥedTttu%8AEl99km׷Q|’2ڽu ڳGp(ogzZ=' Zg&'tGs^Tg1bfy~oL|JaY~_~=8Wdncd9ٹյ&dڽ{lebL[UG+.1r񳤲"72 Ç3,Pډ\߇u" @m3 =|Xy,MZ$o;s#hUr Rze˓f/>|Noa~ƋIJY~ZǧWw߸7nN^}QCm؟ζ hkta}螋G-W'ӆ'S]85@OØkwشL{vlv~tc2w;uq߼TTHQڿ1O5~m,+Y}D#g897.Ͼ5"JioY7 cM}gjQ}!mmҠic瀀*DP?{܊ƚ36D!|gDZMkTLi0}j3JEx>R<:7R*|?~d.(c3sk)G-pkdZ1:w^qW/"&#dh7njRaoJ}[<UGe|{8F @v ̘ Ȥy+?T~SBH^wǥ/7z9?Ӈ褌f K?~ڒCqo4 lwA7m!HVl z{TBe:JA#,&͜2\/-ܻxӇԼ 3 I=Ͳ^=SIi);zˏ]qz9c I1ce " %eXY)ὦ&Ϧ3Z?ɷ/oݹ}94!!ï?| xA#vC޼M^1,F ӭ? ߭/D*H ?~&؁,@%""kjjsh3C :?'; @ :!9)1_]60(9rC|t ^ޯEd5Ӕ{dzγgRO4 ӎٰќO6Tj!Z! =.&*##G' ".-]YMSWME썃%;?m?8 ߽W0D%gTVՐ(&!.]]KS]UUF),߳'1"""2!;+ (ӽnomeLyɵ+Bcs JH8 IU-z.yߦ{;|]*Sm6L].;)>,4&-3CRꡦo#'!ؼTG\~~\QK릤K_GS#Jx>-.6::)3;D IlDn< $ϧ985RUUCLS] @̟bfS 1(ڌ* GGHDo>(羻V iG*@ʯ^DZ 徿%h&P^^qtXYYUZR*&.f ]"af4Qça9:*@km7'tSB[D#Fђ QY[9Z҃r @@P"r > @S*medRYiqy |_3}i]/pAcԕ{5Y)))32rp8y;@ rsحuGIvkR@ @xԧy?=xY#%VT'%%&&$dU ;MW5+0e2}0l mL[drc4@ nq1 LVVV4Ձ@ @ @tKRKkZw=z}SU @  bh}]=Wڜm9C @ \!$2$2b}OˮvS1`||+ΊwwWX[PTQ3klfj$W1yk%G)HNNN-(.fFVcbB|CRҳ+kH"JjFLz*J~;T9Mm=@ ޿ڜr9B @ @ q~ -#V`KXƼ9t#]c惛g Yy+.{-q+y^݀ʭ'28WIDKl>F4Fzt7T6.ľ#g;kS"bcPT8L GRaj'\>Ǥ7"h4a8lC i6F184UO *kY۝[<;9sz@\v<Fٸ}g#=I-&5iZL=qtG7CcW`j8^AQ8)J@ *+*;!-dg!R  @ h5qGSrU,ԭXXI4>pˢƔ߰%0I$sB%>&Gܣ{"6}v5kõ,)J̓#6ns^(ɋ ^q_lHKf#_0SLnsk]h0#r}nJ.f̛kIR9tyjT;N9[3 7_jzNpp[n?@Mc d@ K B9Xb"@ @ @?2 !%,@jb((Lja#YEG*L9REͬY ex q4sMcNQjkjA-:`~F'06#($Ę&M4-.pLS2ڌ?dP;f ywUeeM~UU*ݭ @C@@Pl`@C6#LNL @ FEsaQ@ @ ZirKjW߈ ڞ'HuS_t4u (/+.(aS/zccYEdJ}NFg(ʲ Agة,+ <]^A ޮIMs[:~DKs-5%^\q^VdςYjj$VV#*kcKKS meYrl苧ri-`Da>[\QSQ_T^$*%'&%68pP>aJ蠔qŅʋC߽Y]+K2iA+3ށg6I.)gX@Ol9fXyGۻҋ21^Ε15H @=4TcLeIuUuXH_wg`Qn(@ @ @ak0&ەnLǶMH<߇%=i!Өs\7|m~XTNxa)3ߦힹ3j]W4)2qv{lҞƠOSE4?wn욷K&/)xǦ%)Jw`JCTxqU> B Ni2fWǍ}>8[Rj[[@(͋ LǷm=vwPvOlg` uԙӄX{ڃh'zyu([jϿ[wjҊvON7`R @ 4drI FGMCHQ kY@ @ 'R>)GL1RbrOaݶ9UnJJ%Dzo`ŭivD)W]D2x2W_xXvkZ1--1vEQf_@LBrAQi/"*.У!]T^t!@6Y땄ttoH^zu:+" mps);3+LuiP0ٷjרKwNX6R]gӣ86M 94@ Љ0zs>))Xd1Rd%RHNLЮrrT{0C @ h[D۰FiHHphW[;JquqA#[dl2^Dg:ȱ`$[\T3,8 L^  @ h W'Q7ǫip/'+GsJ@ ' FEb52Ҍ Ia#͐Qi @ @K]&@H"yg\ ȾûLцf  m$&U  @ @@ 5YJ ' /I%h|i蠬FU-M8RIqAfzj|lyt!^XDEw9)PpآTZ+#keΤ]P?v׋(zO\{k}} @6m5Ø4@ @ !PRX#-txI .!*'gxQc]yRשڜq:Aqn :Fru/ⴵqpšqD_p&7o6u.~grASN Ggށ@ il  A.lKmh/AkPYdLjW{ҍ-ifx{FRV׎rumu \tYo\̳ d Pjn쵍m\0Úf6D05x5MH>{`r.Hxu_ի5d+#Fu(/<'._u0("#x2r n1~99B}wi"f|!7q铯 ha cȎN(`0&rv,) q(qX)%2bLrE鑷8p+btIxS8K!U{{x />35xJ\Yk̳Ƕ.br _QTm|į8MVj[:uEQRA3|?6x)&@ ~Ljd}~W@KpnU;'CgYWL.x|(@ lB#DgIE$22% hFxa9*VYR4d|f=%R/)ILg͛o·M w~$:)^ 1O~~~[1XȀ  t"E,t"+ t Ī6 &°*򀬐*'+;2"22)@2cK~p2,@@!\!:5/c@s9t?| /ÖӲE/2H $b$Fl.(q[icp"jܲǷ ӃHaLn#LBB[/Hs-:4AQU9݆N\[q6"(ĉ#X=s2,Ϋ݌guK}3 ٱp̊DؕA  *UU:{@:;7㢃 ݿcx;C"vvc?tzrU}k!  WnUظù*7^з)%.Y1yH_,$sib۪qDL)ͫC:v11)@Pd[ٞt! 1r#lۼ?I-IS/7N22'0fs'.\IQcN9Ddڸp37=rM<-DUm5$@@:Ɔƨ#3B@!7>~oL_sBa6;v~ǭL2oUkf  $ 9߿&fF0g 6$2nޱ.yGV K26x%ty iqdeƻ7xkr! Ywg5֔E$$U# QSĠsLjq=+j'qf9QPYh $(5civxGV J)*ˈRv#8Y圦jp뭯[^BJzYerwz}(Ɉq.KhE[GEeTT, JچFFʲ[ s /@ru{L#Ȁ  ) bb, |ٴ9rZ=:A!pټ;0|ۣH4ɵV:t?f ~xAaљ5B"EUކ}G8͙>N^s _4K鞩S]LT?\CO0܅<뱧}_@O{WlV6 @RlB,QHa4A1CE+N5mQKQCEY2=FM&sfo3diQܺO̫3wrta'@@:pKx@@dd֡o7@y].GebYPJמT_[Xa`e$+֚nܟ)٥U"b-͌l6(+=82!-^HDUym) O/*& vWS50ۭ%3[ &WV^ Io~G}YX<=~xyv{ݟ9gEHz)e %Ȓ'֑zxށ.-7$rwr'[s+6ẻ/mBK>87a`PpcΝǰ7Zߺ_Oy& @Va/HI_#V NkNwL.Hه~VRhPe~ʩ/?UNJJ7Ay ܸiS]7-4VE+sqOlI4J \_3_bIEy+KwFIUg:z]N9m&2Hl. ΃y!Kjs]4\5=%XPLw[!R茍6ZvFPco%9WR6~F9^Ħ$EZVS&/F8ZO=8s*vцp7mP l[#ѥ qm9P@"UPOTL[ 11t9H< $CGExҢCI,3vCU<sʙ yU CGLZϘW-9đ07.Ȉ=w|TJnṟ;|rfq9bM~” Eq4';!Ǿcv[>@DD!B=%SS] $W\EaREyie "lfk*_p6US7fe @H}/q:d_TrqQAqE_25|C'C @' ?:ܓRTWyq|WcAP c>JaP%   &a&s˾@* 5^=zOcQ=L?X@ 5R3“#˲W^?jxOU K&G!u3!#&XV\]N+Pua!M9w0vUA@ʠXuyitxRqjΏ2Ge⣃{֯lcmUb|߼k;7jk u0@Ei`jk|7HC1KwVNF*-LIINNJ*0j,R|1o&[ W󖞣dy y+&g{ p礙ۻh嶴 mי.Pæe#P_U;&w̺&@@,q">ag2@PwKVZ7K><5Ԁ}-.7r`d;y2kmyJ1P*p5$OWijvGSupCRkfoSA{yЙncT>sF,:M}N ;B5+=<>B^0ou]Iшo7ϡUA_uȑrnzg)K?B2޵91+珱5GE*z@Pc%P^>@ _cH܁ I*!KޭU\Q Qpd Ж^[, 1`Rc##udΌ @@ oj d@DY m$9dZ:wfS |hT~SVx\]*!%t}~r1jCaދ(\s#o̯WG% oĄвkO *8⹸LSWy<@@@ 0jhb@@_"۶OCSQ>'W/"!`.ߢIRQۣ` $vȅ%:f%Tv)IԷ^my:EU]D 'ީEjQG{uŎ?DsbJg6Ӌw aj} dSʇ({Lh^w`7S+`"B B6Bǟ?Qd(燏zٯuW2Ylsq!6PY@BOJWQ@u{MDh|1+%뮽>cE 6y#ã B;Ik *=Jj3qAjVs[n 1&8ы,|ZVxŧ^^ G"A4Fypg M@bJzK\ϸc}SEyԜzA7}: d?sY_]OE0|n|1{pu9 ctuY,ȵ%~(zްk01x2y3swE HLRzG6(Vtn iel뎅v:Zp[ψ @gZ~t}Ya @@@^>"!9*@@tw_ޖ]xIo#]$QQǠ,lRi5Gr ͘SnBxxאf3EddEK [M=8,^CKWa5 6f\kzoyu;睏5䇹]3;^۷_X޸j,h8h(?_&Ș <%'ˍ{9 ;XZ\ylW\hK.z=ٳkʳY337̛`!8?lɞZE Ԗd=yF$T,\||bK=pV|uW.\術&Rnw>cd!MYܴiܘ%svxꅫ]W-yC?"bqs~ _ >cUd.ON M)gbqxLfcC_Ifx\<ѽ}$&9S# :o)94Lm׍FƼ*秗+hY9dpYi,6B}}qMr+v9wڲpM3"Qg\ %![KMmhqq1cEy%+ @/եE4x2o%03dyduқ0a<6zܦ׃=0џ Ȗiˎ,Ob+`#n5f(@,.@~A]'\3t=&Gcmyx">ئs @*ɓ6XJ~1yPF(J`}ŗFM*7XWGBj܌ WM^rSOp}f e珟]0gշ zbs Ĺ>HbfEgT)ޛ=|}KysӢJ6]7h@oluei|k+8o::jCcHEEhJZb"gs`3w[k͚c&pߥaH0̷/> -c阬f:P  /YϐD  WD4Y t@k @BC^:}Yk) 2S<~O+ZUK&O I 8ov#,:tJC9%d1/+#[D?_ZUWUVXRIZIwb1KT6^8Z"$䙌{Cs3y~C+_JSpy2!S\& Еh ũAc'.G5 nja##!\YZR%}Ez!4Y^[˨US]6Lcs% xlz/k?.{3n4 aA7~b33 D! }ֽH}uEjBċϒ}tsQ ױ,ʢR[2v|؇w@@@HX8-D#J@?vp@@ [ vDSƤ(_A|C~f뭆}=uidϐ)W uU8M(!/+-ALNtIXNXVRѱc2jwNrz5L`#d壡CPD,'IT{7B&I iZle+]}>c0ra X ";]I#IKn(A `=l{ޗT엢ǕTרǻ15_6Fqhs-guɔNF.>2kݐj,W\k.mp 'ו=9gŶEGcVl>prvDv^z@H~{f3Fz0Re(a)B)o;`#]Nzלw:-(4󿱣_ yAurIK tʚABn~?s`OdBکQ@j;Po GNB]Nf!Ё\PJkS%! ̻vxCwEpSr;qζEU67^yx%"WD?g6ZrNz}>g(/iLq k߄njZFrٶT`]Ak͋6= S6P0?aP>h;y NcKq:6Ic :aaUfO}g8@@o'0i86w5i)h[8D@XlY;<$%\% "?|쓔W5+;f9AƑYĚBj.%fZ((=ǭU%5Xg{,-B*,Bz%j\QFNAR2Hd $./ +pȐԝ_b m?ef7qc5}롯I&O6m/m]vUT|" .up_{'b("3}I]ATRQ=y> q FXu|ZtNxFZa2E1A@E:L$)+zwp:mWR)B]y}X"?u/ܢPU SY)W,͢ *'(Td'7!I)n=zЉUIk3^6'%Tt,:awj@[iq_ ـ@@= HKim@:}^J UUVeebVF*@A@RJGߛh,3ä:Bk/\|ӎI^Wupõux$$"̗aµX|f*J?>=@Hf mu2yW-0{d/hD9vGqZ{k>Ԕׯ? pr;{|36D$ ϲ]uYU8ד-'أX߰fԠQC׈s,ܿX(%7hXY2|.+'"Lȩ(4| *Z4Pvk3_0/2xLc֭{L?*+ 6TRހtIA=yͷZ"JN^;!d VN[uGQd   Qd{}b@_ Xpz1lĽC;yNFHu`0uc,@ޟW77,0ðL <dT?!05b%2bb ȩrX؜c{fLpDGE!KS蓦B' z"S>2kħ̙}ȨCE8~ = $"@ȑEs0_WVcb Tv)JH_Wj~TnZ[ edor4o]83_HAÓ˰qwB8?ŀѹO~pgp|u=I/@@haxæ}@@hQdё'ʟ$m{Bb~sN('8c>6Ww;Ebm䢁 DpCEbZ7TW1s@E$vWS[M2o&:wgE9e&%E DUm >wtou4JL'pyS/T֖)+NI/&q7HTVQJF_D((j lnփD$2pU9Pl1$%%M$c=J YyʃB|:)))$‡YY;%z]#("S"! I݃GF/ Mh|)0ꯗ/O~glSzeˮIU1=Y7Fg|tPI%.6j ҢĄ8 Z?0瑭]ѻ,5qI!8$:vm>PE'(^y=TU= "߲͋[ @M.4(X@e, ȍ)=[-ɣ/AH@4u5BcrJHOPפfzVP'SjẬRhh- &6@H+4FFHݟ?>(ڗH㇏]؈w45 IyY>6# |̙e#Os$>{V1&7[OuB]VQ__@BW["?v\#!l !SJv〔s7=SeqUQU73xȯ4@n6jB/ѨC y#ezLp8ht?4--n1+ 1SvE;NGJ..*(^wtw} @g"]XPy^@ sG? /v-PVVCREGa4: &զÉau RR>GZo(SK+jL+@QP{JT3`w_! uGK?Ҩ~{OZ;䞛>$qN#rg&Ԟw1fI-͋9 .UAqfGH/M[9u Z'${pCa99+"F;v'ŗ)H2 J* ycG}Ndy_~} {V,}/QB<0 vv_7̗tFUB^@ʬkFM/\;]{ڊf[0n_hFEFf eR0P,n)8iOd /w*}qI3w9/w&W@@h5S>6  е @©>~~ɇehU3\U=]F2`۟67Ϫc$uµ[,|Fay)O7ʿ!6oԬq=&בEM"`nz`7v8O\YV@*vU׉^pVCX+fT>Ժ K=E?;`Wwsm\_)Asf P<9W8m=+rt8?1`2#?p9_ZfvRGj݈ڌoO<UwZө2z{zX{`=w7l $$g`RϻǏx#ZLϋ/߇0mMYOnv 264ZT}І UY?={n8ly|[4k-R_CH"q s~Ǽѵ(gBƪFEidܺvr/ߔ IEySſgnKi>Lb9wKv7ӎѕ@@@&oe_*@@gO8: %/ClQGyjXhG;|AQ:7*"lPwL/x}NT6K8HJ4A ͟˖rGk zrnËAG <~(ޓ7}dgF~zSXg n ĄCAs C9sޖrZϥX."(nV5nZd= n(۶ {E߈iOQ xSkjc?ʼn ߮h͗Oƃ!>{Pj.\гwGRÙ?U95\_coy9$XN\[)!o_-ER9*ǻG±D1'o)g 2{PΚ=D{ b;tX$.;v'dCt}!6;0UǶ{8==|pYy5>]a=[j/%*lڼg3lCyνRHI1綺 =nzI,,'5VHk>)>Ez'1`"/GכֿxRZc>4?͂UcoIߖt+קƅ_q]b3j¼~3^=yw}n՚7naIyEB;[RҼcoHYC>c~(X3f$~·NIF#Q>w(|xli-Nu]!!+k+! m,vui߸0sE/oٶC>3Uח>8{+љ%~ I(:2_GBܽ1Es-aߌ>f(2DT[zuP.q#Rȗ%>0a׾&`eS&pݵGzّ%*6nɋύ M{?̃b"Z:I[}[q("!Gy=?|ԋ~5t5)h,Xv91d4p韗Wc:.p$2)kS1 Cʒ}_?~tMˏ^SC-h6l c Gg3x@@@d\Έ Xz3taӵ~Jp0&*?![)!Y_z/}WZ/++m|<),Ct]B˗ >QB㶇)g_1ѫM$2==^;)An=mu;C76ӲE/2'540#w:qEn5vX!APBe+G#CX-⳹)?BcCG$yEu5H}Zy24|iϜ*۴3*0eS N  t%/0hWxW@hr|r?>zږZ3G! $ MIAFRH)*(*(c#$۶މvKݬ_NGk.F$e$G39vk)V=rpe쭢H)ُpɊCG!u;C|rzUYUh񚲂/>1",=t|P0IdN7JX fs$ ōY)xSrZw\!q]\@t" Jse>Bhn@$@4vajea|bZqE&:s dIkbV!{ԜIQ&"ahiπT#  Ywg5֔E$$U#a-Iij= L z qC隯iڲoHdҽc]6 +(ed1liØfTnuKOHI/STSo%^× @› )k B>fFxj<[e%lOR0 BRm=hˑȴ̜Z05 e[yEkgО,0o( 9"`bIԌG_Z%,*!o`djj,#J>" WU[x,_! X 0SoW0dD)EU!CYg>M2jk t ^mKaJG SwƥIm,LTe 59iI|^^:}vkɱ+A11h.*D(-ȉ |Y(;pGT_l\Q)E-kȒRN $$1prw_esWN3-f  ]@(B&Q@ړJSnzK?x"]C9̘1q6TwșOtї2Z,$!7nT U%vy{p֙AI_lc%=?wo%,wn`yGk*aDJH$5m'^%"؏xZs )ysIIכ BT8$>Bh.T6VcX K:NmyA| DeA1C=SBV(5ǨR5kB h6CW%TtMTt9g"~7o6,<hP'|܇98H=Xh9l%@YOM#4oզA  XU] ݃Ĕ}j.'=Pck;݄n־ IyԴd%@MٶT`B%ɴ5p_!\n a9<myu֢/y4= S6\ϯrs1LG\pآNW<>k6bc%Kz^KwotLe7[LJr}I,j ߱pi>XI,.7-ƐE- *'* OJ-*)o CR2TU{PU'&T(*+|P _}hh#vUVqq^4${9ڜYP3~ڲX g'.cYM)'?ef7qcP.cޣO_'L \rQwQ4N& ` @D 031)xaWxW@P[|T@DYj >jIDL\᪅d@zx6,A"  \ !aTʋOˋ{ (kF ̭W)Xu+H~f>>8zo\^˻2Gl܌M9FQY.y#2?|f|N^;!{3^hD9vG18=}5jJן\8c!XoktZ@ >  t-aE,L[@@F M{Ƥ|#̴ i/qed69 o]ԚͥcƯ<_EOaR^z| [VU;6,R0mߚPuS|r-7O+IL0hىr}eֈO3BhsO.g2q&#&69Xwi3o2B6,}.&C@  @HP8ހ%2  @ VY5A,PFz"S^I"p@[ 9.?S"3ms?zU?M3vr+tRep/A../J jkp% wt.9"#tB |rT6#MMM JmJEy 6&@@_$ *)+jh@@@Enkj!¯@/$prhP 1nCW7) ʻhԲNfd9ahDʨDo+BK~A526b@(KzV@@@ >wUU5f1@@@h%hЌ-34(Lńo]䒅#=,A6{gI`dvjGSo)B?!X48_PRQ\ʬ kFƩYxhckjԞw~ ͋9 .UV>6ނq{B+2lo0c.^'aҏ#Ҩ~{OZpUF9=AdLaLZTXYuwN`o30  @ W`^@d@@@@ pARf(Jt9@/sXcF9)1TB- !o}|Y,`RϻǏx#ZLϋ/߇ȣjkkQkpcUYu4)ɌuٛBq.jeݳIrᘭ*5Ā-\n~DRo{"4Ns򌕧^N=6ѻNf8Eg u>{ ?;`Wwsm=>r}os+R4g7B7--d**x{cW2P+s,wl[`Y$ei'Y@@ڛ@X0#@H$w>     0 WW" Fv5t1X_G^ZTWwzFHΆ#}[4ۅu3V 7&DGN+G*#]xy3E(`gvJF8P5n[V]VfCs- @@ \7%.!@@@: $1<Üƴf4vP-$儹 ( گO}J ɘS,v> FK2P5 (|xli-e zNKb# ]wQc ;Օ됐D Rl5q;ݺo\"Ƶ|%J~6s'ih P#d)0TܞkȜL 6m^3`Ć{cU­jbBmu8z>pe9kbq B}U1+{c"$&$],Hfo>  ]@Yiya30(   @G{h(ɚ!Dr " ?qVCR%\8rCzEjoqJ""=N9*)L jܲǷ SɅqUfZE11vJ9!<{샷1rbOL'bˡoxq!#lQ勩yJI uR]׺Zk$aX@COn>C7@@BC#aq!fnշ 9\@@@zsI+ܲZV@rؕ6eF X`mGXgE E ,N2}Ἑ=$X,Hi^oױy,B v#\,x:3RN7L4d՗rsۅ?,aR9~7 r;YUFQ<ѳiHk t諀Pa$ ֧)<|?{.Xfي07^з@@H5ů~CPXtfnAM}gRUaNs?*iS-:w*z/ϿJ:{2^YET?\Sqp_wP` ;"2mdaJmՊrQ{jUY(diUidAy m9e4Eˈ;ْư8g6+l0YJmg;P  | L.2C$@@{ݟ9gEHz)U %Ȓ'֑/F8ZnDʁ }cڿX0i{@jQ=!ڭ>nӵ7< H@@~HMN+-)ìE1@@J5&+[ 09Nh -\b)#-wH"9ɯx@3.!4@%@[(a;@&@@# &2@74P?zS*mB6>#u`fe_2 ИtQ1Bb'f@ /}.\{X[Wŕ?pv^B"0"e3[gh]~QE5TkSդU v@;@ܬQ  ЕڤHԔ‚FYPXTRJVAY]S~u"IwRc8D0!>>65qF&s~| M/-lh$ uP72RVF{AQn>ԄRtULQoo5,9\S6~5#6mEFo<8DDt*=Ą&at@#P\RvKND ,L 9T?_`(tgaK0%%99))jFl?3g774suL>E]˘B+ W󖞣d8oSxTwꖀ}XI  tf韏YGlk9n}{m,+I WsKx7\Ais*I @g&{LgϾ7wn{y.ί[[S[QV+B fXY$7>^su'mAaaIYU &FۍOZ4/jULywꏻnN{Y=-rs9ʹB_U hO{g(3Rsۺf1Ə2Rcɑbȩn@ pѣkjj K12;\BJfҟ7]Q Q ~vڅ@g;>.Fw\-,@@$5`]LDoί?tjy='΢[{v O-txvD]snҝ'ğ&PS]My 1K7? kKsZƣ7Tz݃K];N{ǫ҂|d ~qܵW8J4.wjFmpq?䆚@dyt/+7p㗿mbȟ+ @Mm/^{^=p#Pke޻wɉ6Wd%U"*u010)Ԓ:21m8ۃ9s/FWUZpQVRDt\jjzQiyM]TwumSƺ[Ɋ2C2sHb2ں{7DgRIѾ> ;8kTu?|N#JX 5nrhbg"bCR3rʫEŕT{`)+\DsEމ~`v=!G@xQEva#Gh)4(HˢM2 = i4Yd=W@=ۀ  t|zY\sk㏼6%5ZhjjHK @ui с )X2;l_#Me;0G; *\WvYL!AȈ^*bBՕ!YQ1/-d V)0?~UKG8bB=doݔNOuDχS8BU-bI7@!~y ,lhh AƇ>F˛.l35[Fz~ȩj[-n\1GI6PY@UsR]*( ]wκ bo?n;p.&5D_PXQX} q*2\(!0iC/=qK|F!+9 7qW.LW`GySLy؃$G>}d0 ZYiWNz,3Zqc,=)f ĥ'.:Tƿ͍yAT#@Hג16j};ULnOٿyߒ9Cs.]W-ySєb1vĒ_p]/1*@0Ԙbu*qxqVa?k1ƼHfwE/Lqi٦@n}")kmEح# a㒹^!{&|<+jc(6s\<uiYY/3,?lS/n~mnFK:Pb=:jP   7Ks{ YN].XNp|j$ x \b9N:ۅ,y!U᡼!sY{ ̋6VpA.\m.8$lg/]/=5!;SNaxtK(^:CN̮JO!=X ofc~ A!)9yd54r(7@P{ƈσ2(o_:5ܴBc] 1opC9~yܳojgšw1ß%9Hߞm C/!K0kS?إsPoIO_*o2jg\srq?)ECX46p9hګW,v8ɮIOӴwؠɳmF:Z4oӯl3d`>@ ^KKpUX__g 6Ǎ<ܔ[N*0 $0B]YT[8ܕSj6gk_#zGW5dBDyMǬ~O8eɤr(QPDc;@p}|DTzGJI(s\r*,goVgfB1S{7I1ԄO% ҥgwϱnK }^nRtߒXP<؉Ay 1GHWƅ{)֜C_iJP tz 2'Ҫ’J|7i1gkg>8 'dM&B+_JSPpy2!S\& ЕԭGuAg u0T̘Im,LTe 59iI|b= (et`n}cIg^߸LLZ~cCmnNW%iJJEyA7Kn> Dib*HUĄ{֢ieh_j?a'ʟ\EGuۮ@H\]YIi$Ksc#skEuzjv0gj]djpґj^r Ki2eг$Š̌,,*!@T= Hv!|<^߭O=K.IqayEdHH@#}-vuAqKʫE%{UW,.ՠp6~19r7fԶY܁-ksᅨ[$]Ag/OQkRZmiQL䆯vaRQOjm&T98tD HwA0^^?#x"|@Oӷ= ǧHFFHa?|/wΰ=''|W1wctvEc+ݵ|:#"u%Iy,b'0x7 mF 9l[4PmƍcXDrNz}>g(Kg-wPװMHJ릦%+a4ݞmM&TRuʣ~}fPv$|tPLrR XڻrqB)-$(usGI0OEPw VSEpSQ__?Zњ_N7\iSƎ.~1.V˛ӵו5sC ́=Ug hR[>0߁rW޻j 꺜L~Sn3>Od&gyVmi;+ToQu`nӺ}m4tsO[& B!I۷%dѼc7K|(]Z_AG#hQ?r+Lں|ѵa҈ہs,%q)ln;>P  |&@J7QfH}LF"n,n e_{fhl*+)_Q|܌sQoSyY fZNA H~杤%,/D:ڧU6NP9 Ϙ_72uE7 奛7/'PaHwwp:#Mol8h=3u92 'ͷҋ)oHAJ4l;t~:*]uj" |="Lւwp|cM-WFe'Wq!Ӳą.?*U=a Z\`cUU8<$%@N{0Kd(?DSk$8r~ vv [](;~=E)Eť-G)"1Ed!YqqYQIi^-₾ ~@`.?y2=Rl?a &nDgers+P=D ϱD^ܹFfH߼X[$ *'* OJ-*)o CR2TU{PU'&s~*+U3^6'1S3~ڲ"_^HKk5e6B/N?--${?2n>4:!.;O" Oћ6.۽X+I!m*$9MfIo]{~(mCo~7IP7`0]RGO}-|^;G7͝VwBCWG73Z5qcj*„zJ% Td'l, 1p-?bASd!@F?#s+kg VبX>&~^rmU141s A)sgMC9mdxw: P  @Qy&S(nckin*/#Q[QVO,KX;#_z-:tc}?Y;aŸ>E7_ V}EGbtLMUVRjʋRCCBj)kάvS bj_{]cMUqҧ5"A) % bOReޭ_twF4XH( E\xaPfj5S0.&srn*H.=kG\J VeKc0/"}hm:\V0ag!T?_Y4|>p:?켫^,;'޺,d?^k4y, iq_7+~<*n=\3]훛fG|Y7 j_?܂FA!IieUކ}G8͙>N^=S>LT?ȜĒ-SrXw9 @ W&mtE{9yL[sdӂwHi;5!EZ楦HM]iuY;yJˬ!:͝ٶ[t)"\#(:~L!ǵ_iAaEC׳2aige^}KoAi,={ȏcQۣ2(?Ows+jvq cUD'쮕YU޲JQ6)L~wvَw?wM}O Շ8#/K?jo/筝yX:elaa?@@:4ڽT!Ցg7Sp$M6$*5k&[+婿2rbڌ9FMO©!S]ιPoy<ĕm6~Yt 5a7RBCR@Oth #$ ֝ X?ʇ$<՗f M>ѯ_9^ٰܳ. /yyCu0$Co2N]NE U5faXPxﺰ\_f`~QnkOPӤ2gZm5i&/*FO ՕDVhwCCf\|1J洯Y c Dy dνCV_<گ=r^Hdeg!smYY4\WR]5 qfۗ]Ch)bzr6:㣃J-qqV&H eg'&Ĺ]@o֮݇g@ܞ^Ӷ0#/:-r" ~{@PHL\BjZzqE ,uG{LU1d %b'IM9ڦݔw[' dwK'yQ?.U qqV@ڏ"ݷ4ĺ7٣h.QTec#g ׎쥓T[,h?xDdҏw#~6j)9GˢE49 ҺE텯Nx` N?O&''4*6R"$6ojڪb+|*Ps|t=$8f*uh=A; f8 W#w[fN&Y^=8:Zi78D ^hA'I]AH.JKRlbjl᧧ 6@#E!omU5vz9!&& '#,LY&"ˆ b Ϟ=31k:Hl+F?>(`-N~ 326]u;{k?12<3Bo % ;Cܨ RϱLhԲNfXEUM߰#^٨A 鏾Юc>4هw#5~Qhݒߞ[7<ݮ Td~f4w^B D|ƭR4=;OF񁉵 ZFPnvdƣn}s H.j $((/*K+kI+o]+k IȬo\Er K*@v'a"}N~[Rıj|(_34y"MjŔg^x 䮜Y[~lU'8Ȃf̻};fݽ2 5? u4UDZ/'3\j?֓iJp rORB^69i|z  $#  h;{ A!A>\o:ҿb̯n*WXڢ}OU9I=2&fF# <;PR2 J ɐcwO^u@] @h >-FZZVVFjG,!2 lA": ؏4~YK>iQLD9ݘ8t3~Y%o. z̾AgSh 2靦#g WΤBOk{ڊf[0n_vEFf X~H3o>=_H$&ʋ%Ņ;~d8i2\p䠧%{g6$j=YF][ ן vK6hMBGP7fݮ{y?F}&?q=9oassqiNlW;#4f|X@IDi;+ C7=\UZUP5`d#Y{ W󖞣{ ɶd-Kge yT/8ҁ z9$]1cDQ:Mj n6#lO׻#_?{xxr.[i$;;\C5 3e´y}{it5U:ZЈek Z BXB"ݚ*f\_fh/ڼ$*SE~|u;Е],8{O/-Hh<e?8DK|R_}JKKs&@-)<{Sy7oťm1؜22RWm& u>{ ~v"ƥ#xgO^ͭKR4gNg)!TYwLA }Ў.Skkk1QQ#$3%gofӡ7%C"{~RQk|}#l[d^V;nQN-q>uh\Eq&ЂUY?={n8lEo*|YT'SLV %2DQ͙Mz)(v_SPIyG?依vhn/Qۛ>bIL Ī c߻zkVo-767`Nj ] e _cHU26CM *!#*ѤP@Z@NG(§W ɏk>&2^;Ѱ)]mǒH{9ll65g]Hr”1+@vCF_pC\[Rf>L:hkN$(*&*=5#"*!Yk@P`fiS4/q}icuU~~>C߬T5LMPS5Hpa4g%o? |B@& ) g'}]dYhr޿J˨͒Ηk%~yX|5KGFu1SUm.O MB6c;%.]:VuUA۔N .P=]]5QN+!A{{+?iQ"ȍO?:(-kg15CQR/f2Z͜9P;\_?wC)KζURP`#ː(R׻~QɁ4T?Ϻ,$W笙48dr"Ҋj#O6T?vf:Tqmӻ9:Z J RBB辽b^z }}-ºyC+Hj r#~x{䕣 .<~v)n [@Le-K$*5̹\]YѤNJſX;03 w9369_zb(S|:\6tէw+|-*:rUN:j#/-NL Od=]@a_&66mgpn}:*%2U!>oLfoOtt#M\ P~+j̯ݜIK}نz2QVAYߨO/nmՆ)fUԫh6r6Td%U"*u010tl5Cƍ |A&ećeVב$eԵu-YRtf@V䬌Ll؍`yy)󺀜rt)HKD^KcVkQȲnH  蓞[jntm{ﯙ5dGGzq`,%n*Ԭ,UNxGd.5Wn6vCG jin.)ʧp6a/"- -Duq>-ҔNHDTsI%Ɖftb.Qp}4Ԛu׾]!#㍱HL\%2VKgg#}xiՆ%V/NH$5G5 Ņ"BdRYUv򊪆~YvC(ȗC~! GD1'o)g 2{PΚN D{dKX}vL|1Bh瀼Н)C04o$ptEXQY|dD?ṡ֎6BQ{Tק\!!+k+! 5q;o}7.\Q]UλjJ%{RUsa啍Or=Q^9_M#ZVAdWA6]ŜkڒWNed0 =F延̟hx+ .EO {vh[D%Yo!_ji/?c'cSӵu4b ?E@@@Y6YC9HH}Ckj!KwݴAv& $_eV&XigIa_!_3P~PmQj.4ug>GOho/CHhy׿ű4cUP>8,,X-j}QL@Lz.n3 d!2b^flnn; Á*y,U>>%pV|uW.\術 )kmEN5 =% t;. 49Y.Nh޳o1Eۼ+<oIo@kʳX337̛`!|ծ|>S,`)Yg͘ǹ"j˳ݻGMH@4 ).DZ揀2) ?>p]u}5)܏x%*j@)K?Dx=qנx\wk0>JL%!3<;LM$zzo_w'kQ WĔMϽ }z~ʙ:Hu?_!}+ LXz>D3;7f4@PrK)+iikt)jB(hvb*r7ctl{( (!-ahqȈ!s&[6K m\IS/7{-vdz`c8I>+w =L@g6d S DTT[2u12$!2ytE"~ N?f{: ~))QcWp&9X{eg6Ց8F[d}{(mñUyoЀw>ʲe@ND .\[J DiwaMR;d7\v~+=|wtD#.h|7dvC{ s6zBU~y<?+"! >/_wtC*Yv(US>y^u&AΣh.92 6cyn_ϩgꝂA~>5؇I W"tc!`'&u}=9!?U' h;8C= U5BaH084Rލ-*.r;S+ 3ӊ+ĕ4w6yHG_ ڸKPDҶ۳A|1¨Up絏3r&-9؁_3)&, JXtڪ!>U¢rF2/L+*-<o⯐,h ̉+UÐ!.'/z|і#Aai9յH,DRZFAQI]SHYVjp뭯[^BJzYerwz}(zԩq=+j"zꂈFC)J(8~)d<!W@Ç_dxVd&4f)dA.6EF&dWVB2ںV"EջM[< cS(#].#"K}sʋ6Wl^ fw.3%BZyCjγD3wos&&-67KbKT9t#>@}CP(MTa>: I 6a296WqY>:w唱Z~ uP~\]UݢW!c l7g2 IbLJ/IN\k=D_GFB0.fݻ}}6b75M>7J]rgfXeD-A!xxأ6rASb`L `5 z<^e^.<~T'nS2!.1>y5b0+e': CyE?)s"KBb_a  t~uPP<{tkB֌=Ґ5b ,n:ً{{E'{yDWFr&/fbUqZ{k>ԔZe.ܿO| $ k"ߟJ"Lȡ>ܘ)h@E N@ /7X0:{jkU5\a`lvS]قhjQac۷9ps-_|#Ë+j Z5E5V COS2I{X4" 4H0e@h"@HTZCRYJfFL 39Xw7\AQBT6s)DEGauU&Zڔ@¦0|@$$PSC![ :u婂"#4eұ{taR]=YHD fG~|[Rk N QRzHqqӂ^~S-}eWOPKVSXDj3 {󾴐6&$Q 5T6D"(:>N~~: ?p[:-M@#aUw\r_s5&\sn/q$rx'^֚אH=ކwuZ?x)f5x\DuqM5}V\M<Ѩ3pB;F+0%m*Hw_'Mj!˿|j@H=h](,g!mqŒWA I !:QZZ KdnQJIAxE$vW}{wSLoJ#TSޞdnNvtkEl*ʀ 1_?j -*Y;/zK贞)Cf%}wNW`]e>pĠGOTcQhjr:mhGuQ%j2]X Cy 1Q'2Qsc6JeMN"ze,B,.oy(&&LKcpշa@[4:"E*Je볷r[·{W C'wLm7T\ڰp?Д-;l4[z~nwo)#$6ЮoS刳:(|t|4!1SiSnZ<^\J:) $묫j nNѵo>!G2 @@RqŞrxt#\>Z111X &]B(ݓBVv"pCj@%Uiit. IHU0 *o- &0q msjzHhެ;.SnpM-_ Dz;KO ry%j]2{pU ~{|vCEKdEHn㲹`Ƀ/JO:Ć`$i4~R SU>䵓xwKg(H͡&Yx,zZCW)w<, :}i˄){Ji nÂYkK[eQ{J F/ܽԮ/^hͬG ߒNCK}9 b @r¦׋7Š]gTd@yϗO]KFo(5B".GONVh|   .[ b0(*h?a'CFRW@+g,yJ3go cr C#$ʇV(㓈}y=O~-RQO Doq[r}'5Z.YKIۓ۴Y@icm 7oҧ K3e_{*-]@n(*1JxҕBRK4}kDxm)>@@.A@X>׺mvfc]o336?DmҴS'̨A[ I0Qbaҏ#Ҩ~{O 7'4.߃G}.ȍ߹EA@'Lwn$9s]MC?.+2_1wƬLjzIuL --̻) 5Ia^iHQ5y`VI rs iNow )  @ uեy%jB ;Q1ERV;6v6jǝ{~3] _~zu5>ѹJ2;vEĈxreEB{;^qTl`}ܡ6:*fYHPxjr޸y@@ NlSo?7?r T+3Gavv71WEc -/:FKY鱌߻v"Ƣ^CHn:e96o͢Y_sz]I鿷,Rp})$:h̢eS(Sg/~ n<Щ|TϺXb% grj/zsro2~ݩ*Ρa1{=xi$MNJAoN]S˴'o?|v>7~-rRy]b'ب3 4LN.5(oPZBmÅ=5dox³ +}=ii!MjJ3yZJB~n9L2py}F~c7}lگ;M2kun#,_?0 CB4/zWN ){vYH'Y3+deK+:$=/]6S`aA,o1@ @pt)ob GfLq>=Q]KƑ-yj顥$# usMr`/NqSFP-/ }^$H^tDZ㲜_/2 HdJy7l .;{>,G2ok7ʒ}"6C`Tie YWqf}Ќ5=3%X;OEE \ۻޙcƏ6('נ9 ?BG0A^}z۔ޘ @, MCF䣅x<~}߅|T%UO{N"28H34i-SmSBxE}]|&&1'\b;Ԋ'[ g9citًe[+^<WQʐ sdӇ:^('K~ԯGm@܀NВmB#>5QRYs Bz=[cJ^^9'ܼr9!JZ>a AMڒ?{ӓX OT^s=xïb-DekhQEB4_D\\AYEY?ˡ׫]50C dgoyw][[͇Z @ : ʈ35L▦jn qii$'_${>A $UQjJ?z4Q?q^N1Vb%ej>9'{7<3+3:x1M.ar?%Ć_z4l>c½EDŽ+ pA t| JHJ={{i泊Y% z}\q"|ALqCSWN OO(-E4 ̬e`, xU!74/GltZZJ~~~Ee%UTQeѢ$Ga&5'if5{kRffeDĥ5tz 72.;G\aѱdq)9UV=vZxK 6,!jO H~05p8pC/8*Ea̮16`B!3 ٰ%hrڽ轷 #9*gJJf~aiM-YXu{01--~S±;?7(BJE37F-UM7Izn8b0)3!>*gjZvQITVSaػo]ԝk{,Sנο5RCt$5@‰Oid\'u8*zM-?k1^hܭ1ƠY+!@KȖ7Ou{ИTT) 阾+o6Z1ޔ&|K)j[ֶ䃪zU\Whh P]>[輥+Y;s` x3@ MD"~єf5޿͚a@ @ @pVXmPõx첀e\]A0i<Ҿ47? gR~IQXBUSX].ކ8xyLE ~ JֿXkccxi^x63AXZ}Ŏ+:-$<)5]ddt􌌍U$0yH :  J,[uqķdot7- .@tDpMMu2@I?5I{ Ā@ @;v޼LӼFTi k~C=-`.iY_zXzuX7K!d@nNK{nj""Ȱ5Jm PSy6b=k^8`EZ0pkt=?KHM @ @@@C7@ k߻?{薏R-gٚr*bn5Cu3ioaY'lrѳ1w;@w7,@jjj_kJ&O& @ @ap^@qLMm^sQUlQkh?:06=>4$,'m9 ` go‚fxfa @ >o0ζ33*X %DamU-`>z@@ 77/_DB|,o1nƂ}.c%r⿽qucs\\]>JlmM ';##-{@?ܛ۩Oee5 @%] }"E}M,7:MlbnB4RҲB9@ jk#¾u5mAݫeK|5vx˚C+ @ ڂ@ L ݔ@bb۷.III<-YDWWwƌ'nB2j=kJ 1  B1EH  yi_嶆"@ $B~]@ е ݹs2SCCåK.\P]]XUr M}͐W+*6N 4(wӘ, :)wo>TURXj͋w l @h @ K ڀ@ rss~wH$7edd. ĚU>~ܮC焅EB W#fV @!:%M}?DBU @ ZOg@ B9bFBq3fHHHpkY㇎в  ‚d57z@ }|[07'[@ЀZ 4 @ z l=C]@NNν{.aaa<|G[ :::kMf"h h:0 Q+sMo @&4󯦦/q{B%@ @ @z 4|04Mp֬Yh X&}p@31=2z= ,$(w OJ읔' @ @@ Бnܸ䔕Î)hAYYYbP!BS =رw++0З˞?y9WzVAQȩ$VFrI)I@ @tvy :@aa.<\p! T N WlQa>Fj i;x M2B @%B=4`@₮ܺ6m 7H$rr `^_A @ ;@~q;whnmm;w1@faALx(s-a;g @+kp Mɓ'hO>TVV?>2hnnC @# \|8 Μ9A9@@(H@~i=@t."""` @t[ 춇@t&((x`vkkkuDj{!ֱU$n@/ֽ$; lpQB(_p `j1"B 5l['*],^cCm@ dd=! ;(@@ @Pp ХX2󪪺M>}ŶD"@ddd`TUUbiH  jI$2>#@p$G6*,--G6Rj(yyye@a=v`9@+Eqw殮s(. ''CL0 @ YwSN N~< @"PTT#Ṻ PhȈ`Va+Z\vz#FU@ Z>w&a9 Wa&JWJ X9$2Y-W͵ ~Cqۭ_'`g&9pG -#–qV@ Od2m1rC OPȽ]l|ΒOn\zmf~)ZO1& q]&& )^^^ vyRVV0@!YBTT B@8@ خ]!?:ʬ_pڗAq&O\BFʘ:oR{ @@ X v0a ىrr py9)I03L-ciH  n5 @taB|7wїXtp8c8u} 1ȁ]C9yi\Wj~Ah06-- @?!@!8dUUUnnn(.N?=epРAx<^=ۀ@@Xp sj s@tr|@ й {a}QcZj'ҫ_]uGJz?+^8|g0d!72P"yyyXQP\pƌĠ @ Jk*@n#͠ @#¶c ݑ@NNν{.aaaxm@v ] 2( !)U"bjZ}L,''R|Ok)ZSȘ⊪Q iu-=~Lz{JmbLXDL|~q9QTBUCLUVUǎRS`h^I^1*JToxvx%Y^Ei`oM۶DyAz`τRi/o\MYAHj2ANQج 68@%@}5 z4MMDSGI9s@A GtYi /DD7@ BG>s%2a3aI{ @vU;PO%.N\߸Mḏ ^bԖ}`bӺ)NrNl,MXaiC8(_fיĬZۅ(v]ٺt u{Nߠ,jS9+= .*gY%\sx}-V#W.g֊5fGU@S"*~~qwRe!mcgX#TWuբ OP_e"Pt㽳w畤km?VAx2-}8+bޮ 8g),wcjKYxPsyIǷ/qultYy×><UFtٴnaE"P7Nzj^XTE؂8RA XSq/~?VJ%$0UMLp}s݊Rhoc_}S+15?掳u?c O߯o\'C*+o)mĠ/[Cj!@!ءS @g%@"<==Q\յӦMCqqLO1qk@ ?#HJZ:& @ ~r]JPԝ6c kS5%9\mEFRW6Q 21 SъĈ9+mι|M){173cKS]EJ\$1.'ҙ{J45VI47l 5ʒ ?_V˙K>;"i#{}sQIٵ59` )Ci5U!ܿt7.Z!$(V/õҩ! wϾPXVUV[PZEW."#ѤBz&Pt㽦L_T6ZSY Ď#$@R7oP(5RVVFAA4eܜ(@.G <+x!1@KD@ 4i)i?JaG~wlma,#(;/ (oO R]V-[l|b^Aq /%-@I*-) kW3#1~O 4O=yM*:}ѪɖؕƎN; S\U\: E+Fh@XJu:rDÎ$5KWaPOݾ_5TE662>}k-@s߾Uu)'[iQYMT:RZ!.}ҶdMsC}8C/@NF?₏?F1Bn M4 'O,"I+n  :;.[ dBC[8  -$w=Cԍѫ'j46kle3pr֟kakFϩnVMk^jU\=f2-vud 34]iYLzi<Uu&xt+dNJר"p(M5\mnimvǷCJ7Ƿ%vy6bZ6]/ bllḑm8!hjIM[ϰQX $ @(Gl@d(ZMOc>311Aq r}S;(@.B <$DJJGX@t+̓,@ Bj._3mݿ (8^$a`[VJa8y2Oe ZIPhoϣĩ7 2ffI|?Жj<ú|wYM(J}ٰ m,Qk0%* VL7͓0gÖk"2":g8i4Ow'M p118yJ n1#++l2:@A & 4!`@n!u z@ D?~z[vMgaz}k>x:roA94ya[Ж[qm=Uޱ'p:8zG/xUl 9˳R\TSWekr[Rq\}MU4+V JR,*#SXM{ހGׯ(.xK,Dþhy]pj e@ $b^Y HnMև@ T~WxOZ#;oAQ1qI%l rb^[2G{}#iHEy?bե%hf$m )ee"0ɦ%ĩu&d+jzC!9~QP4-=kgvZZ #-%#5H)JϐbiI-G;@.Ph0""zZh1@nE <~}Q8l@ح>8 j]N ]@IzmxE O)ŹQ!ݹq+"^|cߖI=b$eЅ r" 5 MC#Q&x^aͯQ\~ρ ue6᳍je8\ݐU?2-omd|E(0E? a+L~x661n8ʹsk& pTWXڰ K HSWcf ڃ@uuWP\׵=9u+%%5{%K :;ȩ- @'죩E,VH$v hr C b\gr(i ޓܼaчB_/u荻svPIi֏,Tk3>zcĹ߹p׏~άvOMIu8?[HJIBQ'pҙs}uG0n&l1"=FL0y^"F,}߇e8vjM_QS1>=7wVI5V])ubR}~kAEq:;  ڈ@hh( ޻w/''GÆ [tYPT @w&nOEL|@#@$s S @k Fܶý7;'i2rJjZee%m:UVI%VRuo7Ywh}j#J6_?wR}@5ԵS3(-؎T'>Ek=}5slvE֜^eW~-$X8Ӆ>{{|l4fbl=p1޴eEO/ɺq阕^t3ŏ]lku;X^kL]ܰzA*Reʯo\Ϝ:V[\S݊ m @~~>_͛߿a6ZG&ڻwobP@26:`n=KCnN(n~ @ x1c=K'g4~a RUyfjB//j >])6C`Ti%U,+(3þ}Uxvu*=,,BZ&y3)iI_|>WM$SVԜtǵckF1?e%|_߸fɨAVjҕ%yA~nG%C'Qh9_q>=Q]KƑ q?P(,fѓ5Q/N144ý|ilw&ʼ̸ȰO>> Yt/>{~uOEEH*w3{njomGEQO)J-82Aw}Mal18\6>xqM4ݼз<:nAr1Cz)KH5U9nwLIM9w3ϗe<;< h[h2*O1ĦOxb[[[x (@ƒjkoa}t/d2c8{  '@`_m /9ZES6.et=c[%5ar?%JyuD{,MSRz,`c9>%6 {wۻX=A{N?5ѺrJ+BKP"--<9",klѝ;Z~-v6$vG||1s=[߫WO" npoc:hEAۃnIYy8{G_N( %ӫTBW;c%B!YrF@/niN!3ZD*>qI緙c 2S*_ܿśo!9յB¢R2Z}Fst ȥ).rr+[Szzu7~{}?xE챚7~?&wmxP\ANN@ x`^_I[ Btq5LO tqW= @u⸘aZ<~Dlxp 7 ~ȸiO_M{!3:$st{;z֋.g[3QH|⪺lܭv$j(orYd7@'Oc߮o|U-ZZĦs/4|4m1r==]p'#dٰjRAW\do{};9H!I%ݹBAI){/p:%Z]mA|׾u9 HG/y%2{XS]YY1aߟ=y_=2)YKQ}:Zw^#PTT#!`ˠl@!#*&nhde!@7'@ G7?.>@!6;Ƶ${ ړGfQ%ۈbFMYi8N7>N[l^ ,&OaT!J\}h{3qvW-}ow2cOVа[e^mDP1Wy\rLS^1uz^ls ǥP~^v'wEiZTÎƭw }̘l<}eɆQhǯQco]8yݐ_epx'Nw7Tgv7(O=>q`1TzX,]qJh* Uã7 rv~#1'#xqӖP@ )$ Hamn|~%9+(X>H7r̊FxeE]5L]6Y8uCF,kݞ@k:-|{xlN^!NHLUSjĩ3'fx첀e;jfiԗ[/f%%$V +e`nf,xLXZ}Ŏ+:-$<)5E:ꃊJZ:zFƪr:g/k1(i2y}in@Ϥ2:F6Vl' *=6ȩJڽMyv~''/?-3_ѩ$QmZZ&7W)lφ?x!Q%%! 'j9T˶OUgw$8 >=| @bb;wД*P\paT @%on b0 @ @3ōw30DU{Ziȵ ,oDnqy36lE?S'-|ҝusȉO-JYanB¯_i9e'4cZ%_wʺ#gi & sUnj jb{[| @>} zyyQ(\(**Λ72hee}a@ oa*,!@@;*;> @I E)XإO`$S6Doۊ l5C(mJP\m4X\\̭/"8ve˖ىrr -#Ü4@ @ )@#61&,"&>(*cjnڄzVJMdOy%jz}LHL'.!lRUtĔ*1 e5>&Cr>'Y珵DSO!_L )iYZj)J5):)w޸q#66 h&1NoodUN+b2.X,Рʪ]Up Ԛ Z @tw8QQg.`^?Q"@V6\ &!@5W=):7 7lYL(!m\U@S"*~~qwR_㽳w84TD!f'E~p:KD&MJΰ<ȉۺjхluetbu&V|eؿQ&? Ll^RS+঱Յ_gl#tϬ\냢XRoWQpJCX 88($P\9/5d?5 g̘!!cmLh ɕ>ͧGy|yX ׶GVKm;YJ@NPdtN_[0/X50s{uDW&#&40w#NYD7+.[fihfO3j(C/cj) ]O؀+l0'?}GBIgџX>^RFC Fw@ 5?L]1)Ĵ:# ZQ_'vvȹM47l 5ʒ ?]V˙K>;Boó{UV-VP "b>Bz&P8GP=wI\[SCR]I,\ q !Ez n#DU5Ck*X6>ꕀPԝ6c kS5%9\mEFRWH0GUnji)DNKCBv&%:灠x`5-Jyf0H ڷo^~˗55\FdA 8H p:/`9}U8$9ͺ 8A8L2{@ ҔOpXcJ‚W Jށ/@yn6st>]9NN0XXƑ/@  +l_E] }L\yyṞ//}>bxq['SkË<3fzKx86zc[׶_OyP26KWԥw׫V,>kSؓ%@(?cQ%4c}?."!3 4S`~۞oO]CK}Ru MMp²xhh ɿW8_c&G-g1jmBtsK ** -%䔑 hPVVT& Θ6wSmވ*)/HF_ę n )*&VUY<{|oΠ̗"MžxM (l[Cm"cT\\B^= J)III_CӰ-X@xH,p|oA@ #Bl&>>~t3` vjC=|$>7M>ٞCN~j%vW-$ӎ&2i)i!0f#;L0QgytҝIc}DN Rvt?4 ADϓzCg*kZH-DI@ @@@=^j[@W=s4PI_G0ĢumkŤ'O˷m7'~( zU=o?zDUF}}l\sMr>o^0b~S6ZYL߶iؔω;g@  H(.ZI%""2m4q|N=PǙ׻Z5q"4+`' '(E~~"}g4k_k2 qHsf j.?@ @{ag@{[}Lj-#*S_ifͥѮUa^hKwBHmХkeO )+9??;&a`[VJsN=gf;[$_~ zΝC @+ _z9lGS\g9gRH11Y9UDaqYE5^ƆD^һf6'#H5RbCrxayC uN#WQP\&$&r0-e&A)U^Q%$*գeOuB@"Wuzt{àWƷoѣ>d拓ե1A yUյGy% }3cbB rp@J!1)%5]#u՚uI.񣠨 "f &A&Ȍ ̉D!|&Kϔo~~Yyrj.ԒgY)@f13YHv&@Wwjʹ8R^[ltF@H+ ,@ M$qib3kqm=UJx=up!_õKPZYݣ4k3߲pQ <|uW%nO Ru{ԣ^?J@ii͛7}||(LS.|QVV?>ZJܜ @-1WIOޝ3h{1 )Wo0^l.?ӰV\NkĔWm4mI 3zT.-GPV{R8dP:]XeǯلIIKl "i+P_?Tm~u,'ߡZr ՕjzA9$Qb{זpݛщPҳ:wŎT;qG?ş%w?=F4^m)?]_:W^C²i?6(ptJ3W {N{t3M' N-~*[srQ޴o6lga}֞ ,hAk^1ub}Pﲙ ~o, !dm",;%4(9LL0 @ ?GQ0M5K)+R|+'-W෠Ĥց6.'k_(0h%F}R4>>d0>+3egj;@s@ϟ?ǏQ[BBB&MBqɓ'3ġaW_I2(ͭ}[1zF1rB,ms}?1>+ Sݝ~]76-Jh1:ZPߞXz{Q (KI~vVę~Pr}]Іr8R cﻺhH6$Dm{~1ӊrnՃ=mM55C&Δ8J)t}(!>wyN2HԔ(w;} Eea>wqep PV 'q\m[.y.gQ! ҿo\8/.CGoo,yy 2/qVq*+iMyybL&3 4ot oxQC#]\x]*#=8Զǡ-ԂN @ ښSk~dԌT}Lㄗ֒Jңn;/7fK)͈ ΍[@Lꢱ\Z5#g28|~wvPԉ$''߹s?aҥK,XC @P`X^ha/>rs68֯bcg{z-5Hof 2RE?B|K)Uw7y=9ŒKڊxv Eլ P,Ύ擐OTx`ǮsGu̬,d% /B_V&ż^y _ufל䟾{maO?wPuK+5-;vfn=4J']w* Ý;D6h3}ITc}釶oƖ0ﬨR*AnWn>FVLU)ٰ͆faSYH"PZL}d$_:@ ڄ-lhZRvl׮U-h҄_zXB@!,_&-K2ִLjwh}j$Bgϝ =vuGoQ?4F*}_?qۧHq UT>D/sV8^.#!*T7tˑՋ>03Ɯz_޺uusC(- N>]\\!0d h"\=#NO1Qkf:gO`5\f߾"K&{ywa_P }hғKhYz])?}` 4(1e|?A3KolζW} L#)b<90JZ-=DRPr΀A([u뉍3Z:s{OGw㆟RK_zG%M˗ֿXDׄmGwM5x殯]ľR5Lf:@(89Tf;"{=|DoQ*xR gX /X{ a0Q g @2.(OLjkn%КDw9HtJEtdֽ6DGbi(_ @ `FY2|(!!OiEw/ݹ/BkO- 1?e%G(q͒Q+K"?gJdhލsj64D/.nݶ$2/'3.2쓏OBVݰciz=s3 յd1YWM--L %¢:}lv=9\Kru}Y@"Sʓّa9jq[Q̃R^J*C.K߳t}FٚP Ug&~ CᄇDOKעIӻ4x3飯)IK|r乨:,?|S6gҫm_3W%7Nl v=3an1gh)*>b7?P\a;"8i`a>5H2|gg,X :XW9xH]1%|Mo  O>wQ}geYt'SEy!d s%<'ѻ%*l^7Jrl۰` :AZAHrNXNt띻"g##+_-@(CL{hHghtfy= `%yB8h;jj*F#>>G ǁg" +?/' ~ExWU;(.:֍2r m4nӠ /B#AVT$Ę],Ϟ H||$EΆTJ=f{YT_-KVGf=k~T5){PK8UNHo6U(3+D<./9ZkKx嶺,E8'ֺ.^^}x[ؤس(@yۅvv짺S칷1T^Jq|b&͍&IXDu}]q\ öx!Oޖ6W]Ma_>Sprx\xp k\ߧ42#/p )@3pذah)ٳgKKK* Tira338%~4e[d#ԀEv`auB.2nV3nXu/0d$7mrJ蚩.*ҳ5vJJX KOOqf;& 9gXV&z.4U<Gstk1 \5>?';=sL %zfɔ`inaxYxQU%YB"9H}^|3B̀LR6ӹg|DNkxXԂo;"[ hjKmC ;M{^d0 -klR1g[ഖr[:@"sQaIIwyˡIʊI[o @N/ymA,Xξg岔c=Z;zc% FVso}̘l<}e?x='%ZCz"}q/gb7E_T0ħN\xil\{Iǀ=/^xdUPD~۴}~D"!ok?ײ6,d' y~n()vdϿ-ejԄ:L|tywG )߾}!ECT!@7ݾ)/\;ݚӂ$ׇߜ VNJ(?.L-i_HӟSڴ/ͭ8"SCK[kTHTLShfTx#K02`~#nUĄSp U i#nx꒨vlwhbi3&%˼O%Dz1/^\~RsbQ0rnrR!CZA#mŸ{« 5N>|0k1Ȝ4#JEkW4R(`j?f*ڎ^O@DD @@eddym((=kayAz`τRi/N7\l )ݗP‑1U%߿_E4>vHߡ u(ЀФ*^O{k*4"iFF{8aeig-;9yh>UM3Μ4¢) #o;\TZ)$"ޣl$ocD/c+lI/}mϴ\m9GUBFՠ?M/ }FFǥfdW² Jz= P`t6P ²K7]ϟ *k3hY#-{~^bf/QC_c -{ gl;†u*~:EŇb7@qϟm5>}:2hkk ZsN`\KBs>>:6'_ݚnu9)RZj:&@ Ѻő3)rb}1i`Q \!bˆJK 6R@WF3$7! ?~nz"h[gM1nJsW59\2҄_ss*jI, ҎS1^F^30SCNe-6R[!(qR__Xgl7@R\B /J~܎߷5G'aN.hcs,@ ~v& ͵VtW?bĈiNrGN?}_QKf~).]?%Mě';?ŖW݅]U%ġ^>ZtqٟzvrX ?9'aRxq)9EUZJ̠Ȭh6K|S.\#!hڍVڏen#c hieNF zؔ\wYo1j>}p_Kfhl_a[ל >v6/>=Y7 3DR[ rrl Q HE[mR"D$ȸuBb}8k9<*OTaIBao*$cYQHo}3Ӕb1 lS,F=;6p.gk%!_?rf-0ITtDa_ @+ZÇo޼ˣc,Ybb3'xpE?u Se??y*|vr.Ξah VH~ 9 |||Д'OxxMXXx(.8qD@+E+{[m#3j>b!&sB9=+gDu>0fX_הTZs]{)LM--:r)۫) >2PY Rjq7数q,ԥ$5X\Lm|3,=ݔ3홆8l l2YgBAsHp6 ڇz<}:^@= +_KMMMJJB3m"@( F }81G’CL9ZKUTY+:Ջ1it]udĺ3\+qү=>A27u/ai ݛF^>cyV˿G=Q~daE4NFKI+)Fl8SR<VU썻^#U5eJ蠂IL {JƄ~}#ngI<בD~9bň9+mι|M)FikcKS]EJ\$1.'٥6KguWA,vudI@$&&޹sx`ffh֠21^~;yAv}p{#U)/MRS,/*!ZC~UU0Mo rdA֊ d6Qf/#PiA=N[Bi6v,YvٛQmC{4d%,%o2/b#ߤ*9RSTZlh`" 4ȟrs@f;Vl%@ RHVTE4mUsmEkjjvţX J?S}1CQQq޼yhA++1zKjڊG..|_zD. ~6 Gp /$'ϤGAE58!6¼bETk"5XtY}E$)t)%%6a:g[/f ڈc'9qJs,u2yo4k9v|@sE[[-Z20N]{DHQKꢃ(.imx-Bȉ8~Il4? דJ ->REϮ(= ,m9[w׋giӰߖ) 2 [ļ2^JZVECC__CKG_TZ;-;3߸qѣGL'cǎEqiӦq­-NM c2{_^f~(aA6[GK%i]uJR*΀ӂH[^j"F"3mHj(-H9aH"XJD HFQet;>ʢ9tntZzP7\lg->D9+?!-`_,%ț^atofiM|i%/^x,^/=X_";(X;lg.[餶 cҒtK(QE \ 4DpT@oy+(5n1 U "}swy_+qٿmٙ UݻwRؘ5 P\-^@'2_?|kXa< u2t(FQ¾6 f\45# ctN2ϔHLY:"#edkrDw`4]_x!fN?Q64$JC N>YQʒ D3s5^I~!WO~Y4HqQ8-x^j`38:2+E1@#!adF )qa!E'iӷAI1 9&8!!\/ za٪*ꃀmB>z䏾(DA!n[iL&؛~D0gÖkȈtL+/=mtOY5R}em9ta)pW6*:)@ȫQשN?c\ׇNE{AC];edd. O Kq曟"M??P"ݗ2&3[(s7hszr}~{~+<HR(BHfSԥ|x!:01`-!=@Y)7B& c!*>lg-y1BJ)T+r!sk6ꪺFԌ()$q/ ,X,a3+~O)s;|bAF0 f3X7 4\ i mغlK C^ݙ[zs a746oɴ 8LJl@@uu5yQP, XXXMk#))EP9-!!4N lP uF}ц4N%EpM]%ۧeͼlM';ۚ- 4yaoD:-; %yyyLDQF3%(vhTI9ߧ27vW^ttΤf {c%?_O#LT?qd] xuUB"} 8z2d v?XQszIcACW=uyoў[VC 5~<[\%?a !&K Mnc$rOf9-;|$tͰ0.Z&C @$`nXT2܇d& ̡̈́G}Qs=e1BVӻ2BC')Ұ%dW3 ;#={tgր- Teݸ"趺fjrv_M>ٶ׽9_k3U~o>57 R~ޣHYڪY˩c͚/ڣXb<-&FtpXC%5ʼnORflf2Rsn%5Lm}47+l~9}4)կ,5PD|YB}`" cd!" 3с$hӚZ j X%0r*1Kj NDoܝ8EJ~`!&Q_N8 :@TTZJaQ\m4(++C O2T%gϟߔ J~'ΟZd]A55naϬO03+#!Z^_QKzJ->v`r]{Z wkX{~>az԰V-;7%/<+2[nn59 i @ChP"z6V{"m :/#,S?h&V#_=§@@@%2|cɕ-)!L,9l8%䊏GST$Ab'*jku!gl1*hZ}yQ% fiSj>FVqbWjz3f)K{D)Ab\2=RG=>l^VRɵ;fxk>*xrManVϸo5Ǩ_}z۔5iANяG K9rMgyfSӒ| b;aBҊSvl u%;GVm hߣ+z⃛N ݒ%K&Lq@|=LY=|0%Ӄ%d 'YtJ 91ޣlo8xP_dII)TV Ѧy5צȫO{N"8M& 4wٖJfï2v֢הl'G NfP=suiUx] Wz򺬺HJ)" #=1{ɩi$ iEަFtX"MB_a Y lTTRDz@ T׏NG`{/+n7{P&zEdi F25$ýxtg(4Z(-IIShjnҴz(=1=4OVDƧe_|aQ1%>} l`%(=Wܸ[imJNtQ +**8FQai)lo#ъGhv(lC%A $ѻԔ~z9NW;c%ږ?1+,/mz)7IV[>:k/X:1&ʾU&&w}[6muBjUmѝG2; RFvv<%]?+X(SLwSd|֏ Q-* p|E<Or4lZTo&ncwz %c8gBkLJaw@]KRɍKO:/Ş$?] "O^SQ]+$,*%#l8;G  䪿N͇C!vuIm -dKKK;wR)͠ gYwߠI=︼hZ1IEUF}z#|˸ޡݹSr#B3ʫjDeu -Pf#lnmQ %&=NԞ.!gp2d}K 8u塩+g'FFDgW"rJzFfʲ\6Xg퍷!ȟXܸi|,vnjא2^JV E,Ɉ׍">|´Z<kj\}4gX)-r?H包LbußQakϊffS9DY ! _gbҪ'nV* uЄլMO5<_5t;]JDGj0rh\8qޔj^Gꓞl#=~UM_[|dæN5uc(qcJ_[{TP.iXŔ'ۑnr7ގxƠ/8)a=tiW417 S">{s뇐RL Cٳٱsy7I{KNy<\NIQUhQtCkG+hQжPmEP222m/h(r==]p'[^:;f2G'WUT?bRHHG/Ժ꼨|($K$}J#z0${竢7oԢ&ك>wy0ߍבjSu;.?MCL k ,;:K&u>~x;_[7nk`ĵo]N*ыxoAɅ.TWwVLgn=5E :6M);kWO5zǡ\\\n޼c"ee磥D́EO7mi^It).DĥxvPo6Z1,AVIxVrz"5ňZUn?Dba}Q $N@LA}PuB@XA:m%<p >̪ ?W_70:"X:k!GTЖ>JCwh,2h:CUy&uB˫wEJm[CQeyAƝ g^R^^vGA^^cPCC({L0i<Ҿ47? gR~IQXBUSXɫ7[7ܠ P9u 8xl_p#jk:TӠ91&×E$ui%G366fhI/08fFOeb% ,$&,$;>Q^v|cKŢT߸8see߰r !reӵ8qEuLf !Tf SyxmKulmB ;.WbYaވJdaMAnO б_{X㇣ATu!l@4M^c+E[:0)%1SǴC~i!x=!ͣl]c*՞szZ4*֨iuXwN_GޅV;E7b"zljo_vr\3ھ.Qz᫫HIo')BH&?X)ͩAT;挑Z܄_rOi!ڻn?'vY5FU@- g̘ ڲ 04@3rNZeN_rh-@jI?V4yT; $[ % fK4Nk5|}c,K>>ϭU}0œДFLR~I{ku$BynӁhE>x۵)x"DWo?;}_+r +$$y7@槤fhBV9$GVլZ"tttPBC`0@t: tL .JI"=.ݽxe3^RN`Xd =@践Eqӧgd0   կ?BpgӟĪ[תHb(eۻoe=gzLR`%E~`Y@"Pp$/),Bf˦(j-2׳[0ӆw#GDqA#^- @ ڙԿ~}M7<O\~p0(*/ =B+l9Y1eOҋoޙYyٹYمq />ad0Mq<ee:_nsD"S'e , %ШZ)TRѭ 4 X lCSS~WS X:&YzÖӆs"W8.{̺+\sYt ZCY`վsr1|yϚ]gjɌq8>3ʢ]Dp$gW<9:KnϩY7m9$\]źҺ5DTP9C9T:?+lLhZ%F+W%cr40lEOW֣+T5]ϭ8*K0Ua㝇/TC*$"o<`gH>Nw_v)*;x޶Pp+ ^9uµo?2Y4 hڍVڏej*ƷӆU-8{^P@ $8iu7?|mynLҶyMNvB@MM p4M2 [}KV-JC8T8fNQ'*(a_FSWSd8,Iy.;_r//.xGmh~@*))#P@dCxT 45 M.2J'||l/>;{=CNErmvRԡW9ڪ7Ҷ3, իȉۺjхlu(t|S`@L,ۯx׷gj+ҊԔ-g-~_5nD&X,W~h n (`&Q[£w?35Е~|W\w~0FǼ)KjDG {Y%6䫿ŰMi.U sB x~./xse] P@s n{W.=8i4dMgI1wC1Ce{@0 3m \NF r';secNjoq 3 e|?+Ȗ*lv"BXd VI)'ߺ~Iyy(qSjz&۷ob$@SaFz::c#Tqh:r::F.0GtMmU%uoV4~aEٚsٺ|C4?c O߯ϸTq~M]eYM*R^^!v+RKY\>x )^ف )@ HBV^$'!<4(9%#Kɩji(qn$BP‚P3&sr 3!ad`љңnzo:8iw= ŵ;MEY!}]5lF記)%e$p/qdGuϙ\^6>zw+$5t4ZjQPP\y!\(B ڽC=Ѱ߰a,+K">W2vYa/g.}7! wϾPXVUV[PZ(VPN(gq Ec8swo.*)23#~G)H. fp¾*eS+dT%px~YVmCOPPp l |t+!Pԝ6c kS5%9\mEFRW+`_km=e@?muZ,d Tz `^ufٮf2˓`~<3K~{, olUwwIS{}{8P^ƶmQJdZ^^lnPWX||׶=0'KPFzKj#V"W3^˙)L}\mϷ'GكG/ -MU]c~aO/xb-&,v񨵾"Vϔ<<;WSPTjhhG:skV[>K"]LmSx7oQ1(&L<>iHڣVIkM&J.qVSu--΋|ˣiiSR@ `^_if_Ӏ`$]eݷ~ pwvIiż(=O~K3k0ʛ)Jmɉ[v]Ĉ6/nVv{}51ӠnW@4]5N()gǏna0䇚gn~JU˛&xM$G&[bBR;:hfIKIcm(1{wa8o{L7ܶf_zX C)>u2=PZ_5 "Ohv+s߾U} 93zۛwnѣF50aS(uDnWQeR]}趎n-ZzL_tEISONV@@ OaA`!3 H FkY~P0suIw%)O=nYs(cULz~ܜR{~˺m7"J&4T"0??׉wk%5G+)t@4Ϊ;{at z7iI_I_G0M+k}Ǎjв}F%(w=Cԍѫ'j46kle3pr֟/)Uտ>,KM_0:1۴wk/<^9p=ѻE6햓&޸ʲȏY0#oD_ ҭq/kTBx}k&ˠMb l6-B @7 -Ф >|̵rT+Lqәf71(ݧte8#5ˮ/OY6{ݓktXV<`NPUQtq:[T\-(kPxw;-=M/+/TTk^@9@ \ l.?|Pĩ7 Uff\|+!hB[q7ō(Rtib (8^Np P|KO#6VL&؛I3a5_z02"1@8.Ku—/Ն>cbTSo$"Ç8q#F`]FDF`a,X6*:ׄaG6@@tDK oPSj.h@'$PYYn b, @ qkkBSf>>Fi#ѷܢ&%W/Jmm (ͫuO2pGye v.휫Hێt3`ᷠ¢ %5@\(w}zܯ)=zB)?kjGiמge)^ط/>#I_z8nrYI ik`>n T45mVz(/m2Z)K.Rvb nشGt%f0;񧆺ʴɶ| )?Ѵ¯A_EWUVjɩf}[Z |&B>塮M@D)eeX01{y~ KLJ/ȉ>{m; Y~i9oiiitlwii?JҰ7)BhȡW<7|XWݣL(/S+71@QdIs @UUNjO~D{Y{:/`3|!\Rha=/jA ]@M5v[U]n~,u;vs O\= ~_:QӁD<6GMlh24=1a$j Rj(S_B*x])o[:LEUWׄEơH!-dLfihasA h-sW@@F }uS>&Ue8u’/*ƋMm5Qs3C>ysVDj!<=Ǿ-z0>MmsF(WDtÜK0ĘjÌm<Ì=6\t eFR: qzU?dO 1 {ۣBeQQ1@t7aˢbk-.ABENEP1NԿbcآ``Httwmnc ǽv{wh$@h.1!$|.2$ѸgFpB qYdmES+%+]6`tas.C|+# K5=3*Pt1 IkϿ.q脠KN}y ` *Ĭa(}ѕ_ԗgׯvbM%噙ϣYϘ)ThPWۗB |uر^cD }Os e|Fm~-sElAɱ3l=}HSWzu'^xDeٵrRz,R2g %!4OR-ղ6D-}3\ѡB_@2b[ʳNt+/+_Yqi:ntlIE  /4(P7%[5.C@r3?T @i.#h+9zS Bh=WWO?k};hhAs⠎kYA/?F}O>.Ӻ4Q)/s %"Z7qW}!%R{~#Ik sc"?[TdE{>ʊݾT26xa @]m__쿓Ɖ\:| ӨM<ϒW)6o=#D,f )!zgi76Dm?Kw0+S0W]N't3{k}:?bQ/qfB͜6!:' )|aVv޿ > P@'`;G滛͍Xbho~{ͳ'b.6Sb/ Is6/k|_yX - @=kxEpnd*4IKIr* af 1JG"]W1{b%4a%SKXqf`&T~5Q[fE#ՙy$#=!wsP{'p>7wO/[őcOGxC/3ƎxHR+>z5Dxӏ^sfЃö fQOq"=:>m%gj'A/V6ϱw2.zp݋-2Rn^!1<6h B #5/HKCSG$.L(x l ͘7}˕rxRO_8'tgsJxVgfnH^ e 6 U'N{J?꼅,#~Фn[Vs9дDCMؘ $(unSЍbE׿$nj/N6nHˁzPCNFZx]woG 1ftkr5aݳ?!@$w&!0tg{~/**),NA/㖝ޚ60gjο2pt@]l\'Jn:t4Sr{N_S q$),z1Z 502,ɺZ2|1脖R; Kc KW5@HS]fk:[Y7^ o&:'3s&Ą:5}u#0ǔǦ{>\[M `y H aan練jm=a3̹k"2;شe N>(/}Ƿ~pٓVW7. ta'(uW@xҺ#] o c'H1rd1{DkeaֻY48%HuaBC/3 ʨyt~5Q34"jgVY|& {&Ĕ<4ZK$2萣!A}֮,-2s =;AI//0>N@f˫@NmY@_NGw].iAr9v˙7 t1Izvm=KB'7Qs"..JTU¢,TP[ot63jR@Lf..u=_:z2B ,  e$~)F_ (q8lM__XOQɀqXfRֵҊbw} Dj;zap¦j[Q86Wf] QHLlh~o?Ǎ_uC\ KΫR͔eY#=F "ʫ{ቊ#-ޭȐY9[9T'J-*$ZDgWgdrj{> Uj̪F)[ᴄoQU5p X\RJN^A4|uyٰ@{ $^OdMŐm[[ ĸ( @ؕv.+),36fTals qobhT'Cw蔊ZN_uy-MrS~|Mȯѳ@  ]#^G3b, Ўqz|'F[\G$ed_OF ^D~'4edK%T)H_GB=<%=VPXTAIѐAf҂gN"Xr,C-HQ (433l RM!|}> ""7zrmoŋ=HxTqf_9 8%՞[k*bnhٹ⬐_Ʌ8Bw5xT77X묯,wZv/-kޭ%X[x2bpDy%5ccU9q^>AsHDayFQcǨ5?ua1YM9M뛃MQ?eFjU*R9ξV0NPbS1Se{(2˨5We6묪6gi^J8Wq.z58ׂR@|v=68!,"EUgm4@ X M MMŀ@pD ngFf4M\i#h&}]̡.d7vI@Lު~JubcX{_VEd'YuxG# 7[wh;FΥCU?Y{J4OEOihSI xSQK%L>yk^Ut3ƣ>6\;kNirǧ|>~I,&`M(4Q[޼LlJn= ^~HVC~) ܥ7,!'sV۟)J|[c Y_i2Kq %.`0N{*_޴mߗ,!^Xf'Uæ-cՙ 7[ #/݋G?m6VSl cY @% l=@5yNv㟅4ek}e{kW 4nkfX>Ħ٥>o|R^&96$}ytbpۚآ:ZR eoS7o~)kCiJ~+?C aGhD=@PCE֞F SnYpk&c6!箝's\kϜ+eAJ뎒r`/\pδަ#/M>!Zr]66)Oܲj[d!<NU'nZ6 :rMݣB’BH@ްi㘁uaò[a?R9@ ?/^aA50h-= |P0WVN@uy@:Ѷ %5pwKٚaS.~MWSd2+ eɟyp6K9EldR,Y3ʬӧŅss1#~Kgm!W+hg[6nJm%cTs6[R+p+/Xiay}Fɡ 18!:lA@}i\VF%wy]nش}P:a6:ܴI3h < PTlMl¨ L 3 aE?Q`}QH@H$eŚ @'P_2jM䥡^~h*#ڜ-!Cwեޟ"S'i WTSbfsɉ28f9N`E憽U[(%G+{rn'8,ҳ9G΁(A Np“׸u]]|l~2iqX]lشo:߰am̀af @X~W|L{/^gٺ@ ]_vG߸p O,NA % !.*~`xS#=IF9l{'lD޼b&OA=7/5xc}8~ aϽ=DMƓ:}To*?ݼuAxA>^HqÉ~Lf}2 Wᰀ*[[.17>$Ӆq.79Y J:?GCnJ4N&RVH[(ݭ,K 6؝4cN  -ښb.=;16=7͈p8G$͈*@ @6l>QZR& K aYMzUUUzGx>AG ]@?"R/KW___?]s Unkܸd1`ֶ37S懲i1JWd=| 'aly4ϝ2=:H +/!TW[jJ]Ƿ1H^F]h  {lH+|hlWzоĬ72M#ZR'rSOo^3b~ڶkwd7oww}pP5y2\6JÆOc& y1ǜ曀Z@@h;zrLAzmW֖ eՂImm @'+ϑ< @g#0v}g3  Qiţ+&>Dz#z@I+kZvbE'؉D28<_|h$uYƢa0z2/҉>j aLt4ACJ BlFlA< DMwr.p3iӧ!@WSMyNh]iga[mp>6Uh -mAT   !^y拆V׏QMPpv|:MypBݕ %"ܺPe!X HJ2wtEEKve(E ^4iΟpĹRbk4E]geir_[  %9 4N[r.Kp8|sq@"v`큂x 1u-r@@:a@xJg6~͂a_|VTx?i+n) zD~MLtRqIIZWzXƦhd)dƢRRh AAF$ɌD2ʙ5UpE|Lua×1cÆ'/8tO@$Р>*xnrw!,om;mh@&e]A|  ~Y=qtδ2:)eٱQ߾~3:) qn&C3~vtt(_)GE[ / B^aUnn+ƞmFnR3_Mt8uuu"eՔftXkUW630?8lڿ@7@@c;q-wBb|Tzٺ׭UM0 #"ÂQO{atU  U PtU_ r±CGnq J J+[ lo\km㛌,odR]IjI_鏐Nּ=ANx"гsˣ(/=t-h&ӵ nvc2 @""  t.slp0UfmyL^AUM0  *+Ǡ@ @jѥZ @g'@M›ZrX#Xɱ ?׉NNbs#}XyM4dueZt)={*CĔF$EAH߰`]B5l^e9`WxRܻ*?x8ZP;]Ob1~υoָn[Vu"6iZa@GD`=50Oby4 ZH @`0-j,kKNvݢ"RPg1EJo w9!^4bX8@g&8j*kyrRMYTFNSR#.^6pܤye9E^8g]\ggl=nw"UT;6Em&8%;Qp*RBG`(~VWx(%)XZ'i('X[΋;7="kv{}cjZx*NPc6ATՋ˫Y.@KL '&4n&ǁԉv:29u|ƃ7~gQ#IMXBn+&#}/1?3R4U]u i8tC05ɺu4ZbEJTTQM ;~?gJfu=$)#K[w1&Ɉt QRࡇvj.%~:æ1tX5'[=aؽQ&A{U o U% @*\]A855FCff.(@H*۶fsQw}0:e& .,2!M?(/%u8{p'm$fPW.(i pGwe32xcnݮ>u1/*oH{2~% @%RX00AnPZ{F ¬ *-}cil 鴄JT .?X!* tN?b3*!F/m3s3eY1>.DUi#{+IZᖜ|5?,!T]Μ/py:]؉히f9_]~!<('*6NX t{k1fvO١Mr+_*\e&Sg.zZPP.WRV;yB ,U9kY@PEBB H@%ն@+6F8D Zo&r\jl$3K>l  @JxA_ IYl N@h)n^#ms[We6 ZJ?Qu\;^E9VK4fڼmn ([РtII Z a1)&`K@w@StٰtZ7э( miK8a:`O۾ [gٺ@v}QĂMd@hV0pN'*Ym#~<~ɗEpsZ1-^>u1i,xAalR1s{ؔl=pXά4ԕ9t嘌ƒbV{} mIu""WhUɟ{αֶ*&G|zg!Ʋɩ/X~Ӫ9 /ggcSrkEzq𰅦 rݣ/=_]I?NbRV<[ӞaC SaA .'\T"=i۾/qYxawسVATKxWwĮ8!<4Hh膱hM; U)s˪mf7-{Y[{tCHXRV.Z5 Gnty͝I8x'N]~0ovM F .:y#X_@` x7d{?2ޢpᯠVg}XnKTTslU,SSYc4$0P UN@H`}i\V["%K;٢C>b]&wsZ[TZA;#,.*jJ ¬/  d@h'߾իx֜b>bp` T<#t7v+3ɠZ)@@"@ebwLB~A%E wD_ʪ錷)%&XQRoig~CyuWbCfDusuq#Xgow]pIemeiAqVq n"ˆ'59&4E4EMg8=8Z|0kDFYP!R2>&qNt {$f,6;ڂGW*Q9+KC ٩=р&'¨1mHt[8{6暐ʜAQ*ʐj*"^y,ArVOUn# Tܵh߹ehxۗ(u ѕ$zh[2EemqA!SGdPU\۴-65&';ER9I(=ovnO&Q%̋v4* &#+-gZf$@FY! (W@d􊰵M< ߏޙ~l#!.My)1k'4R=*15N@Hn? | +ʪ# H b"|+!LL)3/j+BťV P4\T5rV𘤌ʚ:V-~rsZO!W763爌 1S&jʲϽ0l Nr~qRHuFReRQyWtaڽ$#JD ՝'/m;NrtȽzh;eaKcO횆ZvPZҷO7xӴV8FF?{`,"Qv}-smE'"}H Еre 'YomkU WV;o亲',z!ִv=EA\W:51: Y(lwT+%!:D{|ӎ`ٞϠ;g%.ziO9N=UΕNQe]/pVsvm`嫁HM׾cc7VFəI zMEӄ g>RBB<66ݑs^)A Ӑ!'xXO^zumw ul~2iqKݛ0÷0~zƧ$ӳ8a,Z$tXܐ).j6g%%P],uoj )(ǙMvdނmH2ڛWdwO0Z%ewSN^B;GƓ:}To*]yEi r59ⵟ?em|v& ?{qz4DHBږjo NBrOzl1z +o=}w+'Dj8q[O9;~Ҍ=G. <d@@A~nVF**ih~2V \[(lEahoA7J{Lh3r٭>LOѦ2d.y !n~הأ_sÉԚ "^^5X)P~}(Ef/pE^:隘[Xr_^:!{-r$.ۘrZGQ_ 6*rYnѠH0+KH1)Y!wh[#o>r<2c`H̲x?l3SF6OL%UR\rfX†.ـ1wV@h?AX%Y@ ` Ҥ^PkmѧۇY; }g5b x |,cFisc>Zٮ/DCymSK~ݤDJb"<DNTM ؁mvW_U3`ʒptPXR~P3nU߂g"eN|ږ HtPQC{hbɍpMSE5|af'gn6HKAZWBRғVa;D ݏԯ[1b=jYLOmzЃYk+Hg(Oy#EfTN  7;7(H X:ͶhKVsjhM]XPՃv#;mHL\ER:CfS)iӧtL4WcYӔBMK|_ nI+kZvbEc1uݝ:4MO|5 DGթUoa(w]r@@Jv̽YU:zſ}}4 ktë &E2ҒKI;>phl!H s?:K4@'@P:`lH$'6q^muY|7Y7s_/sd'={|_Jiqߒf܄F1rά63lݪmfʳ8Z/Gp r 7&W&,til%qu7T~lϼȍ!NS;p# tuS%jKm35uM$@`T|7%5 4  pO  d;ݾ>ljH|FUMPRo=GL8i6kZþto ɱO(i?M~/4,O v91 g2CodPF-L8ݟ|^Ok[4'vuXNO\t}XIlȍr20C HxGc*x_"P?kg-| ޻EWn8<mʬkOT >x_!N/|L5U{d/KKw' -Oi7;|-6@@xIyIUNLl}SeiSm 'YZ sJ *f)^R qAv{=D(U\'ohBlb?þx #6>1%5}.?~ʨ۲;u7q{n gfe?V;zFN\RsYJr%9RX Iuiloz^hj! H 5qd1f}ǯqJ( @h3 moM m& YhS*N#Tˀz_>Pb(WX1r*8wx̅&G.u?'r܁A/vz!]nPP(n8b^M1^55:T S;>&>2ԡ+}g;'vK ]/[ogR Go %==1:78JOT7oBa g ':>'̛5s ̛O.DmwOE ${ȴd߼iBݺ0B Is_VwӐng?y_r֍ޭhtlk2uuw3 Nn"lPVv>C3[KyӍYaUY#zzF-T?8C+MKZ:J[š&.kʇG CLY.G7} g[wƙV@@>Ŀc>de~r"KN'~ܺs "tZ;nwZw)&Wn1"f4uC|8n(+#ˋ §߬^ qJZGՔU!Jwی2h` oJٹCQKb}±Džc<*վq p?Z&#CHݾu8{tmoc$Fm% *H{n2z`7~XXj C b:C_@a#bcc'MO, Iga-i@J (ܑv ):c'Da=%H.&bdWMM^@ƏlaGr3>u#'47bX& nULR-òbSJݘ1*A3#Gmx?j=@A_! ٘0g?my!o_'n-@҉>oÐ;I!h4{Ll  "@+thVh.MevY':ϕ倽(ߧϟtFuz룃l/ߠAS7y`R[q쫎|Y/v'w 0o Jq2D¨RH5;vs~b[Hϙi݋G h[;+ orM45-9a6⤥#lb<ɒ2B>DSy9"66Pvrl~ld_>OE]2sNM'y㹇'^xDKG+{tfmȀT̙1]k`CZӆ/{+z / <6lAJś_<šg}  `x~UOt+EIYFRl{B"udL`g)?1&?M3'<2 M`@fj :ڵ AY(5;*,"̜a YDD1$q&F:tzڰ?77anZh8!E3DQo{B&7UC8N֍@  < !R@B/qI3sIWw-wj(kkSp_ab~#'{mhjH^v288!@-D 8;>?8}0 A/}o}d=9)1r}Ma~NbLOsȃ>6?V&D?޺_Ss2VX=͵x X$ꇭٸq=UWOw͢[(,z@Ich㔆~^!5ʔP4{7 GPНx$iWcbTǖ i>nQ%pNk/v7ƞ!cG< NE)e/tK[]yr8; ~ [HP7k<\iDh9SK=aCUIݳfWDE#PI_MKIr* JDVlٞꮬ@7Ғh/h$@hAAbKm=Rw#Jx$:hb,*e|%uD.)o5N.RA!A o&:Dk(L21н>TKz9~k8'-Xĩ=F~iأͫ# w|pڣ${wIUPq0M`FkLhGaCUK!a&a)%0!K1`ooGBqAHHot ]@tD(®_@' yXL@ D{7!\@*ۑi5^ߜ}-}Pjjjh1Q sIrJ".SUDX NX#U[| OG81Ԣ/Ay$QT$FWMJ&Z"#i(caVqB=&$ Ƶ #3Un]0ѐ'p[rRW y7Up]r@~B~z@}ǎe#=z{Zvۚ;fk.(&o`1z Gpms0Ѷ&4w0@TxlO 7 聎 $g/*7.˱(h_eͻ 񮥯7hqǚ몀N:smCvbۤ2'|H)<Rn9ފJB2#Bw!uV"~)Z2QXU*dDq#Y@!SqOzjhfE[NXLr;v}nfJiY@@@@S@@-Z`lܸСCeڢ@[tv?P{jIIˢYRivkϯߥ✰4Z:Q':G(w)-᫷֧B聠z_-J=-,6#Xb`4bNp1 3dĴN07d`m+q9~ {}ˊC{`ۣÜ 6nE iԭ7Tâͫe7P$zϛG\ D@@ Kq @!PPPH]]ٳg&&&BnLԞj3jg<|Q¾EwఈR,y2!*bBP+?a唆Z'i|Wv7ygViRpE2PBp٦腫Fkinr)jUv؛/l5Rĺ_e+ҐB=y{CepHG'J`)bi@@xFy(@w/tsssvvnL[YY=zHAAAsO DAI_?K6x{/_|2s0;D n_~#'߰2յS]cҋ,̘r^0.z$f}Q%B>W\we<]Ǽ{)a~vk=~OZmZσ˨j_Bh$; δi(a˨O>ڮU]ey J}MVzZ`@hb65l7vOks 5<R{tauTmq^:|A/iQȈ#*a CO)ƃ*pظ;? s 0&$  tM @5+ @jjj-[v-.V^}q0Y}wh+||O9̘iK-qD5/ ⥿}֯wyx@N^}h~4NTvTM?q5w1|Jrt\]ڤi'Xpʢ 5Oo?`BSO.هC]Mg*"^C ۍ(@.!  t044Q@xy 88Q!ڲRڻ_]SKSRﭫݧ 3亊Ȑ dzo}CcIބk+ka"⭷ջ }q!>#|O_yޔ ]_1QY9UյD!Q9EU;ƽztǷ.**HGoaIG?2;fa!h)țQ VHpFŮ^''Ck'8+-**1+($,[B_̰rvǷ%\blkbG p0"SBsrydtePZd͟A m$v @'HG@Z ˗SbTTT[<$P_~ВV%Jӟ8tW"Ҫ-[.ύڪg=zWJbnDQKVݤ][ts'.B _z |ӤIxDAX3l{ܹs#,.n00cysD?)X2l.sQA. 33O%#P3#Y)_&i![}y jRar;" IpI]D$c9S-;uPcF vȨ( vX|J#ef{ 4i˵WW3_qĵ^FIV|JRMs]cE>+ʃ&ٚy򣄦پw\cx#3? Ԡ%Li=67Zg;}b?}t:PQ8p=MnPCVߋҬsRޝ?hl A:ʭ3WG Uvɜ A@SLWpP̙3K,4?=}x 'ik? d~^v T3iS-i ~^ܵgMETwWƙęLظsJҨdQJay% 6 Җ@ZLP ,HZyx`cpcm^Djb)$Y'7-rB-be +U;oB~zH;8} 'viԼ}5x#IoRUB=' (Ʊ'.s߅bwnJo?tcǛiQ Wo8[2gތF;ʴK+lZAp/ tKvYJ2}mn] lOz9*KH9e>~a8&J i&H'DIR<ɔܨUˎ08s]. &G]jI}ف[,c^eH:2袋ȏzE3CYNP@@l2`7HEo=|-<&#'( $.)Wr "nTǗw2&9D{c}\0 5 t1LH߰`]jsҫة7 I<1/.-o ߗ YY,& >}tРA?ŵſ,a8mX];A{dcjaxS0v{7ֆ3 C/ʑJyrѣiG'2{'춝iYoh_kj( p)gGZn6reI?' ._:)#m10l)h UD6ң<1$ @爛O_j 'k{^Ȁ*N_8n C g=u|0yͬ2Q6" A#frÓwg}}]l7Ch$:QVky_( **qo#)H^_]-,Boc'?KV1wG$M>#>l̡M䇺ёBU rQݧ_%"D7mm~|zdc3/fzNg="% .߸(*ۙﭫD2+97=DNI.~%=pF RVz觱3hP>>|2#+kl?Hc t_BV{x0o7e|;'Ov\d zkkx> .tǪ]Ǽ؆y`rٯfDs8:Ewg5U_:N!ջ;Jp SӪy踼衚}j`͵iÊLe84xǨOC*ljܷ-:ŋ*mu3ZD߇/ Kp/lgK@)  $Z7qNv='@@q7[tjjJbѯ߅,м%7;̼!K@@gB" ]Y ]~wQonO.,BP4xkΝc?煅ʻp6/72?$gԃxK[@M {8ɴݚqGHyeQe(>oLGARnF2P8o&9+$+##/-+]Jnʧk\B?R/q!ՊI9ҾRhwI|F1- !HUrRhUT_{[ @b} _}gcLemmO>}x~c pI}!HAbp-b yx2GV]_xߺ xrYs؛)lK5.~)XMԴ,$3jn>2 yށ9 woFWpEQ\_A!/KOsսz`:|GFsX6{?zhܭɃyte/g IJȉdPddSѿf_)_@! ` ܻp/Uא3>0,S5sjy羅H @!玤TXeRyYIEuꕐP}94۹8l;#1:H.(\FkDs㖝ޚ60gj\mjj*CgCff.!G+?M'l;"xUVa |ظ7Sݲ.#(_?XkiNŐ!E(!6؄( &z&*l~޳> "4 NLL#PjOAԠއ^БcoК?6 mn9IS$q|W |-t<}tk!(O߄= HsUE{yO;%Pe/p-|pᣇքs1pBJu#m%ChhdY̱%&q<}h:A%bEO0#cjI3 % ~F?xXO5mgl  w^P= _^oe^z(idoR*K ~̯4n.W+uhgfvO١\WRC`: Ȭ>py:]؉1/^s,C'|eϟd2ۃMۮ۷o'ONO.nݺu{葥%Z$.-ubrN.[(ʩJUB/ 8}cFO~4.VO3H)ui|^]~o{,({K#UI KQ2z1M~*>*B'E4G{2 &ȳ+Լ`N2m(l'.7m+k"Ju ;A5Jƭ2Yob9tNc&]п(1 oQHH=Ct1qdh@X+L(x18v\kcߋawC>(v0awRPxtp㢓m}b\w.A&琈:%ƎQTW+ NWR306Vo=_;v*pl"lV999]r%"&Euu.X`**ʤyqbeϒ$ҏ_ޯ3Vy|[g~_7s̥_vRQPΥe[>KV!BT\ߒRj pLRRSs_̝^F o}G-iК (ҌoS*/< <3,J*,fz˸n\ Pldfz)ZB[et . |y FMӪ8Ov]f:v}Q}P5 &`M(`!i2?<޶llJn=((Sw-4eXԐ~_~i%t٢YbrM'Ng8#hr%#0MX(p9[@ZO;2 D z~yӶ}_VxawسVAI0.?kR/:rØB,xAalR8s{ؔl=Xά4n.?f/>V1j?3 \gw,{HL1@8raz?/*+ aރ]o_>ʛ0ICqŽvx>pcZP1j:Aa;jkk1)5ՌArh2wү*T$k-gDʼRzir꣉yŸa7|`M*MZ)og bVq֠86^e. oЉW{@@FGoSҔ k~BZ!=}nTK-{l˵233ӜͷN4f:ٍW+@_R*hu҃۞DCE֞Gb*L:e+?hά'篚9s&zM\LdQ=i>Ww`(I R3aR4cǸ\'v͌.zp8k5-Gܲj[d!<þU'nZ6 :8$y膐3R*c89)yr]s;(U,{IshDQP܄Vb !/5vesFve(+1iҤrrr<59rQԃ 4g^ZfѢsY_oc/Õ18W ؄X^ [,lEҤ_YPov2 2U*ooQPKy,DZmM}C=byr`k?~:oxQӅ7<ʃͺ U\%x2WS(*, #+ ͂! o߮YܤQjOmqtUUjҪ޴Pk?R=Pp8~Ҵ_տz7| ue'Xϟ0hU?%\\v17-@@@c !y)݃f#Uf>,.H=ןjkI.Ѷ 1 Zצl}cF>b_A,>Z.L~fv5[!-K*,,Aa8J$N+;dP+wC{I " D>4.ə9wh«"8zA_8iF1j] 0H^HnOnS5|iS;@@0 S7[BZk7xA&Xu,rQU_q;VO1KYubr8G-M<◌ʩOl?T_I^jNMQalWz:ۘ&9;a8[ 2UU!T$E|Y'W嬞fFGZBPrע'~!xۗ(u ѕ`Bq\~񱠌 ;dpò~KAY56c5+J #B_~UBo^j?q=n+ݹi[lj^C}MNv>RW I(=ovnO&Q0Sݩ: c&ہJt9jU ".39ᣯor^ypN#1]V$<{K*k+K +m n" DE u{5:64(`...fbbblNѝIVVpu6KhBJOK.yeꆉrB.)d + ˈ2QJn<8(1Hf|X:خv~zlοĜ,GYY‹סtps; 7NH^V 𢔗WP Z;~Ycj@>oXTnT7^ވvC$<}p{&3m EQ^lӑGΣt\8f0=MLKq2TݨF, C ]1uTYgp>*OzNэ{ wZ{c53b N8mU~R å!GryQa=¯j> O<  @B>S68U#.}%{`ԁlss1։g%{|1tQr9SupRQIGچ6b (њ;=?brveUƖynJ;>}*0C=Y'*xfmTlK1jή_s-|5ѳڗCsty̓Ӑz99i!\oqI;`#<=5ռk=^|"K +?ruƂ-H@{g[mY $(u7"pCa]MM4"ZH̃Ի%g?PO_ݻWSͨ(*D, QW\fȰ`l[,HA} E{yO'$L}TqbC8,vTgA l"kR2Y( /rg7ȃh^eױTHOkލ&h.pP A4@:ܴcefHgSP t  ?ll|MUYpękWo:KKM#,wH+#!ND6JI:75Sa̦nw2oK$+f2Y>yEi2/1 s>H`8A Ǎ'u TJOt݋)2n5js$OcO<}}gICߏqږt%Q/{d{}\lIb>C`&2qTSՙa9%zhxctf(ݭy^pyt28a,@%!7d   C:~14kǍ׹muU.4^(ϿR/#r#Z!g$B0>* Wg j{ n&@HI .C:e_[xJ_ޣoY[z'7_85S2ͷ16wg觧2&&C EۜP8LiKp5 gE𺰟^?k8lE/}|q^^ 34oЂOp매JnM;@7Q@y#j':XmW^3'._r݇x`6`oRשRx=< LnӦ|􌉥aDGC6)E􃜺|g߸ Cu@w 4&?^qAri-wn *,G.ީj8':I==˽K)<:$`>u{t=AV^!Bg5VCd_ `Tz kbna-[3JO3]pSUm GQ=vvp5VRnѠh0+KH1)Y!wD~%1CriaFR4ǸD\]:(fk$ei yp`y6YU#')ñ 5mD1o`99V6IPuj{ܹs+**<\KK [Oܖ̜E T?N)][M<zLC~oj680 šqCh +KS$@v| IPN'r!|}^wބ) 6ʆo^έ6|Ϧ|v!D\cʹ:`Ha0R,SPHud!|fvKpQ]_{7Hcޝ)݊ov?\n6+!)fAt{dih.Ssy$&&A 6#Yяi_ԬjQHuudb_:.:>ْuWh5oX8 >9#*Llx3]]jhj7/" k P`,nC$@Xu|_J͡ $"AI3Tdeo5@@:Owb_]=د#z}v݂cUD˺RnZ"XZY{֒۝+q!N?ccHX=gOg8:_ D1,]ª0\%i΋R-s|po__tf%etL4NERhDCl\"E24𳧚8gK;1=5IpMNk޽{߾} <4۷%$pu-&ͳ7̀9ʈ 8-I埓h4a|g6}k7hʣ>< ̞= eqH߿#\nGM~Tr@@E…DQ/h8I8gh:ouޔ%Y gwx˷/8i+=MUyy9+8sMEEji˨MpzJJWHH!j}--2Ҕ2F՟\K((v`]nGv=JqQ1wB^4O MM868NqiiٳC۷oO U7.b ;) OZ3hrU ֔!}jTX?=a3.KyϤe͝&>R}e = VujvZԌZ>h<֜sIh>oӼui7PޜZɴ=YCBƙBqs?d,pnZF:yً׽ع!-J;U[Qݯz,хIJY|@m T[@_/;ax`<}|rup- nq"#.v33vyq-GNC6LRsdݶޗ q4wk뿼 d:>g_:ymP|ZNpBf![Q?%+*N򥮄q2͊*Ѧ 7dm{zՐb߹#.ѫa"sMF  e ٞËr K=no ~v0's@ /_og~!~'aYFˏ2*vsv?pPʄp-/3}$P\~v#ZY )pv{֥ݲaY>лqb>T0Ik 2TTTA#᳦>AġErǶt9uX$%%ܹ3al?>osfzrSO 5U՞rJU{W="BUI&7TWf%"'9s )v^zo Æ M,._h\#'NUƶ,ˌ8'<^疭LQO^[.0aÌzV<}/Zk@kMN޽ǓORk C`6n R}miavϨA Yw9o~cO>eQ+R?Οlإ}M(ЗtI)x8gH8 [ o݅rB_]/(AdͦVoElE<}qRe* L,x%!E=QvB~M^ǷoհDyr}nYh~j?tTR!wUCՅٔ*(ȣ%yC7Wʜ'7:sX~Y0k!^:Q _BM6^/0;|aPaОX9mtkX$Onau0gؑ-{|JC4e7–p_`,8Ǫy@ H %UK;1nnF;+2prnAfԂO@S_v!P{MwL[-KQI)+Ȏ %HyVwm4bO^aSڬ.+jx?,jD0d}Q8gsjm31:CG R0#ԡ AHʰe?/4a SWW8 ϟҌ9iFUvw/_trr*++ a=<<-g_?xtr3+@X{dfm\s, Z}-ؙs.f$ޙ6鰱$EJU7~Q` [s@HiӇ.PFb߭@!;%&.7 "8O=|׻?J^j7m~V{O5(d.{kMz1p$H=vNT"I_6LWgafjMNR_;'/NR^b8g~Zk#jZ[Wz풠X=ɇQԹrI¥}J1na:F @hFba% %԰ .u-uۇ4 pB{"{g~r"KΦ)Y b <w <;w(j~E>޺6J©]7<`; |%ߵkvy=߽{WZZ,(/b}/#g);2+7k՞3'ح7n _6 VA/mz8RCHkĚW֮u,E) l\f=0~F>'^ܻp Z-&axlG{qSo=u^>?,9#.1NzGpTa_,>v6l[S<}p5> FafxB[BMv*xD ߱|Fg<ׂcy]`۷$$NI 858?Ik ƍ7<"iXU#`椖gez/\0k8^9f|'1Ͷ9=zmeˏє~l _t;7[9ˍ;jsI]-!  tno!IjO33cY1AMfOII+,)讪ad6@WNRc?~(,S;juvRfR\L2(f`l*'ε;lS5ᑩ9U$1) Z ^?GP첪ZAaQ%վzFCI ֺ9cʹB`v8ک 4H!m~-sE-c+ۈ-+=<3^ߟV{a Yrr]٣3Vn;]Jǚ 3cFL> 2I/ղ6D-}3\)(5ptGBO.+:yy(oYeQO?ǥ錚ё>\D'cO\aOH>u~4ikZ^U[~RP{ĸ%[]^}8 +b;vs~|YZm}'nt޼yϞ=tp۶m{/Dٳ1'+rLo(XοB/ $*%ME]~ _?7O_<QOI%m,Z,-ܒZ\ߑ+ }ne p%+mRlIUso] 헎qxlUk:|w5;z-,m趖ͭw/owfLr6+:mf,t0r@NShY}Z+&xxrF^: mhEYtʄm,7ȥu=[7ozDfF B-,?yA_:k&JplLJ8" {4032b ޭ:q6'pIlBӑGw WO]t!~[ D=m:R_\f=a-  t'=~%)9-@0g}iHҏO>x!!. [t X>l3 RPnve%z3g;3TY:&l}*a#Ri( "~ήٰCd$qK7t[ig{L.I&G-[Ε]9ʺ'"RJ˙8 /-eT% S׷=p Dml^5{yc{C$JʪX;ec{v>Uo)ezQ1% \Vց&'t!iI?;iѿhu.. 3Er`zy.<ΈV0^wFFp޿n*>$_I,*^L quR qi6vz\i 8ͳGv5fcUWQVZR'16ࣷo(s6)}Y`˿x? /x . OkE,m8~$_7sC__7rrh=U sF}}qLr?IZ]AՀ<4fT$ꭼ˟(XLo^@u瀩yjs:XV#&$8(8gOɳTM׎YywyXZN\s]MdރVy:9r룻qի'!B(K~ށG#sn߾KqHÂ@1U@x䪡mz)Iʊ3Ą{]l*Sv5B|[Ǐqƅ{$--}ҥ &3۸[AKv.[Yv4htgH\ȨԴrQq)%z=;njۮoS~7YTJN=Md%*bNatPtc1I\ic湥Ņ&'ɢ2] IWeɔaSTY̷󇓯'DZem_h 9^Ɖ\ONpCUE/\'G%2EVAQSGĠDtz]RAuTl@ktKA H X7c-OfuI?\4QR]% EEǥeQ Z]IԌ1\./6@$P9֧_<fxГ)e%#쵰Xqc=6kOwx1ԤuK6|?Y(JY`~}iN^l~y8ժEu[I|RRΡ YoיY1n8XV;0q#jMI~7mz~\K G|Xf=OW}?#?rcwiv}j˞cppZyVd ҪR #tW̵k9j.5-ԋ<'g)?$IB' FzTu!̚> i%tLVKHP|ʃK߇ؚùM]}QS2 2^lXqP^(I5-G:?̭]㯢YlR"w:\A[Sw]$swz~0 q3M `s]ae;uҫXx<==Ntܜ,׵k _Tl:DQ?tCt73tZk0B$>XPE0R߀ G NIj]QD0kpM~ɢ},QZ-$b]? @4:g>WHNoJ|̛SUgP]ޙ~ E^Tܦeՠ~kb)S#gg@ 9t&n--s!%N:tН]G^R\Φ;;&:SE1xo(AR< l_jI 'JTaY9 Ujz Q?:Z(:pZx'spsaTK< ^E5H'π뺿_<}7U"gV8sõ 8S0{꣯8VV"X8nm=u沯9+)Y7ÎNT+1^_ϭ&R}?7;FķrA@\&+]~~ɟN!\BVVڷoZDg aÆݸqCQQQ޲"-564 t7 B͎:j @(/"@ hY;>XKU('=ݷ8!w 9gew&7?z/w.$AOGwc.4ڷ+-̋vݟi c[,$ynlT^hD|ZyYt֝dzV|$p.3f֎tߘ4'v2i3dum "+6[rۗ2j *aS"칢d (HG..l5n;?ybJ*ioXMYP^wSm)j@(իw3/ndҀ3f5?;};= GvSs(od7P/,$1fPLXnK=G/0dqAZ7mw$+;G{vI,Qc:""K]z9i]{z NdNW^Jtf>q,''91z\gl@c0)0bN _̒=#F98N4iլGc$OF9E;蛺a~׊a"u=7{>&`N=nj5NH9,mXr_I+$nOr3kVus̹uOw֬Y{n2DM6zkvAXK@ jG` aB@@ O$1-s;ݳoG;亱,`)τvZRs0ߤk|4ぐރ7BH0qzfY~*}xn/~|2I ڷq}qc@5K\Xh[厌8/Zin ]-qxލlE2S}NZ\Whxfd'v(a9Hxy\&r`4.ʙKH ]ktwɛTԨ70an~tTQݶO]^ M9"͏(bRfzwӬd8j!tOEG$-rz]N硋}+*' "mhceγ"Nddn퐟?G߼FG uAl>9%%E6cZQFy͌ߙ"E$\[7![PHh|bJ^a1 I))hv604TTa8t䜪J0 DٛOMYۯ߇EL%Ik3Ѻ.-*Dd2Ȳ#sX(Ynh}ɧ#1 9Q e'TjދoMX丸8ׯ_KJJ?~ʔ)L쿙n. k"?X @ )+.=`}~d GL[ǡ@ Z!QEmڟ+G_jɶXN~|Ga;l.KAVrP@Pϸ̬2QZF")&^a5HŔ:|{$EZ5:y{#os4 nITa.J6`wy$YNQO;L5~ϭ2z+뚝}Ne.#-ҳn?Q&F*C pu>o]QTfID W?t4dL3i`땫K+w2ÐK+U5=c5=Es=sXBAq*tR9s;-Bq㤜{<4͛ɓ'gff6>x*lyǦ%| ȢRBb* Dkcq @5+X@ L߰ej Ki騐S UA<+bo>Gd(XgvQ]G/:7:Y0_}p,Zv^]7 'VJGݟ grpmqѫ)7tdzѿn-lM_vk A@@9r-"ʳ͠An߾_xrV-(ȃa]>h^_?WEĻ4?:ϭ/ m?p@$\ )Mpx'fBH|? ]E ð&O$eʤ+@XNGڙVZ(bX/sy$E}.=*/χ,My/hpv6wF]ݺyɋ;VJZ>  x@ o{{ϟ?q9s 2vEY39 sk j@i)ɉ'={c2@#@"t@ @ @;f>:gsmjk9ٙɉ ?bl[NkTE+ Km6K 4.!',bZIȥ Ҳ#kjh6j߃?x7HE(oIC?^QR xQ> wM4)-- kMM3AxS{ ʠ @C^UnW @"UE@P T}q%Xtryъ5I5t4l39@H""޿z1>?6nOu΢h;+qi:nUY {xկҪJQ~^reGv3 eznFJDh{WW.^ Kf'_|ɹƦ\- m_ijиqx>YIݒ_\̓"ą7?9|if̙:I7%܌{Ν(ۮ_OAz|tJl%ȸ(!(%6"gHU`jBL)ׁ@UHNN0a((xĉs3AL+o_?_֞hf'@-/ѣ'/F&@{FL hY{ %|~ 1F 饹//3gm0'X e̍zN:@=0)gwCK8]%P(ir~"Iw7M)-h8u55 "b790>DNng\}թ_M^=a} BYRl8w]]L&.'F"˿x? /XΪ .c'յHQ6?NE7+N)L:xk,<сo620ZA#1ꪫ(+I-)7F ط6AG@P \@Q>}B|۷o޽} Or^ו| UK%=:)1  иD/ոu @7D\97҅w`:(@沆@ /?ux@T$iɦoղ~RLLog%n?G())'?!״TC/pU4J^JN;y &Qp_{bꎠm紗UFP/P/<(==%N_TLݽ"t83I&U13D^ #b^1F(F?5  Z-ϣEyV@AuuVmu -.uܜ+,F:Cg& @ @k @ˌV,#@H!"K~ ".gǥ^RU"&Jb*ҩ4ZjpJKh(ZE8D/^f9%zC.RMt*C'Q8u@vͨ%+6F뀆CG"O_r{- BkoA\~w.bW${{ @+&+V8uO̙VɇdeneqQ@SkL&0Œ  N 8S0@i MFxcBfF'ʌ3W|&$ƌ%t<"-NH}XDJi0 f?qI2v8I3|h22Fܯ,AFJAV5Tvz8$;pj0pܱ9MF)4=>#/~I/)+e?iP q2xewפtX=z'\C٠KW6¬"YU(> 1m@jjĉ?|﬈Ç-Z*h3GvUUZ|XbN@4/o_1:KJ2,%@ᮝ@c@ @c ɝymL|,gzV.ILJU]˼嘉=ui?U5$F'A=PرQ}Ul4X3DEUʜ/#)N!Dpt`ME Zr*ʊB#cS $9EemffRu1 BFB6`.@- ?~|bb"ڵs WC@JZGhHۗhahH 0\5 `Q.-@nNVܯ̿=`2@& "*M} @1F[Uo(boq*JI"RfV#QլUIӜLU=z&U|A~h}.ԉ @ \|y\+ݿC|j.hbVm #5+Zc j ZohQ̓ @F'@$VW  @ Pg 32Dȑ#qA!ҋJ)"bҲrݭ99Qh;`ؔ2,p=) BJeô߫fϼ闙WjzORz6T1f 222 e߾}˗/X(#/ef6:TMD SD C3@I^M҂N @q ;SPLG]1Jr;+y\ MP>78 m~{}Ӓ/ J2Б98#]jM+ nBM%bሧ S$lUR3nG@\_;(T_)))ݺu F1kb_}h%Ҝ,  Th~php東9<]& @M@@ @xsm f%{b>Cum|AOn=bƟ"(@u԰g/3 PNȒm|Iz ǯwJ22?wƍsѣǃ @iII৷^-u;cl*5}߾ @ɂ|L Z,_hQ̽,0 @ `& M @?ZH/)._r$ 6/_)bʊ:5/7;C.sR $Q|vI,-!'i%)-[pKZ9iĂ#./33Ӿln]yRZC@(2y .HIICR~(** d(+c-9YhBᗀX)@Xxߺx  hF$@ @ 2hѢe_`gPr, З00ʶWN"/MzAvFl_?&1P>ũefw6>'-f s(t٥E!2=Qk7vR啚GU|z2ǥ)S| Mڵk͚5,KcEV06j":B&-/~&+  I O/m\?[@ 2*{e8^ 0:;!ٛf 1HW1ꉎ> Z VyKɉÄZch?k(((Fmm+KA,|PEUsW#k@4=|P[b- Ж };hiJJJ~ |ݳP7q\jQ@4'6'zMVhE`N-9$wJnae?3yc:.YwU,0Z}N)`$%l=;ΐ-'I+κu烾WھH{k; Uj@xE#)PD%P]aLJdI\aݖHTiSg{L.,$Qqqro~"".&^~c9@s]pNa))ݷOg0d"ҡףl~9Vh4Zt˖-hQ:s:~K.4B`wU/N [Z֟qOX_Z25t@yy_WDoaUuEBB"@  6{7 R[;a|K-eɚ[9HZZrc#݅{>ܤ\ γ1`hELu? ,+xJ~D'5,݆nfŅg13>tߺϝ~sVE -}/sEVPidd֗}OxsnC7VC: X5\sڔYo#Sz2:J6"ZilrEx ]-x7Gd?Po_e|VcN6J7R=}OH$ƍaKc}^`MI`IR_p9BH;n Dʄ[p>=_@7+JIJ֨ @ @!FkQQ*JI("(WYMʺu)/)>…;;&:c WdY)lv^hkV-))1X\P1q`yˆP*e^/EDQ'H~kbϫIǼ>yޕ rފ;;NݔÇD ħKnJx{ ndd$t0::ߊG hO_cu7WP’  BqB I.Bp%{ ؁BD%$DGPFK흇g@ 6(wbgomL9$}G̈q^J}F޳;GzכЅ/ Jjietgg;E(#9XNSI%)ˣ)?}<={` Xue5$H2ʺ8WקF%eY:iC[=35'Qt߷i^-Qy܍3ߟx[1cH58k\OAF3+'%5鳗y cg|Q3I k܎FħIIgYSIFˌY3#8}7&?E whpf6㣃2t A&FM?փG%& [1u|5д/ J r2فmv^$2EUhJ \jxь3rs+>h}}}c6!KvV&b(@BMkp B^d LI@Tp-@nnKo_4YwR3AX?pP  @+%FXڣ7n~mV'#73"A5ˌ]qY:} aNY-d=$fvk+nPl1M|O1-[971kjpGؖeJGk]wuL=rT}n۲xÑA-X1#Kviü}ٿ\ sR.s̜.o|%ݷC (xubk5H/ٶt~L6;Jr̪#(\޲{8Z$y['1,^6NlP=O[o+B)/~|2+f۸_ܸF 2@:a4R+'r>"rKvܱQ'$4 .p0܂\X^IAYM,F!(cm\X=|Vx͑nzuǎ<]zUNN=lFfhz=o^d H 3#5=53Q~c~œV@ ;+ߋ I4Wj"+;MORVƾPo74_׻Pz]2!m([3_Z-ʙέcS*O61uGKY'Is vdƧu}z_YX`2zk;5C22XQ[7(S#Y N>lfo8CUvTN>׻pG{,cu7Jq\}Ƞ@59Hwn|rOӦ kXIo ݿrD?zdYRR@h)–6L-͟VtUVF$|;M@Sp6M0EcZ7NMMACVջN|._K$GDj $i }4fM\JpHM6M; L< -@?>ulN:ل6(bJKpIHP(8si: PTtfq@%[:VZR""*f'O xa~>fVT\\Djy|4%thKhT*G\+[#D]ǥ}FQQr F >>RyRR*Iwγ׋7XZθdd76L-͟l)=hSmi}?2z/c&2QmeUN>;O^0ď(6v=%X8` ϥ.<'+6^X{CscYAK;TrJ ^nyy -8 ˚,&Ni*=1ʊp +A45g<_y}O͋c7m?iq+U,-(;琉 ˎ|蕔M*L%%aSee)֖>>`U}}go7׺|3?%Xiϖ D)i)\w:uZ9,*=L/$dX\^5J )((={۷y[nΝyU|H6=7'kwч`N{7,:x` @Y)vŷ2hN\iWϩU [tVAkDf;(RPAGI qqIq)F)4ZTq)9EuQiQq-hxR==Kb>֞J4̤”Pk<9Za#.?"39ut^VإfDyu⥰lV~rŗ#u9jwA1[dCaF=oң`&2֍F<ZYvr!|sࡿ߾`UG@tt]aB (rHP޿zB5#vTj`/P_~ oJRRŋ'Og\bR~c[   BCOd qƁ@$ "*:h8tdz޾UO) ^ %2Š%$QȐ=O;Ȍ8rj }L3`p)?~ )+Q{O}MeDnhbgKvu,yjCRj1ѿ:wA[xT\\BfvnQIL{Mm>Fz~0ZsHQqI5M}#~f%u\xXֲ,iK5!@si슇+M,'ڦi+iE.SGxzÔҲ *Ǯio t\zWl ?'@((; f/k5HV(+-;l\nl຅knr2װ,NfQ$o녈G_'`Y,m@ .kҤw6^p<0׊+kcی@@@XPE""ߣ, @5 ?(f!NuڒU/uU,^ Q4EnFϡKgp^HG@$QT:tYzu WWp qg-602B޵=_GΨ\Qd4+hKݏ}lxbVIDJٺkojL5S4?^ G-An)]Ȅ2atf4V|iӴ蘻g7+s}5׹Y~_Yi8%6"=bu be;eu#ʬr^xg^wwKxzo/mE!Zic[o8ÉhX:M駆A2cB3|ζ,;32,&;o,;./nʒ|>e>x{>$2`yoyvwu4Wq Hg䥩ABg%q%Yg?/7ḅ?P"]">ѧVtjx>\BCq}XC֮]y󦲲rB x\c"@ `t1!&G + ШH$_6`(t,O_VAX/6`+|,X}E+!(AiF%w.ZvwGCqVI찀)na35iݒ 2YHǬY0}?2Zqֵ}xYBe FpA yY׏\Ϋ2EGX}M{  W 'L DPLGv'|:jhgFJrR?1޿{T~'o =zEEEͻ~:Oݕ+W ӜIEٗo1zĒ  2cb hT*;x @b6c'#OO_.(BII1B$0s Q~aqqQq!H29j|Zƶ̗@BIPd"J8"zR2*- ?n2qX8QDqscidr>'=g-WMڭk=DV@?~Tܪi곣˷2;Ӣ:GW1_R\1?<}dm|_JMdg2}ev3L5|ݒ .\T-PӦX|`TrId~>K;a8z\-<Avm紗mSCBx 2 $=ORzck2(+͋e!m_./JJJª_rS} ܊ae{~8*0/%rܝ@ tyyb1A@@x l@hlGx tвB38 ZKV#Kc/j R2 ê^@Dłƈ5#Y1HcE 82b`$Ah4`Í"+%R"be5,_ FN u >:H8j[.ښ"JiѫָO+/+κJϊ}sȨu1kFv0 RdW"Jn=xm_bʨHkWд/ J r2Y(2d%Dp Djpۯ"|_ s3)G%D:nߥFvbyq1߹3uGzN.@c1SJZc55VS'xP#+( xLF`X@ jhЩI7,JW=g>ꢕ?|DhIɊUWf98߹Kۃ]A vBCVZI4.wSD^.ZP8sm(S6V6Le9 W++?kL= -\51V򉔁S֬GsH?tE8K!נԔC. sX (d׏/SX)DRra5o:88qBСiu5E.i Lf%9ed֘[ PZRd PѱrO=vj[n"b萑[*JKE4E 9錜pգw'MP|1f?r$r[ (܈:^U0[6H"<3|#جG"y Z{7XAߵ#{iqNe׾ .ϝ0qzfY~*}xn/~|2; ڷq}qcd31sR}n3qo`&/> VɊ9sgXdO1&В Mg> ^Dh˜RDb+rl栻;Oޜ$;p焲m'G$ ia ?t۰s?sXHbL8gT)|I]ǏEDy.xΝv7  P;u nX @h?漼 Ȱ|@K>uq1u@NURYDTU=o_g-[fiiYoeee!?+pEL/R:6 (܈̚ XđO"M| U)pcy)DQ4xsL3HuǑ3XsȒuNLӡ˷>HRV#Xdq.]d䠀qYe4\;uuuMERL>>"5?NhtyBώںiGJpƐh6qCi7*t]h*'Q[F^6 F^"eitT߀/˫aeq~#)1)iV2K:XʊB#cS h9EemffG`Q}gfXl#!?㷟ydIU={T]g@|sJ k&ٸnQb73~g2xd\[7![PHh|bJ^a1 I))hv604T䩅% 񏜃%Mh0*QDnSSl|x+aQ?ӳrIbRZ}-LtKqx"E4: ~Q  ]ғOGRc}}cbr)ʪu;uAa? /Z㩳xC NZ2=%GD> 0 @@"`166F { ڃ& ңNXVx|{ssjMm@L嘯ڐQ"M"(YDI)`UXB7"hH X%!FV_96eͭFtv Ii~K`:i2k}tjk'L t^'4%nõ%v16DA U;tG2]r4}&qiζF @(4I`;n%HꇎU=z& lruiCFwRv9]{iv寪gǿ.Q':ܖyK(h:[BR4gn'E(1zxSa3'I N81gN jߛa}Q<,ü7 1  $KkgE=ddvyɍ+oh4j5%|LZPp#z2 5VA5V#b9>41HVEL. _蕙z.8U}E9$vyxX4D)qq .:pG"IN3k91dnSm'%He .s=oإ'J3U:55!^Yc,u囜J5bEi;'zڈg\;@ ?Ψ߻w r $Dm](03  "oL7{mQhflfh[AۉSБ'`7 dֆBAFQ\ (60XRRR.|zᮾxbذF{^7ÜAzɒ/{UI2*m7:f#LO1tP7y>v=] ]U]ݺyɋ;T֐G!iu!@G +g.]ľ}`j z85֨A/S9EE, K ,sdhj%A@4=ѦoZ@E2>k+} Ww rp%$I[na*Q?sb$6S`#nDS ¶ #hkPN k-8ʹwA\3T3߂V}5QԜĄ1QG OoDQ|Fw@ЗyE<Üm?iq+ƣQd$S?) D2@@AA 3gx8o޼cǎEy! D|{vbm t\@‚Z:R2Y @ \ң=C=cAGV?98ڈ-%4;(~2%'݈͵A"LMwqĢˋVlՠaߜ4¼ gNݹ:8s3R"BCrRXb6+?9KW瑺5@2X[¶4W @ E'NG|=‘#G.\%zwy yodA@4 ҒiZ@l#O7&]v y~AYM2}5g,ӫkU* DYeuArr[XքB'Bz S?=Ot=~r@6'%%ቨ޽{ r ' C9y^! ?%Xa;X @ f9gސ %hOA-Iӱ5Q1HB3e2gI~׬Ξ7@oi?/߫PTzEdR*͘.ZDf,ZP~nXA !s+rEU#.>ɹՅB96NM xƈkwvVZd+ALHH/g_WIdOAFNA K.-Xgsskjj y9AXgz[ې)ba61x0u Jd$ZzTSGv E}"n JƸη^zʇz>N73}Mu;jHԂܬ_A~?}.ĩ>Ɩ8ƼpXlJi9@+ÉRx},"Q|{hzN/+ " +* IZQD$i`"I1kW} +ur6heřb¿.65m[Cᖪ3!kSDf>Yܨԩ K ~z?MfhT(2Jg߻:E// y] luUI ZQ#D싍&4pFPᘁ:[u@@lhAΜ9Bm6 ,amt2 ^@ @Ң}W|XO`;M/ FG}1 6C^l| t~MˑN=^NlP/P/<(Virٸ&kd07w.S$vaN#VO6|3a'ὯBe&طr3Le:E"d3"Wl?Q+hUo0 XP{<@ ;88FP߿l2>d __9laB7p@-)M"\$b  @YgCj hHR.x2sұs3|̢[VܡSq[T[]XZ\BCs*,0CCVe6$AҵY#6K̭DRrRtF".gǥ^4E:En%Ab-X~w.c 2X'fZaQ"@勽}B5fo߾=hР?B{Nnj$@/Pvf濱yL d茛@ D@DN'7<凜bq:1c,X2apxQ|󁣱܂cjL"v7u;_d;?pܱ9ժS&tyB -m:xWD$nOr3kOcu Ls/ll߲ԏ|oE`> #dq7f SD]x5)>zG^)q$ tպi#̸NnՆ A{@@ ֆ Z?wwsq=ԳgO頶v+a?_X_3t-/od¶x@@K# ""\@"viE9߾|'-'-+^Sn:K9=1a߰ hZT*8@ GcEݸ|?E1ݭ99Qfpצ  M)p !7dBs?SPPillᡧYH /m*i1ґ/_J\#S؀IL; @ u"?넫e)G>iɗl[eYH鑮t{+7L/ػqcȏ&n@Jn޼-"ʃbĉ4O>$K}m`V(@@ E06΀@"hV @ h @(wzČ?E;epƂ-s[(J dggO6ٳgx$i... ~ ۩k{ X>`(cIMIF] )[@ h6^եl^$RĔ)tj^nv~QŒSb=+5նLyz_bKj@! "1nܸ߿]~}ԨQL@Zr0Av~XAO\  ʚUh @A (6SvC%XI/ΈϤ~#&ʓ6efwsR}bx,Θ1#//o[nh]3An^{i @zJrjo ( K6 @ @ @X[R-J/wBgmQݡ7c(%b}Z H9LoƌsUYYf n$޼,um$@5 F!@4+H @ P JR1g$$%pA,fN-9$wJnae?3yc:.YOa UBZNS[ϴEg :L/ >kf^ôkBn'䄦I&0_hA|>ȭJ y\VAB ̏@yc{ßeR'j”*~O>i@ @aewNE@5,Kxu ܸUΛ?J u8tߺϝ~sV-}/V0o/7ܬ*zC: X5\e$)Fd'h˦8ufkkHQ&[[&;`W(JY`~[ Jl`b%ƭ&ą= @ m[ﰗPѭK4Zj<8J*`O{Je}2^kgomq녶lezq~sJK*= [\P1q[`yˆP*e^/EDD@ idr>>'zW2QC5{͛71b >޼:%#`+b8@BNGDhqa! C3!bF@d⃎7X:l`w1Ӭ:DtckD ݶyqQQjJ @Em^6P$dTeddVe!KϘ6Mw ]?irD,xW};GT;޾<(%c"`_:;ᣃDCFY3TUèlV?K'<@Cyhܵ$n6- K0}2*;qo3r0e"YrQ('hbQ#wՕΈlv@3?(͙z5nG#ˊ,eZftkGqoL"NIBJ ??{.رLI<`Z[27;+s֫60 /b=4橇~W5Y\Qɺk.6w3)@ @a,R[[& 15;Atɶeu{RMwZj!kZ+&RN Qǧ oԊohkiW$4"D/We~iyYɿ"}?9Ng淠vӐd{3qlhI͗bח-- @ZBTD@J@KaА%{cH_9z"ۮQTq!.0~w'׹T9m <;+:3?yHvEɮVl#9z`N>~;JĽH.qͰ풣ˎV㌠TDmJ}$)_8rtn&yRAi`OGD`19F j &Oqt>`4,;b=Kc?~+~}rޠ~zyqr-&8ل_/%k#"c4Ak&PfΛ7ݝǫVڻw/1$[ԤX,la2@;ny ,~Rb#]ʉ{y=DžLk1B$P%U5u {h'a?ja*|}YBgi^ߣ33ʨDi]ӶeNa:#Z_FA|KJ3ӾK̕W0C?B c4L/uǼب srr˨tQq)U]􍺉91^F@ G >>c<))@P$.(;uk#::}vl9|7meЊ;<&.uΓ{w)e&eZaAz{/Fih-%LyC*D#+S嘏Bzϗ9<Q<-f $+ˑ^˺$U֙n므~rM]8wܴi ~<E°C '[Lqf7tf79?Tgsy??zO$5@(Jv? 3&EJyyktR_[.s7{-Ǒc?9F,os^ǒ$f8fB?! tqd N]ڥ=}j峙@k?~UݴJM/7}"@ xh4Σn^kxv@F%Fk˃k38HRgӍWqDQ|Fw@Зy7&ң'-xŦeA>rRr_iIIؔ@iYYne幏X_ٛQW|.V}stRRtv'\*t֝S6;M{| OE%zÞch0@ ú$+رS,ZڪݮG/f=Uw쌡|7g|KQf/W\=tn˥[V&qjD[<6~U>:SYQn\28Ջ_Y;U_T 2g9 yG]ԸZ#Q\LH(e BI!JH9uQQ5jnYc=B~INK{nYq_TSOE4*U@ MEME@kMYY'gd#.?"39ut^VNH yKa٬/]GޅN|``|9{9ិuQE0֍30Fso_e0|<8* +ltڕ@`?x1pwS:JѣWypڵhS`L8|ڳ1 , BMN16j<:CWU7LdN@czen('UgRܸvֻI0Xy7xwvʢ>t >aŕ_/Y0$@ ZsHQqI5M}#~f%u\xb~g%żzrNVƺu, f%D'RTw0injINVd|3!FWRjؽVlMC~7 gk[G= EN7S`z?v4YVN[^5g,dU5]pe-cmM bY <BAipEu EhasuclN}{|?_NbcqdUgqB4cܢz!b qvO\bu3w>}c ܋siXtPզ2X6է-/~?3~x);%u>Lo<3kW~5nF?;sD]v趗jd?d FNY֑3L:+qqoxHyE(.ҋ _8QJSsX\NJ$oRg8 rݺs;:(bkc2)m^x{F@iw9vO} oDm,m' ;w|6BT'ȝ;|~Wvz_1Rs}WjLM x||tJl%<䄔؈obe;eaa&ʬr^xg^֗;^<"847 Ӕ~jx$3!4Sϗl;ɲ93ގB^h$dGMSe%00'11SN?LO> ր@@k"Y -RX?O̤(#!tσbsY|d: >:q߇6UF=sD{T_*q=yp!tlhHwȽsё*zHqDad2}aB/Hxx/JBgQpصwq.HemXtnK>* %sRe΁%F0@ ^?rœrF/pi;V vhO\HhHՁ}YY$o5iM^ķ/f76c%iFxW{Ÿ|? c;+^?'bwT:>)v#V*eg'-W% ;xc͵liΛdƉ,w?v"3QzRtU3ΞHBw_EH2p սH#s{e޼]"zZ $|S6!>ܼ(iFP]`#U?i="6 -[Alݫu28 rFfIܲ?$k69֮v{FMW\U%@ @& !)YW((/;zSTzъ 8L9ybhΎTG+3Ԝ_ 7&O|Zhd@e>ʲOfs/7 _9xk_/5P;i WnwUsO|f[W+,xo!F v#|tZ]4i 5me1,S)I d&DN^1F;@k9k`j[O^[ysYH$q,ezY7SD7~2Po@k F C2k(NЙ3mWX-%S+#ŭu).}>ckXQV3FMM$8߭eNJ+򢤤$SJ5-G:<|yȴӧ-[VVu#wi߾}2tREU|[V܏@չlQ)/7o:$:g̔Ne4 GXj~AU暑;uH#&|3Y1fʒ{}JaU,V㛊x~7s[Cgtv﫰u4T1EyEeyelk qfdQ/2sFDwM3Ǿ`{ٮ?SCBFsޱX}MՔ E)?}<={pָ~}x4oPtgzɠ߈#;++=[]hEeTSUZ+GY?K'<@m;[[ *f_UZ*djIbl mj4hq]wsDBK#16" ˿.O!!sK\R?;vIq}%2b'48ZVmOh]Ny]7~*kQSs̵L>\jZ9h3> ʊ#Sqo;3}M̩|"e5eҩ4_\^biq #R: %sx?cDEȣVLmţ ]k bAFRRD_?,=#NJV(ذcڢ7ut]i3>loer~0שBŠL-ۯ(2LSՍ#oG~(ykT1TyгngJBTtI@ P3cV{Ί9\!;D%]{!9Y6(6{ۥY.}tH-efQ\|:;S0,U=>tޡpAwwЈvz7zo'uX@"q'R\=߼~Msn?2sd'(a9Hw8ֱ-h%yq n9W57@u23+jcڢ>dLe#;tcq:BJ|:tϟXPP+**bIMVPP$QnI_Ibh/M (}+&R?9SvfF/]n3u8N ljdQc:|2Rjn^Nuwמ9l0StMf/Zztj7w%ZDHgV$N}![?gB!$ v z篆JUDqZw}q|h2IJsI+L0$Yہ5 )#{G-[_mmӖi@߁@+#DV1m:XSwtZVVDQ_#8ILL8r=HA@B`:a:KL^ʏ2Pw3Ƙ+Ͼ'$GOUլƄFqIzݺq亼 uiU#mAOS޼Z0Ʈ#Z8-zUFL5+r j́ @0 [l^b彮 GɘmD DZ22,#zvM8HEVO4P]4]T͗oM5˗#y_H'pmqE=x 2U4w溛 abGTq!.0/t~e{EQѻ6]xc.$19UZ_z2wIi;mR@_~]Kb"$++*eklIANUZ|!o/*SĤ %P&ZrzʊB#cSу$9EemffRUfe N硋}+*-v!fXWjܭ3~GW@ThӼN1 Hl>9%%E61Ay͌ߙ"j1%e唔U4;jK’CG qǏONNsPSS{.Z\ r[&">o0eXA@XPkFfm,@H+(,~%+y=&&#CƳ,T4ֳ?#3Ո#&MxtY5Ze|67h{BV(+Kh}$-%7[D;u%Z&Z6*jƑ׾o^ F\s׺;r=~wp#}- GO+ir<,+-Ȁ&@ jM8eO³%ɾ;O@KSsٱEI޻YזXJrCHVghMfveGq'.2uɊ $+@H(-]$8m@h֣nȔQTN $5R^Rܛ˨@/+Bܸ 0kݫ/!Ce*c{b0۠9ϤUܕ YuY][ ЮfyEEyjn8 pv]horUC;s9-A1jqnCtٸq3lbiX3'AJ"b4@M`={g# e;N>n4 Iξb!JoޱAʟDD>hpv6wUD.uΓ{wJyyX|^Jg9 `,!x^UEPv80߉bR^LS2"Ђn. 4HYYY@HKKsppx-=qK3A,~o^b(d zaIv) ^[`r>bAJSh?BRQrXy)W_d*E=PCx.+ʱ(VT+bƊTj.׾iV3 ˿rƱ%@&I(Opj܊~Oy|H!^Άw}^gӘ?@ p 2*m. G3sݐTM1KgXkDQ|Fw@ЗyE<ң'-xEQatZN$)9 8%Ӓ8 iYYGPv8:\܉,C^NAv>hk]_\*BFh***o߶g Xr?b4z[!X @X? F/K}<ź٥4vqr%ı$٘CVNçtg9?,J1ezj%0ohqT9,]k˵nՏc WUAb S$-VV>畟T%t"'ݞUg~JՀ@ Pы79rcۏ=q\;9+ݶ;WT^reGv3 )!}^]x),1~H]`;(_G\o]zTݺqШbAѣZŊ2###b-(@UFa}Q!>u ]6"'z6H@/i4lv 6 K 2 X_ȼE>|M$ZÇԈ Du6=/?NPG#\URG}wg,U\#z"&Sd-%׾:Fr]}~X5Vi86yU+D GBafϝ.9+%U+GӬHc7m @43*VCeWrQ%E)*4Iu3mWcٻum"+f6y"5n5g,*Ǯi t\zW,v?ABQ]FBhasuclOx}{|?_NO A /Nqrҕ\*͎_0oJatds(}Ѹ>sܯ~Z73]K wo[$5K5VUh.6"t(X0-CNy-[V0z؃P@@&' "">̈́ZP~n|s\}Xdjfǰk}Rq%:DG쬴V'BJlDz~9K OdeB ʬr^xg^;^<6=Zic[o8ÉjX:MVၠTXdIGx\ֺF;( KnP rL  Ye@ɓ'y_VܻwU[u@t:nBn0F"PVZ-3ndڶ~i8}Ek*deta< +-gј~z Lf,dseH-{S6Ew{K"&@H/+H`d((]֩8vu0BYD^wﭝ ?H~y?Z;#  0U@x䪡mz)Iʊ3Ą{]lj.K!?[Xg,%mk8gOfo$%+ %TD+H<{aWa6F]ue%%c>z{rrL?;iѿ]>\0fG/;'HM~gnk bW0o\q,__7rrh=U sF}}qLrE4$u.MA=.Y*ҕ$ '7gaVҀ a#W\(Z_tܸq\ _~;x͡C3A FG%'b}m`  `i QY֫M/5q.;FpR6k%GcWJ8N-)T+~=Q w.z~GLg87YBBЋEHTi3+ 1Mlqct}f-DZfkߐql^9iv3֯`u_LS𣄑*//*UN )/> @BlLGB2io\xs׈:-]|[D)V{@"l~>oG6b+6iE${biAKuzlGTgWEz]9Ʊ;'I$q,[Oo$vц<A٩KgE)[ұpx蒆d +ՃMez2 닶 @ oߞ={vAOݻ{xxVQ @AkV aA~n[V^nwkn꧈:+Wz<+֣' `_yuK=7viy&Mw“>= fNrՅyM#&jH*f>xDxL΋|Êz*5R12kPDZ :'qITΗC `<.$jh4!@)Gl)7jb|[2u$QXvU _ԒϭJu%ŌGЫ +? PB@_<}7Uf+w{}vEe$(;l$?]yڋ".v޶gum~DY鳆o;O/<^)WTI9?m:\WZz+qLw;n#rQ`%Iq(bD^ȬVtj>/tB/:)sq2CCdž4WMzcsAVQ80ry:,WDZxe @Q  [9ۆu.7Tĺ?qά:*UvIPvXL_3zьL(LXnK=G/0cY' tպi#𿩈rXz?S8?Q%qc-g2?;};= EGvSk%.4==^s~ajtMf/ZzteI/X9[9Iծ{.yw%d]hKVᑈ2#&yv e=*W3BZrVeT#3A@uؼ& @ώ8rAW2` ByNK-G?>nahѫO/%yzYQ|&davڛOݺuw +d *츖-δ2 9V2BlU;wEWTWxTO*ADak*4JUFǪk`~DZY dn8:iCi}zc<9SߠXR3"(03+6Tl UQ:x { @D</A~Gƨ"aClZPv>-4@XUFa}Q;c V gΜ5=;wnT$%&l@2h>{adfOnYQlҢ3EIjωgHn矯?3p_tK4tU$j,aM_+Hv9F7lk"k眜]6{:*bJ9g; 5g%|jHf]DZ!mUU!,^{L Stjrd:w"m5Sk8W@ny9L gl޼ˎ.2zO D֒%Z_޾ʠh.4mt:6/ Mxr ^~o^`Ϋiv%A@ Ȭ7>ٚd"=+Ю~7}lFz]6vG[/ELÇ,rE#!W?yU(2Ø#wȝbWE7]?-JP$:̟v,zeA]F]r2uiv L4v7|ra2ϢNȴ;jeL 49yN"<<@J^lV$m6ηi=~&&ĉk D*))51>z'""&-+ojEN^5{5O?ߞ}?=ҕ&0-9#wcigLc(GBA ::MEǎԞ'部ͰM Z>\YxF$v^[f#Jwcy!T]nPOr@&BG3vիA)@ jIvƼ-rdǐ+uT.w&/-OKb6id P'~o*6 $ɽvsNm2OWTDa~ b2@B>[@ в ЩڸHGTFtˊKjK }nBZhfQ(` "HSr*.T{0V N#qu៙=`6xN;II9<^^vڲe }o`ȑׯ_g D`yU14-#'%A@/cdn :]@@PA׀@!@0nWբ\; ѪJ$@sl碍#Ӱ\1e"}mr0}څ D]Fms}ሩl;}e!ѤdggRzA :(t٥E!2=Y;s sF(nQ ށ@Ϛ5޽{:hu8۷æx, ׃@lb! @#$ٰ  @% @@Pflas[X%ZNYmzaqll-7@XUFEDDж^ͨÙ;tYqA}QOt:TRrR-6CǏaaaRRR.]BO?3A#{a@B- r aw  @ @ZnuUFEp+,KHJࢃu8Ԓ/~C~KiX3-q?v3j2O& 7J(u޳t\:%8R$ŋ)Sdee6466g MnB9Ef6hsrbc0nja}Q@$%UՒ_@ CEk\eT(wN-DC1dY«#̍ XCHL~q,AMs%ȊVm[5ْop^~lg,#"Rg| .@[u/Lh @xۏi'X@ KH2@ @>"e/YU]QAEs=ZF+ꩨ1svTsTH>}j$NIJI5*xs@}Q'mwNf.vQof~{Qij2 ;l}\ :$q{j) !B$w^  ʖ-[vMW>~8rG>$.0pQ JE m` @!1{O@ @!reT%)^|:ﮏO*q#w7M3ki{ ".AD~^ȿ-VZ}1~_1c~zD[Lݴ~'hN ;BZ&\ q),EM -- Y! &<{%K #*Ug/4 A 3kQ##,@ 2Y8./ pp @I@h~ wO{_! y Fǯ0s =mLzð=BDv;8 QNYb7~݆5V&T +@H8h;'-1RFSy#HWv)ͤ(KL# #ĺֹsgd[сb3A-G =9)!6o9 A@@d} D} M4tZ+*hN=@ @@DMeT\2ibfI\ǝo;ŰԔ5A̦=6YI1QڢȬbf$@ "0/=+[s=Y5*3BLJ0UjĊMVΑ{P@k< Fj˻Ǐ;?!nϘފhDիW*++0ɓ'L@E4@"C eOLX"/8Pb,Ծ@ @!c)S ^4qR̍ʬew(/.^Z@Vf/'dŹGKCk:NNhAQ6#Rأ[fEZIEN9[e('E(?Y@:k Q- oʨ OK[CPȴBliky ^wpdq9sU$0o''V,I`t)'[j^/#}~ϣ*KӪs~F|QWqCqY6A Au֝?õE!-J (Wa#[;P@JOLKE52@eNH" `@ XB DF*+++**bx3lt18?8i44LƬE͜=غ3=+wO/6V&}jP|-F $E^dÞ0~T7<+?}/J:hg 䞛?;a.䬌3¼ys.6Ș,0t:460I+zx! ${w]ld#|ɓ+Vf Z:S|B+ ] К>a30M dXN  @ ϑ %:0 ꇏ!v7IB4ق dt|xt@ 4kHַz@ b!@ @  @/OPwŋKKY67榭-.ѢE(HΧ̊ h $S(k m @uU  @ Fl8 @{#@RmvQǑF^*-qr4$NtVǽP@ZAT '@pxM(@ @@@PF@Ι3%D:xMࡃX, !_Yi`Q@@t MEQSN @  i@;%>eʔ_~aWPP{56d жK,Fm[{w Z@`Z$@'w @ @KX  ^<~x…X ݻc3A-GY0=APDBSZM@@@(`t`GP  9‚|@qQ!|p4@@wSq @s lF۽{a:u7dee %PzhZ1V6TBI`n0vLgDXsشcZ:@ h{ l1 h5{Gd7={l߾y 6d BdddZH3@ @6)))v@ j @j# ژ@tt4rrro߶f Zɓ'=` @Q  "GݶmAKVVVL@,((@C&0*(/5`Q|@@ TTT@ a ׀@{$PRRxs8iӦ“Q8@R `E !6Ge( .s` @s l"E >>~ԩ߾}z%%%uU[[[l&@@ `ʦo3XVp/ J p  @vN|@@t Ok5kVNN%---777l&@@ dgg#%C3f DB  2222Pw`Q@ @ @a@䡃Nڸq#J7rHdQd6d ޾}}&/*Fp<‚% V*++۪k @> @O6&PVVt۷osرvcǎQ()AkرcI G@$ܨ>@ K"0hM݉aѡCK.ٵ_.๐@yuO>;wF  J DVSS%  v/8 @ }~ ~ ͘1#33@SSɓ'fffLp KKKCm( +/a$@$l @[ lC&pܹuUUUa2dÇa Hk6<Kd J #D=! ǯqa1@ @!B@Zõk8F2O<)..ΑI tBii `0M!_?`:@!@R @fapAe ژ"A+N/nc{ >OAiD@`` rM:bbbBg3 @ 3߁믿ӱv?~e2Y bEz4x 8lR Dp < @  @2KYreee%DKKG!1Bl&@@ `EP&!VEQ  D`Q!,0 @p!H p5k.^aٳgaH ;OOOmm={I%僈_^|@uuuq<@ "F"651clNN>|rQicbbP |E= 1@;'@&9p @ x@}ӴiRRRn=|pȐ!Lh.D<h +x B6---4 @@ `3@ h @nNƍ˖-+//jffMMMl&@@d`@HPF)2#@` OBnAs( .4M k@ @ @EB̙3!O"ȇ$ B}C  JËQA@aBd- @ 4@@ dee͜9k W&|(@HVXXz(@G(❅ 6/@  Dׯ_NJYY" "I( Er)  ɦh Rl @ > @};x }YY8777---l&@@$ `***I7) ,`@ @ @kak҆NJnٲDիRRRG\˗/_cƌ!Hh D@~~~tt4/#PQQ!t6@ @0@fmL 77 ykڡCY&X, 0h( zt:WQ @ @g¦L-رݻw!@eD}DcǎE  b" B_p[wp @# 㨁@@D 477(G@l'@ @@$ @P$M ??ܹ^Z\ݻwmc, $@?~( :' `0@ n "e}t\KKKIII$]ki @҄A?lbso߾=qDl&@~|O@{#BKKF@@T k@ 2233E#p! ++=zasA} "pM OOO/->1BBBMzԿld  wr @HHDEo>~BF<@&@N`߾}>}b4i2DVZ!'}{ѳ!)*)b2rj]zkcg;IQ:-'#ӫ4>sΑ)ꂸ5}3(5rS.A  ?id_EI I7) @ @@(@P( @+2ݝ9v+$cVUe#?w'&<ӑ%GoɊa wy7?H(b?qf tEV l:: @ @N#B@B7Ӝ;]vnA99?7cz5A"EBYYBUJ$1RB5ÁwʷRY]($=;H^Cm99f> @ff/_P[njy@@TVV~ (@@ @ ڄ;tbW ,U\jNSV]E o5ZAqK~J*8n2nf6&$VwRԥf .'~Һ\@QҪJGǥTԺhUF/ x$ ..>l0C3 bbbBb8 @ @& Woگ}%.7fF׮5{3ܺt]>'^|u m4+&^>uI9|IdTgH&w!4VYD.`+Idκv54lʬ%v}-(e=pS.A}~Hد*@ѱk.A1iu!b+::-jH@eϫIUr$'9.{UBA9MKs;cylPřp<3r̈́' U*~( &7RyV8V>㹗]RH ?D9( @ @"ni+;_'@4iFpEQgcK?=<0fΎjl^})S\s+*HhZKq+TgbGۉD9(/K.}hI$Mkix}Uq<`z xzv @ BI1QqhCĕ/ƕ DzCLnţMu?>__'雧JΗbr"O[U=zxO- 25>杷w|fKgҼ –{ET!H ;tBODl-ox#wNZDC.:GWgf4nkv|Q=RJ&OjijIYYO(HlG] &X즦PV{G @!_IBf@ @  @C^ kۏib=?{5gPw$9T"F @ϳYU~Rdy"=%FJmMػnMJm~MZL~K]G,z2yC/9mBY~; [x Ljo>QBe;T_n=SoeQfJlx0CB`cEd"zΐGFYTz 8VzIrgwˣiv+*gep5 vnN #˶O2TXzzg;+dយ3U')y&ei AexuHhDגּjz^NRdo 9@ @zxҊ} BJZ*.:%%Vi--: =!.x:u?s.u=O^5V^dedQѻwSC9If>mwNf.vQoCmY]\qϿvTM5RWP^W$ +B.@K4-88uy!]BbLr N3ncznn[VH: 2$C/9}{ \$ݝ9v+$c5WUe#?w'&<ӑ܏>6H:f  @O4HDhD""fOos8M&S7 GDL=ZKrB̓W "YMS@w/_o~V+d>D۶kΠiɵ۪de#|ԧ9@ n DEE(V|1.ٙA[ Rn[#t \H,U0rA >nﺓ0#eCT8m"T[ Fڈ""QOg͝+XD2c txaslҩIȟS=uyKp\& {".:Z  hg"#+w711iDujjL H:x bo;%6.RB/ٹf6:HH(++RԢ2 C꫅!Dҩ +8,dyyLɺZLȋr"zD.8n.,!b:9ܿ]Y6T@ DHIZ+VF3Q:|mںbkZlX~ق}sf.~ʪ+P{Y$W%qq~f 7@tbYة[Pu+F@  N@\f#(l,U^]oDh6aE?~p%2:6!1)ͬh3V})B絋]=~3%;7p̙NT<2h\P%0:0`Da޽J vF|i|.HN*`hBk̮.Diþ 24@ -J-bnZ?8 HO?ym#ۢ<oψ LoǬJ/N })`kNu2>|h/T΋k3-8ϸsD= wh7EEEh <E!B~OwߣbgVV4j5bi*+^!esW8GOJB|^I#)(wG)(F6TiԨ{;7蜀8ՠ},n&iT/_I)ILt{soA_%.Jw5oCC&_A|^}𷜢NZF[xؙ{DtBBRN~aYEl.Z&f u;7k CE^@Q$BQvA'q'-1RF_y#,NKHd r&+[&[~vp.F QNYb7~݆5V&T lbhn|1Q㠭K)Z1{s mq̊XbׂRփА67 "s081_;{= h:jLa;c䤯weD&dTQmHQpx=ʰz jJlSw9M`Y55qnFm\i"f R/X! p`Sd6;x!M.9\„;?W*KD.` )Wk~Y:hy/8qJȏض52􊵛)2[h0gw:puQϮjÁkZIcq5C9vسFEc̨_>ۘl.%m2q׾MYêS'FC sG8A J:Ëʞଚ)!Sj+7uJ`Z9G&Uu"Jjϙb2u5X/Rsv[~Y96A`WILLdXA a{nX;|/(K4n2wwNtjbu::31r}Fu測UAsaٞkPx4Д^zH݁us8o|oB$Dž5U<Aj~s;{('%zDžKݾ`!í_yO S+}A,eY-8$ >>k\<&۫X KP޽\}̗&<fv @&&n~~P/O4șLDHgNKǙ`酡# Mڮ9oI{,*JGWS7l>qt/m 1BUj|g"TwP_SkoF-Ɋss{;J&i2#|T!2GٌKB;8$wHe0m ݇ ז|WsN/la4k{9A4v`ú UBϧ?gr>ާ!JXEO&.;ϲjk%Yptg J\dFc4202v0B2`&tAA[,*IJI5$(UO_v%ȏ-ɊRNu玣5e2`שǎz̽{Lni->yʠ.S~x%Sc 78euE9h'*L~;fIJqCY}=x`Ko! h&.ӆ9)8rNlibV(ľy hğ?Ygh.̚L,Ra:|~p0@6 6.۵w!/Z<16Z^|ߟcg(2!ee,jξa-J:hwG rg;9{plADF=T\>t(G~4r'̛"ORҦK^ <0c9a__n4~1ZsfM휤H+vĵ2Ys~5.C9Lͮ {9Ԣ Z哘]̨luw$I=a@.jqQ^>{_gIU3&v <^_&?O2pɄKsgKƲm8#(uDb'Y; LTԟ0~xq~vO/_y-v,J4u/AOJ#*^N ~NܺqϙY]z^bTkd;p~oU݋ck?)r[ OɅn>Fzv*#)QYZG2{ԋ\vY p"ԅ4x@ @Mk#P]A$NwO)}(+GNAPZUU͹ pG>u1fAC! _+'`ê>þ2Ηnȥ$>}*jpзq(8, t!?* rgnh74pë۴H( YU!PV>ĝ[W'D[XJRSjXM^1G)(&R$T:k575.V$lט/%WH Jjz=yDDkԊ/AC K+;Htd@3 W?i]WXvCcRc~&&u0#O@ ^zWh5ڀ@|||f&+\%նnkמ{֨KorFvx~s:xֹKǮhPCvrRj/^lU<8gS9"^DF ''MPة.Vl;[(aQk>qs #2ddyHCC+H{By+7& rږ߅2d]4l9Lo[WJػjil~]|"eKfߤ8ፎnvg4Uq7gsq;pt 1`ݫQv@ RLb;E0mtbْǶoz0eqlҴMMAZڳmޠ3ˆe>B v  H @ p ] @t}28y?j{6=M&N$\jhI=:a;v72E5!l;z%`HC3~&aDy{ 6Cu1䤉cW:FţOr440kIj=]m"Y )]'(O\uz_yf(k|nK K~w0;jcDˮW>&:Q,9qd$Qv򤑯O9x!&kɪ´˧s;*% C!8.<{Yuss Ռ/r<]Vh>rɛjASfaƕv{"*+"-~þfc3jd"sh Bc^Ȅ m癩w(8Ǟye؃g/$19-+-^rЩo}[qv (f0i78z9f{g:>>B3VC=i{܄ _1‚۞ia)_ݮU0*Qu%[ "xӗ~Ւ|Q=9$mן譫6xږ/R7@6E/<| aa>sם=+톓=5NXPK:'j ҖN5dSEmOE[wg]矿[2L~b2j@x-uk Iu\NKZA>$`لo~ӷ6@AgO9$/-$8$WBN^a(#+ErZ\TJ'q!jsp`@ h  l B@uPTC( gVgr)%w C/_p)'/yaO=xYWHVSfn9x_r~^1N,$Fw,{#ǫfӧ"!o6.1fEL oEel59HEM`0zbjˉhwGgK00=b\s#Po@$(PU5"*6%= E^QYKWL]Qk{Iy޿rh:*Y սSͩrhɗ3}$Wb5B{_"[`a--}z~҇NwWR^z2.Er.[kFfuqoycg9a6/嫮&ws{6NLܿy2QI+n -cb4v'׸˫]f%=5&_vﮍiWRS"0X(噑sm&< Nfb媒7>ٌ:a2zɼ~do V|r׷c^|m?R^iMӆ]z~{ )"_1d twwMF2KY}Iv4$y[G}&;|c[t#LR>ʸwu"H^]?:val~n#wrm%|$Em@"M}_sFk$ DYܽ>lai1FS̮ASl.C__<2IOEV`:1{(C0lbi->y-B& II.;6zE s6nXO),yhxkY*"@+vHtUV(/C.cS(?0=q&%ifE}2Q<6>x#s%E VSS,K:eL;N)aƏlTyx!M  LD#43 ;4kƭtm >EQn>q<<"YqͪhOBudT,A-1gz@4)`V&m]l{QekICp0n@6 y L.Og6dNE9_|>|-g"g|1\f@}L_bSYj6$I=a@.jqQ^>{J+jn+2rz ,gP~IEIAv^qkrYkvq%7+4ԬEq`Q3rE7 2"%>'1ίeӧ}4k㝘. pf?'MJ)urN"sn??$&)S+6~|In'~c Uߜyu}R=4PAh{塡B ܼ?I]KSqRo(!#5DNN).FCs A:jU5j7e (*DvGI:2OI䆛Ox7򬓟[c5NCC>@ Z[ 5t@;%p/7^qbPŸ9}pㄞ # #V0hvB}g=k+j+a;Ute׊ms+knԢe˷G^5(u[mOx0ڮ=vVe!Z/=bܭ/hRj/^l}#=+ʩ}*7r!ww4ngB=ܨaQO@l"ۺ՛v<:0r΢E#B(n|˸GlFG[%: 6mma7Fo^oҶNƪ{Թ{wA!JHEe\x"3ˑc^4^Kh<*/"_ԴTDD?35C_FNmT}yC #J4&tHMڪUZ` E;q>6:keV,4T)jA~NZJ{7Gd5t a{:ѡ3 @@$ '"8hc{}yO bS'A~RV5˥ v O8_BϳgC]vm71tƂ'$]d?׭Zbg~? &v޺jm)_5 Z%2-+ZBϏ޽l'I2vdm69a7.T.l59kVm-[=ZKrB̓W "YMS@w/_o~V+d>D9pFk[;!]5m = ):W jFP407",%#YZ8䞝Tm5ը%ON^|r83۱督d(k匚fN\HezMU #N 6V4/}r`(Ğ? I6c\)\2} Aꏰ֬Tۯ:<O3;$La{Zn3~!‚?{9@ hi~t >T,fnW&vWSWįrcctV#T>bzaW'fͶu85GBC8^i1c$ƍ(uB!:&@h( 7cWݟ NHEݟ2-]q4??' 4 ^z 4 FMAhV& &Z;@kC{O魈5r>˩-fM|}ulT7aH 줃ۺ5˭[jkqޯNp O-@ڬŗSD.8ТkbVYe7 9cvvb7#ŗSxKEKnPf+UY;n|]K:̦(!R\~K~{>4*I}l2Ӧe _N y/v3$^]RPZ,6y)/>oZacwoBX0@ h}l?Z{ $5ttB57[;s\ ]UfM E[4o}99Y4qDkV+\>hqܺ]XrCb:9ܿ]Y| ^zf$@zzzBBjˀP'׋@x1G7TUd/>Q8Ǹ,){.22߹5|lKS]dtjIa^Jb\H/1U+ GY /Y|sTy{vuoWzt*S473~WNb]{o?lGI_!vR>3gVgzeijO~*C\ז6D"1q+G|Ň0~s8 @6!6 .T!  8L/).A1KLYYYz"RMjZ*"ԿU^^CN童EF&nHTA^qDnf_7>!_"cr궼CfEL"ƞ`n״K4 `/X la1do9Zë^ؽnbñ_ J+WE}Ad5nROP KjKh[|_:E="z䃠DF/! Iv:DeL~b_25 p134ÅLF0oEG5scN"|DbsjeKާW7wn>w9هfd- {>g=r^gߊQ,,55d_$}܆Y;~fK?w$-$('uf͛o*W[=ps@ 8~v@@ TWP~iq^!8QN44 (_$"|ڧ{kJ8b/9`}RHjk/2CCCT].Jj`!L\Ȱon^O v8^Y w ^zؽnvQ ;޹szԕK/:QU:;[$yOe [arH+ $}ugn{q PUް%ܿwJLA+׊yox141rw\]ka|;Mi't)854f !.z腿Br6l B!Rv֜4SB qZy }@J&fxj rGzIBUhMHR$0Oŋ0s'8M@U@  @҄A?@u׆k{i0\0kCa5qh}w6Ou|~bϿ>%&p6,yRZUIQœҷ1?6C:&nX>OcT??l]<ЕvMa2aRW_!* 9D3&iHOcu>Vh\&jDQqmflԕ#Ľ O| N;t>zؔ_c/Y\\ v#x}]Q̡HLZiXe^8Mb~I6;N5Nj0s?8X@@ @a`Nh/ޜ]E/\l8p[͞~)_FD.8¸C2]8;900(gbnQ YLJMQ_~~V>sMd-Tw1+b~+BMxI5-Q9=Dٳ>^?SZ]F7ҿwFd%[$| KLI/*-GMѤҥT#-a=gQ8Ǹ,g$5UY G&6a>J8l!/79i%i;;o5vwJ|GJf5dСgv7&.SׇL?A%:W`i~w{ܚq Ms $ݗnu1sQNQpHJMIɥWyhH$;~b2R) L6ͮJʽ^zȵ"#ؽ.[105S KI3_=!-ɽrE|Id=r"]D6`ZQ vk؇$GdUG혊 ^]\| 4zvlejr6x驱j@`` ־`  @ @@@PFl@@ MkaQ/kh#Ah2>}* )zZ1 /2-:W)Zl;GM]S)*[Wq>Gc#ypfݙexQUް%M HCh7v7IBtYCst|x40F(dbSXÉ״KOm4X@`d3336L@ @ )Ӂ;ݷ=͖vݭNq6. FYЊlɄI]qBt==g,|t AK][UEk7,ٞM/3?v6o:~$tдVf6b{^)5z/GB*ʺzCF9cfGI6 Hpe]miuϞy.(炍lU7g IT$;aá01JC&/qbkvxOV$(O\uz_ylxM0lk Bm322]fDxܴ%2wf:㎖7Ǭ@"j-q޺pIp M{q B2`Mb!"w6Jϙe7>br j] 7ia&b l{c̊AߊxW^DEM`0ڇo@2>uE%d1) hU_A`3b )jucl"ߔhnƠ[Z4‚7qʓⅪ谯Q)ťe"001_@P{kBK6x3%(گ@Xc+ZT䤄$f>k' Ip dpޔio# @@`@P` @@ HvԘ0}v}'Ȓ'Y/j Ƅ<;8hAb!m ="?DA3e @'%@O' @H _T^EwVԣY]MV;JoJm"ELLZVVVR)urӃ"LUIdS߳~F$&K4l{Mg @@ `WQ(3;119թ<ཅ 4{Z?ǁ@2q.)vKlJeu\)Lc REYn 49s'wWjYZAD)K7HFAWoOl=ܤkW#g:3m Q{m2hj r! \ @ DIIIiiihe_Fcbbel@ 7BQ-džP&@`61:mlyڠeWrXU:NFZ򅍇o%5e7=%63'ۯ]{h4q<#gOuFSM( Y@ B(b`& @ deY-\s}\s.ɹx`۝C}:a)as֧G4&`žçFQBd4x ! B絋]=~3t ̜I3)sF{ .-'#ӫw?vuBJ6?vXL vQĒ={~OwߣbgVV4j5bi*+^!esW8GJB|^I#)(ڬ9\)K  SPLHt4knj]SX/oɿbh4HGM|Sc}򻴂*)#EK״E ? G\s$d89GD'$$UTIHveb6Pss'.Nᢄf7>Ƅ>#T'aa&/=hnP 2R'N\둤dq# ];+HJTWe1}╜W֏y3b\חuD3̭N~^$TۺJ>w~ِd0k@w6}Lm['zɑC6eۄְ:/v$>VxXiӤ!J<v'|5̊XbׂJ}fDEE23N G42k]{Vr4RCGI#_>ۘd̅!]7u ̪V'\{Ŵw/W~|jEތgjpQB >q"Rj"`ڴ}V^.Fjc-( UO_v%+ tdE b sϲjrOf E{pl u^cQC9K 6쓷˭  )U=lZ0kY'e[҃tߑ6KRV/rHF<0楷]. b_3p2Hc!F(%3kv3=bq U>0Nq3("gp""1,->?K3~E>?%~:mF뾇hbu|噖_(=*ӂ¸=,+d| caجgJ.$4u{16ճFgU7E ߟ<|+q4Q/rmg!_N1%a;gx|F '( Ayׇ0^zP}h@ Zɾ}Yw$Νhztas'їwL(O2@M72Nr3LGvM?Oװ9%4QRZS'5^=>etre܋m6O\#W5xݦmʹ;P-mdlD=􊽫J ISv_UqFCJk#9/?~{6|wa M> XҴMMA=ۖ -!?}EvfXpQ{DO- HD=| 9>^z_Sǿ; V_xWSBbc_={s_7XvJ!B|8 @c ã%Ի1VlI˔G)(&R$T:k575ꎽ&2=sh_XVQ%!%۹Cl09ڵ\NM[b |-]=4y3/5gjX. 3Qk_?}}[YMSe|xՃ3|~]8*e9pӜS+}MHN/, ҩK/C+\]Nu]bDf'8|Њ+;tЊjW_ݮU0(?}>DKDO_z^T{V<ĶᇞQ{={?'-IBv޺jm) b>>yF# O8_b,C0Y@OpRЩ jL~7~Z}9=yzM5VHfƣn]f&& Cd$xSeu?d^ALFm]63.?nX$rZ AgO9D(K WXE#ʫkkwQ$2!3^pQnP%I\F>cL|!\ ʛD;j@%@Eݹ$&Dž<%:=~@UTTH3º}YZdž4gj=2 h>_߾8g&'Ԩ%@P@@T]>uQ)j$w;zDž5y߹ȄtctntYiT{|'o˪n"B+u3ZnJ;)tZ@n$s}R#Qd}:)YK?ZPZ5kl}52DQQ`ݿ|+!?~B?zMKgwKWA<"+j Zz贃VI[g:>>B3g_;{= 2GyF=yty}r׻58pARf>_"iGn$| ALR֠؃GtW怣w~ -1mx3.P[OLZyxݷ>AȈVt;!C?dPc)[.f; W'9]_~vYXX tww+d\ 2KY}Iv4냈ʼ-h>N-&*;R{QRcv[ſ63OR/`m rG $TVk19Ix"Jd闅:$N/`ϐ%p T%b-EczsOM@g .Nᢄfǐ#̩MT}<ǘ$/=\79O&@KzlɈ**r9ClobxibwPMD _"k_fhR*{BUW:\ 񗵥QϮ 2$Bu~NNdxޅ2 ʊ ֻܶU n@< @H0T+jW>=?n[+IpC/uZed"A9&RlfuqoyEerNbر͋sJ^1էZݝ7݃h Vev:7 Rϝ]TwtjL.ͻw #~"E V|jo]~^Rfe{xYPI/ iȆ~R6Ȓќ3ncfy ݝ^p:;p6,ȹ6'`3rUIlFN0U /deO.DzM:"? ՑQVQH[ 0ibۓ860.Nᢄw?Ha|i?ai</=˧b %GoYo_xHPНF E5c<<[" [ 8i!S(!79JC($z$x#1y^0|J;@G?GyW-ߝ> jCy']5I$T)9*6uhiO-ڬ{F+jn+6I3+ OɅn>Fzv*#)QYZG2O춳*0haw=K*J N dU$9/5-õ{+[Ygԥ蠢ctŋ󳣿}z3-.Vi_vӸpܭ!1I}6~|In'~cvU7^WdJlt,9t_AG_8院5[/_tC,H3糯nГ}`={^}ĊF5B}g=kn"cL?lͶSE]vv:f-Zl( Xs|ҴMMAҾ=ۖ -0"G&BHT⺭ֈOx0Y{^[Fػjil~]"eK]UqFCJkDp<Ս}hwD\#W˒4hݦmʹ;zu2#ziMu玺o?p߯SGu# b`7G-=$o.V(h[-{gTQ~1[K|,s 6m3(0 9D>π\igȣ#g,{]48 C?dPcȘz6F>=3nN~13"ڋ'H!p ;F FmE@Hl>[Q JvT&B8T$Kk-_>s)|+pаNM}$WP {>Dl`hy VG 3LۏhؕUk)2 cN\ Κ5G!.Nᢄ@m30y!l+Z8T=(<%x t3yim!^zPm. g% XlekVne3.3+y: %j ϢPm^Bs[EBy&d5'c[wN5^J[[,@kw{_G; r}|-s.Op[^x#|N;{d`HDqY '{j ZcKNj ?r=%yi!!1r hDYyUuumm.Xƞ"d3LGS N! ө_LFm]6^@&?nKoV=yzM5$)s֬xԍ|fb" nW*(OߺђeSR Rp @dgs:[\icw#N֩}|r3:HP?w4DN[ xa/=?z`jb"'4h׶;'3mDa3winq=.^La;l-'oMu|lk*D?N(ͤ(KsDH2>dh'?*SS f|]~~Xö F@k/<=l=-Ec!!c ~q7OUv~_#%Iȕ.Nᢄœ*j͟3\ɓ1T}x"1"y^09}Jp霧Ed @Ii)Nl-6a?/YFV45:!OatuN kVkC@4خGIj9prvw"J 2GDekRGw׏۬+KϞy*PC]py(}dS~Yثa 4:k/Ug{4_ZdE$B3++Е.$3{aAFeҹCV_C]Ŷ08{ӑ/"k.V,d4d_i׉Gwq\gޖ.;gz֒ܟPX.AD~^ȿ-VZ}QBR4rdu\=Qǻm쓊GґqՃ/]lf@@6r 5ѩ/?}>cz+b4orjEjaִ WA̟[}Sz#*hN:[j*Z Tͪ}S c?\p9ТkbVYe7Ԟq ҿ`]A|Z̲5qSFqU'<@Yy_In_CG/طŁ0s w> ?>ꒂje9K~q ])\E s)40!9E\F/Q? ՑQAQ1u Sh̞$:8?h|;Lj\Eg:=d4uiұ=dџGdhv-jc3N9L^uyEf$"O zDMEG߆D~LQ iU9Yc#(dT(N~1.˱t wC 6\EAVH$ƅSŜXelr7kabOh:c. c۾޳lqŬc'쫮P]Z+۫c PsH2.^qI'9.IW޲sE4?s~;<:NzHϝt2'q.N-ΩݣLz*iU?cC|UJ~F馉=POq!CEwYǂI_ BUsþsLL,MM)}PXs Q*iL\t\E 9\/,¨> ci.aa^zjҫ<,.gaX *":iLn"Uwc~ LJ k]{Vr|:ԡƤFO#?ONz ^|WÇN]HɫWHV_OrVtu2NeVO2-7xlN5DEE23N Gq3Q^IF?j0&#wԁ/0=@ǜP+ʫo~UV׍;gm,&<ͯr򆉮_2F{6%u+֣f"|-z sA#@@HjjaonW#@H"%r=Yៀujh KK l,SI ԬV-S^XPw47 ԪkiJp%K[|fw߳n_kץVe?扌J%qo^f^'ܿm!&P___NNNldYd6eɦӡٵWi^z=d֓ݻ$9e 9*W&[Fqz"1\^y9R.%,m]@CuExC="z䃠DVzU?L,9"N?x1@͢/[AeI1~\ӜZ+٭O5KX|Xߍ\޵}E!XB; c1o{p#cA LwГ~H8FgC"('uf͛o~j.JpXF=gPM8xH;3aa01H#3¼yu'Kpa/$W9lϵb$ά;sۣP޶DQ˕ \Sq b^ _ީL?S̵ܤVT4،^Q^wCwU55'/=-lwjY[JGܓD7Q aD'.s6~ۃ D$dX]fjD`S(A@H#h ]BcB%h*~edR,0'95W>syvgo"+ؚl;g"o>#,3GaOzavzdX{77]Og^trìۑ<)$v킡=p؝T@bDaav,8vb-spm{ȹ R$tдViF 1n<>3 :x۠,rXauz߮c~fб6p 4I6;N5ᬏэ0#oT.5R;?Kq6.rpuɄI]17)tk(O i%6:H:IVzju FJ czxf m򴩖Fe鉿|ݞw{2zTdW0*Ir^")jZkWb)@Zj~BILSWc^=#Q1IҢO>Axexᴯ:ݵ8 s,h_E,-[Ǩ'n7o蘳BQknI-_v~ Ҽ;λ{{SH7rx@&6@eT:|mؙ5.ֈi0;P\gB7Z~9y'ߟ?$.F'L[DA{Zn??R00ʼŭ~an&EFYV{3~>]~~ TGpY^=;ʱ[GԒ^N'/Z\WG!I ȿr6w29vnѪemz,pu9C[l=aXb؛nU#)[@Ư۰D;KZ0@Hp{2Ow(7^y+{?y\yv=;C' BPPL" "tFPl|RAq9E\RYNUyIS DܛVs wfNiy%#9yz!) bU~kw[˙ݴ4>Ɛ t>8;900(gbnQ YLJMQ_~ |"lVG(˽Vbl=dˑ!a)E$82FJ*]j)4s|4X+p,D ";{Yw=*3DtGe5&&j~rَMҘS(!py aΠ1z;a܌a@1fEL oEȽ3Mx!S25̜; O ~K4#4n&g;Y;gᮃǮnl0QO@l"՛v<:0r΢+%]4ПDIm.j^`mƒ{{miRJJ" 6WRF7i63l[j7(&S!ݞ&݊{@)7V4ǚ.#xO%ťhckk4CQѴ_MK7Op|V1m1 &~MCv}x^ Z?:0(#* ^t3z1nN:娧ꒂje9/Ow7iGac 5ѩ/?}>czWekƧNN.)J;yqU JrS??z Hf6p>˩u=50kdžsm3xoQ1/Ks8O ηRj~RsBW͛:ND}W;׸ɑٖ~ s66WFښeFa 59_dB L)TljzRJ:ZyI-dյfCh?#{{LZ)GAs@hi\t`{b!I.=uj_e@F -:oP^ܵII('Kk.z"w߽v[/>Ͻ#% >vrb!6:iLnk=;:8ܲBuio?M+@&tx c6FǤ_oUsþsLL,MM)}Pfw!*Y% d+G0׋@x01G7TUd/>Q8Ǹ,g$k'?qI'9Ƅsa5ܰ߸7Ao=iU|Fsj!:](JԒ¼ĸ_b'Y"ֻӋ(m6g }^dm.&[BX{ak0gl0' $mqƎ2QVNV󈊺СL?2W7M\zL:beiײG(ˬˠYaxNfߟLU@ LtktQF!/!.F*pk- 5D^]؃!wƨimj噬gjwף'\{~K-)' VggYҙnmɛyQ^NZ|âbΦ몡H"d]GD"55%%Mj?$Jw~+u+__Lj ڹWڞA"«uPZ$'k#Rr +b4iX>jd'9 p@5 ſ1kD}5Hһ/4|{@#y8[ 0=aGot?V&v}b=AP6{GCF$ 0k 7F Lw(/9ܑp6gҋrR[7nּr") L6ͮ ʽ^z-琉ݻrdѧz>w9w6hkjFf8|(ֆ 5*KD\ԎHηГJ__n4~1#9eݠ:ȋ^c׸I=Yj ]u=JDiv_]3d|t> se 3z%Ud0q(y D:(O-ίg+}Zm6npXZ),@ @ TGԵ"JO2Q DoIͰ[Po!1ɫU`ЩIȟSruyKp\&݂W .=(.lԨcp]0n4'9v`Szldƣ)m>w]vԢj8,"tIǿzfͭՙ/}x$>jҗcT#cRg>«5q=>zO1( 79?.Gg,ܴq  BӹG"3L9} m$:QMOt0Xt\{wW7?Z]Pns-Ŷ^zau;?P$؟ 'w4s+sD6Houv^5^ v֜/C q5EԊ++ͫ}VU0IZ󆇟ʂ.OYDr#Orcxˆ{v,8Dc݃4Buﱀk0-!w&I.cN8ՃwK&48MJ jaE 0A L\#"nn;3yu Hd1UMe;[\2 8p٠  0N A BH^ Ե4p~MR|}~V|u߻䆪">tҊ݈gYgggqYyv-"(!ݻβ lu?3p_$[|U/cҥϠ)3~ֽ@,X2dqo%<; ];QC~dnK݋ FM<~1ի)`eg'FFm_4waJiu}?/(]C^;0zEs'bV]䟦i}-S Qc,?Xq$|ž^']Wr˱W?Ϣ}ʊÂCF~Vt=cꢢiT %;FQjTgvw IbԲesFfZ o?V?}li!5BS\xw|$ Li$8GI?BŤوA OM"fV[8o7)yM{MnaiMhts.|'ݥ]G} ghR=qmٺ*QVghL ʀg^ MWU206wtvssS|$#{i9]\}=93"YMhj!mA|Ӗm/bs Um]=#cK+'gg3}ME?nbB4559$Y5زB""R J{LR1khձXXR4݌js_=A7y~/h~J={J Â^F%t=Cc;{憢dP 폋U80=  311v"  LA+P+m|4 Fćg~}lMe1B=yU9i!A9zȻ,n~CW-ۘWL9g=Eֹ!Nͻsoo;@HhZ޿jdŧx?lN\/;i&!K/<~„_kPy J׆N8nSMɱ_ vɝHzo >i2M'dn:nWr2/*4BӲn'yւ@(>+}~]qؔTøKOio/FF\C{ܶ?w"y= y|xW,dC_EaG07fUuH;:W۸QzʺA(3t-.S^H^B{V-6<תCwH!vĭAAUs+R[͗UmOpJlU4ZݫIdjteS}CDQQћ7o| ;˶P?s1k5zUq̃"}W!\螞mw?\n޺zGM#DK'2&Ϝig,p 9OdzAT;a.+0Mؼ]7w¹^nN'=# X(`x霌m\ƕTP}t<}Iq3x5V*G39}N}vgV6wҀ +AOW|'go:ݔQ M+/w!:Ng7.O ݳnĞsNط`Ѥm۔:?[{O^h޸|fx9[vNjR:JxU]m"l=xɿgg!1qG-gȹ/5x NZȇ ԞYbF_{3~V{AJJxߡ u?׬0JA+P+m|oI.}wo$!FB1D'Ǻ"YEϮܴi/h_#?jEg 2BݾisKKH( 5֫,]{ Jf=WJY&-ohۯO'VvV tԉԤw!w<|#\ksq"k_a$vo^[x7b?Okٷ[6fF:rb|_\r?JJFh<W9}@JOR(Bܳ3ސiL6G un~qntʾ58޿.Kw ?ϋ_?N{? @p18ۜM_z___1"@(4 @,@\WTyVRU*gQ7h`~(*9[ /vs&n-xLVy4Ikc siH|xc {:0u,O^v`,0*~mff?[%t\z38} -Lʠi*N $K}^Ռ:ҮJSYuۉ9U /M=ؚj1v^NvLtlbf`[M_;]0%I?y/۸s$:]?<\CZdĞ97[~5MzY"U 톖6ڶwi@2=.ZIy*ꚦ 9tjܐ^m-$+/7SUG{ <-FMhlX#Iżjjgt(@r cͮ3)Y d$Dު}I/c-ZBo@ޢEo @-cwQB+AW!7/T}ƣ:h, }C=E: 2=Y~AgJN 9q Dd)I=Ot[=0q[x?wBj;۰UT2&.ǯ3?+4P6yNЮI\B bB1o1@xqHp);5]7T94ڿNqY{?+햃/-y}kò}-srmg@y1!/1 IE5tyvM*2%رӷv&O fZ7AŜ̺NE+Z7cwxk٤ dV?U?lO)C6 (Spߒ~&R{I~! Z:w#dvc3m]3y`WK=YʸqC.}x?_V~ΟX誚Gw %mb[7x:|oXK2W?gM*Rb#CCl>N;U~<-T7YU'LG8h*~q ꔿ˳򓒹'V֑S1YKU/OTi Wz\ 2|Ґ.7hPq# JV )2`SwAV5[s|wo=tGraB Yee| vdWy7 /I !!!66c\_O @jCxwJi nZ G1U=O}j/~]$9cҥϠ)3~ֽ@3NMΛ1m;1._]$al un,X:CNgw\t٩; >jʤ1&Z r oؾ{"D hu=`WW{)ch>EC\ES GzCkܢZ깑L}!SrzfNQ)j`hhecXA Z~ʪ:H+|}Λ',&Џ YԱ&uMMS;;.M5d%GEoG'e4YfZ8ZbW^5OZgܷF~!SN^wal^Er)S=N>iAOS%;-!iD|fEђe' p3u>4,W/ޣsvnZir?'2>yX G+ <@XPuw "hmon>+Dy75L{}Oi_EAm :@ /@ia-,Iwits.Eʪ4أ^c  LJI/(*gТcS (eh~J={J Â^F%t=Cc;{BzR]\}=93"YMhj+(ϑ? xД|Uu-c3{Gg77=uQQc$/,)WHь-[|G]LfS:m{iQ"LVY mi;6+dm\u^Fy(pPvT5C%_ľ=gǭxAM9~DV㤀"Vn[d]޳|ܼ(q!EM}gD24umRYA$?7OSKrc&2𮝎gj%Q vQ=K c9 aA4 WC=?_2!RкP!@>H;7WV==ajНz fʚh8jO=e]P[oOUME |AAA| DYf/G}fo E!\;ʯkJO/uʭVzՙ:XI!l?S @ Pdi~Al` y im&4U3}zh2!'!yރȟMTs|}EN8X E |3OéY//8lZcŐ)妫 Nӕ[a=^m33u4ԩcSE4@>7 sNW4E&oͪ87jIz",Vyx,:seܵ]?jEF[6H9^SݑcGTed܊Aī;/!T`_c4s@EiNmw},Td3[v3sI:GYQo(Cզ'Lن2bos?cӅdnxsl er+f{x~QLB#kɳ/q;hmBmc oC i ri||0p`Sy~THmE<.m'۷?+MSKaR\g _h |%BCCC{{d`ۏaM$,%&kgLѰX̚P`8O3yY#Iw͔K^wz;,=J;-CNpn}yM,LXawOj`SKRBw?Zlي;>i܉Ϟ I[J^STJ W/Cf6wa@WzMlҬngHMr=fwe]OEdJJg6澑懧J`a>8=m?**+,/a/UTzLƥSZ[WΨr @Iޭ /2˿œO\r|kιY? n/_=JYabCK&J@ EK!ڶmU} MMYPbta3]]EQ,$4;&ڴWNkZ]<`rצB%h*jTS$H_y& ⫵s?8}8ey>8ֱ};*YXI2:>+[o؎M0_H>%RFp0kHn*4;oѨUDER[J^SrHe ki żEVG$SCzW6[81L(x%o=υf2=PdW6#0r9L}چ_ט_*/^TE{8SUmKڵݮ wgz9WV]m'P7,?] o_E}åێܑwÚTe$dq}Z2hsDDǤH@ T+/Fi$(r_V~q~vZf^1ETW<7`ٸ[H'V(dht@.l,UĐΟ{1Eԟh冥OcI.5nѽs;k 8.*= ;?aư_twv{p!1)eEIZE: 4є~rgbsAFNvoŵ*Njɍ}1dKVZFJVƠu9/>tFv-:4kbT[C 7:텳>pyY{\9]UOicg/cEV%ݩо]g$>yoAҴ7A4 X(+L+>&}"{BkJ깲=/[2譗yINoELdeUԩ[C>O^"o~4x1a%n{ fjf%;S+C'ٵ, x~TJT r huٯk{wK3VQ7._ 6.H3﫾NeV? HhsL#ˆ =|"E |ޠgM5{ա)vԣ$o^05];Q?U%kJV.WvbEt_[M^@ ^_:"럦a|*RI%ص?vDVB ۺ?|g¦6wxӦ8_\-꣥!?.W%{Lc kc+r M}OkwP 1m_\<{]y,^3gzDVEc} &zk1%Xx-(i_]N+LSAk6j<ӇdS k}- g^>;CDWNd y'}gK˿qeZm8xjwԽ!*,w /rݽ^6H$.WysC.dst"\B *qM)#PP򾠠0Md+Bdu r7<NN;'-GY(޼zZ;5Lz+l+̈́Hw8qQ䯝=:rͤuD[Zȇ Hay H&8o1@HT~޼9d~,5Y1/.xccX2nyκs8K;uoYW+;9ܱ{oooaÆX9WfA oQ~ҥܑ'$$| u4f͛c4}l>&-;tq?obТyb('4ֽ&JKX=wX[j6Wp#T=NAwI  TSRoLXjZQA zyğ[v^ە,y%,ahl@'B) Ǯ}l4~Vߞԓ19;9:P[nԟvljZʽ/Wa߶r,]lۮ J+:zpg`z/kumԣmэasW܆,Y0 ?($Qϒor+ܻR<:o+˗oOm??W$!-R&!@ZOqI[z6DZ-dfn]Ǭ^?{` B\{;V/\3c/UO67b&S\`U,gɂ&u~vŴFbeDܟhkQNݽ~ץ`k#B2eۗ-Ψ5%2Z*Q CsszثKe+C2WC|֠B&J?Pͻo*axS $0&@HFڌ*|_&ӹs'KFu =/͉]6esyߎBig lwAs: Jo^.on?n_M3Ӫ&ZWBö5{!LN7Um{_^R̪Gpȱc/4-6vSH?ZgA՟#G-:6)obȀ TF - ܧuB\}|(WZj=ravq\wlO4|섁}{vs43\Ide$'}uOG$DS5iݚsDf *pԡCm$Ew6~=OՐAN{XG?IVу{g,"k^5m\nٰv @ PS #0ܦZ 24M\PuUẺ:Jii Y ׉ojȲ"ƃʋ߫i**Y4;شXlàOҚJߵjSg޺ƪrK+kcGоP5٘U^4"BM aʬz b8vv6Uצqcj8QuQ5 悇kLZpy\\3J" ݕ!@ |OQBgv G~CSSqŒأ;3U4TK KY~eɳ8T<ڵcG͉ڤi-T"#\z?̹U,mԅ#{4dx'g]۷27'K b"=~㘰n7𺩒 !@ P[Lw:U27$$m%CBBsi4-m-+! Pp|w//4aT.6L}T ߧjgT4~w|_EDf43d젧G H?Y ;5(Ez'\[ݛ-ܯ_Rmê+EyyQՏ xwQhRѡpDdRUՕZXѽ[j1dMݧ]k7S(! ץ@>T' 4K޶=,.+/V)6w60jDU'R9i1Ŕ,(Q#&>}V^;S)fZ?9B.i. @ P4 FćdNg~}lǎY}J~kV\\\i ,r<=oxkuT+CxѼgm9ujNͻso &s2yM[u9i!A9zȻ,n~CW-ۘWʎ@Q}n~c.X\j؏,Uvi,\9DhY[[Dh؏?uXc|i3.ăoyEU?U`>ygY1ȿwքi@P'R(0n"@EP};}oy2GЮ*ؙSt<Iv⺱4]cvݨg z9ݏkֳ~D]e_S#P>r VUSt۷*CueR<e[ˎDG\$HQ[FU_\M˾Դr3hnߡmM 9՘NGiF]焲=ڸKA9TwS~XuZ]&nolY} j%ʹwK Y LdH!uBi(ij$}vc'!mptL\ZfVQ KEUDW֡u&ݛ~oZV]lyp݈Wꛓ5*0G|6:.1'T74kР1ʅfp+F}!@ +>'ǺnM6_ya]^8{?/ҏ=֥{)?ϚzNϭDګ(ʾu냋ikaJg}vm[vW8`/- uݴQE9DroDcM 3 %9gv|{6MԢTِ<{wb& (S2Rhn6lpxAP¨Koqr,-B3hâEcju3C%VYh ͩg@A@ ($`o@WU߽Wm\إEIo?z-O9SI]ǁw73.g=:vtoleI#Y9q1_>~^;4p|ZwNíVGU3"E+J,)cޙHDYƖ0TԬZ~s]g_[ov={`Vrwu05ңKҒ"#_|w[wLܗo*TA(.ԧY]-{ڥ1Ül%1^</7:0u,O^v`ì;KuѼuH8ٓ#Nݲv^y,O~J@׶޻w/-0& Z ///'G]eddp'$H-W3zZ"RXΨ&D e @  x d{`ZͼEwiM؉ͦxY@Ǔ~>SPft z9™SF\|Ģ]?L Ȑxъz끳NFUt|?1DZ[k 2~˧ "0d@aNMXFfğe,"y}ߗo?.R1gД?B2~*if+N8u񅥄sG @C|Eb󎨟V+zWoEr OxȮͻQhL;65X +iP oؾ{"DhT:0u䫫=;]|pNڶy!ciдY]u:a{/f4mCsRZqtos?i5nюq".q&YtG[RhM7R}Atn{dͶ\A]݆N+?u^~Cl H:T7Yf4|޲u[5SL\%n{+xB)'0Ե>t˯y YvPtNh1b꯼]#+C`'[~Ż~r&Ϛp8ccɷw I@ LEDر;Q?dEK>@ |]\}=93"YMhj')'>lkQeo^DD&*ںzf -;:6PgSVV'ސ^SBH/Ӗm/bs "5#cK+'gg3}͚TEc^ȟV<{mhJV:1҇C&)gMt͡kkUSߏ['О°q)y]EޣԘt =W5UѨwZijg?u髧_ KSk޳[{yC$8$8GI;N*&F 7/~UPxjzT7jުa}H+q۫4+a씢)t^l {F˥sg<~!53efnڳFum٘4$Ph֭c vOފtq눼O~1 MjvmٺmXyek*]zQ jGa\Zk" @zfCle_Pd ^ݩH:IcX9_YŠ􄮢śz*Rrh8jO=[")eM՟AYӺk_YC[ ~Ffh3z 9J(kL=ن5 ,GM[@=%ЍLD6nԳj @={ШٯjSCtrj:o-#G*.8ӡ]+/( QʒD= @ @ z'Qwݪ]v ;s|+7@`(P18 @ @ @> ~$_Bg!@ *]fgHtRJbaEAv+5AJʴ_7LӢ:0_a-~c6,mCF !+~\g+:MWC@gzUB @`'ϝ0^BiIܜOѳ稍K.)|e ۰i2ȇ P?ԋ:_qBЋ J @ /I,-*fWa6fCo}؆m&|@U Cth ~Ue@P, @b?f-$&Ls?o^ZD!}As `i2ȇ 9hSi*A @[qoG!/|U((mX d @ 7M]G :ZkUB @ @  .L #!@ @ @ AP/(Y__* @ @ @PO3 վmK:ܚQa- @ @ #.GAW l- @ @ @ P +] @ @ @ T( @[Uq?.xZXRTQ33tpjޥ j0j>vާ'雞[_ʦ6H@ @ ͪ@G @@ ?<>fYMeQ䰷;uxϞ_n]^ȵV4 z+P1v-g:߉yԷ @ D_a@ j|wTHV]2ۯft2:KR~i$ |VGǷyNuM`YSo2?hU+ @PPD @ +ҘjƆLWXYNFjMgl_u_zzjˣ (k,>>  o2? @J JV 3~Xbl1㿈db Z(ҋԊL=:gdO} _d~VZTǏ>ħOy7h*sퟳ( \i~f`%ls֦E$ #}?|QRFh[X4oٺj,;{QGjWpWk^L68,::6=+TMSG[2?KoB9$S71qI, m=K;6ZR Qe8 C @@TBQovڴ]_(5=#>)wb4?/Z2Ε|iYU!@_Yubm)]-mQǖ /a{x~~>YN5L4%_<'O#J%_bi7 +0lh7a#;J;n[۟9.,AWuF eKPD&ϺW\ƭm>]dT@̖=Gl޾SCTHW;dΐʋ Ni|뎞YX1h'0gF8?xJRMrR%֎nvPIjFg4j6cՎUzH{?/ƪ֜[#u\/\Iɧ}}RFҎ/߻k-Ȱ?v⍛w?I^Iu践? i/FV !Eoٲbwɥ1=oժ| @_֯9{Й3 X17l/A`==_IfK/ @(J ;hi#)OƝs$tY^Y{_0F$XgzLƥSZ[ ⦏mZzœO&}+]_Bw7rB8'?h S2G6,`Ѿ^ [r^'zy+S S_ ;y)W/-'vY,nIƇ[0~=Nn%<LAR~3HvYJLKΘУaA#f>' VVB<7'D>鮙R;J oC! *ɨ>f (?on=ahh/$+Oo7Ա=-%#U|@ |v?*@ @yF":(2B*$|S4=ο$R^|2=whv.ܝ,>Wb8G1fK?^ ߋSXSUO5rG 7cbJV8_GN'6`NZ]<`rצHC445͒Рn A_0lƶk|鋔F- *]&&2 u}ܕ7LlϢ't`stcۇ_>?2pe%HA @ dgݫ(J˂bPjgȲ谷>d04̭\[47Ө5mLI =G m'?VM#ACx60' c>vRX|!vAҜ}_u27M%-\w/h-|59 it/H?WNZ!1ٳk3 &Y\$M(.3?{"ݩо]g$>yoW,(M{_U) VhrykDƧq²b-wngma`E=-O1ld{ |/#(m̼bu Mu5D@@g0l\$vX bՏAF~ R]JM |qEެv|E].e;οCU9hЬECSm 舷Ξ}+cݶsvQbr>2]0:hȩ_ޮT޼vvBVř/ wJKsPʍ)@ 39+DFGP 3ƒ*IؿmοB޲SsMAEJ[G(3 M ]P oMBgdst"w͚%9Z=s WŬ^q-O^x&:D0u6<5;O/Y_}]A"JVZ ?jMp7ii<bgk˷;ft˓_ @@Da|*RI%ص?vDVB ۺ?|B;"ʕF٨ObM->Owy ~l!!TZ9#*~Աmy/^ ?Ӄ.t«PDX*LB @|B-B,gvNob%d~v{O?&Fj(/KYP^P @@u?b?[S&/2_2k |Eٲy7G:4|Ψ[Yt8i9|f*0|ݨ hXQ誺#onfcq/CJXհ٩[ ͡1̝ϋ)11T|S7+~WL0 .?zܣLި[:9|hw?_B'1 (W*Ad:]{M;?-Ss߷.)r=~QPdǯwWɜd6kK-e̜R6M[GҐ'+/zܘ3b&`Ӻ4/m6~|]ܽ-Մ7a  @r|B!| P 736İ7:vsamRڭ#$ @=scAQߞԓ19;9:P[nԟvHCLJ+I7p=wxt` V~qg9E/ߞ~<G7slSUrht߄ې% X{!#P\FBjwDt $J'pkPR*3A~C996? @[7o 1zzAn7hNvi#jҲCO `WkUoY/os+_~E]+/mk 뻂I-~>`',;!@ 7 e?!g%6Űԩ[C>O^NmH~{u+[' X}5P#j& B`6ܻq֠9 ]nd%FP7/q77}i=)$Xӥ/!I9zԢϸQ}X ;V|^9|*+ +I#Q]R>?ݷOE B>wK@Ӵ UeY4?W9|D'*kå~lʨ,VSft5n,$4+!@ PjEowYP'Р'iqf *![G(? Pk'6--[0蓴Y V:}[X ].7$ ~JԂsIrsr $332y2lxiɯ6SL駋(*#Us"J$EϪL^^^UE1PYYq}QnnӨ[-04M\PM/˻++/v|Nco&ssnDҲEXAaA@  |B7BwYP:Vng z?|xw8qQV_;-跹s7s{눪\@h=;'7  -i"5gAO QC8O6ϿnE>T=+ ДEWVQHWJVJ%FB憄0Kzz4@0UdQuSŽ_]ٻy0cWO S&JO۾fipjaH֒{c +>E*"]$ @ < 1ac!?~tK͗:yF(. DQm^ amW\q2b^\K%{n? @VrB9qf[5fR'hy ? P4 FćdN[g~}lnWmq>nlQklޮ;\/7Q YϞB]\n\I[olY}/Cm޼r^w@r.H!dO7>jks//GmErkGX$!@;_aT ͭ6[jW;X)ʿ˂Pzg4٘U}PJc;+ $Xo! @Vw忛!+;h/ 1ĨԼ@# t,yvW蜛|hV6]fٹrټ'n@i{QMVrz[ro^:Hwq M(֢rd.kRR䞳/o3zZYE>ݿqqۖ?*@$tW@@Jm>}}c݄߂ UM6_ya]^Booxc]\b)?ϚzN5[K䊜;])**)Y]Pf+t!3.vN}nI$e*v#G啕n1 7aM4'|=~sڅJ  |69o޼ l=@õ Z UJ4gʺoD-eAO(3%ySo3_ $j tB yG +&{o[ջ}cC]MVqAR\SgA%bҹHm2-7Bonhvc]=[|{sʕX]zuǹS?^x5*)& b^L ~jS]۹MZFj}.ֿ Q~͛w$"kY4snp=52dwph~__ɑNnY;KdY:x{^K+6-QR*DLG3wE둻&!q;_hb.-JOM~ѣ7}Ιu:\5=aOtv9ѱ{c+K}MɌR{VӨREi؜uYNdԧYm){ڥ1Ül%1^</XAvB'El%r3GCY֛${`Vrwu05ңKҒ"#_|ױ8/ߴ v*铝_! |^z"t߈ڹ˂Δ~K,f^[ ,UImd/R@I:B`q@@tȹ!'TsyS{X?q5O3~1YdikhiFgw6Jݱ@j s&s6HOf|N87?71ESX$RJt ט_PX{7e¼JއcۦǯCCć{lw40C]˦v;pTs"JDM0tUs)Zm7#"GŊ!qbpkd·H Ab'ObBS[yT>S\U\,,.Ui\JK+hP\G)u;8vPZ/\~4C5IۍP]$0 \cȏI$d1L.ۼyNotFzd2qK/~ ^"zQ,TP_;Tu~j4T#hݨߞڌӛ}ߗo?._RMǤKASf8{ Լwu C[=mW_|^~tlZU9Pi`Gd3T4,\[nl+ i:n}d J2մ<:qM^g\:wwaR3sjZf֭=;nTזH3~Ph֭c򃭊z tJBfе5$̒peJ:*ejjފV0(Mܹ둫Ϸ'G={WT06kظI]Mj1ܽ[V6UHDdRJzAQ F[WϬE{GWǦ*5\;B И:9ze`lf梧91wPUN(nӖm/bs UjXZ89;kJ79 @So7@H.TF(. +_O(ؙnmV oW_hrQj~m?aws*AW!7 hSii  h8jO=oJ۸QzXMUN2.UEq QPOeFMLm"qHeDj{R>aRHjO@{ 05ZuN=I  ]Eˣ7Tt<ʡ%U)竲lA @@yrP}#}Κu&CP`uSww):Hadlwr~H@ @ @ oYR+^F(. T"3 ݖdӄe[׮M9Dgz;oe3jp7JxuDG!@ @ @ |BUJw"yq|TLo^<}ϫ,Ny"p9Pia-Ѡ`44qѥ{))|1 @>@K NJI, ;sh~Ưi{! `W*'* @@NF(. ==%_g4 , -CàߘS|V9fvn#ܪ.#\%:Bv @P;y)DNK)6ztKAm@]`ǯ e@ T/aF(@  Ejev$fC_v/b5 @ V2@ Pw1m!1),h]7pݡK[;tX @PJ唽2Bv3,@Z?1v9 @ P2pU`. @ @ @ TL2q0 @ @ @*LH"zX @ @ @@ve7S25Q @ @ @@VK @ @ @z z%F@ @ @ j  @ @ @  @K @ @ @ @X- @ @ @ @@Y  @ @ @@Z" @ @ @#׳.1@ @ @ T+aD(@ @ @ G¯g]b$ @ @ @VjP @ @ @__ϺH @ @ @ P @ @ @u@ @ @k <YJд:Ǥ}}ly.tW1K)FR>ISI:gGL}:kU+)CֲlLy ]G `2[zҋcn%AK^{; @ @-*FuiT!A7iы ~']::swV1K)*@xM aٻ&ºg|puPjEײLLyjW Y}[Q7hl'Yyff];QGYI_)3'M0u-l[lia]@I~F૗b3s4{Kg-TQVRiڧ{jp*mBSȴKK7'jmL  @ @ +CDr)ȩtU̒X5_=_UWxz (!•}tu$ѕSC}?@;*d .aQOkL0_ M&ԧB*nғ  IKrn=zqPP>Ķ唩l5E"&t @ @@mf^gfE%\3MņSi:"]bHI4i#]MW(XY諛!ZjWa [ Erdʒe1q4FV &bb`@׾?.z= |46L41ctb.{&+d$o!c6M[1ȒQIL_H@$K}.ppOT$YwM’9YtUffeyũQn^+=e_ȊB7!@ @ YѱYb8 TmجP:-}V;Io`e't}g4K|-KYe>n&bUV%?KI49&Ƞ0cb Ie1A]S7\՝/}>i߶!(%s ]hY-+"Y.;y4WGYGI좩GSKUJN>͍j7>' }n9Vq]8㑓7~a Uշ\A @ @ e ĕ'^i vNhXծȡ*f)΢ js-15fURJZ[ dYlLm siQվ'os ޾Ź2靜-yTù˰Iq}]{ۊd;²t5#iao*Qɦdi{/9ӵ͘do7pҬ9^.4!m"@ @ |yeII)%,5M335fu`bb YKe&f-]&tBeilcbb,+LŇG尩 ET1d$gi50SW9P& 5g_Bred'f&r.-JII+(fiiWsU@UWHZz&F*VȂ,6id@_[]x&+#593a֠uTZM`B "{Rm^'&  XɺɺT>.&:J,II,6rx(c-Mx iZl"(vOra4WLC^m*V<{ 4MޖfIF.SNj@`W.!%18:j0qЪQet[Я狿.dKO)lA @ @ 4(I|̓ТS@꺭:>n w/.=p5''zOZfrSڬa6EaY$ciٱΜ9}8qC~][ \A&v.QtzcٙqO_xSw5߷b;/9p4vlb&$"ܼtǗ3CMe HZ5wM0ig!Mdosߤi6lYfD'ɏddK4=EU"-x26f."ä&3bߝ;ߛOLh7o*&&8?';+^ /,X4vb- bm/Od'F޸~śмʋUh蹴jX_ k} @ @97o^H`d2ʨqS9DUU'>OEsd$GڱL4!:̄F%3`2Xk +}Oc+DlݝrW%%v0{@&Ǘ7:r9[ee9ٯ~Ũ~d.(]E:bg'lټ+EApXw϶*mٴwZ bQb\./K8!a- (6m~wjիνί|wCnNW2ۼL]#F ľ|nzAMHc+qk!jX,J1 /IկRr stut%40b$$?5=J3 cp4@ @ (*@S]b!oIWC ;37rˈ7w?h{|wqj:ke;5T4YCdQƱKSꊔy:sA!{tᮋS %ع1GOޢ MFsV}-) ~܅kiŷNq~ʒ%aT Dΐ8+9ntPU| mmT贒G7_ȩ(Q10^I*k//Vxߍڦ*ΙK XD [8vmPm owDijz^up1,pkXλ~`N݇/Q 24 ѧ&Jy]>÷(=+V45R΅%K=Zb0s0TX16<Ks_w׮u{ZJsF>vxnۆĵoN0!k-[ԸWHFyQH,A]e`M˦+gc!2an,kܹ)YZj&5JƭH֭Ew2+\Ѳm?G-n^C;w)= 9!#ڗXOoK 99;c))y4c#i5G)iDc+iŪΧeeӹ%,˼}ϟ31jeUz~7MOY{O9+,~f(9s%QA4ȢC7م)xh\wzn0dև ̵Uڎ}n\o)C2մZt1h>meC|- +/݇lU@iv ߆gp^Jy~SO/q|ν->/"}*i#p&XYQG VݛhIFijB&0deT1c+i{y_ZJ|x>1 mzE+lԉ{E)>#n4{ DZZAT2\ymsy4 ^r- _xQ~ӳ,e*4JYRt[Uͷ"YYb)|ֆC%¸R6ZU , 4(u -\ vq'p*=| daaD3E+UWSfRWVF!u?r#3rTB. @ @ P+̖nkQzQGt C TCkѢx=453{lgTА*F5n'q",TYAM6q4Yq11CMŎ`4OAK%~sI YŇSy6MEK)spqJQy_gU~ֹ]Z;52,!ت"kTɩ)/]Jէ*b4m+F'Sד meǎG}pl"R˨8! @ @fR &uBu9I( d͢iZZJZnjjBPBH 8s2Rto۶kչm#Uǯ%U[GDŽ}*&DgS'qff܄Sו C| sO8T 3)|Ie*)3)2AdRR"yE/tDduױtBh>]Ȁ R =hL&@xБRq^p*Ma^NaҼ\MxA/˯mKkG;UCm^ήV@²ƊYH֭E&VzB!蘨hZ r,"}iH/H:{ﻸ,*uDGy-I=1@WK?wW[ vkoi_V>+עRcb93L2/} @ @jIrtt%\Ҫ8,s:,?ڷ{DRnaV⃛QOmS{Zik69CFNYh" m-MI}#C*[_G v>o/Eg/#5ûe^^XmTO7g-)!q- p˶mO*&Wzz:͹(rN6V$&d*uN&S x+ykq2ke+|q\2::4 9@zD^t  @ @ )gP׶vͽAh5g>Nd8?]gQklXh(U9]׶g*.GB)zZH8ɬ?pi/-4xygLRrsXJH3ײH>a -5^ƾyZڛ{%[v~ːff퍪fd\LR"uB; gY"YYʳ?rN͋ ! KmTJm\ͥh42n rxʱ5 2} K"#>z5ڱZ}N^h?(/n0*&ʙіXƙ4n#nhsҍIYlz߷9UѴuejh]ѧQ]tm@ @ @o$9/M F*Z]Rœ0G^[du%TMEQ"eOm:=(bY%O̫T#%kTj&Q*eOJY+4m^=' bBS47|Z~JV[vRq6/W۔*JH֭EdIE"un.A˝gT‘SEĻ(S/44uEejaVf $ z$33[Bş9zjOph7ǡ]aNFruv7P],9)ӌsUkJ]b뮠=@ @ @KUWHbܮj.w)s ]m:ROd} 8s4uќ゙]x,=2"Y+23=-24!>.r$D _.|q'k Β\=2 QJ,`,ZE:V6eW>4KE;7+UY6HW[,[[,k >ë->>yέeKn(9',Y1[F( @ @ HIJo»ԔIb2IChX#MW}9&4Q'8ڋi*?\Rr2A4AFGeM|ԹA6‰H|9 S!T5U}<'Z NfJ|N!7T54obqqa/^&$PCaCLЂ,(TLf e~ zVeT! "jŦUbJb3ltUb;TfVlEn-2oݽ?g숇- 0~~!F'6`W)"0%eWt»NޣC-3y f[Ws@ST4.M g6D%}̦ҪfMmEOĮ/F ֧l|~.y @ @ ԭHu:i=a hZd6YT51 e) b?}b4sk"xjFV uhdqQԦ* CFUeg$rVFVTБ:G}hhYjV~㗑"Cdu}A%jjToH0;}#-[Z oY옘Ěz3!?)"#ڗ\OyniV߇ 嬟fUض*m~|6ihˆnOK8xsށ~\>>)i1wPw>4h!r[' |Ih @ @@] Na# 5!ۇ7cYsDijq3'j4,??䱵,l~*r(N721>H凇}nE5읚T,HJ::D鳴m\tsXdF}+ǎ&R1NS5yԄ&pTL .$Ɲ:?=>Z?/9r˒>in^40x: JaeE\4ѫyA$1 zz 0lLwG~tZypP+'&I_ &찵[j'jV1vto/؄4ɖyWQ3oE2o-2n]Rbcwkkk2cs4,:bG9oQWs'дSңð ,v4NG vWGR45$-^.h=D*i }c6QQR+.*Nv4c闧V}FwJ.>-ojt]TT |[ݫ)te Y ^Y=Gx}.ξj1ϝy /W),-'yC{/GW)%dЩ;RM0ú~TDP9"I0}vo3?/>w9ik[5e΃rKKoB%ggj7=ugK _8s阞QV[]V~f hJkz-KFڴm{E\qjB1U;M][ϔcW]JޭH֭EdLl,u)QjI.ikc+OK9`rxʱ' 9־tRG~ʉ;4?_R>iG~:;yvl{‹]U0n$ BMZ c.c#.ͽ߿jG93̵$vJ JA @ @  hYyL~"VENUqU̪h$M;H܅Ko#2ƍzڻ?FDcꌘ{" r Әj6\JhaTF#k-UDchaq>K*(aij4uԽ7u[DvbwJjW= dM^i&3,?Z$IE{7=DII)Ғ5-z.]}oW[Pu޿.vڋH$]ֱe\mElԢǯ޸v?0$4+#M=3wN޽i ]rVdq&YjVƎv#.RWsw6𫶒+uk<;;&6KTl5ngjcPxSUrxʱx[(4m7M7t޺w|{.'R:]#C=-^*fM g'Ff⇞it5N}giQ^ZZF~aIchhH8tHob ǎHCkꞁB*fUw ~SWMu=2 ,rsjU7Eyɩ%ej LT=«=^~g]XZ->U^'7o6vzѥE))LU c33uq>]#'8U_HҙF뽼*LzU"cv{u nRW!c+{@].rFAel-}bbl"Pd YBxbi- uϿ ):EWQo`i%zVR4IC˚U.'ofküȱ+U٭d݊dZd-ëkr,"r}ȟIc44UCZ@~ֳ% g+݁ @ @ YzvIy֮u4J$eye!@@[ @ @ Tkȥ?LgmTqղ̛Q yYJ7B ַ @ @ <xSvzT_̭ʊrccYUt-&*rkï` .fX @ @@=M]SJ#=1{9H |~kc @ @$ =# sSh^C3#G&1U Ml45GhPF& oTotc @ @tG9jZnu9@~!@ @ @ P֥6ڂ @ @ @g@34@ @ @ @. @ @ @>yy@ @ @ ԥu @ @ @  +C @ @ @. Km@ @ @ ,g^h @ @ @u)a]j-@ @ @ |f? @ @ @ @KRmA @ @ @3 @W @ @ @@] @Xh  @ @ @Yϼ< @ @ @RºF[ @ @ @~!@ @ @ P֥6ڂ @ @ @g@34@ @ @ ?_IENDB`bpfcc-0.12.0/images/logo1.png000066400000000000000000000246421357404205000156530ustar00rootroot00000000000000PNG  IHDR9t)UsBIT|d pHYs ~MEtEXtSoftwarewww.inkscape.org< IDATxwUߡCh @HI4V"  ETb$# RER 2Ti.HЖ8 *D$HNB~f;>zkݝ9>[r ?fl8p|ywp899dedp8ZG_Frq#98'p87]u8}9ƍG_FrqNp5np87s8}srqU׸kHp5n$p86V,-, |doS1OP`&v]q8'YMW?Pclǭi/ALVN|_kW*\2XcȰ/Ǡ9ޥ٩jљjҪsrGHwq#9#HwBǻQR^80 Xޖ>iq;zWFx9U)'xh`'uj+ҠLn.hz;NUF?3#9UFڶɽ쁁D'Ѭ6m61Ol'2K[Sooy1-G7jMF^ &оd~^pbd{LvziuǀUuɾﷺVpʰc&.q]S ̠j| '0~8r.4Ƽ݁QȈZ_ < lo1-Lϑ F%cnoGm'ѝO^9p> l}$j8Ҭ?xc-[Iw m6FRc >p!G[cJ/ ɍ'/ X$Z`8lDBFY 8i[k/m*p̚c>}MdmmzRt8$ "#^qp k'!; }"%0[ cd1?4O=2pQZs8 ')8M"AKdNx}ZDU}<$4PȂUCؘ{:=KN8f^÷bmɑP7N?3N5ܘw'zd K 6bƘѩc GO[Y,, \xau2c̿62y=ONnI[ʛ 5c&!qv83-N8ˑ.]uě e.2\w'fqNn^15N p O/YEcuy]O c jCu%z%&cλG^Baޝp8Z[#ǀ$zM'sn \L[!"Igv/r;pB7l{i9q= |1M-C:Sh+ՒtO>"ov醓,NǺN}Xּu^ n m:}ENn&pTmwYpc̰9Ng2n# m? |d؆1dzww@vwN9HތFq`'c̻v8,Y䞊hFb l98#trdh;Opr+pdCվprG6Gē)t8,NG!Nl4sht_cj8Mv*p>j efȧHȈq4Y873dZFvӦ0fx29|Z. u'7ͻG{=#iw 9p=Y9E26y;sn{rrKfd7mvrܾ u'y?X+cP21f6n,˱}e!ES:yqSw80GrƘ큥Mg#yX 8Km9CaLb3?N6Ċg<~OƘ-/c.NˮMҎ;5Nw/Ąd`8fubsVJ)ݑ\jNTF;4Gr0wFD>X`ےSAɽfiqp %trI4O{Do ӝ;Sl' CZ!8HU_Uq_SDd'nZcfϴxZSUHkM8]x}tG*$+FNQAZ#T($X)!zJm7CdZ_t#94Fr5{pD'^xp&oUĹ'kl>|u8 GD Hɷټ,N: 䞪|p u͞^\/]QXX X X&MGF0wInmOpHjeK |"#1gUg)'p8iD3G_㜜ks8}srqNp59W:JQH+Z+-ԏ^Z!ΰc&tRVn&.)2-N|/6N=}\DS^M/XmUPlU= Rk )|;#8-Q$EJRpGf*/!Zh΀QJ=Tk=sk8>ZSj5[%wǀk R?~|WkPcvifAYZS;kd>r|)tz@ xHZd80 _G@7wIEe_n#(6w*8dyi|$[u$c` `K }RjeuMԾ> `4aYɚRukKڿwBu:=Y=*1̞1;pR`uRbue H6Nɯl݋8 !`+'J Z9bYMWֶ78.'= m xEq=kJ́ˑ_~JoRMCծaUJõ'5/ l@vrJU'ZTJ-8ޟ#Rimpֺ]-i*F.$cRhg@')2kð&Q$h %VF~H^xp\;C Z_Hz8Z7pRRd;Zf^|Z8#I4jb(CW(Z IJӀ,0:$l+ ^8 X Bjn!rȔہdHU"S%"GD6f`~ [q=Z)k5+"+V@Qghݭ-iFoZ'J̀"pRZF p6tSSXD#7E'OΠAzRjwxRZ:I4Y|x xˆ }ũ p\Di hjr!\br G{"ӌr>xq6#NݦX,D./ f&E?I CkZ?:q\vmp-RZb;"'"TZϲ#M!֯uo"38nh2t eهv8.hk?*Y-5޹.P,Ń+s{bbfsR)vkrAk}OvA߮ca^إRn2{Ȃ"2?ؘjB$z psK=L"PY\yEdj܏μ8A͔bZswi.:|ޟ׉ldsl֚CvpU+m)V.og#Kg`t9).; YH/g\Х(+ -X YR*߫N*l`7i`k-Rkߘn{-m[uSzRg .^ ?Ha)Klű}r:56r}v4rEZS09Y:fȮќRjA2Ek=+7(ş> 9ڝ;+F]i [Ȋgks/W,9RG;ϥ70 Όlې}R 7qLe/Mi. k|sW/E"KHIwc{ vl| ;T3; hg(& z{U;wȏ$ WP(,   U(NkJj+VJ/v8R^Eۍ_j牙SJ9x>Gs\TJ}<\e\rE{TB[wn߫{TdHN͖Zw6RJ}3eظǃ /P,z0QkC D@T g#ߓm6A[Xo"ٕȔ0 {ֺR1Z;ֱTh"NF^DePj27J眖ݗu"r2~zj' V $u8,ppTPd]b/)QUݏԘ>^.?@vDVJ9m)bt- JXNxdP:r@k2$eNݗ9 "&=$ Y/\ X쩙 h_AR>WJcKvju$&`R7`ZP)u$2B$($pͯ͝}mVH!$ΏvQ,It_}H_W<5N4s*-JmӁAc;jZߥ K:Y; Qk=O-#(ֺ2-ե ՕRxFH,oi;i?RJ](u'xHu|Y(r8^J, pc$vL怣 Z{R -"WٓR o3W di,|iv'Z(޲v@vKC4N`'w{ddw'[MA)u=r۷}&[H1RqHݙqF^8qIv꠵~ RHֈ(2"/?xٝlXN]VJB6;JuXDnFq ̯֏JH?TwTjcX/98VIrw/FƴZ`{"Əe* Hhd,^#SI4 6 \Ǽq v^^ 8VGv^xmD̸pe B'-S.GU(uHJ'C6U7Xv5{8>)H#'QC̱m'd]hK<l'Qe~]!#!C' p$0!ޗFBG*A4$*X)¯y}~!GֲMm٤U4YpLW$zN p%$``/HULōb{tXD Go8^xY.sdG]` g R8ҭaPGU=8^ H>e/xCq FF4D0vGt]4j^%^xED+9۶cT>5+X uh8C!#*iRE:#͡H}co>DG'TPJ-/̚fhZ 7''Kü ${ҹsVTH*@;L-)r#>PZOR k+ ,vj{/ѲԓRj: ֺRj>`;} q %f۶n@W IDAT[XYV)IR)Z[[:TvCz|ֺnRȆNZbVJ= >=ϑHT[7$r"G*'#F͏aHYu\IH'g/B[BtCQEBv@)ֺgsw[eY/] H̝MlRk!k=zkȬhUQ9X$M/,ܙW|NT7~m$DJ]SjI]R)1'w9o'7ɗ+R[&䋶*MC$ZV|}~*FvQJmփuHZ ;Vk]?ȾRUi9 Qp~<l~7pRj}K)uQ?x4m$Y[OW8 V_&5:xX,D%,oVIoHFeUʎ#iȃgbݥ|7/xrMQ>ld_t A$ڥ$nݣ݉:OkvNUGkXٱ։;d /ȉb)qP_!a D:#H@F<6,k+=G"qp$ޤKkԱDUZtuק(h3=t&Y:wx }I#SzPRA/XX?x m$xX|jpM3X,.[(*USAnṷ H65Z[֮: Gx8: ;p}4^Wwd'*"<5 @ ~k͖~yw[$w^8,]Jw'}>d) "xŃoDv6;E"I;zBn  r%K)Um ;ɅW)Z*TJ WX]w+6͹K`BTTJv[^dG5hdFFs2'p8Jp O(M~}} X񯌈ֿb1&wTQx))??+uaR&;֗U image/svg+xml : BPF COMPILERCOLLECTION bpfcc-0.12.0/images/logo2.png000066400000000000000000000077331357404205000156560ustar00rootroot00000000000000PNG  IHDR|3ysBIT|d pHYs CtEXtSoftwarewww.inkscape.org<XIDATx{U}? !1JE<#-V"&%"cJbWCglxF7^J-19TRz* S)~T={@OS)^JCR|/@O!S)TRz*e\xCDcRvkcZ{2Da`?:jףΰ=f} "\|S,S\o}@= n(*țxR/ԂHQdK/wZ þNG}hh=`Db4k$_if5طT/Ķ{ + ![=aJ^|1{XUX7MV= 3KvL>я[IEɖ:U-V=se3"'("3TuElWuzcD : xN|8x* z[ImDD&ȑl3ty|X yB|Rc9GUIϻ! 9}Z /JҸJ'N,9==4U\eWzoI(I] hLTƞ}%i Tgzh`qCt3Www]qGT;8/I/=X`iSIm" Fd{0GU,V_`D7 ؾ[;d9pp~vt"u1zI/n>`E(0#Inzm1;ï\7x`yꜵ _R-~"2f)TumJw4>dcf86IuB@U-ڜi^oꊗ+Uu86WG~ vd`ID^; (.>_I" I=&BIۋ,b<0Iea#4^wS6{c%rAtpe[e30+5VOg3zķHUo.0pKDmUM<< ؄l6GӀ2XyZv1[0 6#6>s0mQb<PE';gFDG sy#.CňXyylL* f6`ށoX zO &ųz ^3U~v&%wcv|M„cȕvܽ/ nPAP\ TMRUl/74~4羧0vN.0=.sqQ= Tl &i iT욤qn/EɤdZE<쫪-_YV5 ߋ4pu+vwRr"g W=gaY G&i{gA4 裘ɿ(y=1-u6 Iu:051~< ڎjVRkh d)s:4 7U]vO5Iơ*9w{QqdT\ p-p#[u`Цq%%il3?JmqTM5_ԁi0f6jp4 ?mp#cvM^k}eg+"#PdC{6[j2\Ge6{w-ޤve26J'H.Z ęEq!9a0uyLd07\E)N"}Hٶjm p2ti(V-l $5_JzIU;` =`R^Z ƶ9b{ =uv^H`{$@v8./ry.gvU;Q6.oCe[FUWeEbJ6p29L:0qJV 6 rjyETa6BCmԂ,Gf9poBF-PM*+-hkp!lԆ,l]yX""}=!"2ˁ-kY  onE$7yz]q S{0"LiLoTmb1pe 1u~ ,Sڗs!ilq< p˶y/"2&+Y:ѓ4v_[1ly%IO"YFw)pP}-Vmm؏l+u-pvMUx#;Cm=gBvCGUjwѕ;:aMs`yULyy] 0rdK+RȻ2#P&apd.W"29E`DSٚ Nt/"bʐkLd֭..Tc h&mwADb^ɍWCJd30l_cF;slߋUF] p,A;10 .YɼH̯"r8&J oHyRE@D A4Rm4bqUDvC)ܺTU-s;{aiAtp n k_fzW'i :'IOzRDXؗ~I@UC֘'9a %iv]Ʒ?TıD$~dJUTn`D`CVY얤i粉1y:/[1ĺ;̉rc20L/2p86bv݇WF1ƃzW^|cL-ax.)l;PUO w^ 8י4E9jm?V7q; [X\D_}.pzvγ=ȅn۸XU_iMcmTᾐmJ0:Ѫ_'NMҸ:m"r50[?-∈L9 \-!Fto~9+Q `-aB`g ,ZD,lsyj/eo롉Tu^|h̺n q!dxfM'cۛY|@UOU|y{R:ڎPկa6mFCCv_NLn@#MSĦKr +xw^som i`ZӁX/$iSLlGV٪R9z(lTt`oU ^[J$iu$/+!U]eՆ^@Uo{lp=CJ&i)_,I㳪v p5=cC'1Tw.IV˲aC-Ñ4ސ)P|%ͬH8wRSS&i|o I_ɔXɶ{a)ph\#]{$c-וC6bR"ZߐY&hr*&'^G$i 83 +0/?Y?ylz,Iٴ#:xs&B$i8% ˁNIK6H'i)$ik`nDƔEJWf $i\Bp(:iSc vL$y=3]TLQL#9x1=GSqW"/IENDB`bpfcc-0.12.0/images/logo2.svg000066400000000000000000000122221357404205000156560ustar00rootroot00000000000000 image/svg+xml bpfcc-0.12.0/introspection/000077500000000000000000000000001357404205000155475ustar00rootroot00000000000000bpfcc-0.12.0/introspection/CMakeLists.txt000066400000000000000000000007151357404205000203120ustar00rootroot00000000000000# Copyright (c) Facebook, Inc. # Licensed under the Apache License, Version 2.0 (the "License") include_directories(${CMAKE_SOURCE_DIR}/src/cc) include_directories(${CMAKE_SOURCE_DIR}/src/cc/api) include_directories(${CMAKE_SOURCE_DIR}/src/cc/libbpf/include/uapi) option(INSTALL_INTROSPECTION "Install BPF introspection tools" ON) add_executable(bps bps.c) target_link_libraries(bps bpf-static elf) install (TARGETS bps DESTINATION share/bcc/introspection) bpfcc-0.12.0/introspection/bps.c000066400000000000000000000227601357404205000165060ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include "libbpf.h" // TODO: Remove this when CentOS 6 support is not needed anymore #ifndef CLOCK_BOOTTIME #define CLOCK_BOOTTIME 7 #endif static const char * const prog_type_strings[] = { [BPF_PROG_TYPE_UNSPEC] = "unspec", [BPF_PROG_TYPE_SOCKET_FILTER] = "socket filter", [BPF_PROG_TYPE_KPROBE] = "kprobe", [BPF_PROG_TYPE_SCHED_CLS] = "sched cls", [BPF_PROG_TYPE_SCHED_ACT] = "sched act", [BPF_PROG_TYPE_TRACEPOINT] = "tracepoint", [BPF_PROG_TYPE_XDP] = "xdp", [BPF_PROG_TYPE_PERF_EVENT] = "perf event", [BPF_PROG_TYPE_CGROUP_SKB] = "cgroup skb", [BPF_PROG_TYPE_CGROUP_SOCK] = "cgroup sock", [BPF_PROG_TYPE_LWT_IN] = "lwt in", [BPF_PROG_TYPE_LWT_OUT] = "lwt out", [BPF_PROG_TYPE_LWT_XMIT] = "lwt xmit", [BPF_PROG_TYPE_SOCK_OPS] = "sock ops", [BPF_PROG_TYPE_SK_SKB] = "sk skb", [BPF_PROG_TYPE_CGROUP_DEVICE] = "cgroup_device", [BPF_PROG_TYPE_SK_MSG] = "sk_msg", [BPF_PROG_TYPE_RAW_TRACEPOINT] = "raw_tracepoint", [BPF_PROG_TYPE_CGROUP_SOCK_ADDR] = "cgroup_sock_addr", [BPF_PROG_TYPE_LIRC_MODE2] = "lirc_mode2", [BPF_PROG_TYPE_SK_REUSEPORT] = "sk_reuseport", [BPF_PROG_TYPE_FLOW_DISSECTOR] = "flow_dissector", [BPF_PROG_TYPE_CGROUP_SYSCTL] = "cgroup_sysctl", [BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE] = "raw_tracepoint_writable", [BPF_PROG_TYPE_CGROUP_SOCKOPT] = "cgroup_sockopt", [BPF_PROG_TYPE_TRACING] = "tracing", }; static const char * const map_type_strings[] = { [BPF_MAP_TYPE_UNSPEC] = "unspec", [BPF_MAP_TYPE_HASH] = "hash", [BPF_MAP_TYPE_ARRAY] = "array", [BPF_MAP_TYPE_PROG_ARRAY] = "prog array", [BPF_MAP_TYPE_PERF_EVENT_ARRAY] = "perf-ev array", [BPF_MAP_TYPE_PERCPU_HASH] = "percpu hash", [BPF_MAP_TYPE_PERCPU_ARRAY] = "percpu array", [BPF_MAP_TYPE_STACK_TRACE] = "stack trace", [BPF_MAP_TYPE_CGROUP_ARRAY] = "cgroup array", [BPF_MAP_TYPE_LRU_HASH] = "lru hash", [BPF_MAP_TYPE_LRU_PERCPU_HASH] = "lru percpu hash", [BPF_MAP_TYPE_LPM_TRIE] = "lpm trie", [BPF_MAP_TYPE_ARRAY_OF_MAPS] = "array of maps", [BPF_MAP_TYPE_HASH_OF_MAPS] = "hash of maps", [BPF_MAP_TYPE_DEVMAP] = "devmap", [BPF_MAP_TYPE_SOCKMAP] = "sockmap", [BPF_MAP_TYPE_CPUMAP] = "cpumap", [BPF_MAP_TYPE_SOCKHASH] = "sockhash", [BPF_MAP_TYPE_CGROUP_STORAGE] = "cgroup_storage", [BPF_MAP_TYPE_REUSEPORT_SOCKARRAY] = "reuseport_sockarray", [BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE] = "precpu_cgroup_storage", [BPF_MAP_TYPE_QUEUE] = "queue", [BPF_MAP_TYPE_STACK] = "stack", [BPF_MAP_TYPE_SK_STORAGE] = "sk_storage", [BPF_MAP_TYPE_DEVMAP_HASH] = "devmap_hash", }; #define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x))) #define LAST_KNOWN_PROG_TYPE (ARRAY_SIZE(prog_type_strings) - 1) #define LAST_KNOWN_MAP_TYPE (ARRAY_SIZE(map_type_strings) - 1) #define min(x, y) ((x) < (y) ? (x) : (y)) static inline uint64_t ptr_to_u64(const void *ptr) { return (uint64_t) (unsigned long) ptr; } static inline void * u64_to_ptr(uint64_t ptr) { return (void *) (unsigned long ) ptr; } static int handle_get_next_errno(int eno) { switch (eno) { case ENOENT: return 0; case EINVAL: fprintf(stderr, "Kernel does not support BPF introspection\n"); return EX_UNAVAILABLE; case EPERM: fprintf(stderr, "Require CAP_SYS_ADMIN capability. Please retry as root\n"); return EX_NOPERM; default: fprintf(stderr, "%s\n", strerror(errno)); return 1; } } static void print_prog_hdr(void) { printf("%9s %-15s %8s %6s %-12s %-15s\n", "BID", "TYPE", "UID", "#MAPS", "LoadTime", "NAME"); } static void print_prog_info(const struct bpf_prog_info *prog_info) { struct timespec real_time_ts, boot_time_ts; time_t wallclock_load_time = 0; char unknown_prog_type[16]; const char *prog_type; char load_time[16]; struct tm load_tm; if (prog_info->type > LAST_KNOWN_PROG_TYPE) { snprintf(unknown_prog_type, sizeof(unknown_prog_type), "<%u>", prog_info->type); unknown_prog_type[sizeof(unknown_prog_type) - 1] = '\0'; prog_type = unknown_prog_type; } else { prog_type = prog_type_strings[prog_info->type]; } if (!clock_gettime(CLOCK_REALTIME, &real_time_ts) && !clock_gettime(CLOCK_BOOTTIME, &boot_time_ts) && real_time_ts.tv_sec >= boot_time_ts.tv_sec) wallclock_load_time = (real_time_ts.tv_sec - boot_time_ts.tv_sec) + prog_info->load_time / 1000000000; if (wallclock_load_time && localtime_r(&wallclock_load_time, &load_tm)) strftime(load_time, sizeof(load_time), "%b%d/%H:%M", &load_tm); else snprintf(load_time, sizeof(load_time), "<%llu>", prog_info->load_time / 1000000000); load_time[sizeof(load_time) - 1] = '\0'; if (prog_info->jited_prog_len) printf("%9u %-15s %8u %6u %-12s %-15s\n", prog_info->id, prog_type, prog_info->created_by_uid, prog_info->nr_map_ids, load_time, prog_info->name); else printf("%8u- %-15s %8u %6u %-12s %-15s\n", prog_info->id, prog_type, prog_info->created_by_uid, prog_info->nr_map_ids, load_time, prog_info->name); } static void print_map_hdr(void) { printf("%8s %-15s %-10s %8s %8s %8s %-15s\n", "MID", "TYPE", "FLAGS", "KeySz", "ValueSz", "MaxEnts", "NAME"); } static void print_map_info(const struct bpf_map_info *map_info) { char unknown_map_type[16]; const char *map_type; if (map_info->type > LAST_KNOWN_MAP_TYPE) { snprintf(unknown_map_type, sizeof(unknown_map_type), "<%u>", map_info->type); unknown_map_type[sizeof(unknown_map_type) - 1] = '\0'; map_type = unknown_map_type; } else { map_type = map_type_strings[map_info->type]; } printf("%8u %-15s 0x%-8x %8u %8u %8u %-15s\n", map_info->id, map_type, map_info->map_flags, map_info->key_size, map_info->value_size, map_info->max_entries, map_info->name); } static int print_one_prog(uint32_t prog_id) { const uint32_t usual_nr_map_ids = 64; uint32_t nr_map_ids = usual_nr_map_ids; struct bpf_prog_info prog_info; uint32_t *map_ids = NULL; uint32_t info_len; int ret = 0; int prog_fd; uint32_t i; prog_fd = bpf_prog_get_fd_by_id(prog_id); if (prog_fd == -1) { if (errno == ENOENT) { fprintf(stderr, "BID:%u not found\n", prog_id); return EX_DATAERR; } else { return handle_get_next_errno(errno); } } /* Retry at most one time for larger map_ids array */ for (i = 0; i < 2; i++) { bzero(&prog_info, sizeof(prog_info)); prog_info.map_ids = ptr_to_u64(realloc(map_ids, nr_map_ids * sizeof(*map_ids))); if (!prog_info.map_ids) { fprintf(stderr, "Cannot allocate memory for %u map_ids for BID:%u\n", nr_map_ids, prog_id); close(prog_fd); free(map_ids); return 1; } map_ids = u64_to_ptr(prog_info.map_ids); prog_info.nr_map_ids = nr_map_ids; info_len = sizeof(prog_info); ret = bpf_obj_get_info(prog_fd, &prog_info, &info_len); if (ret) { fprintf(stderr, "Cannot get info for BID:%u. %s(%d)\n", prog_id, strerror(errno), errno); close(prog_fd); free(map_ids); return ret; } if (prog_info.nr_map_ids <= nr_map_ids) break; nr_map_ids = prog_info.nr_map_ids; } close(prog_fd); print_prog_hdr(); print_prog_info(&prog_info); printf("\n"); /* Print all map_info used by the prog */ print_map_hdr(); nr_map_ids = min(prog_info.nr_map_ids, nr_map_ids); for (i = 0; i < nr_map_ids; i++) { struct bpf_map_info map_info = {}; info_len = sizeof(map_info); int map_fd; map_fd = bpf_map_get_fd_by_id(map_ids[i]); if (map_fd == -1) { if (errno == -ENOENT) continue; fprintf(stderr, "Cannot get fd for map:%u. %s(%d)\n", map_ids[i], strerror(errno), errno); ret = map_fd; break; } ret = bpf_obj_get_info(map_fd, &map_info, &info_len); close(map_fd); if (ret) { fprintf(stderr, "Cannot get info for map:%u. %s(%d)\n", map_ids[i], strerror(errno), errno); break; } print_map_info(&map_info); } free(map_ids); return ret; } int print_all_progs(void) { uint32_t next_id = 0; print_prog_hdr(); while (!bpf_prog_get_next_id(next_id, &next_id)) { struct bpf_prog_info prog_info = {}; uint32_t prog_info_len = sizeof(prog_info); int prog_fd; int ret; prog_fd = bpf_prog_get_fd_by_id(next_id); if (prog_fd < 0) { if (errno == ENOENT) continue; fprintf(stderr, "Cannot get fd for BID:%u. %s(%d)\n", next_id, strerror(errno), errno); return 1; } ret = bpf_obj_get_info(prog_fd, &prog_info, &prog_info_len); close(prog_fd); if (ret) { fprintf(stderr, "Cannot get bpf_prog_info for BID:%u. %s(%d)\n", next_id, strerror(errno), errno); return ret; } print_prog_info(&prog_info); } return handle_get_next_errno(errno); } void usage(void) { printf("BPF Program Snapshot (bps):\n" "List of all BPF programs loaded into the system.\n\n"); printf("Usage: bps [bpf-prog-id]\n"); printf(" [bpf-prog-id] If specified, it shows the details info of the bpf-prog\n"); printf("\n"); } int main(int argc, char **argv) { if (argc > 1) { if (!isdigit(*argv[1])) { usage(); return EX_USAGE; } return print_one_prog((uint32_t)atoi(argv[1])); } return print_all_progs(); } bpfcc-0.12.0/introspection/bps_example.txt000066400000000000000000000021401357404205000206040ustar00rootroot00000000000000* List all BPF programs * # bps BID TYPE UID #MAPS LoadTime NAME 82 kprobe 0 1 Oct19/23:52 map_perf_test 83 kprobe 0 1 Oct19/23:52 map_perf_test 84 kprobe 0 1 Oct19/23:52 map_perf_test 85 kprobe 0 1 Oct19/23:52 map_perf_test 86 kprobe 0 4 Oct19/23:52 map_perf_test 87 kprobe 0 1 Oct19/23:52 map_perf_test 88 kprobe 0 1 Oct19/23:52 map_perf_test 89 kprobe 0 1 Oct19/23:52 map_perf_test * List a particular BPF program and its maps * # bps 86 BID TYPE UID #MAPS LoadTime NAME 86 kprobe 0 4 Oct19/23:52 map_perf_test MID TYPE FLAGS KeySz ValueSz MaxEnts NAME 120 lru hash 0x0 4 8 10000 lru_hash_map 129 lru hash 0x0 4 8 43 lru_hash_lookup 123 array of maps 0x0 4 4 1024 array_of_lru_ha 121 lru hash 0x2 4 bpfcc-0.12.0/man/000077500000000000000000000000001357404205000134225ustar00rootroot00000000000000bpfcc-0.12.0/man/CMakeLists.txt000066400000000000000000000000271357404205000161610ustar00rootroot00000000000000add_subdirectory(man8) bpfcc-0.12.0/man/man8/000077500000000000000000000000001357404205000142655ustar00rootroot00000000000000bpfcc-0.12.0/man/man8/CMakeLists.txt000066400000000000000000000007201357404205000170240ustar00rootroot00000000000000find_program(GZIP gzip) 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 ALL DEPENDS ${GZFILES}) install(FILES ${GZFILES} DESTINATION share/bcc/man/man8) bpfcc-0.12.0/man/man8/argdist.8000066400000000000000000000177401357404205000160240ustar00rootroot00000000000000.TH argdist 8 "2016-02-11" "USER COMMANDS" .SH NAME argdist \- Trace a function and display a histogram or frequency count of its parameter values. Uses Linux eBPF/bcc. .SH SYNOPSIS .B argdist [-h] [-p PID] [-z STRING_SIZE] [-i INTERVAL] [-d DURATION] [-n COUNT] [-v] [-T TOP] [-H specifier] [-C specifier] [-I header] .SH DESCRIPTION argdist attaches to function entry and exit points, collects specified parameter values, and stores them in a histogram or a frequency collection that counts the number of times a parameter value occurred. It can also filter parameter values and instrument multiple entry points at once. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bcc. .SH OPTIONS .TP \-h Print usage message. .TP \-p PID Trace only functions in the process PID. .TP \-z STRING_SIZE When collecting string arguments (of type char*), collect up to STRING_SIZE characters. Longer strings will be truncated. .TP \-i INTERVAL Print the collected data every INTERVAL seconds. The default is 1 second. .TP \-d DURATION Total duration of trace in seconds. .TP \-n NUMBER Print the collected data COUNT times and then exit. .TP \-v Display the generated BPF program, for debugging purposes. .TP \-T TOP When collecting frequency counts, display only the top TOP entries. .TP \-H specifiers, \-C specifiers One or more probe specifications that instruct argdist which functions to probe, which parameters to collect, how to aggregate them, and whether to perform any filtering. See SPECIFIER SYNTAX below. .TP \-I header One or more header files that should be included in the BPF program. This enables the use of structure definitions, enumerations, and constants that are available in these headers. You should provide the same path you would include in the BPF program, e.g. 'linux/blkdev.h' or 'linux/time.h'. Note: in many cases, argdist will deduce the necessary header files automatically. .SH SPECIFIER SYNTAX The general specifier syntax is as follows: .B {p,r,t,u}:{[library],category}:function(signature)[:type[,type...]:expr[,expr...][:filter]][#label] .TP .B {p,r,t,u} Probe type \- "p" for function entry, "r" for function return, "t" for kernel tracepoint, "u" for USDT probe; \-H for histogram collection, \-C for frequency count. Indicates where to place the probe and whether the probe should collect frequency count information, or aggregate the collected values into a histogram. Counting probes will collect the number of times every parameter value was observed, whereas histogram probes will collect the parameter values into a histogram. Only integral types can be used with histogram probes; there is no such limitation for counting probes. .TP .B [library] Library containing the probe. Specify the full path to the .so or executable file where the function to probe resides. Alternatively, you can specify just the lib name: for example, "c" refers to libc. If no library name is specified, the kernel is assumed. .TP .B category The category of the kernel tracepoint. For example: net, sched, block. .TP .B function(signature) The function to probe, and its signature. The function name must match exactly for the probe to be placed. The signature, on the other hand, is only required if you plan to collect parameter values based on that signature. For example, if you only want to collect the first parameter, you don't have to specify the rest of the parameters in the signature. When capturing kernel tracepoints, this should be the name of the event, e.g. net_dev_start_xmit. The signature for kernel tracepoints should be empty. When capturing USDT probes, this should be the name of the probe, e.g. reloc_complete. The signature for USDT probes should be empty. .TP .B [type[,type...]] The type(s) of the expression(s) to capture. This is the type of the keys in the histogram or raw event collection that are collected by the probes. .TP .B [expr[,expr...]] The expression(s) to capture. These are the values that are assigned to the histogram or raw event collection. You may use the parameters directly, or valid C expressions that involve the parameters, such as "size % 10". Tracepoints may access a special structure called "args" that is formatted according to the tracepoint format (which you can obtain using tplist). For example, the block:block_rq_complete tracepoint can access args->nr_sector. USDT probes may access the arguments defined by the tracing program in the special arg1, arg2, ... variables. To obtain their types, use the tplist tool. Return probes can use the argument values received by the function when it was entered, through the $entry(paramname) special variable. Return probes can also access the function's return value in $retval, and the function's execution time in nanoseconds in $latency. Note that adding the $latency or $entry(paramname) variables to the expression will introduce an additional probe at the function's entry to collect this data, and therefore introduce additional overhead. .TP .B [filter] The filter applied to the captured data. Only parameter values that pass the filter will be collected. This is any valid C expression that refers to the parameter values, such as "fd == 1 && length > 16". The $entry, $retval, and $latency variables can be used here as well, in return probes. The filter expression may also use the STRCMP pseudo-function to compare a predefined string to a string argument. For example: STRCMP("test.txt", file). The order of arguments is important: the first argument MUST be a quoted literal string, and the second argument can be a runtime string. .TP .B [label] The label that will be displayed when printing the probed values. By default, this is the probe specifier. .SH EXAMPLES .TP Print a histogram of allocation sizes passed to kmalloc: # .B argdist -H 'p::__kmalloc(u64 size):u64:size' .TP Print a count of how many times process 1005 called malloc with an allocation size of 16 bytes: # .B argdist -p 1005 -C 'p:c:malloc(size_t size):size_t:size:size==16' .TP Snoop on all strings returned by gets(): # .B argdist -C 'r:c:gets():char*:$retval' .TP Print a histogram of read sizes that were longer than 1ms: # .B argdist -H 'r::__vfs_read(void *file, void *buf, size_t count):size_t:$entry(count):$latency > 1000000' .TP Print frequency counts of how many times writes were issued to a particular file descriptor number, in process 1005: # .B argdist -p 1005 -C 'p:c:write(int fd):int:fd' .TP Print a histogram of error codes returned by read() in process 1005: # .B argdist -p 1005 -H 'r:c:read()' .TP Print a histogram of buffer sizes passed to write() across all processes, where the file descriptor was 1 (STDOUT): # .B argdist -H 'p:c:write(int fd, const void *buf, size_t count):size_t:count:fd==1' .TP Count fork() calls in libc across all processes, grouped by pid: # .B argdist -C 'p:c:fork():int:$PID;fork per process' .TP Print histogram of number of sectors in completing block I/O requests: # .B argdist -H 't:block:block_rq_complete():u32:nr_sector' .TP Aggregate interrupts by interrupt request (IRQ): # .B argdist -C 't:irq:irq_handler_entry():int:irq' .TP Print the functions used as thread entry points and how common they are: # .B argdist -C 'u:pthread:pthread_start():u64:arg2' -p 1337 .TP Print histograms of sleep() and nanosleep() parameter values: # .B argdist -H 'p:c:sleep(u32 seconds):u32:seconds' -H 'p:c:nanosleep(struct timespec *req):long:req->tv_nsec' .TP Spy on writes to STDOUT performed by process 2780, up to a string size of 120 characters: # .B argdist -p 2780 -z 120 -C 'p:c:write(int fd, char* buf, size_t len):char*:buf:fd==1' .TP Group files being read from and the read sizes from __vfs_read: # .B argdist -C 'p::__vfs_read(struct file *file, void *buf, size_t count):char*,size_t:file->f_path.dentry->d_iname,count:file->f_path.dentry->d_iname[0]!=0' .SH SOURCE This is from bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 Sasha Goldshtein bpfcc-0.12.0/man/man8/bashreadline.8000066400000000000000000000031461357404205000170030ustar00rootroot00000000000000.TH bashreadline 8 "2016-01-28" "USER COMMANDS" .SH NAME bashreadline \- Print entered bash commands system wide. Uses Linux eBPF/bcc. .SH SYNOPSIS .B bashreadline [\-h] [\-s SHARED] .SH DESCRIPTION bashreadline traces the return of the readline() function using uprobes, 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 eBPF/bcc and uprobes. This makes use of a Linux 4.5 feature (bpf_perf_event_output()); for kernels older than 4.5, see the version under tools/old, which uses an older mechanism Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bcc. .SH OPTIONS .TP \-h Print usage message. .TP \-s Specify the location of libreadline.so shared library when you failed to run the script directly with error: "Exception: could not determine address of symbol \'readline\'". Default value is /lib/libreadline.so. .SH EXAMPLES .TP Trace bash commands system wide: # .B bashreadline .SH FIELDS .TP TIME Time of the command (HH:MM:SS). .TP PID Process ID of the bash shell. .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 bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 opensnoop(8) bpfcc-0.12.0/man/man8/biolatency.8000066400000000000000000000045751357404205000165220ustar00rootroot00000000000000.TH biolatency 8 "2015-08-20" "USER COMMANDS" .SH NAME biolatency \- Summarize block device I/O latency as a histogram. .SH SYNOPSIS .B biolatency [\-h] [\-F] [\-T] [\-Q] [\-m] [\-D] [interval [count]] .SH DESCRIPTION biolatency traces block device I/O (disk I/O), and records the distribution of I/O latency (time). This is printed as a histogram either on Ctrl-C, or after a given interval in seconds. The latency of the disk I/O is measured from the issue to the device to its completion. A \-Q option can be used to include time queued in the kernel. This tool uses in-kernel eBPF maps for storing timestamps and the histogram, for efficiency. This works by tracing various kernel blk_*() 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 bcc. .SH OPTIONS \-h Print usage message. .TP \-T Include timestamps on output. .TP \-m Output histogram in milliseconds. .TP \-D Print a histogram per disk device. .TP \-F Print a histogram per set of I/O flags. .TP interval Output interval, in seconds. .TP count Number of outputs. .SH EXAMPLES .TP Summarize block device I/O latency as a histogram: # .B biolatency .TP Print 1 second summaries, 10 times: # .B biolatency 1 10 .TP Print 1 second summaries, using milliseconds as units for the histogram, and include timestamps on output: # .B biolatency \-mT 1 .TP Include OS queued time in I/O time: # .B biolatency \-Q .TP Show a latency histogram for each disk device separately: # .B biolatency \-D .SH FIELDS .TP usecs Microsecond range .TP msecs Millisecond range .TP count How many I/O fell into this range .TP distribution An ASCII bar chart to visualize the distribution (count column) .SH OVERHEAD This traces kernel functions and maintains in-kernel timestamps and a histogram, which are asynchronously copied to user-space. This method is very efficient, and the overhead for most storage I/O rates (< 10k IOPS) should be negligible. If you have a higher IOPS storage environment, test and quantify the overhead before use. .SH SOURCE This is from bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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) bpfcc-0.12.0/man/man8/biosnoop.8000066400000000000000000000046471357404205000162210ustar00rootroot00000000000000.TH biosnoop 8 "2015-09-16" "USER COMMANDS" .SH NAME biosnoop \- Trace block device I/O and print details incl. issuing PID. .SH SYNOPSIS .B biosnoop [\-hQ] .SH DESCRIPTION This tools traces block device I/O (disk I/O), and prints a one-line summary for each I/O showing various details. These include the latency from the time of issue to the device to its completion, and the PID and process name from when the I/O was first created (which usually identifies the responsible process). This uses in-kernel eBPF maps to cache process details (PID and comm) by I/O request, as well as a starting timestamp for calculating I/O latency. This works by tracing various kernel blk_*() functions using dynamic tracing, and will need updating to match any changes to these functions. This makes use of a Linux 4.5 feature (bpf_perf_event_output()); for kernels older than 4.5, see the version under tools/old, which uses an older mechanism Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bcc. .SH OPTIONS .TP \-h Print usage message. .TP \-Q Include a column showing the time spent quueued in the OS. .SH EXAMPLES .TP Trace all block device I/O and print a summary line per I/O: # .B biosnoop .SH FIELDS .TP TIME(s) Time of the I/O completion, in seconds since the first I/O was seen. .TP COMM Cached process name, if present. This usually (but isn't guaranteed) to identify the responsible process for the I/O. .TP PID Cached process ID, if present. This usually (but isn't guaranteed) to identify the responsible process for the I/O. .TP DISK Disk device name. .TP T Type of I/O: R = read, W = write. This is a simplification. .TP SECTOR Device sector for the I/O. .TP BYTES Size of the I/O, in bytes. .TP QUE(ms) Time the I/O was queued in the OS before being issued to the device, in milliseconds. .TP LAT(ms) Time for the I/O (latency) from the issue to the device, to its completion, in milliseconds. .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 bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 disksnoop(8), iostat(1) bpfcc-0.12.0/man/man8/biotop.8000066400000000000000000000050501357404205000156520ustar00rootroot00000000000000.TH biotop 8 "2016-02-06" "USER COMMANDS" .SH NAME biotop \- Block device (disk) I/O by process top. .SH SYNOPSIS .B biotop [\-h] [\-C] [\-r MAXROWS] [interval] [count] .SH DESCRIPTION This is top for disks. This traces block device I/O (disk I/O), and prints a per-process summary every interval (by default, 1 second). The summary is sorted on the top disk consumers by throughput (Kbytes). The PID and process name shown are measured from when the I/O was first created, which usually identifies the responsible process. For efficiency, this uses in-kernel eBPF maps to cache process details (PID and comm) by I/O request, as well as a starting timestamp for calculating I/O latency, and the final summary. This works by tracing various kernel blk_*() 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 bcc. .SH OPTIONS .TP \-C Don't clear the screen. .TP \-r MAXROWS Maximum number of rows to print. Default is 20. .TP interval Interval between updates, seconds. .TP count Number of interval summaries. .SH EXAMPLES .TP Summarize block device I/O by process, 1 second screen refresh: # .B biotop .TP Don't clear the screen: # .B biotop -C .TP 5 second summaries, 10 times only: # .B biotop 5 10 .SH FIELDS .TP loadavg: The contents of /proc/loadavg .TP PID Cached process ID, if present. This usually (but isn't guaranteed) to identify the responsible process for the I/O. .TP COMM Cached process name, if present. This usually (but isn't guaranteed) to identify the responsible process for the I/O. .TP D Direction: R == read, W == write. This is a simplification. .TP MAJ Major device number. .TP MIN Minor device number. .TP DISK Disk device name. .TP I/O Number of I/O during the interval. .TP Kbytes Total Kbytes for these I/O, during the interval. .TP AVGms Average time for the I/O (latency) from the issue to the device, to its completion, in milliseconds. .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 bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 INSPIRATION top(1) by William LeFebvre .SH SEE ALSO biosnoop(8), biolatency(8), iostat(1) bpfcc-0.12.0/man/man8/bitesize.8000066400000000000000000000025301357404205000161740ustar00rootroot00000000000000.TH bitesize 8 "2016-02-05" "USER COMMANDS" .SH NAME bitesize \- Summarize block device I/O size as a histogram \- Linux eBPF/bcc. .SH SYNOPSIS .B bitesize .SH DESCRIPTION Show I/O distribution for requested block sizes, by process name. This works by tracing block:block_rq_issue and prints a historgram of I/O size. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bcc. .SH EXAMPLES .TP Count I/O size per process until Ctrl-C is hit: # .B bitesize .SH FIELDS .TP Kbtes Size in kilobytes of range .TP count How many I/O fell into this range .TP distribution An ASCII bar chart to visualize the distribution (count column) .SH OVERHEAD This traces a block I/O tracepoint to update a histogram, which is asynchronously copied to user-space. This method is very efficient, and the overhead for most storage I/O rates (< 10k IOPS) should be negligible. If you have a higher IOPS storage environment, test and quantify the overhead before use. .SH SOURCE This is from bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 Allan McAleavy .SH SEE ALSO https://github.com/brendangregg/systemtap-lwtools/blob/master/disk/bitesize-nd.stp bpfcc-0.12.0/man/man8/bpflist.8000066400000000000000000000030241357404205000160200ustar00rootroot00000000000000.TH bpflist 8 "2017-03-09" "USER COMMANDS" .SH NAME bpflist \- Display processes currently using BPF programs and maps. .SH SYNOPSIS .B bpflist [-v] .SH DESCRIPTION This tool displays processes currently using BPF programs and maps, and optionally also kprobes and uprobes on the system. This is useful to understand which BPF programs are loaded on the system. Currently, for lack of a better alternative, this tool pipes into 'ls' and parses its output to snoop for BPF file descriptors in all running processes. In the future, when BPF accounting is provided by the kernel, this tool should use these accounting features. Only the root user can use this tool, because it accesses debugfs. .SH REQUIREMENTS bcc, debugfs .SH OPTIONS \-h Print usage message. .TP \-v Count kprobes and uprobes as well as BPF programs. Repeating verbose mode twice also prints the kprobe and uprobe definitions in addition to counting them. .SH EXAMPLES .TP Display processes currently using BPF programs: # .B bpflist .TP Also count kprobes and uprobes: # .B bpflist -v .SH FIELDS .TP PID Process ID. .TP COMM Process comm. .TP TYPE The type of the data displayed: BPF program, BPF map, kprobe, or uprobe. .TP COUNT The number of items of this type that belong to the specified process. .SH SOURCE This is from bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 Sasha Goldshtein bpfcc-0.12.0/man/man8/bps.8000066400000000000000000000033371357404205000151500ustar00rootroot00000000000000.TH bps 8 "2017-10-19" "USER COMMANDS" .SH NAME bps \- List all BPF programs. 'ps' for BPF programs. .SH SYNOPSIS .B bps [bpf-prog-id] .SH DESCRIPTION .B bps lists all BPF programs loaded into the kernel. It is similar to the ps command but for the BPF programs. Each loaded bpf program is identified by an unique integer (i.e. .B bpf-prog-id or simply BID). If a .B bpf-prog-id is specified, the maps used by .B bpf-prog-id will also be listed. .SH EXAMPLES .TP List all BPF programs loaded into the kernel: .B bps .TP Show the details and maps of BID 6: .B bps 6 .SH BPF PROGRAM FIELDS .TP .B BID BPF program ID. It ends with '-' if it is not jitted. .TP .B TYPE The type of a BPF program. e.g. kprobe, tracepoint, xdp...etc. .TP .B UID The user ID that loaded the BPF program. .TP .B #MAPS Total number of maps used by a BPF program. .TP .B LoadTime When was the BPF program loaded? .TP .B NAME The name of a BPF program. The user space library (like .B bcc ) usually uses the C function name of the original BPF's source code as the program name. It could be empty if the user space did not provide a name. .SH BPF MAP FIELDS .TP .B MID BPF map ID. .TP .B TYPE The type of a BPF map. e.g. hash, array, stack trace...etc. .TP .B FLAGS The flags used to create the BP map. .TP .B KeySz The key size of a BPF map. .TP .B ValueSz The value size of a BPF map. .TP .B MaxEnts The maximum number of entries of a map. .TP .B NAME The name of a BPF map. The user space library (like .B bcc ) usually uses the C variable name of the BPF map as its name. It could be empty if the user space did not provide a name. .SH SOURCE This is from bcc. .IP https://github.com/iovisor/bcc .SH OS Linux .SH STABILITY Unstable - in development. .SH AUTHOR Martin Lau bpfcc-0.12.0/man/man8/btrfsdist.8000066400000000000000000000041021357404205000163570ustar00rootroot00000000000000.TH btrfsdist 8 "2016-02-15" "USER COMMANDS" .SH NAME btrfsdist \- Summarize btrfs operation latency. Uses Linux eBPF/bcc. .SH SYNOPSIS .B btrfsdist [\-h] [\-T] [\-N] [\-d] [interval] [count] .SH DESCRIPTION This tool summarizes time (latency) spent in common btrfs 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 btrfs_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 bcc. .SH OPTIONS .TP \-h Print usage message. .TP \-T Don't include timestamps on interval output. .TP \-m Output in milliseconds. .TP \-p PID Trace this PID only. .SH EXAMPLES .TP Trace btrfs operation time, and print a summary on Ctrl-C: # .B btrfsdist .TP Trace PID 181 only: # .B btrfsdist -p 181 .TP Print 1 second summaries, 10 times: # .B btrfsdist 1 10 .TP 1 second summaries, printed in milliseconds # .B btrfsdist \-m 1 .SH FIELDS .TP msecs Range of milliseconds for this bucket. .TP usecs Range of microseconds for this bucket. .TP count Number of operations in this time range. .TP distribution ASCII representation of the distribution (the count column). .SH OVERHEAD This adds low-overhead instrumentation to btrfs writes and fsyncs, as well as all system reads and opens (due to the current implementation of the btrfs_file_operations interface). Particularly, all reads and writes from the file system cache will incur extra overhead while tracing. 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 bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 btrfsslower(8) bpfcc-0.12.0/man/man8/btrfsslower.8000066400000000000000000000066601357404205000167420ustar00rootroot00000000000000.TH btrfsslower 8 "2016-02-15" "USER COMMANDS" .SH NAME btrfsslower \- Trace slow btrfs file operations, with per-event details. .SH SYNOPSIS .B btrfsslower [\-h] [\-j] [\-p PID] [min_ms] [\-d DURATION] .SH DESCRIPTION This tool traces common btrfs file operations: reads, writes, opens, and syncs. It measures the time spent in these operations, and prints details for each that exceeded a threshold. WARNING: See the OVERHEAD section. By default, a minimum millisecond threshold of 10 is used. If a threshold of 0 is used, all events are printed (warning: verbose). Since this works by tracing the btrfs_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 bcc. .SH OPTIONS \-p PID Trace this PID only. .TP min_ms Minimum I/O latency (duration) to trace, in milliseconds. Default is 10 ms. .TP \-d DURATION Total duration of trace in seconds. .SH EXAMPLES .TP Trace synchronous file reads and writes slower than 10 ms: # .B btrfsslower .TP Trace slower than 1 ms: # .B btrfsslower 1 .TP Trace slower than 1 ms, and output just the fields in parsable format (csv): # .B btrfsslower \-j 1 .TP Trace all file reads and writes (warning: the output will be verbose): # .B btrfsslower 0 .TP Trace slower than 1 ms, for PID 181 only: # .B btrfsslower \-p 181 1 .TP Trace for 10 seconds only: # .B btrfsslower \-d 10 .SH FIELDS .TP TIME(s) Time of I/O completion since the first I/O seen, in seconds. .TP COMM Process name. .TP PID Process ID. .TP T Type of operation. R == read, W == write, O == open, S == fsync. .TP OFF_KB File offset for the I/O, in Kbytes. .TP BYTES Size of I/O, in bytes. .TP LAT(ms) Latency (duration) of I/O, measured from when it was issued by VFS to the filesystem, to when it completed. This time is inclusive of block device I/O, file system CPU cycles, file system locks, run queue latency, etc. It's a more accurate measure of the latency suffered by applications performing file system I/O, than to measure this down at the block device interface. .TP FILENAME A cached kernel file name (comes from dentry->d_name.name). .TP ENDTIME_us Completion timestamp, microseconds (\-j only). .TP OFFSET_b File offset, bytes (\-j only). .TP LATENCY_us Latency (duration) of the I/O, in microseconds (\-j only). .SH OVERHEAD This adds low-overhead instrumentation to btrfs writes and fsyncs, as well as all system reads and opens (due to the current implementation of the btrfs_file_operations interface). Particularly, all reads and writes from the file system cache will incur extra overhead while tracing. 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. If this continues to be a problem, consider switching to a tool that prints in-kernel summaries only, such as btrfsdist(8). .PP Note that the overhead of this tool should be less than fileslower(8), as this tool targets btrfs functions only, and not all file read/write paths (which can include socket I/O). .SH SOURCE This is from bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 btrfsdist(8), biosnoop(8), funccount(8), fileslower(8) bpfcc-0.12.0/man/man8/cachestat.8000066400000000000000000000041671357404205000163250ustar00rootroot00000000000000.TH cachestat 8 "2016-01-30" "USER COMMANDS" .SH NAME cachestat \- Statistics for linux page cache hit/miss ratios. Uses Linux eBPF/bcc. .SH SYNOPSIS .B cachestat [-T] [interval [count]] .SH DESCRIPTION This traces four kernel functions 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 kernel page cache functions using dynamic tracing, and will need updating to match any changes to these functions. Edit the script to customize which functions are traced. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bcc. .SH EXAMPLES .TP Print summaries every second: # .B cachestat .TP Print summaries every second with timestamp: # .B cachestat -T .TP Print output every five seconds, three times: # .B cachestat 5 3 .TP Print output with timestamp every five seconds, three times: # .B cachestat -T 5 3 .SH FIELDS .TP TIME Timestamp. .TP HITS Number of page cache hits. .TP MISSES Number of page cache misses. .TP DIRTIES Number of dirty pages added to the page cache. .TP HITRATIO The hit ratio as a percentage. .TP READ_HIT% Read hit percent of page cache usage. .TP WRITE_HIT% Write hit percent of page cache usage. .TP BUFFERS_MB Buffers size taken from /proc/meminfo. .TP CACHED_MB Cached amount of data in current page cache taken from /proc/meminfo. .SH OVERHEAD This traces various kernel page cache functions and maintains in-kernel counts, which are asynchronously copied to user-space. While the rate of operations can be very high (>1G/sec) we can have up to 34% overhead, this is still 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 bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 Allan McAleavy .SH SEE ALSO https://github.com/brendangregg/perf-tools/blob/master/fs/cachestat bpfcc-0.12.0/man/man8/cachetop.8000066400000000000000000000045251357404205000161520ustar00rootroot00000000000000.TH cachetop 8 "2016-01-30" "USER COMMANDS" .SH NAME cachetop \- Statistics for linux page cache hit/miss ratios per processes. Uses Linux eBPF/bcc. .SH SYNOPSIS .B cachetop [interval] .SH DESCRIPTION This traces four kernel functions and prints per-processes summaries every \fBinterval\fR seconds. This can be useful for processes workload characterization, and looking for patterns in operation usage over time. It provides a \fBtop\fR-like interface which by default sorts by \fBHITS\fR in ascending order. This works by tracing kernel page cache functions using dynamic tracing, and will need updating to match any changes to these functions. Edit the script to customize which functions are traced. Since this uses BPF, only the root user can use this tool. .SH KEYBINDINGS The following keybindings can be used to control the output of \fBcachetop\fR. .TP .B < Use the previous column for sorting. .TP .B > Use the next column for sorting. .TP .B r Toggle sorting order (default ascending). .TP .B q Quit cachetop. .SH REQUIREMENTS CONFIG_BPF and bcc. .SH EXAMPLES .TP Update summaries every five second: # .B cachetop .TP Print summaries each second: # .B cachetop 1 .SH FIELDS .TP PID Process ID of the process causing the cache activity. .TP UID User ID of the process causing the cache activity. .TP HITS Number of page cache hits. .TP MISSES Number of page cache misses. .TP DIRTIES Number of dirty pages added to the page cache. .TP READ_HIT% Read hit percent of page cache usage. .TP WRITE_HIT% Write hit percent of page cache usage. .TP BUFFERS_MB Buffers size taken from /proc/meminfo. .TP CACHED_MB Cached amount of data in current page cache taken from /proc/meminfo. .SH OVERHEAD This traces various kernel page cache functions and maintains in-kernel counts, which are asynchronously copied to user-space. While the rate of operations can be very high (>1G/sec) we can have up to 34% overhead, this is still 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 bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 Emmanuel Bretelle .SH SEE ALSO cachestat (8) bpfcc-0.12.0/man/man8/capable.8000066400000000000000000000035171357404205000157530ustar00rootroot00000000000000.TH capable 8 "2016-09-13" "USER COMMANDS" .SH NAME capable \- Trace security capability checks (cap_capable()). .SH SYNOPSIS .B capable [\-h] [\-v] [\-p PID] [\-K] [\-U] .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, bcc. .SH OPTIONS \-h USAGE message. .TP \-v Include non-audit capability checks. These are those deemed not interesting and not necessary to audit, such as CAP_SYS_ADMIN checks on memory allocation to affect the behavior of overcommit. .TP \-K Include kernel stack traces to the output. .TP \-U Include user-space stack traces to the output. .TP \-x Show extra fields in TID and INSETID columns. .SH EXAMPLES .TP Trace all capability checks system-wide: # .B capable .TP Trace capability checks for PID 181: # .B capable \-p 181 .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. Use \-v to include non-audit events. INSETID Whether the INSETID bit was set (Linux >= 5.1). .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 bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 capabilities(7) bpfcc-0.12.0/man/man8/cobjnew.8000077700000000000000000000000001357404205000175422uobjnew.8ustar00rootroot00000000000000bpfcc-0.12.0/man/man8/cpudist.8000066400000000000000000000054631357404205000160410ustar00rootroot00000000000000.TH cpudist 8 "2016-06-28" "USER COMMANDS" .SH NAME cpudist \- On- and off-CPU task time as a histogram. .SH SYNOPSIS .B cpudist [\-h] [-O] [\-T] [\-m] [\-P] [\-L] [\-p PID] [interval] [count] .SH DESCRIPTION This measures the time a task spends on the CPU before being descheduled, and shows the times as a histogram. Tasks that spend a very short time on the CPU can be indicative of excessive context-switches and poor workload distribution, and possibly point to a shared source of contention that keeps tasks switching in and out as it becomes available (such as a mutex). Similarly, the tool can also measure the time a task spends off-CPU before it is scheduled again. This can be helpful in identifying long blocking and I/O operations, or alternatively very short descheduling times due to short-lived locks or timers. This tool uses in-kernel eBPF maps for storing timestamps and the histogram, for efficiency. Despite this, the overhead of this tool may become significant for some workloads: see the OVERHEAD section. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bcc. .SH OPTIONS .TP \-h Print usage message. .TP \-O Measure off-CPU time instead of on-CPU time. .TP \-T Include timestamps on output. .TP \-m Output histogram in milliseconds. .TP \-P Print a histogram for each PID (tgid from the kernel's perspective). .TP \-L Print a histogram for each TID (pid from the kernel's perspective). .TP \-p PID Only show this PID (filtered in kernel for efficiency). .TP interval Output interval, in seconds. .TP count Number of outputs. .SH EXAMPLES .TP Summarize task on-CPU time as a histogram: # .B cpudist .TP Summarize task off-CPU time as a histogram: # .B cpudist -O .TP Print 1 second summaries, 10 times: # .B cpudist 1 10 .TP Print 1 second summaries, using milliseconds as units for the histogram, and include timestamps on output: # .B cpudist \-mT 1 .TP Trace PID 186 only, 1 second summaries: # .B cpudist -P 185 1 .SH FIELDS .TP usecs Microsecond range .TP msecs Millisecond range .TP count How many times a task event fell into this range .TP distribution An ASCII bar chart to visualize the distribution (count column) .SH OVERHEAD This traces scheduler tracepoints, 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 bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc distribution for a companion _example.txt file containing example usage, output, and commentary for this tool. .SH OS Linux .SH STABILITY Unstable - in development. .SH AUTHOR Sasha Goldshtein .SH SEE ALSO pidstat(1), runqlat(8) bpfcc-0.12.0/man/man8/cpuunclaimed.8000066400000000000000000000067361357404205000170430ustar00rootroot00000000000000.TH cpuunclaimed 8 "2016-12-21" "USER COMMANDS" .SH NAME cpuunclaimed \- Sample CPU run queues and calculate unclaimed idle CPU. Uses Linux eBPF/bcc. .SH SYNOPSIS .B cpuunclaimed [\-T] [\-j] [\-J] [interval [count]] .SH DESCRIPTION This tool samples the length of the run queues and determine when there are idle CPUs, yet queued threads waiting their turn. It reports the amount of idle (yet unclaimed by waiting threads) CPU as a system-wide percentage. This situation can happen for a number of reasons: .IP - An application has been bound to some, but not all, CPUs, and has runnable threads that cannot migrate to other CPUs due to this configuration. .IP - CPU affinity: an optimization that leaves threads on CPUs where the CPU caches are warm, even if this means short periods of waiting while other CPUs are idle. The wait period is tunale (see sysctl, kernel.sched*). .IP - Scheduler bugs. .P An unclaimed idle of < 1% is likely to be CPU affinity, and not usually a cause for concern. By leaving the CPU idle, overall throughput of the system may be improved. This tool is best for identifying larger issues, > 2%, due to the coarseness of its 99 Hertz samples. This is an experimental tool that currently works by use of sampling to keep overheads low. Tool assumptions: .IP - CPU samples consistently fire around the same offset. There will sometimes be a lag as a sample is delayed by higher-priority interrupts, but it is assumed the subsequent samples will catch up to the expected offsets (as is seen in practice). You can use -J to inspect sample offsets. Some systems can power down CPUs when idle, and when they wake up again they may begin firing at a skewed offset: this tool will detect the skew, print an error, and exit. .IP - All CPUs are online (see ncpu). .P If this identifies unclaimed CPU, you can double check it by dumping raw samples (-j), as well as using other tracing tools to instrument scheduler events (although this latter approach has much higher overhead). Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bcc. .SH EXAMPLES .TP Sample and calculate unclaimed idle CPUs, output every 1 second (default: # .B cpuunclaimed .TP Print 5 second summaries, 10 times: # .B cpuunclaimed 5 10 .TP Print 1 second summaries with timestamps: # .B cpuunclaimed \-T 1 .TP Raw dump of all samples (verbose), as comma-separated values: # .B cpuunclaimed \-j .SH FIELDS .TP %CPU CPU utilization as a system-wide percentage. .TP unclaimed idle Percentage of CPU resources that were idle when work was queued on other CPUs, as a system-wide percentage. .TP TIME Time (HH:MM:SS) .TP TIMESTAMP_ns Timestamp, nanoseconds. .TP CPU# CPU ID. .TP OFFSET_ns_CPU# Time offset that a sample fired within a sample group for this CPU. .SH OVERHEAD The overhead is expected to be low/negligible as this tool uses sampling at 99 Hertz (on all CPUs), which has a fixed and low cost, rather than sampling every scheduler event as many other approaches use (which can involve instrumenting millions of events per second). Sampled CPUs, run queue lengths, and timestamps are written to ring buffers that are periodically read by user space for reporting. Measure overhead in a test environment. .SH SOURCE This is from bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 runqlen(8) bpfcc-0.12.0/man/man8/criticalstat.8000066400000000000000000000044361357404205000170530ustar00rootroot00000000000000.TH criticalstat 8 "2018-06-07" "USER COMMANDS" .SH NAME criticalstat \- A tracer to find and report long atomic critical sections in kernel .SH SYNOPSIS .B criticalstat [\-h] [\-p] [\-i] [\-d DURATION] .SH DESCRIPTION criticalstat traces and reports occurences of atomic critical sections in the kernel with useful stacktraces showing the origin of them. Such critical sections frequently occur due to use of spinlocks, or if interrupts or preemption were explicity disabled by a driver. IRQ routines in Linux are also executed with interrupts disabled. There are many reasons. Such critical sections are a source of long latency/responsive issues for real-time systems. This works by probing the preempt/irq and cpuidle tracepoints in the kernel. Since this uses BPF, only the root user can use this tool. Further, the kernel has to be built with certain CONFIG options enabled. See below. .SH REQUIREMENTS Enable CONFIG_PREEMPTIRQ_EVENTS and CONFIG_DEBUG_PREEMPT. Additionally, the following options should be DISABLED on older kernels: CONFIG_PROVE_LOCKING, CONFIG_LOCKDEP. .SH OPTIONS .TP \-h Print usage message. .TP \-p Find long sections where preemption was disabled on local CPU. .TP \-i Find long sections where interrupt was disabled on local CPU. .TP \-d DURATION Only identify sections that are longer than DURATION in microseconds. .SH EXAMPLES .TP Run with default options: irq disabled for more than 100 uS # .B criticalstat .TP Find sections with preemption disabled for more than 100 uS. # .B criticalstat -p .TP Find sections with IRQ disabled for more than 500 uS. # .B criticalstat -d 500 .TP Find sections with preemption disabled for more than 500 uS. # .B criticalstat -p -d 500 .SH OVERHEAD This tool can cause overhead if the application is spending a lot of time in kernel mode. The overhead is variable but can be 2-4% of performance degradation. If overhead is seen to be too much, please pass a higher DURATION to the -d option to filter more aggressively. .SH SOURCE This is from bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 Joel Fernandes .SH SEE ALSO Linux kernel's preemptoff and irqoff tracers. bpfcc-0.12.0/man/man8/cthreads.8000077700000000000000000000000001357404205000200562uthreads.8ustar00rootroot00000000000000bpfcc-0.12.0/man/man8/dbslower.8000066400000000000000000000046261357404205000162070ustar00rootroot00000000000000.TH dbslower 8 "2017-02-15" "USER COMMANDS" .SH NAME dbslower \- Trace MySQL/PostgreSQL server queries slower than a threshold. .SH SYNOPSIS .B dbslower [-v] [-p PID [PID ...]] [-x PATH] [-m THRESHOLD] {mysql,postgres} .SH DESCRIPTION This traces queries served by a MySQL or PostgreSQL server, and prints those that exceed a latency (query time) threshold. By default a threshold of 1 ms is used. This uses User Statically-Defined Tracing (USDT) probes, a feature added to MySQL and PostgreSQL for DTrace support, but which may not be enabled on a given installation. See requirements. Alternativly, MySQL queries can be traced without the USDT support using the -x option. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF, bcc, and MySQL server with USDT probe support (when configuring the build: \-DENABLE_DTRACE=1) or PostgreSQL server with USDT probe support (when configuring the build: \-\-enable-dtrace). .SH OPTIONS \-h Print usage message. .TP \-p PID Trace this PID. If no PID is specified, the tool will attempt to automatically detect the MySQL or PostgreSQL processes running on the system. .TP \-x PATH Path to MySQL binary. This option allow to MySQL queries even when USDT probes aren't enabled on the MySQL server. .TP \-m THRESHOLD Minimum query latency (duration) to trace, in milliseconds. Default is 1 ms. .TP {mysql,postgres} The database engine to trace. .SH EXAMPLES .TP Trace MySQL server queries slower than 1 ms: # .B dbslower mysql .TP Trace slower than 10 ms for PostgreSQL in process 408: # .B dbslower postgres -p 408 -m 10 .SH FIELDS .TP TIME(s) Time of query start, in seconds. .TP PID Process ID of the traced server. .TP MS Milliseconds for the query, from start to end. .TP QUERY Query string, truncated to 256 characters. .SH OVERHEAD This adds low-overhead instrumentation to queries, and only emits output data from kernel to user-level if they query exceeds the threshold. If the server query rate is less than 1,000/sec, the overhead is expected to be negligible. If the query rate is higher, test to gauge overhead. .SH SOURCE This is from bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 Sasha Goldshtein, Brendan Gregg .SH SEE ALSO biosnoop(8), mysqld_qslower(8), dbstat(8) bpfcc-0.12.0/man/man8/dbstat.8000066400000000000000000000043621357404205000156440ustar00rootroot00000000000000.TH dbstat 8 "2017-02-15" "USER COMMANDS" .SH NAME dbstat \- Collect histograms of MySQL/PostgreSQL query latencies. .SH SYNOPSIS . B dbstat [-v] [-p PID [PID ...]] [-m THRESHOLD] [-u] [-i INTERVAL] {mysql,postgres} .SH DESCRIPTION This traces queries served by a MySQL or PostgreSQL server, and collects a histogram of query latencies. The histogram is printed at the end of collection, or at specified intervals. This uses User Statically-Defined Tracing (USDT) probes, a feature added to MySQL and PostgreSQL for DTrace support, but which may not be enabled on a given installation. See requirements. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF, bcc, and MySQL server with USDT probe support (when configuring the build: \-DENABLE_DTRACE=1) or PostgreSQL server with USDT probe support (when configuring the build: \-\-enable-dtrace). .SH OPTIONS \-h Print usage message. .TP \-p PID Trace this PID. If no PID is specified, the tool will attempt to automatically detect the MySQL or PostgreSQL processes running on the system. .TP \-m THRESHOLD Minimum query latency (duration) to trace, in milliseconds. Default is all queries. .TP \-u Display query latencies in microseconds (default: milliseconds). .TP \-i INTERVAL Print summaries (histograms) at this interval, specified in seconds. .TP {mysql,postgres} The database engine to trace. .SH EXAMPLES .TP Display histogram of MySQL query latencies: # .B dbstat mysql .TP Display histogram of PostgreSQL query latencies slower than 10ms in pid 408: # .B dbstat postgres -p 408 -m 10 .TP Display histogram of PostgreSQL query latencies at 3-second intervals: # .B dbstat postgres -i 3 .SH OVERHEAD This adds low-overhead instrumentation to queries, and only emits output data from kernel to user-level if they query exceeds the threshold. If the server query rate is less than 1,000/sec, the overhead is expected to be negligible. If the query rate is higher, test to gauge overhead. .SH SOURCE This is from bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 Sasha Goldshtein .SH SEE ALSO dbslower(8) bpfcc-0.12.0/man/man8/dcsnoop.8000066400000000000000000000041371357404205000160300ustar00rootroot00000000000000.TH dcsnoop 8 "2016-02-10" "USER COMMANDS" .SH NAME dcsnoop \- Trace directory entry cache (dcache) lookups. Uses Linux eBPF/bcc. .SH SYNOPSIS .B dcsnoop [\-h] [\-a] .SH DESCRIPTION By default, this traces every failed dcache lookup (cache miss), and shows the process performing the lookup and the filename requested. A \-a option can be used to show all lookups, not just failed ones. 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 OPTIONS .TP \-h Print usage message. .TP \-a Trace references, not just failed lookups. .SH EXAMPLES .TP Trace failed dcache lookups: # .B dcsnoop .TP Trace all dcache lookups: # .B dcsnoop \-a .SH FIELDS .TP TIME(s) Time of lookup, in seconds. .TP PID Process ID. .TP COMM Process name. .TP T Type: R == reference (only visible with \-a), 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 bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 dcstat(1) bpfcc-0.12.0/man/man8/dcstat.8000066400000000000000000000031661357404205000156460ustar00rootroot00000000000000.TH dcstat 8 "2016-02-09" "USER COMMANDS" .SH NAME dcstat \- Directory entry cache (dcache) stats. Uses Linux eBPF/bcc. .SH SYNOPSIS .B dcstat [interval [count]] .SH DESCRIPTION The Linux directory entry cache (dcache) improves the performance of file and directory name lookups. This tool provides per-second summary statistics of dcache performance. This uses kernel dynamic tracing of kernel functions, lookup_fast() and d_lookup(), which will need to be modified to match kernel changes. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bcc. .SH EXAMPLES .TP Print summaries each second: # .B dcstat .TP Print output every five seconds, three times: # .B dcstat 5 3 .SH FIELDS .TP REFS/s Number dcache lookups (references) per second. .TP SLOW/s Number of dcache lookups that failed the lookup_fast() path and executed the lookup_slow() path instead. .TP MISS/s Number of dcache misses (failed both fast and slow lookups). .TP HIT% Percentage of dcache hits over total references. .SH OVERHEAD The overhead depends on the frequency of file and directory name lookups. While the per-event overhead is low, some applications may make over 100k lookups per second, and the low per-event overhead will begin to add up, and could begin to be measurable (over 10% CPU usage). Measure in a test environment. .SH SOURCE This is from bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 dcsnoop(8) bpfcc-0.12.0/man/man8/deadlock.8000066400000000000000000000114721357404205000161310ustar00rootroot00000000000000.TH deadlock 8 "2017-02-01" "USER COMMANDS" .SH NAME deadlock \- Find potential deadlocks (lock order inversions) in a running program. .SH SYNOPSIS .B deadlock [\-h] [\--binary BINARY] [\--dump-graph DUMP_GRAPH] .B [\--verbose] [\--lock-symbols LOCK_SYMBOLS] .B [\--unlock-symbols UNLOCK_SYMBOLS] .B pid .SH DESCRIPTION deadlock finds potential deadlocks in a running process. The program attaches uprobes on `pthread_mutex_lock` and `pthread_mutex_unlock` by default to build a mutex wait directed graph, and then looks for a cycle in this graph. This graph has the following properties: - Nodes in the graph represent mutexes. - Edge (A, B) exists if there exists some thread T where lock(A) was called and lock(B) was called before unlock(A) was called. If there is a cycle in this graph, this indicates that there is a lock order inversion (potential deadlock). If the program finds a lock order inversion, the program will dump the cycle of mutexes, dump the stack traces where each mutex was acquired, and then exit. This program can only find potential deadlocks that occur while the program is tracing the process. It cannot find deadlocks that may have occurred before the program was attached to the process. This tool does not work for shared mutexes or recursive mutexes. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bcc .SH OPTIONS .TP \-h, --help show this help message and exit .TP \--binary BINARY If set, trace the mutexes from the binary at this path. For statically-linked binaries, this argument is not required. For dynamically-linked binaries, this argument is required and should be the path of the pthread library the binary is using. Example: /lib/x86_64-linux-gnu/libpthread.so.0 .TP \--dump-graph DUMP_GRAPH If set, this will dump the mutex graph to the specified file. .TP \--verbose Print statistics about the mutex wait graph. .TP \--lock-symbols LOCK_SYMBOLS Comma-separated list of lock symbols to trace. Default is pthread_mutex_lock. These symbols cannot be inlined in the binary. .TP \--unlock-symbols UNLOCK_SYMBOLS Comma-separated list of unlock symbols to trace. Default is pthread_mutex_unlock. These symbols cannot be inlined in the binary. .TP pid Pid to trace .SH EXAMPLES .TP Find potential deadlocks in PID 181. The --binary argument is not needed for \ statically-linked binaries. # .B deadlock 181 .TP Find potential deadlocks in PID 181. If the process was created from a \ dynamically-linked executable, the --binary argument is required and must be \ the path of the pthread library: # .B deadlock 181 --binary /lib/x86_64-linux-gnu/libpthread.so.0 .TP Find potential deadlocks in PID 181. If the process was created from a \ statically-linked executable, optionally pass the location of the binary. \ On older kernels without https://lkml.org/lkml/2017/1/13/585, binaries that \ contain `:` in the path cannot be attached with uprobes. As a workaround, we \ can create a symlink to the binary, and provide the symlink name instead with \ the `--binary` option: # .B deadlock 181 --binary /usr/local/bin/lockinversion .TP Find potential deadlocks in PID 181 and dump the mutex wait graph to a file: # .B deadlock 181 --dump-graph graph.json .TP Find potential deadlocks in PID 181 and print mutex wait graph statistics: # .B deadlock 181 --verbose .TP Find potential deadlocks in PID 181 with custom mutexes: # .B deadlock 181 .B --lock-symbols custom_mutex1_lock,custom_mutex2_lock .B --unlock_symbols custom_mutex1_unlock,custom_mutex2_unlock .SH OUTPUT This program does not output any fields. Rather, it will keep running until it finds a potential deadlock, or the user hits Ctrl-C. If the program finds a potential deadlock, it will output the stack traces and lock order inversion in the following format and exit: .TP Potential Deadlock Detected! .TP Cycle in lock order graph: Mutex M0 => Mutex M1 => Mutex M0 .TP Mutex M1 acquired here while holding Mutex M0 in Thread T: .B [stack trace] .TP Mutex M0 previously acquired by the same Thread T here: .B [stack trace] .TP Mutex M0 acquired here while holding Mutex M1 in Thread S: .B [stack trace] .TP Mutex M1 previously acquired by the same Thread S here: .B [stack trace] .TP Thread T created by Thread R here: .B [stack trace] .TP Thread S created by Thread Q here: .B [stack trace] .SH OVERHEAD This traces all mutex lock and unlock events and all thread creation events on the traced process. The overhead of this can be high if the process has many threads and mutexes. You should only run this on a process where the slowdown is acceptable. .SH SOURCE This is from bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 Kenny Yu bpfcc-0.12.0/man/man8/drsnoop.8000066400000000000000000000044761357404205000160550ustar00rootroot00000000000000.TH drsnoop 8 "2019-02-20" "USER COMMANDS" .SH NAME drsnoop \- Trace direct reclaim events. Uses Linux eBPF/bcc. .SH SYNOPSIS .B drsnoop.py [\-h] [\-T] [\-U] [\-p PID] [\-t TID] [\-u UID] [\-d DURATION] [-n name] [-v] .SH DESCRIPTION drsnoop trace direct reclaim events, showing which processes are allocing pages with direct reclaiming. This can be useful for discovering when allocstall (/p- roc/vmstat) continues to increase, whether it is caused by some critical proc- esses or not. This works by tracing the direct reclaim events using kernel tracepoints. This makes use of a Linux 4.5 feature (bpf_perf_event_output()); for kernels older than 4.5, see the version under tools/old, which uses an older mechanism. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bcc. .SH OPTIONS .TP \-h Print usage message. .TP \-T Include a timestamp column. .TP \-U Show UID. .TP \-p PID Trace this process ID only (filtered in-kernel). .TP \-t TID Trace this thread ID only (filtered in-kernel). .TP \-u UID Trace this UID only (filtered in-kernel). .TP \-d DURATION Total duration of trace in seconds. .TP \-n name Only print processes where its name partially matches 'name' \-v verbose Run in verbose mode. Will output system memory state .SH EXAMPLES .TP Trace all direct reclaim events: # .B drsnoop .TP Trace all direct reclaim events, for 10 seconds only: # .B drsnoop -d 10 .TP Trace all direct reclaim events, and include timestamps: # .B drsnoop \-T .TP Show UID: # .B drsnoop \-U .TP Trace PID 181 only: # .B drsnoop \-p 181 .TP Trace UID 1000 only: # .B drsnoop \-u 1000 .TP Trace all direct reclaim events from processes where its name partially match- es 'mond': # .B drnsnoop \-n mond .SH FIELDS .TP TIME(s) Time of the call, in seconds. .TP UID User ID .TP PID Process ID .TP TID Thread ID .TP COMM Process name .SH OVERHEAD This traces the kernel direct reclaim 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. .SH SOURCE This is from bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 Ethercflow bpfcc-0.12.0/man/man8/execsnoop.8000066400000000000000000000055051357404205000163660ustar00rootroot00000000000000.TH execsnoop 8 "2016-02-07" "USER COMMANDS" .SH NAME execsnoop \- Trace new processes via exec() syscalls. Uses Linux eBPF/bcc. .SH SYNOPSIS .B execsnoop [\-h] [\-T] [\-t] [\-x] [\-q] [\-n NAME] [\-l LINE] .B [\-\-max-args MAX_ARGS] .SH DESCRIPTION execsnoop traces new processes, showing the filename executed and argument list. It works by traces the execve() system call (commonly used exec() variant). This catches new processes that follow the fork->exec sequence, as well as processes that re-exec() themselves. Some applications fork() but do not exec(), eg, for worker processes, which won't be included in the execsnoop output. This works by tracing the kernel sys_execve() function using dynamic tracing, 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 OPTIONS .TP \-h Print usage message. .TP \-T Include a time column (HH:MM:SS). .TP \-t Include a timestamp column. .TP \-x Include failed exec()s .TP \-q Add "quotemarks" around arguments. Escape quotemarks in arguments with a backslash. For tracing empty arguments or arguments that contain whitespace. .TP \-n NAME Only print command lines matching this name (regex) .TP \-l LINE Only print commands where arg contains this line (regex) .TP \--max-args MAXARGS Maximum number of arguments parsed and displayed, defaults to 20 .SH EXAMPLES .TP Trace all exec() syscalls: # .B execsnoop .TP Trace all exec() syscalls, and include timestamps: # .B execsnoop \-t .TP Include failed exec()s: # .B execsnoop \-x .TP Put quotemarks around arguments. # .B execsnoop \-q .TP Only trace exec()s where the filename contains "mount": # .B execsnoop \-n mount .TP Only trace exec()s where argument's line contains "testpkg": # .B execsnoop \-l testpkg .SH FIELDS .TP TIME Time of exec() return, in HH:MM:SS format. .TP TIME(s) Time of exec() return, in seconds. .TP PCOMM Parent process/command name. .TP PID Process ID .TP PPID Parent process ID .TP RET Return value of exec(). 0 == successs. Failures are only shown when using the \-x option. .TP ARGS Filename for the exec(), followed be up to 19 arguments. An ellipsis "..." is shown if the argument list is known to be truncated. .SH OVERHEAD This traces the kernel execve function 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 exec()s, then test and understand overhead before use. .SH SOURCE This is from bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 opensnoop(1) bpfcc-0.12.0/man/man8/exitsnoop.8000066400000000000000000000051041357404205000164060ustar00rootroot00000000000000.TH exitsnoop 8 "2019-05-28" "USER COMMANDS" .SH NAME exitsnoop \- Trace all process termination (exit, fatal signal). Uses Linux eBPF/bcc. .SH SYNOPSIS .B exitsnoop [\-h] [\-t] [\-\-utc] [\-x] [\-p PID] [\-\-label LABEL] .SH DESCRIPTION exitsnoop traces process termination, showing the command name and reason for termination, either an exit or a fatal signal. It catches processes of all users, processes in containers, as well as processes that become zombie. This works by tracing the kernel sched_process_exit() function using dynamic tracing, 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 OPTIONS .TP \-h Print usage message. .TP \-t Include a timestamp column. .TP \-\-utc Include a timestamp column, use UTC timezone. .TP \-x Exclude successful exits, exit( 0 ) .TP \-p PID Trace this process ID only (filtered in-kernel). .TP \-\-label LABEL Label each line with LABEL (default 'exit') in first column (2nd if timestamp is present). .SH EXAMPLES .TP Trace all process termination # .B exitsnoop .TP Trace all process termination, and include timestamps: # .B exitsnoop \-t .TP Exclude successful exits, only include non-zero exit codes and fatal signals: # .B exitsnoop \-x .TP Trace PID 181 only: # .B exitsnoop \-p 181 .TP Label each output line with 'EXIT': # .B exitsnoop \-\-label EXIT .SH FIELDS .TP TIME-TZ Time of process termination HH:MM:SS.sss with milliseconds, where TZ is the local time zone, 'UTC' with \-\-utc option. .TP LABEL The optional label if \-\-label option is used. This is useful with the \-t option for timestamps when the output of several tracing tools is sorted into one combined output. .TP PCOMM Process/command name. .TP PID Process ID .TP PPID The process ID of the process that will be notified of PID termination. .TP TID Thread ID. .TP EXIT_CODE The exit code for exit() or the signal number for a fatal signal. .SH OVERHEAD This traces the kernel sched_process_exit() function 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 has a high rate of process termination, then test and understand overhead before use. .SH SOURCE This is from bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 Arturo Martin-de-Nicolas .SH SEE ALSO execsnoop(8) bpfcc-0.12.0/man/man8/ext4dist.8000066400000000000000000000036251357404205000161340ustar00rootroot00000000000000.TH ext4dist 8 "2016-02-12" "USER COMMANDS" .SH NAME ext4dist \- Summarize ext4 operation latency. Uses Linux eBPF/bcc. .SH SYNOPSIS .B ext4dist [\-h] [\-T] [\-m] [\-p PID] [interval] [count] .SH DESCRIPTION This tool summarizes time (latency) spent in common ext4 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 ext4_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 bcc. .SH OPTIONS .TP \-h Print usage message. .TP \-T Don't include timestamps on interval output. .TP \-m Output in milliseconds. .TP \-p PID Trace this PID only. .SH EXAMPLES .TP Trace ext4 operation time, and print a summary on Ctrl-C: # .B ext4dist .TP Trace PID 181 only: # .B ext4dist -p 181 .TP Print 1 second summaries, 10 times: # .B ext4dist 1 10 .TP 1 second summaries, printed in milliseconds # .B ext4dist \-m 1 .SH FIELDS .TP msecs Range of milliseconds for this bucket. .TP usecs Range of microseconds for this bucket. .TP count Number of operations in this time range. .TP distribution ASCII representation of the distribution (the count column). .SH OVERHEAD This adds low-overhead instrumentation to these ext4 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 bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 ext4snoop(8) bpfcc-0.12.0/man/man8/ext4slower.8000066400000000000000000000062221357404205000165000ustar00rootroot00000000000000.TH ext4slower 8 "2016-02-11" "USER COMMANDS" .SH NAME ext4slower \- Trace slow ext4 file operations, with per-event details. .SH SYNOPSIS .B ext4slower [\-h] [\-j] [\-p PID] [min_ms] .SH DESCRIPTION This tool traces common ext4 file operations: reads, writes, opens, and syncs. It measures the time spent in these operations, and prints details for each that exceeded a threshold. WARNING: See the OVERHEAD section. By default, a minimum millisecond threshold of 10 is used. If a threshold of 0 is used, all events are printed (warning: verbose). Since this works by tracing the ext4_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 bcc. .SH OPTIONS \-p PID Trace this PID only. .TP min_ms Minimum I/O latency (duration) to trace, in milliseconds. Default is 10 ms. .SH EXAMPLES .TP Trace synchronous file reads and writes slower than 10 ms: # .B ext4slower .TP Trace slower than 1 ms: # .B ext4slower 1 .TP Trace slower than 1 ms, and output just the fields in parsable format (csv): # .B ext4slower \-j 1 .TP Trace all file reads and writes (warning: the output will be verbose): # .B ext4slower 0 .TP Trace slower than 1 ms, for PID 181 only: # .B ext4slower \-p 181 1 .SH FIELDS .TP TIME(s) Time of I/O completion since the first I/O seen, in seconds. .TP COMM Process name. .TP PID Process ID. .TP T Type of operation. R == read, W == write, O == open, S == fsync. .TP OFF_KB File offset for the I/O, in Kbytes. .TP BYTES Size of I/O, in bytes. .TP LAT(ms) Latency (duration) of I/O, measured from when it was issued by VFS to the filesystem, to when it completed. This time is inclusive of block device I/O, file system CPU cycles, file system locks, run queue latency, etc. It's a more accurate measure of the latency suffered by applications performing file system I/O, than to measure this down at the block device interface. .TP FILENAME A cached kernel file name (comes from dentry->d_name.name). .TP ENDTIME_us Completion timestamp, microseconds (\-j only). .TP OFFSET_b File offset, bytes (\-j only). .TP LATENCY_us Latency (duration) of the I/O, in microseconds (\-j only). .SH OVERHEAD This adds low-overhead instrumentation to these ext4 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 (even if it prints no "slower" events) can begin to become significant. Measure and quantify before use. If this continues to be a problem, consider switching to a tool that prints in-kernel summaries only. .PP Note that the overhead of this tool should be less than fileslower(8), as this tool targets ext4 functions only, and not all file read/write paths (which can include socket I/O). .SH SOURCE This is from bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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), funccount(8), fileslower(8) bpfcc-0.12.0/man/man8/filelife.8000066400000000000000000000034671357404205000161470ustar00rootroot00000000000000.TH filelife 8 "2016-02-08" "USER COMMANDS" .SH NAME filelife \- Trace the lifespan of short-lived files. Uses Linux eBPF/bcc. .SH SYNOPSIS .B filelife [\-h] [\-p PID] .SH DESCRIPTION This traces the creation and deletion of files, providing information on who deleted the file, the file age, and the file name. The intent is to provide information on short-lived files, for debugging or performance analysis. This works by tracing the kernel vfs_create() and vfs_delete() functions (and maybe more, see the source) using dynamic tracing, and will need updating to match any changes to these functions. This makes use of a Linux 4.5 feature (bpf_perf_event_output()); for kernels older than 4.5, see the version under tools/old, which uses an older mechanism. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bcc. .SH OPTIONS .TP \-h Print usage message. .TP \-p PID Trace this process ID only (filtered in-kernel). .SH EXAMPLES .TP Trace all short-lived files, and print details: # .B filelife .TP Trace all short-lived files created AND deleted by PID 181: # .B filelife \-p 181 .SH FIELDS .TP TIME Time of the deletion. .TP PID Process ID that deleted the file. .TP COMM Process name for the PID. .TP AGE(s) Age of the file, from creation to deletion, in seconds. .TP FILE Filename. .SH OVERHEAD This traces the kernel VFS file create and delete functions and prints output for each delete. As the rate of this is generally expected to be low (< 1000/s), the overhead is also expected to be negligible. This is from bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 opensnoop(1) bpfcc-0.12.0/man/man8/fileslower.8000066400000000000000000000075041357404205000165370ustar00rootroot00000000000000.TH fileslower 8 "2016-02-07" "USER COMMANDS" .SH NAME fileslower \- Trace slow synchronous file reads and writes. .SH SYNOPSIS .B fileslower [\-h] [\-p PID] [-a] [min_ms] .SH DESCRIPTION This script uses kernel dynamic tracing of synchronous reads and writes at the VFS interface, to identify slow file reads and writes for any file system. This version traces __vfs_read() and __vfs_write() and only showing synchronous I/O (the path to new_sync_read() and new_sync_write()), and I/O with filenames. This approach provides a view of just two file system request types: file reads and writes. There are typically many others: asynchronous I/O, directory operations, file handle operations, file open()s, fflush(), etc. WARNING: See the OVERHEAD section. By default, a minimum millisecond threshold of 10 is used. Since this works by tracing various kernel __vfs_*() functions using dynamic tracing, it will need updating to match any changes to these functions. A future version should switch to using FS tracepoints instead. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bcc. .SH OPTIONS \-p PID Trace this PID only. .TP \-a Include non-regular file types in output (sockets, FIFOs, etc). .TP min_ms Minimum I/O latency (duration) to trace, in milliseconds. Default is 10 ms. .SH EXAMPLES .TP Trace synchronous file reads and writes slower than 10 ms: # .B fileslower .TP Trace slower than 1 ms: # .B fileslower 1 .TP Trace slower than 1 ms, for PID 181 only: # .B fileslower \-p 181 1 .SH FIELDS .TP TIME(s) Time of I/O completion since the first I/O seen, in seconds. .TP COMM Process name. .TP PID Process ID. .TP D Direction of I/O. R == read, W == write. .TP BYTES Size of I/O, in bytes. .TP LAT(ms) Latency (duration) of I/O, measured from when the application issued it to VFS to when it completed. This time is inclusive of block device I/O, file system CPU cycles, file system locks, run queue latency, etc. It's a more accurate measure of the latency suffered by applications performing file system I/O, than to measure this down at the block device interface. .TP FILENAME A cached kernel file name (comes from dentry->d_name.name). .SH OVERHEAD Depending on the frequency of application reads and writes, overhead can become severe, in the worst case slowing applications by 2x. In the best case, the overhead is negligible. Hopefully for real world workloads the overhead is often at the lower end of the spectrum -- test before use. The reason for high overhead is that this traces VFS reads and writes, which includes FS cache reads and writes, and can exceed one million events per second if the application is I/O heavy. While the instrumentation is extremely lightweight, and uses in-kernel eBPF maps for efficient timing and filtering, multiply that cost by one million events per second and that cost becomes a million times worse. You can get an idea of the possible cost by just counting the instrumented events using the bcc funccount tool, eg: .PP # ./funccount.py -i 1 -r '^__vfs_(read|write)$' .PP This also costs overhead, but is somewhat less than fileslower. .PP If the overhead is prohibitive for your workload, I'd recommend moving down-stack a little from VFS into the file system functions (ext4, xfs, etc). Look for updates to bcc for specific file system tools that do this. The advantage of a per-file system approach is that we can trace post-cache, greatly reducing events and overhead. The disadvantage is needing custom tracing approaches for each different file system (whereas VFS is generic). .SH SOURCE This is from bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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), funccount(8) bpfcc-0.12.0/man/man8/filetop.8000066400000000000000000000063621357404205000160270ustar00rootroot00000000000000.TH filetop 8 "2016-02-08" "USER COMMANDS" .SH NAME filetop \- File reads and writes by filename and process. Top for files. .SH SYNOPSIS .B filetop [\-h] [\-C] [\-r MAXROWS] [\-s {reads,writes,rbytes,wbytes}] [\-p PID] [interval] [count] .SH DESCRIPTION This is top for files. This traces file reads and writes, and prints a per-file summary every interval (by default, 1 second). By default the summary is sorted on the highest read throughput (Kbytes). Sorting order can be changed via -s option. By default only IO on regular files is shown. The -a option will list all file types (sokets, FIFOs, etc). This uses in-kernel eBPF maps to store per process summaries for efficiency. This script works by tracing the __vfs_read() and __vfs_write() functions using kernel dynamic tracing, which instruments explicit read and write calls. If files are read or written using another means (eg, via mmap()), then they will not be visible using this tool. Also, this tool will need updating to match any code changes to those vfs functions. This should be useful for file system workload characterization when analyzing the performance of applications. Note that tracing VFS level reads and writes can be a frequent activity, and this tool can begin to cost measurable overhead at high I/O rates. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bcc. .SH OPTIONS .TP \-a Include non-regular file types (sockets, FIFOs, etc). .TP \-C Don't clear the screen. .TP \-r MAXROWS Maximum number of rows to print. Default is 20. .TP \-s {reads,writes,rbytes,wbytes} Sort column. Default is rbytes (read throughput). .TP \-p PID Trace this PID only. .TP interval Interval between updates, seconds. .TP count Number of interval summaries. .SH EXAMPLES .TP Summarize block device I/O by process, 1 second screen refresh: # .B filetop .TP Don't clear the screen, and top 8 rows only: # .B filetop -Cr 8 .TP 5 second summaries, 10 times only: # .B filetop 5 10 .SH FIELDS .TP loadavg: The contents of /proc/loadavg .TP PID Process ID. .TP COMM Process name. .TP READS Count of reads during interval. .TP WRITES Count of writes during interval. .TP R_Kb Total read Kbytes during interval. .TP W_Kb Total write Kbytes during interval. .TP T Type of file: R == regular, S == socket, O == other (pipe, etc). .TP FILE Filename. .SH OVERHEAD Depending on the frequency of application reads and writes, overhead can become significant, in the worst case slowing applications by over 50%. Hopefully for real world workloads the overhead is much less -- test before use. The reason for the high overhead is that VFS reads and writes can be a frequent event, and despite the eBPF overhead being very small per event, if you multiply this small overhead by a million events per second, it becomes a million times worse. Literally. You can gauge the number of reads and writes using the vfsstat(8) tool, also from bcc. .SH SOURCE This is from bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 INSPIRATION top(1) by William LeFebvre .SH SEE ALSO vfsstat(8), vfscount(8), fileslower(8) bpfcc-0.12.0/man/man8/funccount.8000066400000000000000000000053231357404205000163650ustar00rootroot00000000000000.TH funccount 8 "2015-08-18" "USER COMMANDS" .SH NAME funccount \- Count function, tracepoint, and USDT probe calls matching a pattern. Uses Linux eBPF/bcc. .SH SYNOPSIS .B funccount [\-h] [\-p PID] [\-i INTERVAL] [\-d DURATION] [\-T] [\-r] [\-D] pattern .SH DESCRIPTION This tool is a quick way to determine which functions are being called, and at what rate. It uses in-kernel eBPF maps to count function calls. WARNING: This uses dynamic tracing of (what can be many) functions, an activity that has had issues on some kernel versions (risk of panics or freezes). Test, and know what you are doing, before use. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bcc. .SH OPTIONS pattern Search pattern. Supports "*" wildcards. See EXAMPLES. You can also use \-r for regular expressions. .TP \-h Print usage message. .TP \-p PID Trace this process ID only. .TP \-i INTERVAL Print output every interval seconds. .TP \-d DURATION Total duration of trace in seconds. .TP \-T Include timestamps on output. .TP \-r Use regular expressions for the search pattern. .TP \-D Print the BPF program before starting (for debugging purposes). .SH EXAMPLES .TP Count kernel functions beginning with "vfs_", until Ctrl-C is hit: # .B funccount 'vfs_*' .TP Count kernel functions beginning with "tcp_send", until Ctrl-C is hit: # .B funccount 'tcp_send*' .TP Print kernel functions beginning with "vfs_", every second: # .B funccount \-i 1 'vfs_*' .TP Print kernel functions beginning with "vfs_", for ten seconds only: # .B funccount \-d 10 'vfs_*' .TP Match kernel functions beginning with "vfs_", using regular expressions: # .B funccount \-r '^vfs_.*' .TP Count vfs calls for process ID 181 only: # .B funccount \-p 181 'vfs_*' .TP Count calls to the sched_fork tracepoint, indicating a fork() performed: # .B funccount t:sched:sched_fork .TP Count all GC USDT probes in the Node process: # .B funccount -p 185 u:node:gc* .TP Count all malloc() calls in libc: # .B funccount c:malloc .SH FIELDS .TP FUNC Function name .TP COUNT Number of calls while tracing .SH OVERHEAD This traces functions and maintains in-kernel counts, which are asynchronously copied to user-space. While the rate of calls 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 before use. .SH SOURCE This is from bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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, Sasha Goldshtein .SH SEE ALSO stackcount(8) funclatency(8) vfscount(8) bpfcc-0.12.0/man/man8/funclatency.8000066400000000000000000000072361357404205000167010ustar00rootroot00000000000000.TH funclatency 8 "2015-08-18" "USER COMMANDS" .SH NAME funclatency \- Time functions and print latency as a histogram. .SH SYNOPSIS .B funclatency [\-h] [\-p PID] [\-i INTERVAL] [\-d DURATION] [\-T] [\-u] [\-m] [\-F] [\-r] [\-v] pattern .SH DESCRIPTION This tool traces function calls and times their duration (latency), and shows the latency distribution as a histogram. The time is measured from when the function is called to when it returns, and is inclusive of both on-CPU time and time spent blocked. This tool uses in-kernel eBPF maps for storing timestamps and the histogram, for efficiency. Currently nested or recursive functions are not supported properly, and timestamps will be overwritten, creating dubious output. Try to match single functions, or groups of functions that run at the same stack layer, and don't ultimately call each other. WARNING: This uses dynamic tracing of (what can be many) functions, an activity that has had issues on some kernel versions (risk of panics or freezes). Test, and know what you are doing, before use. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bcc. .SH OPTIONS pattern Function name or search pattern. Supports "*" wildcards. See EXAMPLES. You can also use \-r for regular expressions. \-h Print usage message. .TP \-p PID Trace this process ID only. .TP \-i INTERVAL Print output every interval seconds. .TP \-d DURATION Total duration of trace, in seconds. .TP \-T Include timestamps on output. .TP \-u Output histogram in microseconds. .TP \-m Output histogram in milliseconds. .TP \-F Print a separate histogram per function matched. .TP \-r Use regular expressions for the search pattern. .TP \-v Print the BPF program (for debugging purposes). .SH EXAMPLES .TP Time the do_sys_open() kernel function, and print the distribution as a histogram: # .B funclatency do_sys_open .TP Time the read() function in libc across all processes on the system: # .B funclatency c:read .TP Time vfs_read(), and print the histogram in units of microseconds: # .B funclatency \-u vfs_read .TP Time do_nanosleep(), and print the histogram in units of milliseconds: # .B funclatency \-m do_nanosleep .TP Time libc open(), and print output every 2 seconds, for duration 10 seconds: # .B funclatency \-i 2 -d 10 c:read .TP Time vfs_read(), and print output every 5 seconds, with timestamps: # .B funclatency \-mTi 5 vfs_read .TP Time vfs_read() for process ID 181 only: # .B funclatency \-p 181 vfs_read: .TP Time both vfs_fstat() and vfs_fstatat() calls, by use of a wildcard: # .B funclatency 'vfs_fstat*' .TP Time both vfs_fstat* calls, and print a separate histogram for each: # .B funclatency -F 'vfs_fstat*' .SH FIELDS .TP necs Nanosecond range .TP usecs Microsecond range .TP msecs Millisecond range .TP count How many calls fell into this range .TP distribution An ASCII bar chart to visualize the distribution (count column) .SH OVERHEAD This traces kernel functions and maintains in-kernel timestamps and a histogram, which are asynchronously copied to user-space. While this method is very efficient, the rate of kernel functions can also be very high (>1M/sec), at which point the overhead is expected to be measurable. Measure in a test environment and understand overheads before use. You can also use funccount to measure the rate of kernel functions over a short duration, to set some expectations before use. .SH SOURCE This is from bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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, Sasha Goldshtein .SH SEE ALSO funccount(8) bpfcc-0.12.0/man/man8/funcslower.8000066400000000000000000000071531357404205000165530ustar00rootroot00000000000000.TH funcslower 8 "2017-03-30" "USER COMMANDS" .SH NAME funcslower \- Trace slow kernel or user function calls. .SH SYNOPSIS .B funcslower [\-hf] [\-p PID] [\-U | \-K] [-m MIN_MS] [-u MIN_US] [-a ARGUMENTS] [-T] [-t] [-v] function [function ...] .SH DESCRIPTION This script traces a kernel or user function's entry and return points, and prints a message when the function's latency exceeded the specified threshold. Multiple functions are supported, and you can mix kernel functions with user functions in different libraries. WARNING: See the OVERHEAD section. By default, a minimum millisecond threshold of 1 is used. Recursive functions are not supported: only the inner-most recursive invocation will be traced. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bcc. .SH OPTIONS \-p PID Trace this PID only. .TP \-m MIN_NS Minimum duration to trace, in milliseconds. Default is 1 ms. .TP \-u MIN_US Minimum duration to trace, in microseconds. .TP \-a ARGUMENTS Print the function's arguments, up to 6. .TP \-T Print a HH:MM:SS timestamp with each entry. .TP \-t Print a seconds timestamp with each entry, at microsecond resolution. .TP \-f Print output in folded stack format. .TP \-U Show stacks from user space only (no kernel space stacks). .TP \-K Show stacks from kernel space only (no user space stacks). .TP \-v Print the resulting BPF program, for debugging purposes. .TP function The function to trace -- multiple functions are supported. If a plain function name is provided, the function is assumed to be a kernel function. For user functions, provide the library name and the function name, e.g. bash:readline or c:malloc. .SH EXAMPLES .TP Trace vfs_write calls slower than 1ms: # .B funcslower vfs_write .TP Trace open() calls in libc slower than 10us: # .B funcslower \-u 10 c:open .TP Trace both malloc() and free() slower than 10us, in pid 135 only: # .B funcslower \-p 135 \-u 10 c:malloc c:free .TP Trace the write syscall and print its first 4 arguments: # .B funcslower -a 4 SyS_write .TP Trace opens from libc and print the user and kernel stack frames: # .B funcslower -UK c:open .SH FIELDS .TP TIME Time of the event as a human-readable HH:MM:SS format, or a timestamp in seconds at microsecond-accuracy from the first event seen. .TP COMM Process name. .TP PID Process ID. .TP LAT Latency of the operation in either microseconds (us) or milliseconds (ms). .TP RVAL The return value from the function. Often useful for diagnosing a relationship between slow and failed function calls. .TP FUNC The function name, followed by its arguments if requested. .SH OVERHEAD Depending on the function(s) being traced, overhead can become severe. For example, tracing a common function like malloc() can slow down a C/C++ program by a factor of 2 or more. On the other hand, tracing a low-frequency event like the SyS_setreuid() function will probably not be as prohibitive, and in fact negligible for functions that are called up to 100-1000 times per second. You should first use the funclatency and argdist tools for investigation, because they summarize data in-kernel and have a much lower overhead than this tool. To get a general idea of the number of times a particular function is called (and estimate the overhead), use the funccount tool, e.g.: .PP # funccount c:open .SH SOURCE This is from bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 Sasha Goldshtein .SH SEE ALSO funccount(8), funclatency(8), argdist(8), trace(8) bpfcc-0.12.0/man/man8/gethostlatency.8000066400000000000000000000033311357404205000174130ustar00rootroot00000000000000.TH gethostlatency 8 "2016-01-28" "USER COMMANDS" .SH NAME gethostlatency \- Show latency for getaddrinfo/gethostbyname[2] calls. Uses Linux eBPF/bcc. .SH SYNOPSIS .B gethostlatency .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 makes use of a Linux 4.5 feature (bpf_perf_event_output()); for kernels older than 4.5, see the version under tools/old, which uses an older mechanism 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 OPTIONS .TP \-p PID Trace this process ID only. .SH EXAMPLES .TP Trace host lookups (getaddrinfo/gethostbyname[2]) system wide: # .B gethostlatency .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 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 bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 tcpdump(8) bpfcc-0.12.0/man/man8/hardirqs.8000066400000000000000000000044771357404205000162070ustar00rootroot00000000000000.TH hardirqs 8 "2015-10-20" "USER COMMANDS" .SH NAME hardirqs \- Measure hard IRQ (hard interrupt) event time. Uses Linux eBPF/bcc. .SH SYNOPSIS .B hardirqs [\-h] [\-T] [\-N] [\-C] [\-d] [interval] [outputs] .SH DESCRIPTION This summarizes the time spent servicing hard IRQs (hard interrupts), and can show this time as either totals or histogram distributions. A system-wide summary of this time is shown by the %irq column of mpstat(1), and event counts (but not times) are shown by /proc/interrupts. WARNING: This currently uses dynamic tracing of hard interrupts. You should understand what this means before use. Try in a test environment. Future versions should switch to tracepoints. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bcc. .SH OPTIONS .TP \-h Print usage message. .TP \-T Include timestamps on output. .TP \-N Output in nanoseconds. .TP \-C Count events only. .TP \-d Show IRQ time distribution as histograms. .SH EXAMPLES .TP Sum hard IRQ event time until Ctrl-C: # .B hardirqs .TP Show hard IRQ event time as histograms: # .B hardirqs \-d .TP Print 1 second summaries, 10 times: # .B hardirqs 1 10 .TP 1 second summaries, printed in nanoseconds, with timestamps: # .B hardirqs \-NT 1 .SH FIELDS .TP HARDIRQ The irq action name for this hard IRQ. .TP TOTAL_usecs Total time spent in this hard IRQ in microseconds. .TP TOTAL_nsecs Total time spent in this hard IRQ in nanoseconds. .TP usecs Range of microseconds for this bucket. .TP nsecs Range of nanoseconds for this bucket. .TP count Number of hard IRQs in this time range. .TP distribution ASCII representation of the distribution (the count column). .SH OVERHEAD This traces kernel functions and maintains in-kernel counts, which are asynchronously copied to user-space. While the rate of interrupts 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, but could become noticeable for heavy workloads. Measure in a test environment before use. .SH SOURCE This is from bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 softirqs(8) bpfcc-0.12.0/man/man8/inject.8000066400000000000000000000051121357404205000156310ustar00rootroot00000000000000.TH inject 8 "2018-03-16" "USER COMMANDS" .SH NAME inject \- injects appropriate error into function if input call chain and predicates are satisfied. Uses Linux eBPF/bcc. .SH SYNOPSIS .B inject -h [-I header] [-P probability] [-v] [-c count] .SH DESCRIPTION inject injects errors into specified kernel functionality when a given call chain and associated predicates are satisfied. WARNING: This tool injects failures into key kernel functions and may crash the kernel. You should know what you're doing if you're using this tool. This makes use of a Linux 4.16 feature (bpf_override_return()) Since this uses BPF, only the root user can use this tool. .SH OPTIONS .TP \-h Print usage message. .TP \-v Display the generated BPF program, for debugging or modification. .TP \-I header Necessary headers to be included. .TP \-P probability Optional probability of failure, default 1. .TP \-c count Number of errors to inject before stopping, default never stops. .SH MODE .TP \fBkmalloc\fR Make the following function indicate failure .RS 14 int should_failslab(struct kmem_cache *s, gfp_t gfpflags) .RE .TP \fBbio\fR Make the following function indicate failure .RS 14 int should_fail_bio(struct bio *bio) .RE .TP \fBalloc_page\fR Make the following function indicate failure .RS 14 bool should_fail_alloc_page(gfp_t gfp_mask, unsigned int order) .RE .SH SPEC .B FUNCTION([ARGS])[(TEST)] [=> ...] A list of predicates separated by "=>". A predicate is a function signature (name and arguments) in a call stack and a test on the function's arguments. Missing predicates are implicitly true. Missing tests are implicitly true. Specifying the function arguments is optional if the test does not use them. If the error injection function is not listed as the first predicate, it is implicitly added. Functions are listed in the reverse order that they are called, ie. if a() calls b(), the spec would be "b() => a()". .SH REQUIREMENTS CONFIG_BPF, CONFIG_BPF_KPROBE_OVERRIDE, bcc .SH EXAMPLES .EX inject kmalloc -v 'SyS_mount()' .EE .EX inject kmalloc -v 'mount_subtree() => btrfs_mount()' .EE .EX inject -P 0.5 -c 100 alloc_page "should_fail_alloc_page(gfp_t gfp_mask, unsigned int order) (order == 1) => qlge_refill_bq()" .EE Please see the output of '-h' and tools/inject_example.txt for more examples. .SH SOURCE This is from bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 Howard McLauchlan bpfcc-0.12.0/man/man8/javacalls.8000077700000000000000000000000001357404205000176652ucalls.8ustar00rootroot00000000000000bpfcc-0.12.0/man/man8/javaflow.8000077700000000000000000000000001357404205000174072uflow.8ustar00rootroot00000000000000bpfcc-0.12.0/man/man8/javagc.8000077700000000000000000000000001357404205000164532ugc.8ustar00rootroot00000000000000bpfcc-0.12.0/man/man8/javaobjnew.8000077700000000000000000000000001357404205000202412uobjnew.8ustar00rootroot00000000000000bpfcc-0.12.0/man/man8/javastat.8000077700000000000000000000000001357404205000174172ustat.8ustar00rootroot00000000000000bpfcc-0.12.0/man/man8/javathreads.8000077700000000000000000000000001357404205000205552uthreads.8ustar00rootroot00000000000000bpfcc-0.12.0/man/man8/killsnoop.8000066400000000000000000000036671357404205000164040ustar00rootroot00000000000000.TH killsnoop 8 "2015-08-20" "USER COMMANDS" .SH NAME killsnoop \- Trace signals issued by the kill() syscall. Uses Linux eBPF/bcc. .SH SYNOPSIS .B killsnoop [\-h] [\-x] [-p PID] .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 kernel sys_kill() function using dynamic tracing, and will need updating to match any changes to this function. This makes use of a Linux 4.5 feature (bpf_perf_event_output()); for kernels older than 4.5, see the version under tools/old, which uses an older mechanism. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bcc. .SH OPTIONS .TP \-h Print usage message. .TP \-x Only print failed kill() syscalls. .TP \-p PID Trace this process ID only (filtered in-kernel). .SH EXAMPLES .TP Trace all kill() syscalls: # .B killsnoop .TP Trace only kill() syscalls that failed: # .B killsnoop \-x .TP Trace PID 181 only: # .B killsnoop \-p 181 .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 bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 opensnoop(8), funccount(8) bpfcc-0.12.0/man/man8/klockstat.8000066400000000000000000000127761357404205000163720ustar00rootroot00000000000000.TH klockstat 8 "2019-10-22" "USER COMMANDS" .SH NAME klockstat \- Traces kernel mutex lock events and display locks statistics. Uses Linux eBPF/bcc. .SH SYNOPSIS .B klockstat [\-h] [\-i] [\-n] [\-s] [\-c] [\-S FIELDS] [\-p] [\-t] [\-d DURATION] .SH DESCRIPTION klockstat traces kernel mutex lock events and display locks statistics and displays following data: Caller Avg Spin Count Max spin Total spin psi_avgs_work+0x2e 3675 5 5468 18379 flush_to_ldisc+0x22 2833 2 4210 5667 n_tty_write+0x30c 3914 1 3914 3914 isig+0x5d 2390 1 2390 2390 tty_buffer_flush+0x2a 1604 1 1604 1604 commit_echoes+0x22 1400 1 1400 1400 n_tty_receive_buf_common+0x3b9 1399 1 1399 1399 Caller Avg Hold Count Max hold Total hold flush_to_ldisc+0x22 42558 2 76135 85116 psi_avgs_work+0x2e 14821 5 20446 74106 n_tty_receive_buf_common+0x3b9 12300 1 12300 12300 n_tty_write+0x30c 10712 1 10712 10712 isig+0x5d 3362 1 3362 3362 tty_buffer_flush+0x2a 3078 1 3078 3078 commit_echoes+0x22 3017 1 3017 3017 Every caller to using kernel's mutex is displayed on every line. First portion of lines show the lock acquiring data, showing the amount of time it took to acquired given lock. 'Caller' - symbol acquiring the mutex 'Average Spin' - average time to acquire the mutex 'Count' - number of times mutex was acquired 'Max spin' - maximum time to acquire the mutex 'Total spin' - total time spent in acquiring the mutex Second portion of lines show the lock holding data, showing the amount of time it took to hold given lock. 'Caller' - symbol holding the mutex 'Average Hold' - average time mutex was held 'Count' - number of times mutex was held 'Max hold' - maximum time mutex was held 'Total hold' - total time spent in holding the mutex This works by tracing mutex_lock/unlock kprobes, updating the lock stats in maps and processing them in the python part. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bcc. .SH OPTIONS .TP \-h Print usage message. .TP \-i SEC print summary at this interval (seconds) .TP \-c CALLER print locks taken by given caller .TP \-S FIELDS sort data on specific columns defined by FIELDS string (by default the data is sorted by Max columns) FIELDS string contains 1 or 2 fields describing columns to sort on for both acquiring and holding data. Following fields are available: acq_max for 'Max spin' column acq_total for 'Total spin' column acq_count for 'Count' column hld_max for 'Max hold' column hld_total for 'Total hold' column hld_count for 'Count' column See EXAMPLES. .TP \-n COUNT print COUNT number of locks .TP \-s COUNT print COUNT number of stack entries .TP \-p PID Trace this process ID only (filtered in-kernel). .TP \-t TID Trace this thread ID only (filtered in-kernel). .TP \-d DURATION Total duration of trace in seconds. .TP \-\-stack-storage-size STACK_STORAGE_SIZE Change the number of unique stack traces that can be stored and displayed. .SH EXAMPLES .TP Sort lock acquired results on acquired count: # .B klockstat -S acq_count .TP Sort lock held results on total held time: # .B klockstat -S hld_total .TP Combination of above: # .B klockstat -S acq_count,hld_total .TP Trace system wide: # .B klockstat .TP Trace for 5 seconds only: # .B klockstat -d 5 .TP Display stats every 5 seconds # .B klockstat -i 5 .TP Trace locks for PID 123: # .B klockstat -p 123 .TP Trace locks for PID 321: # .B klockstat -t 321 .TP Display stats only for lock callers with 'pipe_' substring # .B klockstat -c pipe_ .TP Display 3 locks: # .B klockstat -n 3 .TP Display 10 levels of stack for the most expensive lock: # .B klockstat -n 1 -s 10 Tracing lock events... Hit Ctrl-C to end. ^C Caller Avg Spin Count Max spin Total spin pipe_wait+0xa9 670 397691 17273 266775437 pipe_wait+0xa9 pipe_read+0x206 new_sync_read+0x12a vfs_read+0x9d ksys_read+0x5f do_syscall_64+0x5b entry_SYSCALL_64_after_hwframe+0x44 Caller Avg Hold Count Max hold Total hold flush_to_ldisc+0x22 28381 3 65484 85144 flush_to_ldisc+0x22 process_one_work+0x1b0 worker_thread+0x50 kthread+0xfb ret_from_fork+0x35 .SH SOURCE This is from bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 CREDITS This tool is based on work of David Valin and his script. .SH AUTHOR Jiri Olsa bpfcc-0.12.0/man/man8/llcstat.8000066400000000000000000000035101357404205000160230ustar00rootroot00000000000000.TH llcstat 8 "2015-08-18" "USER COMMANDS" .SH NAME llcstat \- Summarize CPU cache references and misses by process. Uses Linux eBPF/bcc. .SH SYNOPSIS .B llcstat [\-h] [\-c SAMPLE_PERIOD] [duration] .SH DESCRIPTION llcstat instruments CPU cache references and cache misses system-side, and summarizes them by PID and CPU. These events have different meanings on different architecture. For x86-64, they mean misses and references to LLC. This can be useful to locate and debug performance issues caused by cache hit rate. This works by sampling corresponding events defined in uapi/linux/perf_event.h, namely PERF_COUNT_HW_CACHE_REFERENCES and PERF_COUNT_HW_CACHE_MISSES, using BPF perf event tracing. Upon each sampled event, the attached BPF program records the PID and CPU ID on which the event happened, and stores it in table. This makes use of a Linux 4.9 feature (BPF_PROG_TYPE_PERF_EVENT). Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bcc. .SH OPTIONS .TP \-h Print usage message. .TP \-c SAMPLE_PERIOD Sample one in this many cache reference and cache miss events. .TP duration Duration to trace, in seconds. .SH EXAMPLES .TP Sample one in 100 events, trace for 20 seconds: # .B llcstat -c 100 20 .SH FIELDS .TP PID Process ID .TP NAME Process name .TP CPU CPU ID .TP REFERENCE Number of cache reference events .TP MISS Number of cache miss events .TP HIT% Cache hit ratio .SH SOURCE This is from bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 Teng Qin .SH SEE ALSO .TP Perf can be used as a generic event counter tool. An example for LLC: # .B perf top -e cache-misses -e cache-references -a -ns pid,cpu,comm bpfcc-0.12.0/man/man8/mdflush.8000066400000000000000000000027171357404205000160270ustar00rootroot00000000000000.TH pidpersec 8 "2016-02-13" "USER COMMANDS" .SH NAME mdflush \- Trace md flush events. Uses Linux eBPF/bcc. .SH SYNOPSIS .B mdflush .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 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 bcc. .SH EXAMPLES .TP Trace md flush events: # .B mdflush .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 bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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) bpfcc-0.12.0/man/man8/memleak.8000066400000000000000000000111561357404205000157750ustar00rootroot00000000000000.TH memleak 8 "2016-01-14" "USER COMMANDS" .SH NAME memleak \- Print a summary of outstanding allocations and their call stacks to detect memory leaks. Uses Linux eBPF/bcc. .SH SYNOPSIS .B memleak [-h] [-p PID] [-t] [-a] [-o OLDER] [-c COMMAND] [--combined-only] [-s SAMPLE_RATE] [-T TOP] [-z MIN_SIZE] [-Z MAX_SIZE] [-O OBJ] [INTERVAL] [COUNT] .SH DESCRIPTION memleak traces and matches memory allocation and deallocation requests, and collects call stacks for each allocation. memleak can then print a summary of which call stacks performed allocations that weren't subsequently freed. When tracing a specific process, memleak instruments a list of allocation functions from libc, specifically: malloc, calloc, realloc, posix_memalign, valloc, memalign, pvalloc, aligned_alloc, and free. When tracing all processes, memleak instruments kmalloc/kfree, kmem_cache_alloc/kmem_cache_free, and also page allocations made by get_free_pages/free_pages. memleak may introduce significant overhead when tracing processes that allocate and free many blocks very quickly. See the OVERHEAD section below. This tool only works on Linux 4.6+. Stack traces are obtained using the new BPF_STACK_TRACE` APIs. For kernels older than 4.6, see the version under tools/old. Kernel memory allocations are intercepted through tracepoints, which are available on Linux 4.7+. .SH REQUIREMENTS CONFIG_BPF and bcc. .SH OPTIONS .TP \-h Print usage message. .TP \-p PID Trace this process ID only (filtered in-kernel). This traces libc allocator. .TP \-t Print a trace of all allocation and free requests and results. .TP \-a Print a list of allocations that weren't freed (and their sizes) in addition to their call stacks. .TP \-o OLDER Print only allocations older than OLDER milliseconds. Useful to remove false positives. The default value is 500 milliseconds. .TP \-c COMMAND Run the specified command and trace its allocations only. This traces libc allocator. .TP \-\-combined-only Use statistics precalculated in kernel space. Amount of data to be pulled from kernel significantly decreases, at the cost of losing capabilities of time-based false positives filtering (\-o). .TP \-s SAMPLE_RATE Record roughly every SAMPLE_RATE-th allocation to reduce overhead. .TP \-t TOP Print only the top TOP stacks (sorted by size). The default value is 10. .TP \-z MIN_SIZE Capture only allocations that are larger than or equal to MIN_SIZE bytes. .TP \-Z MAX_SIZE Capture only allocations that are smaller than or equal to MAX_SIZE bytes. .TP \-O OBJ Attach to allocation functions in specified object instead of resolving libc. Ignored when kernel allocations are profiled. .TP INTERVAL Print a summary of outstanding allocations and their call stacks every INTERVAL seconds. The default interval is 5 seconds. .TP COUNT Print the outstanding allocations summary COUNT times and then exit. .SH EXAMPLES .TP Print outstanding kernel allocation stacks every 3 seconds: # .B memleak 3 .TP Print user outstanding allocation stacks and allocation details for the process 1005: # .B memleak -p 1005 -a .TP Sample roughly every 5th allocation (~20%) of the call stacks and print the top 5 stacks 10 times before quitting. # .B memleak -s 5 --top=5 10 .TP Run ./allocs and print outstanding allocation stacks for that process: # .B memleak -c "./allocs" .TP Capture only allocations between 16 and 32 bytes in size: # .B memleak -z 16 -Z 32 .SH OVERHEAD memleak can have significant overhead if the target process or kernel performs allocations at a very high rate. Pathological cases may exhibit up to 100x degradation in running time. Most of the time, however, memleak shouldn't cause a significant slowdown. You can use the \-s switch to reduce the overhead further by capturing only every N-th allocation. The \-z and \-Z switches can also reduce overhead by capturing only allocations of specific sizes. Additionally, option \-\-combined-only saves processing time by reusing already calculated allocation statistics from kernel. It's faster, but lacks information about particular allocations. To determine the rate at which your application is calling malloc/free, or the rate at which your kernel is calling kmalloc/kfree, place a probe with perf and collect statistics. For example, to determine how many calls to __kmalloc are placed in a typical period of 10 seconds: # .B perf probe '__kmalloc' # .B perf stat -a -e 'probe:__kmalloc' -- sleep 10 .SH SOURCE This is from bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 Sasha Goldshtein bpfcc-0.12.0/man/man8/mountsnoop.8000066400000000000000000000027201357404205000166000ustar00rootroot00000000000000.TH mountsnoop 8 "2016-10-14" "USER COMMANDS" .SH NAME mountsnoop \- Trace mount() and umount() syscalls. Uses Linux eBPF/bcc. .SH SYNOPSIS .B mountsnoop .SH DESCRIPTION mountsnoop traces the mount() and umount() syscalls, showing which processes are mounting and unmounting filesystems in what mount namespaces. This can be useful for troubleshooting system and container setup. This works by tracing the kernel sys_mount() and sys_umount() functions using dynamic tracing, and will need updating to match any changes to this function. This makes use of a Linux 4.4 feature (bpf_perf_event_output()). Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bcc. .SH FIELDS .TP COMM Process name .TP PID Process ID .TP TID Thread ID .TP MNT_NS Mount namespace inode number .TP CALL System call, arguments, and return value .SH OVERHEAD This traces the kernel mount and umount functions and prints output for each event. As the rate of these calls is generally expected to be very low, the overhead is also expected to be negligible. If your system calls mount() and umount() at a high rate, then test and understand overhead before use. .SH SOURCE This is from bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 Omar Sandoval .SH SEE ALSO mount(2) umount(2) bpfcc-0.12.0/man/man8/mysqld_qslower.8000066400000000000000000000035531357404205000174510ustar00rootroot00000000000000.TH mysqld_qslower 8 "2016-08-01" "USER COMMANDS" .SH NAME mysqld_qslower \- Trace MySQL server queries slower than a threshold. .SH SYNOPSIS .B mysqld_qslower PID [min_ms] .SH DESCRIPTION This traces queries served by a MySQL server, and prints those that exceed a custom latency (query duration) threshold. By default, a minimum threshold of 1 millisecond is used. If a threshold of 0 is used, all queries are printed. This uses User Statically-Defined Tracing (USDT) probes, a feature added to MySQL for DTrace support, but which may not be enabled on a given MySQL installation. See requirements. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF, bcc, and MySQL server with USDT probe support (when configuring the build: \-DENABLE_DTRACE=1). .SH OPTIONS PID Trace this mysqld PID. .TP min_ms Minimum query latency (duration) to trace, in milliseconds. Default is 1 ms. .SH EXAMPLES .TP Trace MySQL server queries slower than 1 ms for PID 1981: # .B mysqld_qslower 1981 .TP Trace slower than 10 ms for PID 1981: # .B mysqld_qslower 1981 10 .SH FIELDS .TP TIME(s) Time of query start, in seconds. .TP PID Process ID of the traced server. .TP MS Milliseconds for the query, from start to end. .TP QUERY Query string, truncated to 128 characters. .SH OVERHEAD This adds low-overhead instrumentation to MySQL queries, and only emits output data from kernel to user-level if they query exceeds the threshold. If the server query rate is less than 1,000/sec, the overhead is expected to be negligible. If the query rate is higher, test to gauge overhead. .SH SOURCE This is from bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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) bpfcc-0.12.0/man/man8/nfsdist.8000066400000000000000000000036121357404205000160320ustar00rootroot00000000000000.TH nfsdist 8 "2017-09-08" "USER COMMANDS" .SH NAME nfsdist \- Summarize NFS operation latency. Uses Linux eBPF/bcc. .SH SYNOPSIS .B nfsdist [\-h] [\-T] [\-m] [\-p PID] [interval] [count] .SH DESCRIPTION This tool summarizes time (latency) spent in common NFS file operations: reads, writes, opens, and getattrs, 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 nfs_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 bcc. .SH OPTIONS .TP \-h Print usage message. .TP \-T Don't include timestamps on interval output. .TP \-m Output in milliseconds. .TP \-p PID Trace this PID only. .SH EXAMPLES .TP Trace NFS operation time, and print a summary on Ctrl-C: # .B nfsdist .TP Trace PID 181 only: # .B nfsdist -p 181 .TP Print 1 second summaries, 10 times: # .B nfsdist 1 10 .TP 1 second summaries, printed in milliseconds # .B nfsdist \-m 1 .SH FIELDS .TP msecs Range of milliseconds for this bucket. .TP usecs Range of microseconds for this bucket. .TP count Number of operations in this time range. .TP distribution ASCII representation of the distribution (the count column). .SH OVERHEAD This adds low-overhead instrumentation to these NFS 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 bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 Samuel Nair .SH SEE ALSO nfsslower(8) bpfcc-0.12.0/man/man8/nfsslower.8000066400000000000000000000071611357404205000164050ustar00rootroot00000000000000.TH nfsslower 8 "2017-09-01" "USER COMMANDS" .SH NAME nfsslower \- Trace slow NFS file operations, with per-event details. .SH SYNOPSIS .B nfsslower [\-h] [\-j] [\-p PID] [min_ms] .SH DESCRIPTION This tool traces common NFSv3 & NFSv4 file operations: reads, writes, opens, and getattrs. It measures the time spent in these operations, and prints details for each that exceeded a threshold. WARNING: See the OVERHEAD section. By default, a minimum millisecond threshold of 10 is used. If a threshold of 0 is used, all events are printed (warning: verbose). Since this works by tracing the nfs_file_operations interface functions, it will need updating to match any changes to these functions. This tool uses kprobes to instrument the kernel for entry and exit information, in the future a preferred way would be to use tracepoints. Currently there aren't any tracepoints available for nfs_read_file, nfs_write_file and nfs_open_file, nfs_getattr does have entry and exit tracepoints but we chose to use kprobes for consistency Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bcc. .SH OPTIONS \-p PID Trace this PID only. .TP \-j Trace output in CSV format. .TP min_ms Minimum I/O latency (duration) to trace, in milliseconds. Default is 10 ms. .SH EXAMPLES .TP Trace synchronous file reads and writes slower than 10 ms: # .B nfsslower .TP Trace slower than 1 ms: # .B nfsslower 1 .TP Trace slower than 1 ms, and output just the fields in parsable format (CSV): # .B nfsslower \-j 1 .TP Trace all file reads and writes (warning: the output will be verbose): # .B nfsslower 0 .TP Trace slower than 1 ms, for PID 181 only: # .B nfsslower \-p 181 1 .SH FIELDS .TP TIME(s) Time of I/O completion since the first I/O seen, in seconds. .TP COMM Process name. .TP PID Process ID. .TP T Type of operation. R == read, W == write, O == open, G == getattr. .TP OFF_KB File offset for the I/O, in Kbytes. .TP BYTES Size of I/O, in bytes. .TP LAT(ms) Latency (duration) of I/O, measured from when it was issued by VFS to the filesystem, to when it completed. This time is inclusive of RPC latency, network latency, cache lookup, remote fileserver processing latency, etc. Its a more accurate measure of the latency suffered by applications performing NFS read/write calls to a fileserver. .TP FILENAME A cached kernel file name (comes from dentry->d_name.name). .TP ENDTIME_us Completion timestamp, microseconds (\-j only). .TP OFFSET_b File offset, bytes (\-j only). .TP LATENCY_us Latency (duration) of the I/O, in microseconds (\-j only). .SH OVERHEAD This adds low-overhead instrumentation to NFS operations, including reads and writes from the file system cache. Such read, writes and particularly getattrs can be very frequent (depending on the workload; eg, 1M/sec), at which point the overhead of this tool (even if it prints no "slower" events) can begin to become significant. Measure and quantify before use. If this continues to be a problem, consider switching to a tool that prints in-kernel summaries only. This tool has been tested with NFSv3 & NVSv4, but it might work with NFSv{1,2}, since it is tracing the generic functions from nfs_file_operations. .PP Note that the overhead of this tool should be less than fileslower(8), as this tool targets NFS functions only, and not all file read/write paths. .SH SOURCE This is from bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc distribution for a companion nfsslower_examples.txt file containing example usage, output, and commentary for this tool. .SH OS Linux .SH STABILITY Unstable - in development. .SH AUTHOR Samuel Nair .SH SEE ALSO biosnoop(8), funccount(8), fileslower(8) bpfcc-0.12.0/man/man8/nodegc.8000077700000000000000000000000001357404205000164572ugc.8ustar00rootroot00000000000000bpfcc-0.12.0/man/man8/nodestat.8000077700000000000000000000000001357404205000174232ustat.8ustar00rootroot00000000000000bpfcc-0.12.0/man/man8/offcputime.8000066400000000000000000000076021357404205000165240ustar00rootroot00000000000000.TH offcputime 8 "2016-01-14" "USER COMMANDS" .SH NAME offcputime \- Summarize off-CPU time by kernel stack trace. Uses Linux eBPF/bcc. .SH SYNOPSIS .B offcputime [\-h] [\-p PID | \-t TID | \-u | \-k] [\-U | \-K] [\-d] [\-f] [\-\-stack\-storage\-size STACK_STORAGE_SIZE] [\-m MIN_BLOCK_TIME] [\-M MAX_BLOCK_TIME] [\-\-state STATE] [duration] .SH DESCRIPTION This program shows stack traces and task names that were blocked and "off-CPU", and the total duration they were not running: their "off-CPU time". It works by tracing when threads block and when they return to CPU, measuring both the time they were off-CPU and the blocked stack trace and the task name. This data is summarized in the kernel using an eBPF map, and by summing the off-CPU time by unique stack trace and task name. The output summary will help you identify reasons why threads were blocking, and quantify the time they were off-CPU. This spans all types of blocking activity: disk I/O, network I/O, locks, page faults, involuntary context switches, etc. This is complementary to CPU profiling (e.g., CPU flame graphs) which shows the time spent on-CPU. This shows the time spent off-CPU, and the output, especially the -f format, can be used to generate an "off-CPU time flame graph". See http://www.brendangregg.com/FlameGraphs/offcpuflamegraphs.html This tool only works on Linux 4.6+. It uses the new `BPF_STACK_TRACE` table APIs to generate the in-kernel stack traces. For kernels older than 4.6, see the version under tools/old. Note: this tool only traces off-CPU times that began and ended while tracing. .SH REQUIREMENTS CONFIG_BPF and bcc. .SH OPTIONS .TP \-h Print usage message. .TP \-p PID Trace this process ID only (filtered in-kernel). .TP \-t TID Trace this thread ID only (filtered in-kernel). .TP \-u Only trace user threads (no kernel threads). .TP \-k Only trace kernel threads (no user threads). .TP \-U Show stacks from user space only (no kernel space stacks). .TP \-K Show stacks from kernel space only (no user space stacks). .TP \-d Insert delimiter between kernel/user stacks. .TP \-f Print output in folded stack format. .TP \-\-stack-storage-size STACK_STORAGE_SIZE Change the number of unique stack traces that can be stored and displayed. .TP \-m MIN_BLOCK_TIME The minimum time in microseconds over which we store traces (default 1) .TP \-M MAX_BLOCK_TIME The maximum time in microseconds under which we store traces (default U64_MAX) .TP \-\-state Filter on this thread state bitmask (eg, 2 == TASK_UNINTERRUPTIBLE). See include/linux/sched.h for states. .TP duration Duration to trace, in seconds. .SH EXAMPLES .TP Trace all thread blocking events, and summarize (in-kernel) by kernel stack trace and total off-CPU time: # .B offcputime .TP Trace for 5 seconds only: # .B offcputime 5 .TP Trace for 5 seconds, and emit output in folded stack format (suitable for flame graphs): # .B offcputime -f 5 .TP Trace PID 185 only: # .B offcputime -p 185 .SH OVERHEAD This summarizes unique stack traces in-kernel for efficiency, allowing it to trace a higher rate of events than methods that post-process in user space. The stack trace and time data is only copied to user space once, when the output is printed. While these techniques greatly lower overhead, scheduler events are still a high frequency event, as they can exceed 1 million events per second, and so caution should still be used. Test before production use. If the overhead is still a problem, take a look at the MINBLOCK_US tunable in the code. If your aim is to chase down longer blocking events, then this could be increased to filter shorter blocking events, further lowering overhead. .SH SOURCE This is from bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 stackcount(8) bpfcc-0.12.0/man/man8/offwaketime.8000066400000000000000000000073631357404205000166700ustar00rootroot00000000000000.TH offwaketime 8 "2016-01-30" "USER COMMANDS" .SH NAME offwaketime \- Summarize blocked time by off-CPU stack + waker stack. Uses Linux eBPF/bcc. .SH SYNOPSIS .B offwaketime [\-h] [\-p PID | \-t TID | \-u | \-k] [\-U | \-K] [\-f] [\-\-stack-storage-size STACK_STORAGE_SIZE] [\-m MIN_BLOCK_TIME] [\-M MAX_BLOCK_TIME] [duration] .SH DESCRIPTION This program shows kernel stack traces and task names that were blocked and "off-CPU", along with the stack traces and task names for the threads that woke them, and the total elapsed time from when they blocked to when they were woken up. This combines the summaries from both the offcputime and wakeuptime tools. The time measurement will be very similar to off-CPU time, however, off-CPU time may include a little extra time spent waiting on a run queue to be scheduled. The combined stacks, task names, and total time is summarized in kernel context for efficiency, using an eBPF map. The output summary will further help you identify reasons why threads were blocking, and quantify the time from when they were blocked to woken up. This spans all types of blocking activity: disk I/O, network I/O, locks, page faults, swapping, sleeping, involuntary context switches, etc. This is complementary to CPU profiling (e.g., CPU flame graphs) which shows the time spent on-CPU. This shows the time spent blocked off-CPU, and the output, especially the -f format, can be used to generate an "off-wake time flame graph". See http://www.brendangregg.com/FlameGraphs/offcpuflamegraphs.html .SH REQUIREMENTS CONFIG_BPF and bcc. .SH OPTIONS .TP \-h Print usage message. .TP \-f Print output in folded stack format. .TP \-p PID Trace this process ID only (filtered in-kernel). .TP \-t TID Trace this thread ID only (filtered in-kernel). .TP \-u Only trace user threads (no kernel threads). .TP \-k Only trace kernel threads (no user threads). .TP \-U Show stacks from user space only (no kernel space stacks). .TP \-K Show stacks from kernel space only (no user space stacks). .TP \-\-stack-storage-size STACK_STORAGE_SIZE Change the number of unique stack traces that can be stored and displayed. .TP duration Duration to trace, in seconds. .TP \-m MIN_BLOCK_TIME The amount of time in microseconds over which we store traces (default 1) .TP \-M MAX_BLOCK_TIME The amount of time in microseconds under which we store traces (default U64_MAX) .SH EXAMPLES .TP Trace all thread blocking events, and summarize (in-kernel) by user and kernel off-CPU stack trace, waker stack traces, task names, and total blocked time: # .B offwaketime .TP Trace for 5 seconds only: # .B offwaketime 5 .TP Trace for 5 seconds, and emit output in folded stack format (suitable for flame graphs), user-mode threads only: # .B offwaketime -fu 5 .TP Trace PID 185 only: # .B offwaketime -p 185 .SH OVERHEAD This summarizes unique stack trace pairs in-kernel for efficiency, allowing it to trace a higher rate of events than methods that post-process in user space. The stack trace and time data is only copied to user space once, when the output is printed. While these techniques greatly lower overhead, scheduler events are still a high frequency event, as they can exceed 1 million events per second, and so caution should still be used. Test before production use. If the overhead is still a problem, take a look at the min block option. If your aim is to chase down longer blocking events, then this could be increased to filter shorter blocking events, further lowering overhead. .SH SOURCE This is from bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 offcputime(8), wakeuptime(8) bpfcc-0.12.0/man/man8/oomkill.8000066400000000000000000000031411357404205000160230ustar00rootroot00000000000000.TH oomkill 8 "2016-02-09" "USER COMMANDS" .SH NAME oomkill \- Trace oom_kill_process(). Uses Linux eBPF/bcc. .SH SYNOPSIS .B oomkill .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. 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 bcc. .SH EXAMPLES .TP Trace OOM kill events: # .B oomkill .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 bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 memleak(8) bpfcc-0.12.0/man/man8/opensnoop.8000066400000000000000000000056161357404205000164060ustar00rootroot00000000000000.TH opensnoop 8 "2015-08-18" "USER COMMANDS" .SH NAME opensnoop \- Trace open() syscalls. Uses Linux eBPF/bcc. .SH SYNOPSIS .B opensnoop.py [\-h] [\-T] [\-U] [\-x] [\-p PID] [\-t TID] [\-u UID] [\-d DURATION] [\-n NAME] [\-e] [\-f FLAG_FILTER] .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 kernel sys_open() function using dynamic tracing, and will need updating to match any changes to this function. This makes use of a Linux 4.5 feature (bpf_perf_event_output()); for kernels older than 4.5, see the version under tools/old, which uses an older mechanism. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bcc. .SH OPTIONS .TP \-h Print usage message. .TP \-T Include a timestamp column. .TP \-U Show UID. .TP \-x Only print failed opens. .TP \-p PID Trace this process ID only (filtered in-kernel). .TP \-t TID Trace this thread ID only (filtered in-kernel). .TP \-u UID Trace this UID only (filtered in-kernel). .TP \-d DURATION Total duration of trace in seconds. .TP \-n name Only print processes where its name partially matches 'name' .TP \-e Show extended fields. .TP \-f FLAG Filter on open() flags, e.g., O_WRONLY. .SH EXAMPLES .TP Trace all open() syscalls: # .B opensnoop .TP Trace all open() syscalls, for 10 seconds only: # .B opensnoop -d 10 .TP Trace all open() syscalls, and include timestamps: # .B opensnoop \-T .TP Show UID: # .B opensnoop \-U .TP Trace only open() syscalls that failed: # .B opensnoop \-x .TP Trace PID 181 only: # .B opensnoop \-p 181 .TP Trace UID 1000 only: # .B opensnoop \-u 1000 .TP Trace all open() syscalls from processes where its name partially matches 'ed': # .B opensnoop \-n ed .TP Show extended fields: # .B opensnoop \-e .TP Only print calls for writing: # .B opensnoop \-f O_WRONLY \-f O_RDWR .SH FIELDS .TP TIME(s) Time of the call, in seconds. .TP UID User ID .TP 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 FLAGS Flags passed to open(2), in octal .TP PATH Open path .SH OVERHEAD This traces the kernel open function 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 bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 funccount(1) bpfcc-0.12.0/man/man8/perlcalls.8000077700000000000000000000000001357404205000177062ucalls.8ustar00rootroot00000000000000bpfcc-0.12.0/man/man8/perlflow.8000077700000000000000000000000001357404205000174302uflow.8ustar00rootroot00000000000000bpfcc-0.12.0/man/man8/perlstat.8000077700000000000000000000000001357404205000174402ustat.8ustar00rootroot00000000000000bpfcc-0.12.0/man/man8/phpcalls.8000077700000000000000000000000001357404205000175332ucalls.8ustar00rootroot00000000000000bpfcc-0.12.0/man/man8/phpflow.8000077700000000000000000000000001357404205000172552uflow.8ustar00rootroot00000000000000bpfcc-0.12.0/man/man8/phpstat.8000077700000000000000000000000001357404205000172652ustat.8ustar00rootroot00000000000000bpfcc-0.12.0/man/man8/pidpersec.8000066400000000000000000000023631357404205000163400ustar00rootroot00000000000000.TH pidpersec 8 "2015-08-18" "USER COMMANDS" .SH NAME pidpersec \- Count new processes (via fork()). Uses Linux eBPF/bcc. .SH SYNOPSIS .B pidpersec .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 kernel sched_fork() function using dynamic tracing, 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 Count new processes created each second: # .B pidpersec .SH OVERHEAD This traces the kernel fork function, 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 bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 top(1) bpfcc-0.12.0/man/man8/profile.8000066400000000000000000000114541357404205000160230ustar00rootroot00000000000000.TH profile 8 "2016-07-17" "USER COMMANDS" .SH NAME profile \- Profile CPU usage by sampling stack traces. Uses Linux eBPF/bcc. .SH SYNOPSIS .B profile [\-adfh] [\-p PID | \-L TID] [\-U | \-K] [\-F FREQUENCY | \-c COUNT] .B [\-\-stack\-storage\-size COUNT] [duration] .SH DESCRIPTION This is a CPU profiler. It works by taking samples of stack traces at timed intervals. It will help you understand and quantify CPU usage: which code is executing, and by how much, including both user-level and kernel code. By default this samples at 49 Hertz (samples per second), across all CPUs. This frequency can be tuned using a command line option. The reason for 49, and not 50, is to avoid lock-step sampling. This is also an efficient profiler, as stack traces are frequency counted in kernel context, rather than passing each stack to user space for frequency counting there. Only the unique stacks and counts are passed to user space at the end of the profile, greatly reducing the kernel<->user transfer. .SH REQUIREMENTS CONFIG_BPF and bcc. This also requires Linux 4.9+ (BPF_PROG_TYPE_PERF_EVENT support). See tools/old for an older version that may work on Linux 4.6 - 4.8. .SH OPTIONS .TP \-h Print usage message. .TP \-p PID Trace this process ID only (filtered in-kernel). .TP \-L TID Trace this thread ID only (filtered in-kernel). .TP \-F frequency Frequency to sample stacks. .TP \-c count Sample stacks every one in this many events. .TP \-f Print output in folded stack format. .TP \-d Include an output delimiter between kernel and user stacks (either "--", or, in folded mode, "-"). .TP \-U Show stacks from user space only (no kernel space stacks). .TP \-K Show stacks from kernel space only (no user space stacks). .TP \-I Include CPU idle stacks (by default these are excluded). .TP \-\-stack-storage-size COUNT The maximum number of unique stack traces that the kernel will count (default 16384). If the sampled count exceeds this, a warning will be printed. .TP \-C cpu Collect stacks only from specified cpu. .TP duration Duration to trace, in seconds. .SH EXAMPLES .TP Profile (sample) stack traces system-wide at 49 Hertz (samples per second) until Ctrl-C: # .B profile .TP Profile for 5 seconds only: # .B profile 5 .TP Profile at 99 Hertz for 5 seconds only: # .B profile -F 99 5 .TP Profile 1 in a million events for 5 seconds only: # .B profile -c 1000000 5 .TP Profile process with PID 181 only: # .B profile -p 181 .TP Profile thread with TID 181 only: # .B profile -L 181 .TP Profile for 5 seconds and output in folded stack format (suitable as input for flame graphs), including a delimiter between kernel and user stacks: # .B profile -df 5 .TP Profile kernel stacks only: # .B profile -K .SH DEBUGGING See "[unknown]" frames with bogus addresses? This can happen for different reasons. Your best approach is to get Linux perf to work first, and then to try this tool. Eg, "perf record \-F 49 \-a \-g \-\- sleep 1; perf script", and to check for unknown frames there. The most common reason for "[unknown]" frames is that the target software has not been compiled with frame pointers, and so we can't use that simple method for walking the stack. The fix in that case is to use software that does have frame pointers, eg, gcc -fno-omit-frame-pointer, or Java's -XX:+PreserveFramePointer. Another reason for "[unknown]" frames is JIT compilers, which don't use a traditional symbol table. The fix in that case is to populate a /tmp/perf-PID.map file with the symbols, which this tool should read. How you do this depends on the runtime (Java, Node.js). If you seem to have unrelated samples in the output, check for other sampling or tracing tools that may be running. The current version of this tool can include their events if profiling happened concurrently. Those samples may be filtered in a future version. .SH OVERHEAD This is an efficient profiler, as stack traces are frequency counted in kernel context, and only the unique stacks and their counts are passed to user space. Contrast this with the current "perf record -F 99 -a" method of profiling, which writes each sample to user space (via a ring buffer), and then to the file system (perf.data), which must be post-processed. This uses perf_event_open to setup a timer which is instrumented by BPF, and for efficiency it does not initialize the perf ring buffer, so the redundant perf samples are not collected. It's expected that the overhead while sampling at 49 Hertz (the default), across all CPUs, should be negligible. If you increase the sample rate, the overhead might begin to be measurable. .SH SOURCE This is from bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 offcputime(8) bpfcc-0.12.0/man/man8/pythoncalls.8000077700000000000000000000000001357404205000202652ucalls.8ustar00rootroot00000000000000bpfcc-0.12.0/man/man8/pythonflow.8000077700000000000000000000000001357404205000200072uflow.8ustar00rootroot00000000000000bpfcc-0.12.0/man/man8/pythongc.8000077700000000000000000000000001357404205000170532ugc.8ustar00rootroot00000000000000bpfcc-0.12.0/man/man8/pythonstat.8000077700000000000000000000000001357404205000200172ustat.8ustar00rootroot00000000000000bpfcc-0.12.0/man/man8/reset-trace.8000066400000000000000000000032551357404205000166010ustar00rootroot00000000000000.TH reset-trace 8 "2016-10-18" "USER COMMANDS" .SH NAME reset-trace \- reset the state of tracing. .SH SYNOPSIS .B reset-trace [\-F] [\-h] [\-q] [\-v] .SH DESCRIPTION You will probably never need this tool. If you kill \-9 a bcc tool (plus other signals, like SIGTERM), or if a bcc tool crashes, then kernel tracing can be left in a semi-enabled state. It's not as bad as it sounds: there may just be overhead for writing to ring buffers that are never read. This tool can be used to clean up the tracing state, and reset and disable active tracing. Make sure no other tracing sessions are active. This tool might stop them from functioning (perhaps ungracefully). This specifically clears the state in at least the following files in /sys/kernel/debug/tracing: kprobe_events, uprobe_events, trace_pipe. Other tracing facilities (ftrace) are checked, and if not in an expected state, a note is printed. All tracing files can be reset with \-F for force, but this will interfere with any other running tracing sessions (eg, ftrace). .SH REQUIREMENTS /sys/kernel/debug mounted as debugfs .SH OPTIONS .TP \-F Force. Will reset all tracing facilities, including those not used by bcc (ftrace). You shouldn't need to use this. .TP \-h USAGE message. .TP \-q Quiet. No output while working. .TP \-v Verbose: print what it is doing. .SH EXAMPLES .TP Reset the state of tracing: # .B reset-trace .TP Verbose: # .B reset-trace \-v .TP .SH SOURCE This is from bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 bpfcc-0.12.0/man/man8/rubycalls.8000077700000000000000000000000001357404205000177252ucalls.8ustar00rootroot00000000000000bpfcc-0.12.0/man/man8/rubyflow.8000077700000000000000000000000001357404205000174472uflow.8ustar00rootroot00000000000000bpfcc-0.12.0/man/man8/rubygc.8000077700000000000000000000000001357404205000165132ugc.8ustar00rootroot00000000000000bpfcc-0.12.0/man/man8/rubyobjnew.8000077700000000000000000000000001357404205000203012uobjnew.8ustar00rootroot00000000000000bpfcc-0.12.0/man/man8/rubystat.8000077700000000000000000000000001357404205000174572ustat.8ustar00rootroot00000000000000bpfcc-0.12.0/man/man8/runqlat.8000066400000000000000000000061511357404205000160470ustar00rootroot00000000000000.TH runqlat 8 "2016-02-07" "USER COMMANDS" .SH NAME runqlat \- Run queue (scheduler) latency as a histogram. .SH SYNOPSIS .B runqlat [\-h] [\-T] [\-m] [\-P] [\-\-pidnss] [\-L] [\-p PID] [interval] [count] .SH DESCRIPTION This measures the time a task spends waiting on a run queue (or equivalent scheduler data structure) for a turn on-CPU, and shows this time as a histogram. This time should be small, but a task may need to wait its turn due to CPU load. The higher the CPU load, the longer a task will generally need to wait its turn. This tool measures two types of run queue latency: 1. The time from a task being enqueued on a run queue to its context switch and execution. This traces ttwu_do_wakeup(), wake_up_new_task() -> finish_task_switch() with either raw tracepoints (if supported) or kprobes and instruments the run queue latency after a voluntary context switch. 2. The time from when a task was involuntary context switched and still in the runnable state, to when it next executed. This is instrumented from finish_task_switch() alone. This tool uses in-kernel eBPF maps for storing timestamps and the histogram, for efficiency. Despite this, the overhead of this tool may become significant for some workloads: see the OVERHEAD section. This works by tracing various kernel scheduler 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 bcc. .SH OPTIONS .TP \-h Print usage message. .TP \-T Include timestamps on output. .TP \-m Output histogram in milliseconds. .TP \-P Print a histogram for each PID. .TP \-\-pidnss Print a histogram for each PID namespace (short for PID namespaces). For container analysis. .TP \-L Print a histogram for each thread ID. .TP \-p PID Only show this PID (filtered in kernel for efficiency). .TP interval Output interval, in seconds. .TP count Number of outputs. .SH EXAMPLES .TP Summarize run queue latency as a histogram: # .B runqlat .TP Print 1 second summaries, 10 times: # .B runqlat 1 10 .TP Print 1 second summaries, using milliseconds as units for the histogram, and include timestamps on output: # .B runqlat \-mT 1 .TP Trace PID 186 only, 1 second summaries: # .B runqlat -P 185 1 .SH FIELDS .TP usecs Microsecond range .TP msecs Millisecond range .TP count How many times a task event fell into this range .TP distribution An ASCII bar chart to visualize the distribution (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 bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 runqlen(8), runqslower(8), pidstat(1) bpfcc-0.12.0/man/man8/runqlen.8000066400000000000000000000037441357404205000160520ustar00rootroot00000000000000.TH runqlen 8 "2016-12-12" "USER COMMANDS" .SH NAME runqlen \- Scheduler run queue length as a histogram. .SH SYNOPSIS .B runqlen [\-h] [\-T] [\-O] [\-C] [interval] [count] .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 bcc. .SH OPTIONS .TP \-h Print usage message. .TP \-T Include timestamps on output. .TP \-O Report run queue occupancy. .TP \-C Report for each CPU. .TP interval Output interval, in seconds. .TP count Number of outputs. .SH EXAMPLES .TP Summarize run queue length as a histogram: # .B runqlen .TP Print 1 second summaries, 10 times: # .B runqlen 1 10 .TP Print output every second, with timestamps, and show each CPU separately: # .B runqlen \-CT 1 .TP Print run queue occupancy every second: # .B runqlen \-O 1 .TP Print run queue occupancy, with timetamps, for each CPU: # .B runqlen \-COT 1 .SH FIELDS .TP runqlen Scheduler run queue length: the number of threads (tasks) waiting to run, (excluding including the currently running task). .TP count Number of samples at this queue length. .TP distribution An ASCII bar chart to visualize the distribution (count column) .SH OVERHEAD This uses sampling at 99 Hertz (on all CPUs), and in-kernel summaries, which should make overhead negligible. This does not trace scheduler events, like runqlen does, which comes at a much higher overhead cost. .SH SOURCE This is from bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 runqlat(8), runqslower(8), pidstat(1) bpfcc-0.12.0/man/man8/runqslower.8000066400000000000000000000050541357404205000166030ustar00rootroot00000000000000.TH runqslower 8 "2016-02-07" "USER COMMANDS" .SH NAME runqslower \- Trace long process scheduling delays. .SH SYNOPSIS .B runqslower [\-p PID] [min_us] .SH DESCRIPTION This measures the time a task spends waiting on a run queue (or equivalent scheduler data structure) for a turn on-CPU, and shows occurrences of time exceeding passed threshold. This time should be small, but a task may need to wait its turn due to CPU load. The higher the CPU load, the longer a task will generally need to wait its turn. This tool measures two types of run queue latency: 1. The time from a task being enqueued on a run queue to its context switch and execution. This traces ttwu_do_wakeup(), wake_up_new_task() -> finish_task_switch() with either raw tracepoints (if supported) or kprobes and instruments the run queue latency after a voluntary context switch. 2. The time from when a task was involuntary context switched and still in the runnable state, to when it next executed. This is instrumented from finish_task_switch() alone. The overhead of this tool may become significant for some workloads: see the OVERHEAD section. This works by tracing various kernel scheduler 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 bcc. .SH OPTIONS .TP \-h Print usage message. .TP \-p PID Only show this PID (filtered in kernel for efficiency). .TP min_us Minimum scheduling delay in microseconds to output. .SH EXAMPLES .TP Show scheduling delays longer than 10ms: # .B runqslower .TP Show scheduling delays longer than 1ms for process with PID 123: # .B runqslower -p 123 1000 .SH FIELDS .TP TIME Time of when scheduling event occurred. .TP COMM Process name. .TP PID Process ID. .TP LAT(us) Scheduling latency from time when task was ready to run to the time it was assigned to a CPU to run. .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 bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 Ivan Babrou .SH SEE ALSO runqlen(8), runqlat(8), pidstat(1) bpfcc-0.12.0/man/man8/shmsnoop.8000066400000000000000000000027151357404205000162310ustar00rootroot00000000000000.TH shmsnoop 8 "2018-09-24" "USER COMMANDS" .SH NAME shmsnoop \- Trace System V shared memory syscalls. Uses Linux eBPF/bcc. .SH SYNOPSIS .B shmsnoop [\-h] [\-T] [\-p] [\-t] [\-d DURATION] [\-n NAME] .SH DESCRIPTION shmsnoop traces System V shared memory syscalls: shmget, shmat, shmdt, shmctl Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bcc. .SH OPTIONS .TP \-h Print usage message. .TP \-T Include a timestamp column. .TP \-p PID Trace this process ID only (filtered in-kernel). .TP \-t TID Trace this thread ID only (filtered in-kernel). .TP \-d DURATION Total duration of trace in seconds. .TP \-n NAME Only print command lines matching this command name (regex) .SH EXAMPLES .TP Trace all shm* syscalls: # .B shmsnoop .TP Trace all shm* syscalls, and include timestamps: # .B shmsnoop \-T .TP Only trace shm* syscalls where the process contains "server": # .B shmsnoop \-n server .SH FIELDS .TP TIME(s) Time of shm syscall return, in seconds. .TP PID Process ID .TP COMM Process/command name. .TP RET Return value of shm syscall. .TP ARGS "arg: value" couples that represent given syscall arguments as described in their manpage .SH SOURCE This is from bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 Jiri Olsa .SH SEE ALSO opensnoop(1) bpfcc-0.12.0/man/man8/slabratetop.8000066400000000000000000000035321357404205000167010ustar00rootroot00000000000000.TH slabratetop 8 "2016-10-17" "USER COMMANDS" .SH NAME slabratetop \- Kernel SLAB/SLUB memory cache allocation rate top. Uses Linux BPF/bcc. .SH SYNOPSIS .B slabratetop [\-h] [\-C] [\-r MAXROWS] [interval] [count] .SH DESCRIPTION This is top for the the rate of kernel SLAB/SLUB memory allocations. It works by tracing kmem_cache_alloc() calls, a commonly used interface for kernel memory allocation (SLAB or SLUB). It summarizes the rate and total bytes allocated of these calls per interval: the activity. Compare this to slabtop(1), which shows the current static volume of the caches. This tool uses kernel dynamic tracing of the kmem_cache_alloc() function. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bcc. .SH OPTIONS .TP \-C Don't clear the screen. .TP \-r MAXROWS Maximum number of rows to print. Default is 20. .TP interval Interval between updates, seconds. .TP count Number of interval summaries. .SH EXAMPLES .TP Summarize active kernel SLAB/SLUB calls (kmem_cache_alloc()), showing the top 20 caches every second: # .B slabratetop .TP Don't clear the screen, and top 8 rows only: # .B slabratetop -Cr 8 .TP 5 second summaries, 10 times only: # .B slabratetop 5 10 .SH FIELDS .TP loadavg: The contents of /proc/loadavg .TP CACHE Kernel cache name. .TP ALLOCS Allocations (number of calls). .TP BYTES Total bytes allocated. .SH OVERHEAD If kmem_cache_alloc() is called at a high rate (eg, >100k/second) the overhead of this tool might begin to be measurable. The rate can be seen in the ALLOCS column of the output. .SH SOURCE This is from bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 slabtop(1) bpfcc-0.12.0/man/man8/sofdsnoop.8000066400000000000000000000024031357404205000163670ustar00rootroot00000000000000.TH SOFDSNOOP 8 "2019-07-29" "USER COMMANDS" .SH NAME sofdsnoop \- traces FDs passed by sockets .SH SYNOPSIS usage: sofdsnoop [\-h] [\-T] [\-p PID] [\-t TID] [\-n NAME] [\-d DURATION] .SH DESCRIPTION Trace file descriptors passed via socket .SS "optional arguments:" .TP \fB\-h\fR, \fB\-\-help\fR show this help message and exit .TP \fB\-T\fR, \fB\-\-timestamp\fR include timestamp on output .TP \fB\-p\fR PID, \fB\-\-pid\fR PID trace this PID only .TP \fB\-t\fR TID, \fB\-\-tid\fR TID trace this TID only .TP \fB\-n\fR NAME, \fB\-\-name\fR NAME only print process names containing this name .TP \fB\-d\fR DURATION, \fB\-\-duration\fR DURATION total duration of trace in seconds .SH EXAMPLES .TP Trace passed file descriptors # .B sofdsnoop .TP Include timestamps # .B sofdsnoop \fB\-T\fR .TP Only trace PID 181 # .B sofdsnoop \fB\-p\fR 181 .TP Only trace TID 123 # .B sofdsnoop \fB\-t\fR 123 .TP Trace for 10 seconds only # .B sofdsnoop \fB\-d\fR 10 .TP Only print process names containing "main" # .B sofdsnoop \fB\-n\fR main .SH SOURCE This is from bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc distribution for a companion _examples.txt file containing example usage, output, and commentary for this tool. .SH OS Linux .SH STABILITY Unstable - in development. bpfcc-0.12.0/man/man8/softirqs.8000066400000000000000000000046651357404205000162430ustar00rootroot00000000000000.TH softirqs 8 "2015-10-20" "USER COMMANDS" .SH NAME softirqs \- Measure soft IRQ (soft interrupt) event time. Uses Linux eBPF/bcc. .SH SYNOPSIS .B softirqs [\-h] [\-T] [\-N] [\-d] [interval] [count] .SH DESCRIPTION This summarizes the time spent servicing soft IRQs (soft interrupts), and can show this time as either totals or histogram distributions. A system-wide summary of this time is shown by the %soft column of mpstat(1), and soft IRQ event counts (but not times) are available in /proc/softirqs. This tool uses the irq:softirq_enter and irq:softirq_exit kernel tracepoints, which is a stable tracing mechanism. BPF programs can attach to tracepoints from Linux 4.7 only. An older version of this tool is available in tools/old, and uses kprobes instead of tracepoints. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bcc. .SH OPTIONS .TP \-h Print usage message. .TP \-T Include timestamps on output. .TP \-N Output in nanoseconds .TP \-d Show IRQ time distribution as histograms .SH EXAMPLES .TP Sum soft IRQ event time until Ctrl-C: # .B softirqs .TP Show soft IRQ event time as histograms: # .B softirqs \-d .TP Print 1 second summaries, 10 times: # .B softirqs 1 10 .TP 1 second summaries, printed in nanoseconds, with timestamps: # .B softirqs \-NT 1 .SH FIELDS .TP SOFTIRQ The kernel function name that performs the soft IRQ action. .TP TOTAL_usecs Total time spent in this soft IRQ function in microseconds. .TP TOTAL_nsecs Total time spent in this soft IRQ function in nanoseconds. .TP usecs Range of microseconds for this bucket. .TP nsecs Range of nanoseconds for this bucket. .TP count Number of soft IRQs in this time range. .TP distribution ASCII representation of the distribution (the count column). .SH OVERHEAD This traces kernel functions and maintains in-kernel counts, which are asynchronously copied to user-space. While the rate of interrupts 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, but could become noticeable for heavy workloads. Measure in a test environment before use. .SH SOURCE This is from bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 AUTHORS Brendan Gregg, Sasha Goldshtein .SH SEE ALSO hardirqs(8) bpfcc-0.12.0/man/man8/solisten.8000066400000000000000000000021541357404205000162200ustar00rootroot00000000000000.TH SOLISTEN 8 "2019-07-29" "USER COMMANDS" .SH NAME solisten \- Trace listening socket .SH SYNOPSIS usage: solisten [\-h] [\-\-show\-netns] [\-p PID] [\-n NETNS] .SH DESCRIPTION All IPv4 and IPv6 listen attempts are traced, even if they ultimately fail or the listening program is not willing to accept(). .SS "optional arguments:" .TP \fB\-h\fR, \fB\-\-help\fR show this help message and exit .TP \fB\-\-show\-netns\fR show network namespace .TP \fB\-p\fR PID, \fB\-\-pid\fR PID trace this PID only .TP \fB\-n\fR NETNS, \fB\-\-netns\fR NETNS trace this Network Namespace only .SH EXAMPLES .TP Stream socket listen: # .B solisten .TP Stream socket listen for specified PID only # .B solisten \-p 1234 .TP Stream socket listen for the specified network namespace ID only # .B solisten \-\-netns 4242 .TP Show network ns ID (useful for containers) # .B solisten \-\-show\-netns .SH SOURCE This is from bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc distribution for a companion _examples.txt file containing example usage, output, and commentary for this tool. .SH OS Linux .SH STABILITY Unstable - in development. bpfcc-0.12.0/man/man8/spfdsnoop.8000066400000000000000000000032331357404205000163720ustar00rootroot00000000000000.TH sofdsnoop 8 "2018-11-08" "USER COMMANDS" .SH NAME sofdsnoop \- Trace FDs passed through unix sockets. Uses Linux eBPF/bcc. .SH SYNOPSIS .B sofdsnoop [-h] [-T] [-p PID] [-t TID] [-n NAME] [-d DURATION] .SH DESCRIPTION sofdsnoop traces FDs passed through unix sockets Every file descriptor that is passed via unix sockets os displayed on separate line together with process info (TID/COMM columns), ACTION details (SEND/RECV), file descriptor number (FD) and its translation to file if available (NAME). Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bcc. .SH OPTIONS .TP \-h Print usage message. .TP \-T Include a timestamp column. .TP \-p PID Trace this process ID only (filtered in-kernel). .TP \-t TID Trace this thread ID only (filtered in-kernel). .TP \-d DURATION Total duration of trace in seconds. .TP \-n NAME Only print command lines matching this command name (regex) .SH EXAMPLES .TP Trace all sockets: # .B sofdsnoop .TP Trace all sockets, and include timestamps: # .B sofdsnoop \-T .TP Only trace sockets where the process contains "server": # .B sofdsnoop \-n server .SH FIELDS .TP TIME(s) Time of SEDN/RECV actions, in seconds. .TP ACTION Operation on the fd SEND/RECV. .TP TID Process TID .TP COMM Parent process/command name. .TP SOCKET The socket carrier. .TP FD file descriptor number .TP NAME file name for SEND lines .SH SOURCE This is from bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 Jiri Olsa .SH SEE ALSO opensnoop(1) bpfcc-0.12.0/man/man8/sslsniff.8000066400000000000000000000023021357404205000162020ustar00rootroot00000000000000.TH sslsniff 8 "2016-08-16" "USER COMMANDS" .SH NAME sslsniff \- Print data passed to OpenSSL, GnuTLS or NSS. Uses Linux eBPF/bcc. .SH SYNOPSIS .B sslsniff [-h] [-p PID] [-c COMM] [-o] [-g] [-n] [-d] .SH DESCRIPTION sslsniff prints data sent to write/send and read/recv functions of OpenSSL, GnuTLS and NSS, allowing us to read plain text content before encryption (when writing) and after decryption (when reading). This works reading the second parameter of both functions (*buf). Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bcc. .SH EXAMPLES .TP Print all calls to SSL write/send and read/recv system-wide: # .B sslsniff .SH FIELDS .TP FUNC Which function is being called (write/send or read/recv) .TP TIME Time of the command, in seconds. .TP COMM Entered command. .TP PID Process ID calling SSL. .TP LEN Bytes written or read by SSL functions. .SH SOURCE This is from bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 AUTHORS Adrian Lopez and Mark Drayton .SH SEE ALSO trace(8) bpfcc-0.12.0/man/man8/stackcount.8000066400000000000000000000102541357404205000165360ustar00rootroot00000000000000.TH stackcount 8 "2016-01-14" "USER COMMANDS" .SH NAME stackcount \- Count function calls and their stack traces. Uses Linux eBPF/bcc. .SH SYNOPSIS .B stackcount [\-h] [\-p PID] [\-c CPU] [\-i INTERVAL] [\-D DURATION] [\-T] [\-r] [\-s] [\-P] [\-K] [\-U] [\-v] [\-d] [\-f] [\-\-debug] pattern .SH DESCRIPTION stackcount traces functions and frequency counts them with their entire stack trace, kernel stack and user stack, summarized in-kernel for efficiency. This allows higher frequency events to be studied. The output consists of unique stack traces, and their occurrence counts. In addition to kernel and user functions, kernel tracepoints and USDT tracepoint are also supported. The pattern is a string with optional '*' wildcards, similar to file globbing. If you'd prefer to use regular expressions, use the \-r option. This tool only works on Linux 4.6+. Stack traces are obtained using the new `BPF_STACK_TRACE` APIs. For kernels older than 4.6, see the version under tools/old. .SH REQUIREMENTS CONFIG_BPF and bcc. .SH OPTIONS .TP \-h Print usage message. .TP \-r Allow regular expressions for the search pattern. The default allows "*" wildcards only. .TP \-s Show address offsets. .TP \-P Display stacks separately for each process. .TP \-K Show kernel stack only. .TP \-U Show user stack only. .TP \-T Include a timestamp with interval output. .TP \-v Show raw addresses. .TP \-d Print a delimiter ("--") in-between the kernel and user stacks. .TP \-\-debug Print the source of the BPF program when loading it (for debugging purposes). .TP \-i interval Summary interval, in seconds. .TP \-D duration Total duration of trace, in seconds. \-f Folded output format. .TP \-p PID Trace this process ID only (filtered in-kernel). .TP \-c CPU Trace this CPU only (filtered in-kernel). .TP .TP pattern A function name, or a search pattern. Can include wildcards ("*"). If the \-r option is used, can include regular expressions. .SH EXAMPLES .TP Count kernel and user stack traces for submit_bio(): # .B stackcount submit_bio .TP Count stacks with a delimiter for submit_bio(): # .B stackcount \-d submit_bio .TP Count kernel stack trace only for submit_bio(): # .B stackcount \-K submit_bio .TP Count user stack trace only for submit_bio(): # .B stackcount \-U submit_bio .TP Count stack traces for ip_output(): # .B stackcount ip_output .TP Show symbol offsets: # .B stackcount \-s ip_output .TP Show offsets and raw addresses (verbose): # .B stackcount \-sv ip_output .TP Count stacks for kernel functions matching tcp_send*: # .B stackcount 'tcp_send*' .TP Same as previous, but using regular expressions: # .B stackcount \-r '^tcp_send.*' .TP Output every 5 seconds, with timestamps: # .B stackcount \-Ti 5 ip_output .TP Only count stacks when PID 185 is on-CPU: # .B stackcount \-p 185 ip_output .TP Only count stacks for CPU 1: # .B stackcount \-c 1 put_prev_entity .TP Count user stacks for dynamic heap allocations with malloc in PID 185: # .B stackcount \-p 185 c:malloc .TP Count user stacks for thread creation (USDT tracepoint) in PID 185: # .B stackcount \-p 185 u:pthread:pthread_create .TP Count stacks for context switch events using a kernel tracepoint: # .B stackcount t:sched:sched_switch .SH OVERHEAD This summarizes unique stack traces in-kernel for efficiency, allowing it to trace a higher rate of function calls than methods that post-process in user space. The stack trace data is only copied to user space when the output is printed, which usually only happens once. The stack walking also happens in an optimized code path in the kernel thanks to the new BPF_STACK_TRACE table APIs, which should be more efficient than the manual walker in the eBPF tracer which older versions of this script used. With this in mind, call rates of < 10,000/sec would incur negligible overhead. Test before production use. You can also use funccount to get a handle on function call rates first. .SH SOURCE This is from bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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, Sasha Goldshtein .SH SEE ALSO stacksnoop(8), funccount(8) bpfcc-0.12.0/man/man8/statsnoop.8000066400000000000000000000041571357404205000164170ustar00rootroot00000000000000.TH statsnoop 8 "2016-02-08" "USER COMMANDS" .SH NAME statsnoop \- Trace stat() syscalls. Uses Linux eBPF/bcc. .SH SYNOPSIS .B statsnoop [\-h] [\-t] [\-x] [\-p PID] .SH DESCRIPTION statsnoop traces the different stat() syscalls, showing which processes are attempting to read information about which files. This can be useful for determining the location of config and log files, or for troubleshooting applications that are failing, especially on startup. This works by tracing various kernel sys_stat() functions using dynamic tracing, and will need updating to match any changes to these functions. This makes use of a Linux 4.5 feature (bpf_perf_event_output()); for kernels older than 4.5, see the version under tools/old, which uses an older mechanism. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bcc. .SH OPTIONS .TP \-h Print usage message. .TP \-t Include a timestamp column: in seconds since the first event, with decimal places. .TP \-x Only print failed stats. .TP \-p PID Trace this process ID only (filtered in-kernel). .SH EXAMPLES .TP Trace all stat() syscalls: # .B statsnoop .TP Trace all stat() syscalls, and include timestamps: # .B statsnoop \-t .TP Trace only stat() syscalls that failed: # .B statsnoop \-x .TP Trace PID 181 only: # .B statsnoop \-p 181 .SH FIELDS .TP TIME(s) Time of the call, in seconds. .TP PID Process 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 kernel stat function 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 bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 opensnoop(1) bpfcc-0.12.0/man/man8/syncsnoop.8000066400000000000000000000026331357404205000164150ustar00rootroot00000000000000.TH syncsnoop 8 "2015-08-18" "USER COMMANDS" .SH NAME syncsnoop \- Trace sync() syscall. Uses Linux eBPF/bcc. .SH SYNOPSIS .B syncsnoop .SH DESCRIPTION syncsnoop traces calls to sync(), which flushes file system buffers to storage devices. These calls can cause performance perturbations, and it can be useful to know if they are happening and how frequently. This works by tracing the kernel sys_sync() function using dynamic tracing, and will need updating to match any changes to this function. This makes use of a Linux 4.5 feature (bpf_perf_event_output()); for kernels older than 4.5, see the version under tools/old, which uses an older mechanism. 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 bcc. .SH EXAMPLES .TP Trace calls to sync(): # .B syncsnoop .SH FIELDS .TP TIME(s) Time of the call, in seconds. .TP CALL Call traced. .SH OVERHEAD This traces the kernel sync 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. .SH SOURCE This is from bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 iostat(1) bpfcc-0.12.0/man/man8/syscount.8000066400000000000000000000053721357404205000162540ustar00rootroot00000000000000.TH syscount 8 "2017-02-15" "USER COMMANDS" .SH NAME syscount \- Summarize syscall counts and latencies. .SH SYNOPSIS .B syscount [-h] [-p PID] [-i INTERVAL] [-d DURATION] [-T TOP] [-x] [-e ERRNO] [-L] [-m] [-P] [-l] .SH DESCRIPTION This tool traces syscall entry and exit tracepoints and summarizes either the number of syscalls of each type, or the number of syscalls per process. It can also collect latency (invocation time) for each syscall or each process. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bcc. Linux 4.7+ is required to attach a BPF program to the raw_syscalls:sys_{enter,exit} tracepoints, used by this tool. .SH OPTIONS .TP \-h Print usage message. .TP \-p PID Trace only this process. .TP \-i INTERVAL Print the summary at the specified interval (in seconds). .TP \-d DURATION Total duration of trace (in seconds). .TP \-T TOP Print only this many entries. Default: 10. .TP \-x Trace only failed syscalls (i.e., the return value from the syscall was < 0). .TP \-e ERRNO Trace only syscalls that failed with that error (e.g. -e EPERM or -e 1). .TP \-m Display times in milliseconds. Default: microseconds. .TP \-P Summarize by process and not by syscall. .TP \-l List the syscalls recognized by the tool (hard-coded list). Syscalls beyond this list will still be displayed, as "[unknown: nnn]" where nnn is the syscall number. .SH EXAMPLES .TP Summarize all syscalls by syscall: # .B syscount .TP Summarize all syscalls by process: # .B syscount \-P .TP Summarize only failed syscalls: # .B syscount \-x .TP Summarize only syscalls that failed with EPERM: # .B syscount \-e EPERM .TP Trace PID 181 only: # .B syscount \-p 181 .TP Summarize syscalls counts and latencies: # .B syscount \-L .SH FIELDS .TP PID Process ID .TP COMM Process name .TP SYSCALL Syscall name, or "[unknown: nnn]" for syscalls that aren't recognized .TP COUNT The number of events .TP TIME The total elapsed time (in us or ms) .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 loop of 4 million calls to geteuid(), slows it down by 1.85x when tracing only syscall counts, and slows it down by more than 5x when tracing syscall counts and latencies. However, this represents a rate of >3.5 million syscalls per second, which should not be typical. .SH SOURCE This is from bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 Sasha Goldshtein .SH SEE ALSO funccount(8), ucalls(8), argdist(8), trace(8), funclatency(8) bpfcc-0.12.0/man/man8/tclcalls.8000077700000000000000000000000001357404205000175262ucalls.8ustar00rootroot00000000000000bpfcc-0.12.0/man/man8/tclflow.8000077700000000000000000000000001357404205000172502uflow.8ustar00rootroot00000000000000bpfcc-0.12.0/man/man8/tclobjnew.8000077700000000000000000000000001357404205000201022uobjnew.8ustar00rootroot00000000000000bpfcc-0.12.0/man/man8/tclstat.8000077700000000000000000000000001357404205000172602ustat.8ustar00rootroot00000000000000bpfcc-0.12.0/man/man8/tcpaccept.8000066400000000000000000000046301357404205000163270ustar00rootroot00000000000000.TH tcpaccept 8 "2019-03-08" "USER COMMANDS" .SH NAME tcpaccept \- Trace TCP passive connections (accept()). Uses Linux eBPF/bcc. .SH SYNOPSIS .B tcpaccept [\-h] [\-T] [\-t] [\-p PID] [\-P PORTS] .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 bcc. .SH OPTIONS .TP \-h Print usage message. .TP \-T Include a time column on output (HH:MM:SS). .TP \-t Include a timestamp column. .TP \-p PID Trace this process ID only (filtered in-kernel). .TP \-P PORTS Comma-separated list of local ports to trace (filtered in-kernel). .SH EXAMPLES .TP Trace all passive TCP connections (accept()s): # .B tcpaccept .TP Trace all TCP accepts, and include timestamps: # .B tcpaccept \-t .TP Trace connections to local ports 80 and 81 only: # .B tcpaccept \-P 80,81 .TP Trace PID 181 only: # .B tcpaccept \-p 181 .SH FIELDS .TP TIME Time of the event, in HH:MM:SS format. .TP TIME(s) Time of the event, in seconds. .TP PID Process ID .TP COMM Process name .TP IP IP address family (4 or 6) .TP RADDR Remote IP address. .TP RPORT Remote port .TP LADDR Local IP address. .TP LPORT Local port .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 bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 tcpconnect(8), funccount(8), tcpdump(8) bpfcc-0.12.0/man/man8/tcpconnect.8000066400000000000000000000047421357404205000165250ustar00rootroot00000000000000.TH tcpconnect 8 "2015-08-25" "USER COMMANDS" .SH NAME tcpconnect \- Trace TCP active connections (connect()). Uses Linux eBPF/bcc. .SH SYNOPSIS .B tcpconnect [\-h] [\-c] [\-t] [\-x] [\-p PID] [-P PORT] .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 bcc. .SH OPTIONS .TP \-h Print usage message. .TP \-t Include a timestamp column. .TP \-c Count connects per src ip and dest ip/port. .TP \-p PID Trace this process ID only (filtered in-kernel). .TP \-P PORT Comma-separated list of destination ports to trace (filtered in-kernel). .SH EXAMPLES .TP \-U Include a UID column. .TP \-u UID Trace this UID only (filtered in-kernel). .TP Trace all active TCP connections: # .B tcpconnect .TP Trace all TCP connects, and include timestamps: # .B tcpconnect \-t .TP Trace PID 181 only: # .B tcpconnect \-p 181 .TP Trace ports 80 and 81 only: # .B tcpconnect \-P 80,81 .TP Trace all TCP connects, and include UID: # .B tcpconnect \-U .TP Trace UID 1000 only: # .B tcpconnect \-u 1000 .TP Count connects per src ip and dest ip/port: # .B tcpconnect \-c .SH FIELDS .TP TIME(s) Time of the call, in seconds. .TP UID User ID .TP PID Process ID .TP COMM Process name .TP IP IP address family (4 or 6) .TP SADDR Source IP address. .TP DADDR Destination IP address. .TP DPORT Destination port .TP CONNECTS Accumulated active connections since start. .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 connect()s, such as a proxy server, then test and understand this overhead before use. .SH SOURCE This is from bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 tcpaccept(8), funccount(8), tcpdump(8) bpfcc-0.12.0/man/man8/tcpconnlat.8000066400000000000000000000055421357404205000165310ustar00rootroot00000000000000.TH tcpconnlat 8 "2016-02-19" "USER COMMANDS" .SH NAME tcpconnlat \- Trace TCP active connection latency. Uses Linux eBPF/bcc. .SH SYNOPSIS .B tcpconnlat [\-h] [\-t] [\-p PID] [-v] [min_ms] .SH DESCRIPTION This tool traces active TCP connections (eg, via a connect() syscall), and shows the latency (time) for the connection as measured locally: the time from SYN sent to the response packet. This is a useful performance metric that typically spans kernel TCP/IP processing and the network round trip time (not application runtime). All connection attempts are traced, even if they ultimately fail (RST packet in response). This tool works by use of kernel dynamic tracing of TCP/IP functions, and will need updating to match any changes to these functions. This tool should be updated in the future to use static tracepoints, once they are available. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bcc. .SH OPTIONS .TP \-h Print usage message. .TP \-t Include a timestamp column. .TP \-p PID Trace this process ID only (filtered in-kernel). .TP \-v Print the resulting BPF program, for debugging purposes. .TP min_ms Minimum duration to trace, in milliseconds. .SH EXAMPLES .TP Trace all active TCP connections, and show connection latency (SYN->response round trip): # .B tcpconnlat .TP Include timestamps: # .B tcpconnlat \-t .TP Trace PID 181 only: # .B tcpconnlat \-p 181 .TP Trace connects with latency longer than 10 ms: # .B tcpconnlat 10 .TP Print the BPF program: # .B tcpconnlat \-v .SH FIELDS .TP TIME(s) Time of the response packet, in seconds. .TP PID Process ID that initiated the connection. .TP COMM Process name that initiated the connection. .TP IP IP address family (4 or 6). .TP SADDR Source IP address. .TP DADDR Destination IP address. .TP DPORT Destination port .TP LAT(ms) The time from when a TCP connect was issued (measured in-kernel) to when a response packet was received for this connection (can be SYN,ACK, or RST, etc). This time spans kernel to kernel latency, involving kernel TCP/IP processing and the network round trip in between. This typically does not include time spent by the application processing the new connection. .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 bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 tcpconnect(8), tcpaccept(8), funccount(8), tcpdump(8) bpfcc-0.12.0/man/man8/tcpdrop.8000066400000000000000000000034051357404205000160330ustar00rootroot00000000000000.TH tcpdrop 8 "2018-05-30" "USER COMMANDS" .SH NAME tcpdrop \- Trace kernel-based TCP packet drops with details. Uses Linux eBPF/bcc. .SH SYNOPSIS .B tcpdrop [\-h] .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. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bcc. .SH OPTIONS .TP \-h Print usage message. .B tcpdrop .SH FIELDS .TP TIME Time of the drop, 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 IP IP address family (4 or 6) .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). .TP FLAGS TCP flags ("SYN", 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 bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 tcplife(8), tcpaccept(8), tcpconnect(8), tcptop(8) bpfcc-0.12.0/man/man8/tcplife.8000066400000000000000000000060211357404205000160030ustar00rootroot00000000000000.TH tcplife 8 "2016-10-19" "USER COMMANDS" .SH NAME tcplife \- Trace TCP sessions and summarize lifespan. Uses Linux eBPF/bcc. .SH SYNOPSIS .B tcplife [\-h] [\-T] [\-t] [\-w] [\-s] [\-p PID] [\-D PORTS] [\-L PORTS] .SH DESCRIPTION This tool traces TCP sessions that open and close while tracing, and prints a line of output to summarize each one. This includes the IP addresses, ports, duration, and throughput for the session. This is useful for workload characterisation and flow accounting: identifying what connections are happening, with the bytes transferred. This tool works using the sock:inet_sock_set_state tracepoint if it exists, added to Linux 4.16, and switches to using kernel dynamic tracing for older kernels. Only TCP state changes are traced, so it is expected that the overhead of this tool is much lower than typical send/receive tracing. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bcc. .SH OPTIONS .TP \-h Print usage message. .TP \-s Comma separated values output (parseable). .TP \-t Include a timestamp column (seconds). .TP \-T Include a time column (HH:MM:SS). .TP \-w Wide column output (fits IPv6 addresses). .TP \-p PID Trace this process ID only (filtered in-kernel). .TP \-L PORTS Comma-separated list of local ports to trace (filtered in-kernel). .TP \-D PORTS Comma-separated list of destination ports to trace (filtered in-kernel). .SH EXAMPLES .TP Trace all TCP sessions, and summarize lifespan and throughput: # .B tcplife .TP Include a timestamp column, and wide column output: # .B tcplife \-tw .TP Trace PID 181 only: # .B tcplife \-p 181 .TP Trace connections to local ports 80 and 81 only: # .B tcplife \-L 80,81 .TP Trace connections to remote port 80 only: # .B tcplife \-D 80 .SH FIELDS .TP TIME Time of the call, in HH:MM:SS format. .TP TIME(s) Time of the call, in seconds. .TP PID Process ID .TP COMM Process name .TP IP IP address family (4 or 6) .TP LADDR Local IP address. .TP RADDR 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 is from bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 tcpaccept(8), tcpconnect(8), tcptop(8) bpfcc-0.12.0/man/man8/tcpretrans.8000066400000000000000000000042351357404205000165470ustar00rootroot00000000000000.TH tcpretrans 8 "2016-02-14" "USER COMMANDS" .SH NAME tcpretrans \- Trace or count TCP retransmits and TLPs. Uses Linux eBPF/bcc. .SH SYNOPSIS .B tcpretrans [\-h] [\-l] [\-c] .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. .SH OPTIONS .TP \-h Print usage message. .TP \-l Include tail loss probe attempts (in some cases the kernel may not complete the TLP send). .TP \-c Count occurring retransmits per flow. .SH EXAMPLES .TP Trace TCP retransmits: # .B tcpretrans .TP Trace TCP retransmits and TLP attempts: # .B tcpretrans \-l .SH FIELDS .TP TIME Time of the retransmit. .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 IP IP address family (4 or 6). .TP LADDR Local IP address. .TP LPORT Local port. .TP T> Type of event: R> == retransmit, L> == tail loss probe. .TP RADDR Remote IP address. .TP RPORT Remote port. .TP STATE TCP session state. .TP RETRANSMITS Accumulated occurred retransmits since start. .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 bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 tcpconnect(8), tcpaccept(8) bpfcc-0.12.0/man/man8/tcpstates.8000066400000000000000000000066221357404205000163760ustar00rootroot00000000000000.TH tcpstates 8 "2018-03-20" "USER COMMANDS" .SH NAME tcpstates \- Trace TCP session state changes with durations. Uses Linux eBPF/bcc. .SH SYNOPSIS .B tcpstates [\-h] [\-T] [\-t] [\-w] [\-s] [\-D PORTS] [\-L PORTS] [\-Y] .SH DESCRIPTION This tool traces TCP session state changes while tracing, and prints details including the duration in each state. This can help explain the latency of TCP connections: whether the time is spent in the ESTABLISHED state (data transfer), or initialization state (SYN_SENT), etc. This tool works using the sock:inet_sock_set_state tracepoint, which was added to Linux 4.16. Linux 4.16 also included extra state transitions so that all TCP transitions could be observed by this tracepoint. Only TCP state changes are traced, so it is expected that the overhead of this tool is much lower than typical send/receive tracing. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bcc, and the sock:inet_sock_set_state tracepoint. .SH OPTIONS .TP \-h Print usage message. .TP \-s Comma separated values output (parseable). .TP \-t Include a timestamp column (seconds). .TP \-T Include a time column (HH:MM:SS). .TP \-w Wide column output (fits IPv6 addresses). .TP \-L PORTS Comma-separated list of local ports to trace (filtered in-kernel). .TP \-D PORTS Comma-separated list of destination ports to trace (filtered in-kernel). .TP \-Y Log session state changes to the systemd journal. .SH EXAMPLES .TP Trace all TCP sessions, and show all state changes: # .B tcpstates .TP Include a timestamp column, and wide column output: # .B tcpstates \-tw .TP Trace connections to local ports 80 and 81 only: # .B tcpstates \-L 80,81 .TP Trace connections to remote port 80 only: # .B tcpstates \-D 80 .SH FIELDS .TP TIME Time of the change, in HH:MM:SS format. .TP TIME(s) Time of the change, in seconds. .TP C-PID The current on-CPU process ID. This may show the process that owns the TCP session if the state change executes in synchronous process context, else it is likely to show the kernel (asynchronous state change). .TP C-COMM The current on-CPU process name. This may show the process that owns the TCP session if the state change executes in synchronous process context, else it is likely to show the kernel (asynchronous state change). .TP IP IP address family (4 or 6) .TP LADDR Local IP address. .TP RADDR Remote IP address. .TP LPORT Local port. .TP RPORT Remote port. .TP OLDSTATE Previous TCP state. .TP NEWSTATE New TCP state. .TP MS Duration of this state. .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 is from bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 tcpaccept(8), tcpconnect(8), tcptop(8), tcplife(8) bpfcc-0.12.0/man/man8/tcpsubnet.8000066400000000000000000000052601357404205000163700ustar00rootroot00000000000000.TH tcpsubnet 8 "2018-03-01" "USER COMMANDS" .SH NAME tcpsubnet \- Summarize and aggregate IPv4 TCP traffic by subnet. .SH SYNOPSIS .B tcpsubnet [\-h] [\-v] [\--ebpf] [\-J] [\-f FORMAT] [\-i INTERVAL] [subnets] .SH DESCRIPTION This tool summarizes and aggregates IPv4 TCP sent to the subnets passed in argument and prints to stdout on a fixed interval. This uses dynamic tracing of kernel TCP send/receive functions, and will need to be updated to match kernel changes. The traced data is summarized in-kernel using a BPF map to reduce overhead. At very high TCP event rates, the overhead may still be measurable. See the OVERHEAD section for more details. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bcc. .SH OPTIONS .TP \-h Print USAGE message. .TP \-v Run in verbose mode. Will output subnet evaluation and the BPF program .TP \-J Format output in JSON. .TP \-i Interval between updates, seconds (default 1). .TP \-f Format output units. Supported values are bkmBKM. When using kmKM the output will be rounded to floor. .TP \--ebpf Prints the BPF program. .TP subnets Comma separated list of subnets. Traffic will be categorized in theses subnets. Order matters. (default 127.0.0.1/32,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,0.0.0.0/0) .SH EXAMPLES .TP Summarize TCP traffic by the default subnets: # .B tcpsubnet .TP Summarize all TCP traffic: # .B tcpsubnet 0.0.0.0/0 .TP Summarize all TCP traffic and output in JSON and Kb: # .B tcpsubnet -J -fk 0.0.0.0/0 .SH FIELDS .TP (Standad output) Left hand side column: Subnet .TP (Standard output) Right hand side column: Aggregate traffic in units passed as argument .TP (JSON output) date Current date formatted in the system locale .TP (JSON output) time Current time formatted in the system locale .TP (JSON output) entries Map of subnets to aggregates. Values will be in format passed to -f .SH OVERHEAD This traces all tcp_sendmsg function calls in the TCP/IP stack. It summarizes data in-kernel to reduce overhead. A simple iperf test (v2.0.5) with the default values shows a loss of ~5% throughput. On 10 runs without tcpsubnet running the average throughput was 32.42Gb/s, with tcpsubnet enabled it was 31.26Gb/s. This is not meant to be used as a long running service. Use it for troubleshooting or for a controlled interval. As always, try it out in a test environment first. .SH SOURCE This is from bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 Rodrigo Manyari .SH INSPIRATION tcptop(8) by Brendan Gregg .SH SEE ALSO netlink(7) bpfcc-0.12.0/man/man8/tcptop.8000066400000000000000000000055521357404205000156760ustar00rootroot00000000000000.TH tcptop 8 "2016-09-13" "USER COMMANDS" .SH NAME tcptop \- Summarize TCP send/recv throughput by host. Top for TCP. .SH SYNOPSIS .B tcptop [\-h] [\-C] [\-S] [\-p PID] [interval] [count] .SH DESCRIPTION This is top for TCP sessions. This summarizes TCP send/receive Kbytes by host, and prints a summary that refreshes, along other system-wide metrics. This uses dynamic tracing of kernel TCP send/receive functions, and will need to be updated to match kernel changes. The traced TCP functions are usually called at a lower rate than per-packet functions, and therefore have lower overhead. The traced data is summarized in-kernel using a BPF map to further reduce overhead. At very high TCP event rates, the overhead may still be measurable. See the OVERHEAD section for more details. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bcc. .SH OPTIONS .TP \-h Print USAGE message. .TP \-C Don't clear the screen. .TP \-S Don't print the system summary line (load averages). .TP \-p PID Trace this PID only. .TP interval Interval between updates, seconds (default 1). .TP count Number of interval summaries (default is many). .SH EXAMPLES .TP Summarize TCP throughput by active sessions, 1 second refresh: # .B tcptop .TP Don't clear the screen (rolling output), and 5 second summaries: # .B tcptop \-C 5 .TP Trace PID 181 only, and don't clear the screen: # .B tcptop \-Cp 181 .SH FIELDS .TP loadavg: The contents of /proc/loadavg .TP PID Process ID. .TP COMM Process name. .TP LADDR Local address (IPv4), and TCP port .TP RADDR Remote address (IPv4), and TCP port .TP LADDR6 Source address (IPv6), and TCP port .TP RADDR6 Destination address (IPv6), and TCP port .TP RX_KB Received Kbytes .TP TX_KB Transmitted Kbytes .SH OVERHEAD This traces all send/receives in TCP, high in the TCP/IP stack (close to the application) which are usually called at a lower rate than per-packet functions, lowering overhead. It also summarizes data in-kernel to further reduce overhead. These techniques help, but there may still be measurable overhead at high send/receive rates, eg, ~13% of one CPU at 100k events/sec. use funccount to count the kprobes in the tool to find out this rate, as the overhead is relative to the rate. Some sample production servers tested found total TCP event rates of 4k to 15k per second, and the CPU overhead at these rates ranged from 0.5% to 2.0% of one CPU. If your send/receive rate is low (eg, <1000/sec) then the overhead is expected to be negligible; Test in a lab environment first. .SH SOURCE This is from bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 INSPIRATION top(1) by William LeFebvre .SH SEE ALSO tcpconnect(8), tcpaccept(8) bpfcc-0.12.0/man/man8/tcptracer.8000066400000000000000000000043731357404205000163540ustar00rootroot00000000000000.TH tcptracer 8 "2017-03-27" "USER COMMANDS" .SH NAME tcptracer \- Trace TCP established connections. Uses Linux eBPF/bcc. .SH SYNOPSIS .B tcptracer [\-h] [\-v] [\-p PID] [\-N NETNS] .SH DESCRIPTION This tool traces established TCP connections that open and close while tracing, and prints a line of output per connect, accept and close events. This includes the type of event, PID, IP addresses and ports. This tool works by using kernel dynamic tracing, and will need to be updated if the kernel implementation changes. Only established TCP connections are traced, so it is expected that the overhead of this tool is rather low. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bcc. .SH OPTIONS .TP \-h Print usage message. .TP \-v Print full lines, with long event type names and network namespace numbers. .TP \-p PID Trace this process ID only (filtered in-kernel). .TP \-N NETNS Trace this network namespace only (filtered in-kernel). .TP .SH EXAMPLES .TP Trace all TCP established connections: # .B tcptracer .TP Trace all TCP established connections with verbose lines: # .B tcptracer \-v .TP Trace PID 181 only: # .B tcptracer \-p 181 .TP Trace connections in network namespace 4026531969 only: # .B tcptracer \-N 4026531969 .SH FIELDS .TP TYPE Type of event. In non-verbose mode: C for connect, A for accept, X for close. .TP PID Process ID .TP COMM Process name .TP IP IP address family (4 or 6) .TP SADDR Source IP address. .TP DADDR Destination IP address. .TP SPORT Source port. .TP DPORT Destination port. .TP NETNS Network namespace where the event originated. .SH OVERHEAD This traces the kernel inet accept function, and the TCP connect, close, and set state functions. However, it only prints information for connections that are established, so it shouldn't have a huge overhead. As always, test and understand this tools overhead for your types of workloads before production use. .SH SOURCE This is from bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 Iago López Galeiras .SH SEE ALSO tcpaccept(8), tcpconnect(8), tcptop(8), tcplife(8) bpfcc-0.12.0/man/man8/tplist.8000066400000000000000000000035021357404205000156750ustar00rootroot00000000000000.TH tplist 8 "2016-03-20" "USER COMMANDS" .SH NAME tplist \- Display kernel tracepoints or USDT probes and their formats. .SH SYNOPSIS .B tplist [-p PID] [-l LIB] [-v] [filter] .SH DESCRIPTION tplist lists all kernel tracepoints, and can optionally print out the tracepoint format; namely, the variables that you can trace when the tracepoint is hit. tplist can also list USDT probes embedded in a specific library or executable, and can list USDT probes for all the libraries loaded by a specific process. These features are usually used in conjunction with the argdist and/or trace tools. On a typical system, accessing the tracepoint list and format requires root. However, accessing USDT probes does not require root. .SH OPTIONS .TP \-p PID Display the USDT probes from all the libraries loaded by the specified process. .TP \-l LIB Display the USDT probes from the specified library or executable. If the librar or executable can be found in the standard paths, a full path is not required. .TP \-v Increase the verbosity level. Can be used to display the variables, locations, and arguments of tracepoints and USDT probes. .TP [filter] A wildcard expression that specifies which tracepoints or probes to print. For example, block:* will print all block tracepoints (block:block_rq_complete, etc.). Regular expressions are not supported. .SH EXAMPLES .TP Print all kernel tracepoints: # .B tplist .TP Print all net tracepoints with their format: # .B tplist -v 'net:*' .TP Print all USDT probes in libpthread: $ .B tplist -l pthread .TP Print all USDT probes in process 4717 from the libc provider: $ .B tplist -p 4717 'libc:*' .TP Print all the USDT probes in the node executable: $ .B tplist -l node .SH SOURCE This is from bcc. .IP https://github.com/iovisor/bcc .SH OS Linux .SH STABILITY Unstable - in development. .SH AUTHOR Sasha Goldshtein bpfcc-0.12.0/man/man8/trace.8000066400000000000000000000173121357404205000154600ustar00rootroot00000000000000.TH trace 8 "2016-02-18" "USER COMMANDS" .SH NAME trace \- Trace a function and print its arguments or return value, optionally evaluating a filter. Uses Linux eBPF/bcc. .SH SYNOPSIS .B trace [-h] [-b BUFFER_PAGES] [-p PID] [-L TID] [-v] [-Z STRING_SIZE] [-S] [-s SYM_FILE_LIST] [-M MAX_EVENTS] [-t] [-u] [-T] [-C] [-K] [-U] [-a] [-I header] probe [probe ...] .SH DESCRIPTION trace probes functions you specify and displays trace messages if a particular condition is met. You can control the message format to display function arguments and return values. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bcc. .SH OPTIONS .TP \-h Print usage message. .TP \-p PID Trace only functions in the process PID. .TP \-L TID Trace only functions in the thread TID. .TP \-v Display the generated BPF program, for debugging purposes. .TP \-z STRING_SIZE When collecting string arguments (of type char*), collect up to STRING_SIZE characters. Longer strings will be truncated. .TP \-s SYM_FILE_LIST When collecting stack trace in build id format, use the coma separated list for symbol resolution. .TP \-S If set, trace messages from trace's own process. By default, this is off to avoid tracing storms -- for example, if you trace the write system call, and consider that trace is writing to the standard output. .TP \-M MAX_EVENTS Print up to MAX_EVENTS trace messages and then exit. .TP \-t Print times relative to the beginning of the trace (offsets), in seconds. .TP \-u Print UNIX timestamps instead of offsets from trace beginning, requires -t. .TP \-T Print the time column. .TP \-C Print CPU id. .TP \-c CGROUP_PATH Trace only functions in processes under CGROUP_PATH hierarchy. .TP \-n NAME Only print process names containing this name. .TP \-f MSG_FILTER Only print message of event containing this string. .TP \-B Treat argument of STRCMP helper as a binary value .TP \-K Print the kernel stack for each event. .TP \-U Print the user stack for each event. \-a Print virtual address in kernel and user stacks. .TP \-I header Additional header files to include in the BPF program. This is needed if your filter or print expressions use types or data structures that are not available in the standard headers. For example: 'linux/mm.h' .TP probe [probe ...] One or more probes that attach to functions, filter conditions, and print information. See PROBE SYNTAX below. .SH PROBE SYNTAX The general probe syntax is as follows: .B [{p,r}]:[library]:function[(signature)] [(predicate)] ["format string"[, arguments]] .B {t:category:event,u:library:probe} [(predicate)] ["format string"[, arguments]] .TP .B {[{p,r}],t,u} Probe type \- "p" for function entry, "r" for function return, "t" for kernel tracepoint, "u" for USDT probe. The default probe type is "p". .TP .B [library] Library containing the probe. Specify the full path to the .so or executable file where the function to probe resides. Alternatively, you can specify just the lib name: for example, "c" refers to libc. If no library name is specified, the kernel is assumed. Also, you can specify an executable name (without a full path) if it is in the PATH. For example, "bash". .TP .B category The tracepoint category. For example, "sched" or "irq". .TP .B function The function to probe. .TP .B signature The optional signature of the function to probe. This can make it easier to access the function's arguments, instead of using the "arg1", "arg2" etc. argument specifiers. For example, "(struct timespec *ts)" in the signature position lets you use "ts" in the filter or print expressions. .TP .B event The tracepoint event. For example, "block_rq_complete". .TP .B probe The USDT probe name. For example, "pthread_create". .TP .B [(predicate)] The filter applied to the captured data. Only if the filter evaluates as true, the trace message will be printed. The filter can use any valid C expression that refers to the argument values: arg1, arg2, etc., or to the return value retval in a return probe. If necessary, use C cast operators to coerce the arguments to the desired type. For example, if arg1 is of type int, use the expression ((int)arg1 < 0) to trace only invocations where arg1 is negative. Note that only arg1-arg6 are supported, and only if the function is using the standard x86_64 convention where the first six arguments are in the RDI, RSI, RDX, RCX, R8, R9 registers. If no predicate is specified, all function invocations are traced. The predicate expression may also use the STRCMP pseudo-function to compare a predefined string to a string argument. For example: STRCMP("test", arg1). The order of arguments is important: the first argument MUST be a quoted literal string, and the second argument can be a runtime string, most typically an argument. .TP .B ["format string"[, arguments]] A printf-style format string that will be used for the trace message. You can use the following format specifiers: %s, %d, %u, %lld, %llu, %hd, %hu, %c, %x, %llx -- with the same semantics as printf's. Make sure to pass the exact number of arguments as there are placeholders in the format string. The format specifier replacements may be any C expressions, and may refer to the same special keywords as in the predicate (arg1, arg2, etc.). In addition to the above format specifiers, you can also use %K and %U when the expression is an address that potentially points to executable code (i.e., a symbol). trace will resolve %K specifiers to a kernel symbol, such as vfs__read, and will resolve %U specifiers to a user-space symbol in that process, such as sprintf. In tracepoints, both the predicate and the arguments may refer to the tracepoint format structure, which is stored in the special "args" variable. For example, the block:block_rq_complete tracepoint can print or filter by args->nr_sector. To discover the format of your tracepoint, use the tplist tool. In USDT probes, the arg1, ..., argN variables refer to the probe's arguments. To determine which arguments your probe has, use the tplist tool. The predicate expression and the format specifier replacements for printing may also use the following special keywords: $pid, $tgid to refer to the current process' pid and tgid; $uid, $gid to refer to the current user's uid and gid; $cpu to refer to the current processor number. .SH EXAMPLES .TP Trace all invocations of the open system call with the name of the file being opened: # .B trace '::do_sys_open """%s"", arg2' .TP Trace all invocations of the read system call where the number of bytes requested is greater than 20,000: # .B trace '::sys_read (arg3 > 20000) """read %d bytes"", arg3' .TP Trace all malloc calls and print the size of the requested allocation: # .B trace ':c:malloc """size = %d"", arg1' .TP Trace returns from the readline function in bash and print the return value as a string: # .B trace 'r:bash:readline """%s"", retval' .TP Trace the block:block_rq_complete tracepoint and print the number of sectors completed: # .B trace 't:block:block_rq_complete """%d sectors"", args->nr_sector' .TP Trace the pthread_create USDT probe from the pthread library and print the address of the thread's start function: # .B trace 'u:pthread:pthread_create """start addr = %llx"", arg3' .TP Trace the nanosleep system call and print the sleep duration in nanoseconds: # .B trace 'p::SyS_nanosleep(struct timespec *ts) "sleep for %lld ns", ts->tv_nsec' .TP Trace the inet_pton system call using build id mechanism and print the stack # .B trace -s /lib/x86_64-linux-gnu/libc.so.6,/bin/ping 'p:c:inet_pton' -U .SH SOURCE This is from bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 Sasha Goldshtein bpfcc-0.12.0/man/man8/ttysnoop.8000066400000000000000000000027011357404205000162550ustar00rootroot00000000000000.TH ttysnoop 8 "2016-02-08" "USER COMMANDS" .SH NAME ttysnoop \- Watch output from a tty or pts device. Uses Linux eBPF/bcc. .SH SYNOPSIS .B ttysnoop [\-h] [\-C] device .SH DESCRIPTION ttysnoop watches a tty or pts device, and prints the same output that is appearing on that device. It can be used to mirror the output from a shell session, or the system console. This works by use of kernel dynamic tracing of the tty_write() function. This tool will need updating in case that kernel function changes in a future kernel version. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bcc. .SH OPTIONS .TP \-C Don't clear the screen. .TP device Either a path to a tty device (eg, /dev/tty0) or a pts number (eg, the "3" from /dev/pts/3). .SH EXAMPLES .TP Snoop output from /dev/pts/2 # .B ttysnoop /dev/pts/2 .TP Snoop output from /dev/pts/2 (shortcut) # .B ttysnoop 2 .TP Snoop output from the system console # .B ttysnoop /dev/console .TP Snoop output from /dev/tty0 # .B ttysnoop /dev/tty0 .SH OVERHEAD As the rate of tty_write() is expected to be very low (<100/s), the overhead of this tool is expected to be negligible. .SH SOURCE This is from bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 opensnoop(1) bpfcc-0.12.0/man/man8/ucalls.8000066400000000000000000000063001357404205000156400ustar00rootroot00000000000000.TH ucalls 8 "2018-10-09" "USER COMMANDS" .SH NAME ucalls, javacalls, perlcalls, phpcalls, pythoncalls, rubycalls, tclcalls \- Summarize method calls from high-level languages and Linux syscalls. .SH SYNOPSIS .B javacalls [-h] [-T TOP] [-L] [-S] [-v] [-m] pid [interval] .br .B perlcalls [-h] [-T TOP] [-L] [-S] [-v] [-m] pid [interval] .br .B phpcalls [-h] [-T TOP] [-L] [-S] [-v] [-m] pid [interval] .br .B pythoncalls [-h] [-T TOP] [-L] [-S] [-v] [-m] pid [interval] .br .B rubycalls [-h] [-T TOP] [-L] [-S] [-v] [-m] pid [interval] .br .B tclcalls [-h] [-T TOP] [-L] [-S] [-v] [-m] pid [interval] .br .B ucalls [-l {java,perl,php,python,ruby}] [-h] [-T TOP] [-L] [-S] [-v] [-m] pid [interval] .SH DESCRIPTION This tool summarizes method calls from high-level languages such as Java, Perl, PHP, Python, Ruby, and Tcl. It can also trace Linux system calls. Whenever a method is invoked, ucalls records the call count and optionally the method's execution time (latency) and displays a summary. This uses in-kernel eBPF maps to store per process summaries for efficiency. This tool relies on USDT probes embedded in many high-level languages, such as Java, Perl, PHP, Python, Ruby, and Tcl. It requires a runtime instrumented with these probes, which in some cases requires building from source with a USDT-specific flag, such as "--enable-dtrace" or "--with-dtrace". For Java, method probes are not enabled by default, and can be turned on by running the Java process with the "-XX:+ExtendedDTraceProbes" flag. For PHP processes, the environment variable USE_ZEND_DTRACE must be set to 1. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bcc. .SH OPTIONS .TP \-l {java,perl,php,python,ruby,tcl} The language to trace. If not provided, only syscalls are traced (when the \-S option is used). .TP \-T TOP Print only the top methods by frequency or latency. .TP \-L Collect method invocation latency (duration). .TP \-S Collect Linux syscalls frequency and timing. .TP \-v Print the resulting BPF program, for debugging purposes. .TP \-m Print times in milliseconds (the default is microseconds). .TP pid The process id to trace. .TP interval Print summary after this number of seconds and then exit. By default, wait for Ctrl+C to terminate. .SH EXAMPLES .TP Trace the top 10 Ruby method calls: # .B ucalls -T 10 -l ruby 1344 .TP Trace Python method calls and Linux syscalls including latency in milliseconds: # .B ucalls -l python -mL 2020 .TP Trace only syscalls and print a summary after 10 seconds: # .B ucalls -S 788 10 .SH OVERHEAD Tracing individual method calls will produce a considerable overhead in all high-level languages. For languages with just-in-time compilation, such as Java, the overhead can be more considerable than for interpreted languages. On the other hand, syscall tracing will typically be tolerable for most processes, unless they have a very unusual rate of system calls. .SH SOURCE This is from bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc distribution for a companion _example.txt file containing example usage, output, and commentary for this tool. .SH OS Linux .SH STABILITY Unstable - in development. .SH AUTHOR Sasha Goldshtein .SH SEE ALSO ustat(8), argdist(8) bpfcc-0.12.0/man/man8/uflow.8000066400000000000000000000057221357404205000155200ustar00rootroot00000000000000.TH uflow 8 "2018-10-09" "USER COMMANDS" .SH NAME uflow, javaflow, perlflow, phpflow, pythonflow, rubyflow, tclflow \- Print a flow graph of method calls in high-level languages. .SH SYNOPSIS .B javaflow [-h] [-M METHOD] [-C CLAZZ] [-v] pid .br .B perlflow [-h] [-M METHOD] [-C CLAZZ] [-v] pid .br .B phpflow [-h] [-M METHOD] [-C CLAZZ] [-v] pid .br .B pythonflow [-h] [-M METHOD] [-C CLAZZ] [-v] pid .br .B rubyflow [-h] [-M METHOD] [-C CLAZZ] [-v] pid .br .B tclflow [-h] [-M METHOD] [-C CLAZZ] [-v] pid .br .B uflow [-h] [-M METHOD] [-C CLAZZ] [-v] [-l {java,perl,php,python,ruby,tcl}] pid .SH DESCRIPTION uflow traces method calls and prints them in a flow graph that can facilitate debugging and diagnostics by following the program's execution (method flow). This tool relies on USDT probes embedded in many high-level languages, such as Java, Perl, PHP, Python, Ruby, and Tcl. It requires a runtime instrumented with these probes, which in some cases requires building from source with a USDT-specific flag, such as "--enable-dtrace" or "--with-dtrace". For Java processes, the startup flag "-XX:+ExtendedDTraceProbes" is required. For PHP processes, the environment variable USE_ZEND_DTRACE must be set to 1. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bcc. .SH OPTIONS .TP \-M METHOD Print only method calls where the method name begins with this string. .TP \-C CLAZZ Print only method calls where the class name begins with this string. The class name interpretation strongly depends on the language. For example, in Java use "package/subpackage/ClassName" to refer to classes. .TP \-v Print the resulting BPF program, for debugging purposes. .TP {java,perl,php,python,ruby,tcl} The language to trace. .TP pid The process id to trace. .SH EXAMPLES .TP Follow method flow in a Ruby process: # .B uflow ruby 148 .TP Follow method flow in a Java process where the class name is java.lang.Thread: # .B uflow -C java/lang/Thread java 1802 .SH FIELDS .TP CPU The CPU number on which the method was invoked. This is useful to easily see where the output skips to a different CPU. .TP PID The process id. .TP TID The thread id. .TP TIME The duration of the method call. .TP METHOD The method name. .SH OVERHEAD This tool has extremely high overhead because it prints every method call. For some scenarios, you might see lost samples in the output as the tool is unable to keep up with the rate of data coming from the kernel. Filtering by class or method prefix can help reduce the amount of data printed, but there is still a very high overhead in the collection mechanism. Do not use for performance- sensitive production scenarios, and always test first. .SH SOURCE This is from bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc distribution for a companion _example.txt file containing example usage, output, and commentary for this tool. .SH OS Linux .SH STABILITY Unstable - in development. .SH AUTHOR Sasha Goldshtein .SH SEE ALSO trace(8), ustat(8) bpfcc-0.12.0/man/man8/ugc.8000066400000000000000000000060531357404205000151400ustar00rootroot00000000000000.TH ugc 8 "2018-10-09" "USER COMMANDS" .SH NAME ugc, javagc, nodegc, pythongc, rubygc \- Trace garbage collection events in high-level languages. .SH SYNOPSIS .B javagc [-h] [-v] [-m] [-M MINIMUM] [-F FILTER] pid .br .B nodegc [-h] [-v] [-m] [-M MINIMUM] [-F FILTER] pid .br .B pythongc [-h] [-v] [-m] [-M MINIMUM] [-F FILTER] pid .br .B rubygc [-h] [-v] [-m] [-M MINIMUM] [-F FILTER] pid .br .B ugc [-h] [-v] [-m] [-M MINIMUM] [-F FILTER] [-l {java,node,python,ruby}] pid .SH DESCRIPTION This traces garbage collection events as they occur, including their duration and any additional information (such as generation collected or type of GC) provided by the respective language's runtime. This tool relies on USDT probes embedded in many high-level languages, such as Java, Node, Python, and Ruby. It requires a runtime instrumented with these probes, which in some cases requires building from source with a USDT-specific flag, such as "--enable-dtrace" or "--with-dtrace". Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bcc. .SH OPTIONS .TP \-v Print the resulting BPF program, for debugging purposes. .TP \-m Print times in milliseconds. The default is microseconds. .TP \-M MINIMUM Display only collections that are longer than this threshold. The value is given in milliseconds. The default is to display all collections. .TP \-F FILTER Display only collections whose textual description matches (contains) this string. The default is to display all collections. Note that the filtering here is performed in user-space, and not as part of the BPF program. This means that if you have thousands of collection events, specifying this filter will not reduce the amount of data that has to be transferred from the BPF program to the user-space script. .TP {java,node,python,ruby} The language to trace. .TP pid The process id to trace. .SH EXAMPLES .TP Trace garbage collections in a specific Node process: # .B ugc node 148 .TP Trace garbage collections in a specific Java process, and print GC times in milliseconds: # .B ugc -m java 6004 .TP Trace garbage collections in a specific Java process, and display them only if they are longer than 10ms and have the string "Tenured" in their detailed description: # .B ugc -M 10 -F Tenured java 6004 .SH FIELDS .TP START The start time of the GC, in seconds from the beginning of the trace. .TP TIME The duration of the garbage collection event. .TP DESCRIPTION The runtime-provided description of this garbage collection event. .SH OVERHEAD Garbage collection events, even if frequent, should not produce a considerable overhead when traced because they are still not very common. Even hundreds of GCs per second (which is a very high rate) will still produce a fairly negligible overhead. .SH SOURCE This is from bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc distribution for a companion _example.txt file containing example usage, output, and commentary for this tool. .SH OS Linux .SH STABILITY Unstable - in development. .SH AUTHOR Sasha Goldshtein .SH SEE ALSO trace(8), ustat(8), uobjnew(8) bpfcc-0.12.0/man/man8/uobjnew.8000066400000000000000000000051141357404205000160300ustar00rootroot00000000000000.TH uobjnew 8 "2018-10-09" "USER COMMANDS" .SH NAME uobjnew, cobjnew, javaobjnew, rubyobjnew, tclobjnew \- Summarize object allocations in high-level languages. .SH SYNOPSIS .B cobjnew [-h] [-C TOP_COUNT] [-S TOP_SIZE] [-v] pid [interval] .br .B javaobjnew [-h] [-C TOP_COUNT] [-S TOP_SIZE] [-v] pid [interval] .br .B rubyobjnew [-h] [-C TOP_COUNT] [-S TOP_SIZE] [-v] pid [interval] .br .B tclobjnew [-h] [-C TOP_COUNT] [-S TOP_SIZE] [-v] pid [interval] .br .B uobjnew [-h] [-C TOP_COUNT] [-S TOP_SIZE] [-v] [-l {c,java,ruby,tcl}] pid [interval] .SH DESCRIPTION uobjnew traces object allocations in high-level languages (including "malloc") and prints summaries of the most frequently allocated types by number of objects or number of bytes. This tool relies on USDT probes embedded in many high-level languages, such as C, Java, Ruby, and Tcl. It requires a runtime instrumented with these probes, which in some cases requires building from source with a USDT-specific flag, such as "--enable-dtrace" or "--with-dtrace". For Java, the Java process must be started with the "-XX:+ExtendedDTraceProbes" flag. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bcc. .SH OPTIONS .TP \-C TOP_COUNT Print the top object types sorted by number of instances. .TP \-S TOP_SIZE Print the top object types sorted by size. .TP \-v Print the resulting BPF program, for debugging purposes. .TP {c,java,ruby,tcl} The language to trace. .TP pid The process id to trace. .TP interval Wait this many seconds and then print the summary and exit. By default, wait for Ctrl+C to exit. .SH EXAMPLES .TP Trace object allocations in a Ruby process: # .B uobjnew ruby 148 .TP Trace object allocations from "malloc" and print the top 10 by total size: # .B uobjnew -S 10 c 1788 .SH FIELDS .TP TYPE The object type being allocated. For C (malloc), this is the block size. .TP ALLOCS The number of objects allocated. .TP BYTES The number of bytes allocated. .SH OVERHEAD Object allocation events are quite frequent, and therefore the overhead from running this tool can be considerable. Use with caution and make sure to test before using in a production environment. Nonetheless, even thousands of allocations per second will likely produce a reasonable overhead when investigating a problem. .SH SOURCE This is from bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc distribution for a companion _example.txt file containing example usage, output, and commentary for this tool. .SH OS Linux .SH STABILITY Unstable - in development. .SH AUTHOR Sasha Goldshtein .SH SEE ALSO ustat(8), ugc(8), memleak(8) bpfcc-0.12.0/man/man8/ustat.8000066400000000000000000000105571357404205000155260ustar00rootroot00000000000000.TH ustat 8 "2018-10-09" "USER COMMANDS" .SH NAME ustat, javastat, nodestat, perlstat, phpstat, pythonstat, rubystat, tclstat \- Activity stats from high-level languages. .SH SYNOPSIS .B javastat [-C] [-S {cload,excp,gc,method,objnew,thread}] [-r MAXROWS] [-d] [interval [count]] .br .B nodestat [-C] [-S {cload,excp,gc,method,objnew,thread}] [-r MAXROWS] [-d] [interval [count]] .br .B perlstat [-C] [-S {cload,excp,gc,method,objnew,thread}] [-r MAXROWS] [-d] [interval [count]] .br .B phpstat [-C] [-S {cload,excp,gc,method,objnew,thread}] [-r MAXROWS] [-d] [interval [count]] .br .B pythonstat [-C] [-S {cload,excp,gc,method,objnew,thread}] [-r MAXROWS] [-d] [interval [count]] .br .B rubystat [-C] [-S {cload,excp,gc,method,objnew,thread}] [-r MAXROWS] [-d] [interval [count]] .br .B tclstat [-C] [-S {cload,excp,gc,method,objnew,thread}] [-r MAXROWS] [-d] [interval [count]] .br .B ustat [-l {java,node,perl,php,python,ruby,tcl}] [-C] [-S {cload,excp,gc,method,objnew,thread}] [-r MAXROWS] [-d] [interval [count]] .SH DESCRIPTION This is "top" for high-level language events, such as garbage collections, exceptions, thread creations, object allocations, method calls, and more. The events are aggregated for each process and printed in a top-like table, which can be sorted by various fields. Not all language runtimes provide the same set of details. This uses in-kernel eBPF maps to store per process summaries for efficiency. This tool relies on USDT probes embedded in many high-level languages, such as Java, Node, Perl, PHP, Python, Ruby, and Tcl. It requires a runtime instrumented with these probes, which in some cases requires building from source with a USDT-specific flag, such as "--enable-dtrace" or "--with-dtrace". For Java, some probes are not enabled by default, and can be turned on by running the Java process with the "-XX:+ExtendedDTraceProbes" flag. For PHP processes, the environment variable USE_ZEND_DTRACE must be set to 1. Newly-created processes will only be traced at the next interval. If you run this tool with a short interval (say, 1-5 seconds), this should be virtually unnoticeable. For longer intervals, you might miss processes that were started and terminated during the interval window. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bcc. .SH OPTIONS .TP \-l {java,node,perl,php,python,ruby,tcl} The language to trace. By default, all languages are traced. .TP \-C Do not clear the screen between updates. .TP \-S {cload,excp,gc,method,objnew,thread} Sort the output by the specified field. .TP \-r MAXROWS Do not print more than this number of rows. .TP \-d Print the resulting BPF program, for debugging purposes. .TP interval Interval between updates, seconds. .TP count Number of interval summaries. .SH EXAMPLES .TP Summarize activity in high-level languages, 1 second refresh: # .B ustat .TP Don't clear the screen, and top 8 rows only: # .B ustat -Cr 8 .TP 5 second summaries, 10 times only: # .B ustat 5 10 .SH FIELDS .TP loadavg The contents of /proc/loadavg .TP PID Process ID. .TP CMDLINE Process command line (often the second and following arguments will give you a hint as to which application is being run. .TP METHOD/s Count of method invocations during interval. .TP GC/s Count of garbage collections during interval. .TP OBJNEW/s Count of objects allocated during interval. .TP CLOAD/s Count of classes loaded during interval. .TP EXC/s Count of exceptions thrown during interval. .TP THR/s Count of threads created during interval. .SH OVERHEAD When using this tool with high-frequency events, such as method calls, a very significant slow-down can be expected. However, many of the high-level languages covered by this tool already have a fairly high per-method invocation cost, especially when running in interpreted mode. For the lower-frequency events, such as garbage collections or thread creations, the overhead should not be significant. Specifically, when probing Java processes and not using the "-XX:+ExtendedDTraceProbes" flag, the most expensive probes are not emitted, and the overhead should be acceptable. .SH SOURCE This is from bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc distribution for a companion _example.txt file containing example usage, output, and commentary for this tool. .SH OS Linux .SH STABILITY Unstable - in development. .SH AUTHOR Sasha Goldshtein .SH SEE ALSO trace(8), argdist(8), tplist(8) bpfcc-0.12.0/man/man8/uthreads.8000066400000000000000000000031111357404205000161710ustar00rootroot00000000000000.TH uthreads 8 "2018-10-09" "USER COMMANDS" .SH NAME uthreads, cthreads, javathreads \- Trace thread creation events in Java or pthreads. .SH SYNOPSIS .B cthreads [-h] [-v] pid .BR .B javathreads [-h] [-v] pid .BR .B uthreads [-h] [-l {c,java,none}] [-v] pid .SH DESCRIPTION This traces thread creation events in Java processes, or pthread creation events in any process. When a thread is created, its name or start address is printed. Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bcc. .SH OPTIONS .TP \-l {c,java,none} The language to trace. C and none select tracing pthreads only, regardless of the runtime being traced. .TP \-v Print the resulting BPF program, for debugging purposes. .TP pid The process id to trace. .SH EXAMPLES .TP Trace Java thread creations: # .B uthreads -l java 148 .TP Trace pthread creations: # .B uthreads 1802 .SH FIELDS .TP TIME The event's time in seconds from the beginning of the trace. .TP ID The thread's ID. The information in this column depends on the runtime. .TP TYPE Event type -- thread start, stop, or pthread event. .TP DESCRIPTION The thread's name or start address function name. .SH OVERHEAD Thread start and stop events are usually not very frequent, which makes this tool's overhead negligible. .SH SOURCE This is from bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc distribution for a companion _example.txt file containing example usage, output, and commentary for this tool. .SH OS Linux .SH STABILITY Unstable - in development. .SH AUTHOR Sasha Goldshtein .SH SEE ALSO ustat(8), trace(8) bpfcc-0.12.0/man/man8/vfscount.8000066400000000000000000000033641357404205000162330ustar00rootroot00000000000000.TH vfscount 8 "2015-08-18" "USER COMMANDS" .SH NAME vfscount \- Count VFS calls ("vfs_*"). Uses Linux eBPF/bcc. .SH SYNOPSIS .B vfscount [duration] .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 bcc. .SH OPTIONS .TP duration duration of the trace in seconds. .SH EXAMPLES .TP Count some VFS calls until Ctrl-C is hit: # .B vfscount .TP Count some VFS calls in ten seconds # .B vfscount 10 .SH FIELDS .TP ADDR Address of the instruction pointer that was traced (only useful if the FUNC column is suspicious and you would like to double check the translation). .TP FUNC Kernel function name .TP COUNT 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 bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 vfsstat(8) bpfcc-0.12.0/man/man8/vfsstat.8000066400000000000000000000034661357404205000160610ustar00rootroot00000000000000.TH vfsstat 8 "2015-08-18" "USER COMMANDS" .SH NAME vfsstat \- Statistics for some common VFS calls. Uses Linux eBPF/bcc. .SH SYNOPSIS .B vfsstat [interval [count]] .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 bcc. .SH EXAMPLES .TP Print summaries each second: # .B vfsstat .TP Print output every five seconds, three times: # .B vfsstat 5 3 .SH FIELDS .TP READ/s Number of vfs_read() calls as a per-second average. .TP WRITE/s Number of vfs_write() calls as a per-second average. .TP CREATE/s Number of vfs_create() calls as a per-second average. .TP OPEN/s Number of vfs_open() calls as a per-second average. .TP FSYNC/s Number of vfs_fsync() calls as a per-second average. .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 bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 vfscount(8) bpfcc-0.12.0/man/man8/wakeuptime.8000066400000000000000000000070311357404205000165320ustar00rootroot00000000000000.TH wakeuptime 8 "2016-01-27" "USER COMMANDS" .SH NAME wakeuptime \- Summarize sleep to wakeup time by waker kernel stack. Uses Linux eBPF/bcc. .SH SYNOPSIS .B wakeuptime [\-h] [\-u] [\-p PID] [\-v] [\-f] [\-\-stack-storage-size STACK_STORAGE_SIZE] [\-m MIN_BLOCK_TIME] [\-M MAX_BLOCK_TIME] [duration] .SH DESCRIPTION This program shows the kernel stack traces for threads that woke up other blocked threads, along with the process names of the waker and target, along with a sum of the time that the target was blocked: the "blocked time". It works by tracing when threads block and when they were then woken up, and measuring the time delta. This time measurement will be very similar to off-CPU time, however, off-CPU time may include a little extra time spent waiting on a run queue to be scheduled. The stack traces, process names, and time spent blocked is summarized in the kernel using an eBPF map for efficiency. The output summary will help you identify reasons why threads were blocking by showing who woke them up, along with the time they were blocked. This spans all types of blocking activity: disk I/O, network I/O, locks, page faults, involuntary context switches, etc. This can be used in conjunction with offcputime, which shows the stack trace of the blocked thread. wakeuptime shows the stack trace of the waker thread. See http://www.brendangregg.com/FlameGraphs/offcpuflamegraphs.html This tool only works on Linux 4.6+. It uses the new `BPF_STACK_TRACE` table APIs to generate the in-kernel stack traces. .SH REQUIREMENTS CONFIG_BPF and bcc. .SH OPTIONS .TP \-h Print usage message. .TP \-f Print output in folded stack format. .TP \-u Only trace user threads (not kernel threads). .TP \-v Show raw addresses (for non-folded format). .TP \-p PID Trace this process ID only (filtered in-kernel). .TP \-\-stack-storage-size STACK_STORAGE_SIZE Change the number of unique stack traces that can be stored and displayed. .TP duration Duration to trace, in seconds. .TP \-m MIN_BLOCK_TIME The amount of time in microseconds over which we store traces (default 1) .TP \-M MAX_BLOCK_TIME The amount of time in microseconds under which we store traces (default U64_MAX) .SH EXAMPLES .TP Trace all thread blocking events, and summarize (in-kernel) by kernel stack trace and total blocked time: # .B wakeuptime .TP Trace user-mode target threads only: # .B wakeuptime -u .TP Trace for 5 seconds only: # .B wakeuptime 5 .TP Trace for 5 seconds, and emit output in folded stack format (suitable for flame graphs): # .B wakeuptime -f 5 .TP Trace PID 185 only: # .B wakeuptime -p 185 .SH OVERHEAD This summarizes unique stack traces in-kernel for efficiency, allowing it to trace a higher rate of events than methods that post-process in user space. The stack trace and time data is only copied to user space once, when the output is printed. While these techniques greatly lower overhead, scheduler events are still a high frequency event, as they can exceed 1 million events per second, and so caution should still be used. Test before production use. If the overhead is still a problem, take a look at the min block option. If your aim is to chase down longer blocking events, then this could be increased to filter shorter blocking events, further lowering overhead. .SH SOURCE This is from bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 offcputime(8), stackcount(8) bpfcc-0.12.0/man/man8/xfsdist.8000066400000000000000000000036101357404205000160420ustar00rootroot00000000000000.TH xfsdist 8 "2016-02-12" "USER COMMANDS" .SH NAME xfsdist \- Summarize XFS operation latency. Uses Linux eBPF/bcc. .SH SYNOPSIS .B xfsdist [\-h] [\-T] [\-m] [\-p PID] [interval] [count] .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 bcc. .SH OPTIONS .TP \-h Print usage message. .TP \-T Don't include timestamps on interval output. .TP \-m Output in milliseconds. .TP \-p PID Trace this PID only. .SH EXAMPLES .TP Trace XFS operation time, and print a summary on Ctrl-C: # .B xfsdist .TP Trace PID 181 only: # .B xfsdist -p 181 .TP Print 1 second summaries, 10 times: # .B xfsdist 1 10 .TP 1 second summaries, printed in milliseconds # .B xfsdist \-m 1 .SH FIELDS .TP msecs Range of milliseconds for this bucket. .TP usecs Range of microseconds for this bucket. .TP count Number of operations in this time range. .TP distribution ASCII representation of the distribution (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 bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 xfssnoop(8) bpfcc-0.12.0/man/man8/xfsslower.8000066400000000000000000000062051357404205000164150ustar00rootroot00000000000000.TH xfsslower 8 "2016-02-11" "USER COMMANDS" .SH NAME xfsslower \- Trace slow xfs file operations, with per-event details. .SH SYNOPSIS .B xfsslower [\-h] [\-j] [\-p PID] [min_ms] .SH DESCRIPTION This tool traces common XFS file operations: reads, writes, opens, and syncs. It measures the time spent in these operations, and prints details for each that exceeded a threshold. WARNING: See the OVERHEAD section. By default, a minimum millisecond threshold of 10 is used. If a threshold of 0 is used, all events are printed (warning: verbose). 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 bcc. .SH OPTIONS \-p PID Trace this PID only. .TP min_ms Minimum I/O latency (duration) to trace, in milliseconds. Default is 10 ms. .SH EXAMPLES .TP Trace synchronous file reads and writes slower than 10 ms: # .B xfsslower .TP Trace slower than 1 ms: # .B xfsslower 1 .TP Trace slower than 1 ms, and output just the fields in parsable format (csv): # .B xfsslower \-j 1 .TP Trace all file reads and writes (warning: the output will be verbose): # .B xfsslower 0 .TP Trace slower than 1 ms, for PID 181 only: # .B xfsslower \-p 181 1 .SH FIELDS .TP TIME(s) Time of I/O completion since the first I/O seen, in seconds. .TP COMM Process name. .TP PID Process ID. .TP T Type of operation. R == read, W == write, O == open, S == fsync. .TP OFF_KB File offset for the I/O, in Kbytes. .TP BYTES Size of I/O, in bytes. .TP LAT(ms) Latency (duration) of I/O, measured from when it was issued by VFS to the filesystem, to when it completed. This time is inclusive of block device I/O, file system CPU cycles, file system locks, run queue latency, etc. It's a more accurate measure of the latency suffered by applications performing file system I/O, than to measure this down at the block device interface. .TP FILENAME A cached kernel file name (comes from dentry->d_name.name). .TP ENDTIME_us Completion timestamp, microseconds (\-j only). .TP OFFSET_b File offset, bytes (\-j only). .TP LATENCY_us Latency (duration) of the I/O, in microseconds (\-j only). .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 (even if it prints no "slower" events) can begin to become significant. Measure and quantify before use. If this continues to be a problem, consider switching to a tool that prints in-kernel summaries only. .PP Note that the overhead of this tool should be less than fileslower(8), as this tool targets xfs functions only, and not all file read/write paths (which can include socket I/O). .SH SOURCE This is from bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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), funccount(8), fileslower(8) bpfcc-0.12.0/man/man8/zfsdist.8000066400000000000000000000037341357404205000160530ustar00rootroot00000000000000.TH zfsdist 8 "2016-02-12" "USER COMMANDS" .SH NAME zfsdist \- Summarize ZFS operation latency. Uses Linux eBPF/bcc. .SH SYNOPSIS .B zfsdist [\-h] [\-T] [\-m] [\-p PID] [interval] [count] .SH DESCRIPTION This tool summarizes time (latency) spent in common ZFS 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. This uses kernel dynamic tracing of the ZPL interface (ZFS POSIX Layer), and will need updates to match any changes to this interface. .TP This is intended to work with the ZFS on Linux project: http://zfsonlinux.org .PP Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bcc. .SH OPTIONS .TP \-h Print usage message. .TP \-T Don't include timestamps on interval output. .TP \-m Output in milliseconds. .TP \-p PID Trace this PID only. .SH EXAMPLES .TP Trace ZFS operation time, and print a summary on Ctrl-C: # .B zfsdist .TP Trace PID 181 only: # .B zfsdist -p 181 .TP Print 1 second summaries, 10 times: # .B zfsdist 1 10 .TP 1 second summaries, printed in milliseconds # .B zfsdist \-m 1 .SH FIELDS .TP msecs Range of milliseconds for this bucket. .TP usecs Range of microseconds for this bucket. .TP count Number of operations in this time range. .TP distribution ASCII representation of the distribution (the count column). .SH OVERHEAD This adds low-overhead instrumentation to these ZFS 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 bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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 zfssnoop(8) bpfcc-0.12.0/man/man8/zfsslower.8000066400000000000000000000063311357404205000164170ustar00rootroot00000000000000.TH zfsslower 8 "2016-02-11" "USER COMMANDS" .SH NAME zfsslower \- Trace slow zfs file operations, with per-event details. .SH SYNOPSIS .B zfsslower [\-h] [\-j] [\-p PID] [min_ms] .SH DESCRIPTION This tool traces common ZFS file operations: reads, writes, opens, and syncs. It measures the time spent in these operations, and prints details for each that exceeded a threshold. WARNING: See the OVERHEAD section. By default, a minimum millisecond threshold of 10 is used. If a threshold of 0 is used, all events are printed (warning: verbose). This uses kernel dynamic tracing of the ZPL interface (ZFS POSIX Layer), and will need updates to match any changes to this interface. .TP This is intended to work with the ZFS on Linux project: http://zfsonlinux.org .PP Since this uses BPF, only the root user can use this tool. .SH REQUIREMENTS CONFIG_BPF and bcc. .SH OPTIONS \-p PID Trace this PID only. .TP min_ms Minimum I/O latency (duration) to trace, in milliseconds. Default is 10 ms. .SH EXAMPLES .TP Trace synchronous file reads and writes slower than 10 ms: # .B zfsslower .TP Trace slower than 1 ms: # .B zfsslower 1 .TP Trace slower than 1 ms, and output just the fields in parsable format (csv): # .B zfsslower \-j 1 .TP Trace all file reads and writes (warning: the output will be verbose): # .B zfsslower 0 .TP Trace slower than 1 ms, for PID 181 only: # .B zfsslower \-p 181 1 .SH FIELDS .TP TIME(s) Time of I/O completion since the first I/O seen, in seconds. .TP COMM Process name. .TP PID Process ID. .TP T Type of operation. R == read, W == write, O == open, S == fsync. .TP OFF_KB File offset for the I/O, in Kbytes. .TP BYTES Size of I/O, in bytes. .TP LAT(ms) Latency (duration) of I/O, measured from when it was issued by VFS to the filesystem, to when it completed. This time is inclusive of block device I/O, file system CPU cycles, file system locks, run queue latency, etc. It's a more accurate measure of the latency suffered by applications performing file system I/O, than to measure this down at the block device interface. .TP FILENAME A cached kernel file name (comes from dentry->d_name.name). .TP ENDTIME_us Completion timestamp, microseconds (\-j only). .TP OFFSET_b File offset, bytes (\-j only). .TP LATENCY_us Latency (duration) of the I/O, in microseconds (\-j only). .SH OVERHEAD This adds low-overhead instrumentation to these ZFS 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 (even if it prints no "slower" events) can begin to become significant. Measure and quantify before use. If this continues to be a problem, consider switching to a tool that prints in-kernel summaries only. .PP Note that the overhead of this tool should be less than fileslower(8), as this tool targets zfs functions only, and not all file read/write paths (which can include socket I/O). .SH SOURCE This is from bcc. .IP https://github.com/iovisor/bcc .PP Also look in the bcc 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), funccount(8), fileslower(8) bpfcc-0.12.0/scripts/000077500000000000000000000000001357404205000143365ustar00rootroot00000000000000bpfcc-0.12.0/scripts/README.md000066400000000000000000000040521357404205000156160ustar00rootroot00000000000000 ## Fedora Demo VM Before running the script, ensure that virt-install is available on the system. `./build_bpf_demo.sh -n bpf-demo -k bpf_demo.ks.erb` After setting up the initial VM, log in (the default password is 'iovisor') and determine the DHCP IP. SSH to this IP as root. To set up a kernel with the right options, run `bpf-kernel-setup`. ``` [root@bpf-demo ~]# bpf-kernel-setup Cloning into 'net-next'... ``` After pulling the net-next branch, the kernel config menu should pop up. Ensure that the below settings are proper. ``` General setup ---> [*] Enable bpf() system call Networking support ---> Networking options ---> QoS and/or fair queueing ---> BPF-based classifier BPF based action [*] enable BPF Just In Time compiler ``` Once the .config is saved, the build will proceed and install the resulting kernel. This kernel has updated userspace headers (e.g. the bpf() syscall) which install into /usr/local/include...proper packaging for this will be distro-dependent. Next, run `bpf-llvm-setup` to pull and compile LLVM with BPF support enabled. ``` [root@bpf-demo ~]# bpf-llvm-setup Cloning into 'llvm'... ``` The resulting libraries will be installed into /opt/local/llvm. Next, reboot into the new kernel, either manually or by using the kexec helper. ``` [root@bpf-demo ~]# kexec-4.1.0-rc1+ Connection to 192.168.122.247 closed by remote host. Connection to 192.168.122.247 closed. ``` Reconnect and run the final step, building and testing bcc. ``` [root@bpf-demo ~]# bcc-setup Cloning into 'bcc'... ... Linking CXX shared library libcc.so [100%] Built target bcc ... Running tests... Test project /root/bcc/build Start 1: py_test1 1/4 Test #1: py_test1 ......................... Passed 0.24 sec Start 2: py_test2 2/4 Test #2: py_test2 ......................... Passed 0.53 sec Start 3: py_trace1 3/4 Test #3: py_trace1 ........................ Passed 0.09 sec Start 4: py_trace2 4/4 Test #4: py_trace2 ........................ Passed 1.06 sec 100% tests passed, 0 tests failed out of 4 ``` bpfcc-0.12.0/scripts/bpf_demo.ks.erb000066400000000000000000000056431357404205000172270ustar00rootroot00000000000000# Minimal Kickstart file install text reboot lang en_US.UTF-8 # repo to install the OS url --url=<%= @mirror %>/Everything/x86_64/os/ keyboard us network --bootproto dhcp rootpw <%= @password %> authconfig --enableshadow --passalgo=sha512 --enablefingerprint firewall --enabled --ssh selinux --enforcing timezone --utc America/Los_Angeles #firstboot --disable bootloader --location=mbr --append="console=tty0 console=ttyS0,115200 rd_NO_PLYMOUTH crashkernel=auto" zerombr clearpart --all --initlabel autopart --type=lvm repo --name=everything --baseurl=<%= @mirror %>/Everything/x86_64/os/ #Just core packages %packages --nobase @core ntp @c-development @development-tools @rpm-development-tools ncurses-devel vim bc kexec-tools cmake libstdc++-static python-netaddr python-futures %end %post --log=/root/anaconda-post.log echo Kickstart post chkconfig NetworkManager off chkconfig network on chkconfig ntpd on dnf config-manager --add-repo=http://alt.fedoraproject.org/pub/alt/rawhide-kernel-nodebug/fedora-rawhide-kernel-nodebug.repo yum -y clean metadata yum -y update hostname <%= @name %>.<%= @domain %> echo "<%= @name %>.<%= @domain %>" > /etc/hostname cat > /usr/local/bin/bpf-kernel-setup <<'DELIM__' #!/bin/bash set -e -x numcpu=$(grep -c ^processor /proc/cpuinfo) git clone https://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next.git cd net-next/ cp /boot/config-$(uname -r) ./ cp ./config-$(uname -r) .config make -j$numcpu mrproper make -j$numcpu nconfig make -j$numcpu bzImage make -j$numcpu modules sudo make modules_install sudo make install sudo make INSTALL_HDR_PATH=/usr/local headers_install release=$( /usr/local/bin/kexec-$release chmod +x /usr/local/bin/kexec-$release ln -fs kexec-$release /usr/local/bin/kexec-latest DELIM__ chmod +x /usr/local/bin/bpf-kernel-setup cat > /usr/local/bin/bpf-llvm-setup <<'DELIM__' #!/bin/bash set -e -x numcpu=$(grep -c ^processor /proc/cpuinfo) git clone https://github.com/llvm-mirror/llvm.git git clone https://github.com/llvm-mirror/clang.git llvm/tools/clang mkdir llvm/build/ cd llvm/build/ cmake .. \ -DBUILD_SHARED_LIBS=OFF \ -DCMAKE_BUILD_TYPE=Release \ -DLLVM_ENABLE_TERMINFO=OFF \ -DLLVM_TARGETS_TO_BUILD="ARM;CppBackend;X86;BPF" \ -DCMAKE_INSTALL_PREFIX=/opt/local/llvm make -j$numcpu sudo make install grep -q llvm $HOME/.bashrc || echo 'PATH=/opt/local/llvm/bin:$PATH' >> $HOME/.bashrc DELIM__ chmod +x /usr/local/bin/bpf-llvm-setup cat > /usr/local/bin/bcc-setup <<'DELIM__' #!/bin/bash set -e -x git clone https://github.com/svinota/pyroute2.git (cd pyroute2; make install) numcpu=$(grep -c ^processor /proc/cpuinfo) git clone https://github.com/iovisor/bcc.git mkdir bcc/build/ cd bcc/build/ export PATH=/opt/local/llvm/bin:$PATH cmake .. -DCMAKE_INSTALL_PREFIX=/usr make -j$numcpu DELIM__ chmod +x /usr/local/bin/bcc-setup %end bpfcc-0.12.0/scripts/build-deb.sh000077500000000000000000000024211357404205000165230ustar00rootroot00000000000000#!/bin/bash # helper script to be invoked by jenkins/buildbot # $1 [optional]: the build type - release | nightly | test buildtype=${1:-test} set -x set -e PARALLEL=${PARALLEL:-1} TMP=$(mktemp -d /tmp/debuild.XXXXXX) function cleanup() { [[ -d $TMP ]] && rm -rf $TMP } trap cleanup EXIT # populate submodules git submodule update --init --recursive . scripts/git-tag.sh git archive HEAD --prefix=bcc/ --format=tar -o $TMP/bcc_$revision.orig.tar # archive submodules pushd src/cc/libbpf git archive HEAD --prefix=bcc/src/cc/libbpf/ --format=tar -o $TMP/bcc_libbpf_$revision.orig.tar popd pushd $TMP # merge all archives into bcc_$revision.orig.tar.gz tar -A -f bcc_$revision.orig.tar bcc_libbpf_$revision.orig.tar gzip bcc_$revision.orig.tar tar xf bcc_$revision.orig.tar.gz cd bcc debuild=debuild if [[ "$buildtype" = "test" ]]; then # when testing, use faster compression options debuild+=" --preserve-envvar PATH" echo -e '#!/bin/bash\nexec /usr/bin/dpkg-deb -z1 "$@"' \ | sudo tee /usr/local/bin/dpkg-deb sudo chmod +x /usr/local/bin/dpkg-deb dch -b -v $revision-$release "$git_subject" fi if [[ "$buildtype" = "nightly" ]]; then dch -v $revision-$release "$git_subject" fi DEB_BUILD_OPTIONS="nocheck parallel=${PARALLEL}" $debuild -us -uc popd cp $TMP/*.deb . bpfcc-0.12.0/scripts/build-deb.sh.in000077500000000000000000000005311357404205000171300ustar00rootroot00000000000000#!/bin/bash set -x set -e PARALLEL=${PARALLEL:-1} TMP=$(mktemp -d /tmp/debuild.XXXXXX) function cleanup() { [[ -d $TMP ]] && rm -rf $TMP } trap cleanup EXIT mkdir $TMP/bcc cp -a * $TMP/bcc pushd $TMP tar zcf bcc_@REVISION_LAST@.orig.tar.gz bcc/ cd bcc DEB_BUILD_OPTIONS="nocheck parallel=${PARALLEL}" debuild -us -uc popd cp $TMP/*.deb . bpfcc-0.12.0/scripts/build-release-rpm.sh000077500000000000000000000017461357404205000202160ustar00rootroot00000000000000#!/bin/bash set -x set -e TMP=$(mktemp -d /tmp/rpmbuild.XXXXXX) function cleanup() { [[ -d $TMP ]] && rm -rf $TMP } trap cleanup EXIT mkdir $TMP/{BUILD,RPMS,SOURCES,SPECS,SRPMS} llvmver=3.7.1 # populate submodules git submodule update --init --recursive . scripts/git-tag.sh git archive HEAD --prefix=bcc/ --format=tar -o $TMP/SOURCES/bcc.tar # archive submodules pushd src/cc/libbpf git archive HEAD --prefix=bcc/src/cc/libbpf/ --format=tar -o $TMP/SOURCES/bcc_libbpf.tar popd # merge all archives into $git_tag_latest.tar.gz pushd $TMP/SOURCES tar -A -f bcc.tar bcc_libbpf.tar gzip -c bcc.tar > $git_tag_latest.tar.gz popd wget -P $TMP/SOURCES http://llvm.org/releases/$llvmver/{cfe,llvm}-$llvmver.src.tar.xz sed \ -e "s/^\(Version:\s*\)@REVISION@/\1$revision/" \ -e "s/^\(Release:\s*\)@GIT_REV_COUNT@/\1$release/" \ SPECS/bcc+clang.spec > $TMP/SPECS/bcc.spec pushd $TMP rpmbuild --define "_topdir `pwd`" -ba SPECS/bcc.spec popd cp $TMP/RPMS/*/*.rpm . cp $TMP/SRPMS/*.rpm . bpfcc-0.12.0/scripts/build-rpm.sh000077500000000000000000000015601357404205000165720ustar00rootroot00000000000000#!/bin/bash set -x set -e TMP=$(mktemp -d /tmp/rpmbuild.XXXXXX) function cleanup() { [[ -d $TMP ]] && rm -rf $TMP } trap cleanup EXIT mkdir $TMP/{BUILD,RPMS,SOURCES,SPECS,SRPMS} llvmver=3.7.1 # populate submodules git submodule update --init --recursive . scripts/git-tag.sh git archive HEAD --prefix=bcc/ --format=tar -o $TMP/SOURCES/bcc.tar # archive submodules pushd src/cc/libbpf git archive HEAD --prefix=bcc/src/cc/libbpf/ --format=tar -o $TMP/SOURCES/bcc_libbpf.tar popd # merge all archives into bcc.tar.gz pushd $TMP/SOURCES tar -A -f bcc.tar bcc_libbpf.tar gzip bcc.tar popd sed \ -e "s/^\(Version:\s*\)@REVISION@/\1$revision/" \ -e "s/^\(Release:\s*\)@GIT_REV_COUNT@/\1$release/" \ SPECS/bcc.spec > $TMP/SPECS/bcc.spec pushd $TMP rpmbuild $RPM_WITH_OPTS --define "_topdir `pwd`" -ba SPECS/bcc.spec popd cp $TMP/RPMS/*/*.rpm . cp $TMP/SRPMS/*.rpm . bpfcc-0.12.0/scripts/build_bpf_demo.sh000077500000000000000000000057471357404205000176440ustar00rootroot00000000000000#!/bin/bash #set -x set -e function usage() { cat < /dev/null; then echo "Error: virt-install is not installed" exit 1 fi libvirt_dir=/var/lib/libvirt/images img_name=$NAME tmpdir=$(mktemp -d /tmp/virt-install_XXXXX) tmp_ks_file=$tmpdir/$img_name.ks function cleanup() { set +e [[ -d "$tmpdir" ]] && rm -fr "$tmpdir" local destroy_kvm=n [[ -f "/etc/libvirt/qemu/$img_name.xml" ]] && read -p "Destroy libvirt VM (y/n)? " destroy_kvm if [[ "$destroy_kvm" != n* ]]; then virsh destroy $img_name virsh undefine $img_name virsh vol-delete $img_name.img --pool default $sudo rm -f $libvirt_dir/$img_name.img fi } trap cleanup EXIT ruby < /dev/null; then echo "Could not find clang-format tool" 1>&2 exit 1 fi cmd="git clang-format $GITREF --binary $CLANG_FORMAT --diff --extensions h,c,cc" n=$($cmd --quiet | wc -l) if [ $n -gt 0 ]; then $cmd -v exit 1 fi bpfcc-0.12.0/scripts/check-helpers.sh000077500000000000000000000022471357404205000174170ustar00rootroot00000000000000#!/bin/bash ret=0 libbpf=$(grep -oP '(?<={")\w+(?=", "\d\.\d+")' src/cc/libbpf.c | sort) doc=$(grep -oP "(?<=BPF_FUNC_)\w+" docs/kernel-versions.md | sort) dif=$(diff <(echo "$doc") <(echo "$libbpf")) if [ $? -ne 0 ]; then echo "The lists of helpers in src/cc/libbpf.c and docs/kernel-versions.md differ:" echo -e "$dif\n" ((ret++)) fi compat=$(grep -oP "(?<=^\sFN\()\w+" src/cc/compat/linux/bpf.h | tail -n +2 | sort) dif=$(diff <(echo "$doc") <(echo "$compat")) if [ $? -ne 0 ]; then echo "The lists of helpers in docs/kernel-versions.md and src/cc/compat/linux/bpf.h differ:" echo -e "$dif\n" ((ret++)) fi virtual=$(grep -oP "(?<=^\sFN\()\w+" src/cc/compat/linux/virtual_bpf.h | tail -n +2 | sort -u) dif=$(diff <(echo "$compat") <(echo "$virtual")) if [ $? -ne 0 ]; then echo "The lists of helpers in src/cc/compat/linux/bpf.h and src/cc/compat/linux/virtual_bpf.h differ:" echo "$dif" ((ret++)) fi export=$(grep -oP "(?<=BPF_FUNC_)\w+" src/cc/export/helpers.h | sort -u) dif=$(diff <(echo "$compat") <(echo "$export")) if [ $? -ne 0 ]; then echo "The lists of helpers in src/cc/compat/linux/bpf.h and src/cc/export/helpers.h differ:" echo "$dif" ((ret++)) fi exit $ret bpfcc-0.12.0/scripts/git-clang-format000077500000000000000000000432521357404205000174250ustar00rootroot00000000000000#!/usr/bin/env python2 # #===- git-clang-format - ClangFormat Git Integration ---------*- python -*--===# # # The LLVM Compiler Infrastructure # # This file is distributed under the University of Illinois Open Source # License. See LICENSE.TXT for details. # #===------------------------------------------------------------------------===# r""" clang-format git integration ============================ This file provides a clang-format integration for git. Put it somewhere in your path and ensure that it is executable. Then, "git clang-format" will invoke clang-format on the changes in current files or a specific commit. For further details, run: git clang-format -h Requires Python 2.7 """ import argparse import collections import contextlib import errno import os import re import subprocess import sys usage = 'git clang-format [OPTIONS] [] [--] [...]' desc = ''' Run clang-format on all lines that differ between the working directory and , which defaults to HEAD. Changes are only applied to the working directory. The following git-config settings set the default of the corresponding option: clangFormat.binary clangFormat.commit clangFormat.extension clangFormat.style ''' # Name of the temporary index file in which save the output of clang-format. # This file is created within the .git directory. temp_index_basename = 'clang-format-index' Range = collections.namedtuple('Range', 'start, count') def main(): config = load_git_config() # In order to keep '--' yet allow options after positionals, we need to # check for '--' ourselves. (Setting nargs='*' throws away the '--', while # nargs=argparse.REMAINDER disallows options after positionals.) argv = sys.argv[1:] try: idx = argv.index('--') except ValueError: dash_dash = [] else: dash_dash = argv[idx:] argv = argv[:idx] default_extensions = ','.join([ # From clang/lib/Frontend/FrontendOptions.cpp, all lower case 'c', 'h', # C 'm', # ObjC 'mm', # ObjC++ 'cc', 'cp', 'cpp', 'c++', 'cxx', 'hpp', # C++ # Other languages that clang-format supports 'proto', 'protodevel', # Protocol Buffers 'js', # JavaScript 'ts', # TypeScript ]) p = argparse.ArgumentParser( usage=usage, formatter_class=argparse.RawDescriptionHelpFormatter, description=desc) p.add_argument('--binary', default=config.get('clangformat.binary', 'clang-format'), help='path to clang-format'), p.add_argument('--commit', default=config.get('clangformat.commit', 'HEAD'), help='default commit to use if none is specified'), p.add_argument('--diff', action='store_true', help='print a diff instead of applying the changes') p.add_argument('--extensions', default=config.get('clangformat.extensions', default_extensions), help=('comma-separated list of file extensions to format, ' 'excluding the period and case-insensitive')), p.add_argument('-f', '--force', action='store_true', help='allow changes to unstaged files') p.add_argument('-p', '--patch', action='store_true', help='select hunks interactively') p.add_argument('-q', '--quiet', action='count', default=0, help='print less information') p.add_argument('--style', default=config.get('clangformat.style', None), help='passed to clang-format'), p.add_argument('-v', '--verbose', action='count', default=0, help='print extra information') # We gather all the remaining positional arguments into 'args' since we need # to use some heuristics to determine whether or not was present. # However, to print pretty messages, we make use of metavar and help. p.add_argument('args', nargs='*', metavar='', help='revision from which to compute the diff') p.add_argument('ignored', nargs='*', metavar='...', help='if specified, only consider differences in these files') opts = p.parse_args(argv) opts.verbose -= opts.quiet del opts.quiet commit, files = interpret_args(opts.args, dash_dash, opts.commit) changed_lines = compute_diff_and_extract_lines(commit, files) if opts.verbose >= 1: ignored_files = set(changed_lines) filter_by_extension(changed_lines, opts.extensions.lower().split(',')) if opts.verbose >= 1: ignored_files.difference_update(changed_lines) if ignored_files: print 'Ignoring changes in the following files (wrong extension):' for filename in ignored_files: print ' ', filename if changed_lines: print 'Running clang-format on the following files:' for filename in changed_lines: print ' ', filename else: print 'no modified files to format' return # The computed diff outputs absolute paths, so we must cd before accessing # those files. cd_to_toplevel() old_tree = create_tree_from_workdir(changed_lines) new_tree = run_clang_format_and_save_to_tree(changed_lines, binary=opts.binary, style=opts.style) if opts.verbose >= 1: print 'old tree:', old_tree print 'new tree:', new_tree if old_tree == new_tree: if opts.verbose >= 0: print 'clang-format did not modify any files' elif opts.diff: print_diff(old_tree, new_tree) else: changed_files = apply_changes(old_tree, new_tree, force=opts.force, patch_mode=opts.patch) if (opts.verbose >= 0 and not opts.patch) or opts.verbose >= 1: print 'changed files:' for filename in changed_files: print ' ', filename def load_git_config(non_string_options=None): """Return the git configuration as a dictionary. All options are assumed to be strings unless in `non_string_options`, in which is a dictionary mapping option name (in lower case) to either "--bool" or "--int".""" if non_string_options is None: non_string_options = {} out = {} for entry in run('git', 'config', '--list', '--null').split('\0'): if entry: name, value = entry.split('\n', 1) if name in non_string_options: value = run('git', 'config', non_string_options[name], name) out[name] = value return out def interpret_args(args, dash_dash, default_commit): """Interpret `args` as "[commit] [--] [files...]" and return (commit, files). It is assumed that "--" and everything that follows has been removed from args and placed in `dash_dash`. If "--" is present (i.e., `dash_dash` is non-empty), the argument to its left (if present) is taken as commit. Otherwise, the first argument is checked if it is a commit or a file. If commit is not given, `default_commit` is used.""" if dash_dash: if len(args) == 0: commit = default_commit elif len(args) > 1: die('at most one commit allowed; %d given' % len(args)) else: commit = args[0] object_type = get_object_type(commit) if object_type not in ('commit', 'tag'): if object_type is None: die("'%s' is not a commit" % commit) else: die("'%s' is a %s, but a commit was expected" % (commit, object_type)) files = dash_dash[1:] elif args: if disambiguate_revision(args[0]): commit = args[0] files = args[1:] else: commit = default_commit files = args else: commit = default_commit files = [] return commit, files def disambiguate_revision(value): """Returns True if `value` is a revision, False if it is a file, or dies.""" # If `value` is ambiguous (neither a commit nor a file), the following # command will die with an appropriate error message. run('git', 'rev-parse', value, verbose=False) object_type = get_object_type(value) if object_type is None: return False if object_type in ('commit', 'tag'): return True die('`%s` is a %s, but a commit or filename was expected' % (value, object_type)) def get_object_type(value): """Returns a string description of an object's type, or None if it is not a valid git object.""" cmd = ['git', 'cat-file', '-t', value] p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = p.communicate() if p.returncode != 0: return None return stdout.strip() def compute_diff_and_extract_lines(commit, files): """Calls compute_diff() followed by extract_lines().""" diff_process = compute_diff(commit, files) changed_lines = extract_lines(diff_process.stdout) diff_process.stdout.close() diff_process.wait() if diff_process.returncode != 0: # Assume error was already printed to stderr. sys.exit(2) return changed_lines def compute_diff(commit, files): """Return a subprocess object producing the diff from `commit`. The return value's `stdin` file object will produce a patch with the differences between the working directory and `commit`, filtered on `files` (if non-empty). Zero context lines are used in the patch.""" cmd = ['git', 'diff-index', '-p', '-U0', commit, '--'] cmd.extend(files) p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE) p.stdin.close() return p def extract_lines(patch_file): """Extract the changed lines in `patch_file`. The return value is a dictionary mapping filename to a list of (start_line, line_count) pairs. The input must have been produced with ``-U0``, meaning unidiff format with zero lines of context. The return value is a dict mapping filename to a list of line `Range`s.""" matches = {} for line in patch_file: match = re.search(r'^\+\+\+\ [^/]+/(.*)', line) if match: filename = match.group(1).rstrip('\r\n') match = re.search(r'^@@ -[0-9,]+ \+(\d+)(,(\d+))?', line) if match: start_line = int(match.group(1)) line_count = 1 if match.group(3): line_count = int(match.group(3)) if line_count > 0: matches.setdefault(filename, []).append(Range(start_line, line_count)) return matches def filter_by_extension(dictionary, allowed_extensions): """Delete every key in `dictionary` that doesn't have an allowed extension. `allowed_extensions` must be a collection of lowercase file extensions, excluding the period.""" allowed_extensions = frozenset(allowed_extensions) for filename in dictionary.keys(): base_ext = filename.rsplit('.', 1) if len(base_ext) == 1 or base_ext[1].lower() not in allowed_extensions: del dictionary[filename] def cd_to_toplevel(): """Change to the top level of the git repository.""" toplevel = run('git', 'rev-parse', '--show-toplevel') os.chdir(toplevel) def create_tree_from_workdir(filenames): """Create a new git tree with the given files from the working directory. Returns the object ID (SHA-1) of the created tree.""" return create_tree(filenames, '--stdin') def run_clang_format_and_save_to_tree(changed_lines, binary='clang-format', style=None): """Run clang-format on each file and save the result to a git tree. Returns the object ID (SHA-1) of the created tree.""" def index_info_generator(): for filename, line_ranges in changed_lines.iteritems(): mode = oct(os.stat(filename).st_mode) blob_id = clang_format_to_blob(filename, line_ranges, binary=binary, style=style) yield '%s %s\t%s' % (mode, blob_id, filename) return create_tree(index_info_generator(), '--index-info') def create_tree(input_lines, mode): """Create a tree object from the given input. If mode is '--stdin', it must be a list of filenames. If mode is '--index-info' is must be a list of values suitable for "git update-index --index-info", such as " ". Any other mode is invalid.""" assert mode in ('--stdin', '--index-info') cmd = ['git', 'update-index', '--add', '-z', mode] with temporary_index_file(): p = subprocess.Popen(cmd, stdin=subprocess.PIPE) for line in input_lines: p.stdin.write('%s\0' % line) p.stdin.close() if p.wait() != 0: die('`%s` failed' % ' '.join(cmd)) tree_id = run('git', 'write-tree') return tree_id def clang_format_to_blob(filename, line_ranges, binary='clang-format', style=None): """Run clang-format on the given file and save the result to a git blob. Returns the object ID (SHA-1) of the created blob.""" clang_format_cmd = [binary, filename] if style: clang_format_cmd.extend(['-style='+style]) clang_format_cmd.extend([ '-lines=%s:%s' % (start_line, start_line+line_count-1) for start_line, line_count in line_ranges]) try: clang_format = subprocess.Popen(clang_format_cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE) except OSError as e: if e.errno == errno.ENOENT: die('cannot find executable "%s"' % binary) else: raise clang_format.stdin.close() hash_object_cmd = ['git', 'hash-object', '-w', '--path='+filename, '--stdin'] hash_object = subprocess.Popen(hash_object_cmd, stdin=clang_format.stdout, stdout=subprocess.PIPE) clang_format.stdout.close() stdout = hash_object.communicate()[0] if hash_object.returncode != 0: die('`%s` failed' % ' '.join(hash_object_cmd)) if clang_format.wait() != 0: die('`%s` failed' % ' '.join(clang_format_cmd)) return stdout.rstrip('\r\n') @contextlib.contextmanager def temporary_index_file(tree=None): """Context manager for setting GIT_INDEX_FILE to a temporary file and deleting the file afterward.""" index_path = create_temporary_index(tree) old_index_path = os.environ.get('GIT_INDEX_FILE') os.environ['GIT_INDEX_FILE'] = index_path try: yield finally: if old_index_path is None: del os.environ['GIT_INDEX_FILE'] else: os.environ['GIT_INDEX_FILE'] = old_index_path os.remove(index_path) def create_temporary_index(tree=None): """Create a temporary index file and return the created file's path. If `tree` is not None, use that as the tree to read in. Otherwise, an empty index is created.""" gitdir = run('git', 'rev-parse', '--git-dir') path = os.path.join(gitdir, temp_index_basename) if tree is None: tree = '--empty' run('git', 'read-tree', '--index-output='+path, tree) return path def print_diff(old_tree, new_tree): """Print the diff between the two trees to stdout.""" # We use the porcelain 'diff' and not plumbing 'diff-tree' because the output # is expected to be viewed by the user, and only the former does nice things # like color and pagination. subprocess.check_call(['git', 'diff', old_tree, new_tree, '--']) def apply_changes(old_tree, new_tree, force=False, patch_mode=False): """Apply the changes in `new_tree` to the working directory. Bails if there are local changes in those files and not `force`. If `patch_mode`, runs `git checkout --patch` to select hunks interactively.""" changed_files = run('git', 'diff-tree', '-r', '-z', '--name-only', old_tree, new_tree).rstrip('\0').split('\0') if not force: unstaged_files = run('git', 'diff-files', '--name-status', *changed_files) if unstaged_files: print >>sys.stderr, ('The following files would be modified but ' 'have unstaged changes:') print >>sys.stderr, unstaged_files print >>sys.stderr, 'Please commit, stage, or stash them first.' sys.exit(2) if patch_mode: # In patch mode, we could just as well create an index from the new tree # and checkout from that, but then the user will be presented with a # message saying "Discard ... from worktree". Instead, we use the old # tree as the index and checkout from new_tree, which gives the slightly # better message, "Apply ... to index and worktree". This is not quite # right, since it won't be applied to the user's index, but oh well. with temporary_index_file(old_tree): subprocess.check_call(['git', 'checkout', '--patch', new_tree]) index_tree = old_tree else: with temporary_index_file(new_tree): run('git', 'checkout-index', '-a', '-f') return changed_files def run(*args, **kwargs): stdin = kwargs.pop('stdin', '') verbose = kwargs.pop('verbose', True) strip = kwargs.pop('strip', True) for name in kwargs: raise TypeError("run() got an unexpected keyword argument '%s'" % name) p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE) stdout, stderr = p.communicate(input=stdin) if p.returncode == 0: if stderr: if verbose: print >>sys.stderr, '`%s` printed to stderr:' % ' '.join(args) print >>sys.stderr, stderr.rstrip() if strip: stdout = stdout.rstrip('\r\n') return stdout if verbose: print >>sys.stderr, '`%s` returned %s' % (' '.join(args), p.returncode) if stderr: print >>sys.stderr, stderr.rstrip() sys.exit(2) def die(message): print >>sys.stderr, 'error:', message sys.exit(2) if __name__ == '__main__': main() bpfcc-0.12.0/scripts/git-tag.sh000066400000000000000000000004771357404205000162360ustar00rootroot00000000000000git_tag_latest=$(git describe --abbrev=0) git_rev_count=$(git rev-list $git_tag_latest.. --count) git_rev_count=$[$git_rev_count+1] git_subject=$(git log --pretty="%s" -n 1) release=$git_rev_count if [[ "$release" != "1" ]]; then release="${release}.git.$(git log --pretty='%h' -n 1)" fi revision=${git_tag_latest:1} bpfcc-0.12.0/scripts/py-style-check.sh000077500000000000000000000010561357404205000175400ustar00rootroot00000000000000#!/bin/bash set -euo pipefail # TODO: stop ignoring this. Maybe autopep8 existing stuff? find tools -type f -name "*.py" | xargs pycodestyle -r --show-source --ignore=E123,E125,E126,E127,E128,E302 || \ echo "pycodestyle run failed, please fix it" >&2 NO_PROPER_SHEBANG="$(find tools examples -type f -executable -name '*.py' | xargs grep -L '#!/usr/bin/python')" if [ -n "$NO_PROPER_SHEBANG" ]; then echo "bad shebangs found:" echo "$NO_PROPER_SHEBANG" echo echo "either add proper shebang or remove executable bit" >&2 exit 1 fi bpfcc-0.12.0/snapcraft/000077500000000000000000000000001357404205000146305ustar00rootroot00000000000000bpfcc-0.12.0/snapcraft/Makefile000066400000000000000000000027231357404205000162740ustar00rootroot00000000000000# # Copyright (C) 2016 Canonical # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # # # Simple makefile to mangle version info in the yaml file # VERSION=$(shell git tag | sort -V | tail -1 | cut -c2-) COMMITS=$(shell git log --oneline | wc -l) SHA=$(shell git log -1 --oneline | cut -d' ' -f1) DATE=$(shell date +'%Y%m%d') V=$(VERSION)-$(DATE)-$(COMMITS)-$(SHA) all: set_version snapcraft set_version: cat snapcraft.yaml | sed 's/^version: .*/version: $(V)/' > snapcraft-tmp.yaml mv snapcraft-tmp.yaml snapcraft.yaml install: # # Install latest snap # sudo snap install --devmode bcc_*.snap # # Connect up interfaces # sudo snap connect bcc:mount-observe sudo snap connect bcc:system-observe sudo snap connect bcc:system-trace remove: sudo snap remove bcc clean: snapcraft clean rm -rf setup *.snap snapcraft bpfcc-0.12.0/snapcraft/README.md000066400000000000000000000024601357404205000161110ustar00rootroot00000000000000# bcc snap This is an unconfined snap of the BPF Compiler Collection (BCC), a toolkit for creating efficient kernel tracing and manipulation programs. First, install snapcraft, e.g. on Ubuntu: sudo apt install snapcraft Clone the bcc repo (if you haven't done so already) and create the snap: git clone https://github.com/iovisor/bcc.git cd snapcraft make Note: running `make` just gets the version from the current bcc gito repository and uses this in the snapcraft yaml file to version the bcc snap. The Makefile basically runs snapcraft to snap up bcc. Install the snap by running: sudo snap install --devmode bcc_*.snap One may need to ensure the snap plugins are enabled for the snap using: sudo snap connect bcc:mount-observe sudo snap connect bcc:system-observe sudo snap connect bcc:system-trace Now run a bcc tool, for example, to run opensnoop use: sudo bcc.opensnoop Note that this may fail to build and run if you do not have the kernel headers installed or perhaps the kernel config is not set up correctly. This snap has been tested using the mainly 4.8 and 4.9 kernels built with the Ubuntu Yakkety and Zesty kernel configs as well as the default Ubuntu 4.8 Yakkey and 4.9 Zesty kernels. Contact Colin Ian King for support on this bcc snap. Thu 15 Dec 17:08:29 GMT 2016 bpfcc-0.12.0/snapcraft/bcc-wrapper000077500000000000000000000006061357404205000167650ustar00rootroot00000000000000#!/bin/bash set -e # Snappy does not yet support CAP_SYS_ADMIN for unconfined snaps, thus sudo: # https://bugs.launchpad.net/snappy/+bug/1586581 # stdout isn't set to line buffered mode: # https://bugs.launchpad.net/snappy/+bug/1587675 cmd="$1" if [ `id -u` = 0 ] ; then shift stdbuf -oL $SNAP/usr/share/bcc/tools/$cmd "$@" else echo "Need to run $cmd as root (use sudo $@)" exit 1 fi bpfcc-0.12.0/snapcraft/snapcraft.yaml000066400000000000000000000170531357404205000175030ustar00rootroot00000000000000# # Copyright (C) 2016 Canonical # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # name: bcc version: 0.9.0-20190318-2991-6cf63612 summary: BPF Compiler Collection (BCC) description: A toolkit for creating efficient kernel tracing and manipulation programs confinement: strict grade: stable plugs: mount-observe: null system-observe: null system-trace: null assumes: [snapd2.37] base: core18 parts: bcc: plugin: cmake override-pull: | snapcraftctl pull find . -type f -exec sed -i 's|^#\!/usr/bin/python|#\!/usr/bin/env python|' {} \; configflags: - '-DCMAKE_INSTALL_PREFIX=/usr' source: .. source-type: git stage-packages: - libbz2-1.0 - liblzma5 - libncursesw5 - libtinfo5 - libzzip-0-13 build-packages: - bison - build-essential - cmake - flex - libedit-dev - libllvm4.0 - llvm-4.0-dev - libclang-4.0-dev - python - zlib1g-dev - libelf-dev - iperf prime: - usr/share/bcc/tools - usr/lib/python2.7 - usr/lib/*/lib*.so* - -usr/share/bcc/tools/doc python-deps: plugin: python python-version: python2 wrapper: plugin: dump after: [bcc] source: . organize: wrapper: bin/bcc-wrapper apps: argdist: command: bcc-wrapper argdist bashreadline: command: bcc-wrapper bashreadline biolatency: command: bcc-wrapper biolatency biosnoop: command: bcc-wrapper biosnoop biotop: command: bcc-wrapper biotop bitesize: command: bcc-wrapper bitesize bpflist: command: bcc-wrapper bpflist btrfsdist: command: bcc-wrapper btrfsdist btrfsslower: command: bcc-wrapper btrfsslower cachestat: command: bcc-wrapper cachestat cachetop: command: bcc-wrapper cachetop capable: command: bcc-wrapper capable cobjnew: command: bcc-wrapper cobjnew cpudist: command: bcc-wrapper cpudist cpuunclaimed: command: bcc-wrapper cpuunclaimed dbslower: command: bcc-wrapper dbslower dbstat: command: bcc-wrapper dbstat dcsnoop: command: bcc-wrapper dcsnoop dcstat: command: bcc-wrapper dcstat deadlock: command: bcc-wrapper deadlock execsnoop: command: bcc-wrapper execsnoop ext4dist: command: bcc-wrapper ext4dist ext4slower: command: bcc-wrapper ext4slower filelife: command: bcc-wrapper filelife fileslower: command: bcc-wrapper fileslower filetop: command: bcc-wrapper filetop funccount: command: bcc-wrapper funccount funclatency: command: bcc-wrapper funclatency funcslower: command: bcc-wrapper funcslower gethostlatency: command: bcc-wrapper gethostlatency hardirqs: command: bcc-wrapper hardirqs javacalls: command: bcc-wrapper javacalls javaflow: command: bcc-wrapper javaflow javagc: command: bcc-wrapper javagc javaobjnew: command: bcc-wrapper javaobjnew javastat: command: bcc-wrapper javastat javathreads: command: bcc-wrapper javathreads killsnoop: command: bcc-wrapper killsnoop klockstat: command: bcc-wrapper klockstat llcstat: command: bcc-wrapper llcstat mdflush: command: bcc-wrapper mdflush memleak: command: bcc-wrapper memleak mountsnoop: command: bcc-wrapper mountsnoop mysqld-qslower: command: bcc-wrapper mysqld_qslower nfsdist: command: bcc-wrapper nfsdist nfsslower: command: bcc-wrapper nfsslower nodegc: command: bcc-wrapper nodegc nodestat: command: bcc-wrapper nodestat offcputime: command: bcc-wrapper offcputime offwaketime: command: bcc-wrapper offwaketime oomkill: command: bcc-wrapper oomkill opensnoop: command: bcc-wrapper opensnoop perlcalls: command: bcc-wrapper perlcalls perlflow: command: bcc-wrapper perlflow perlstat: command: bcc-wrapper perlstat shmsnoop: command: bcc-wrapper shmsnoop sofdsnoop: command: bcc-wrapper sofdsnoop phpcalls: command: bcc-wrapper phpcalls phpflow: command: bcc-wrapper phpflow phpstat: command: bcc-wrapper phpstat pidpersec: command: bcc-wrapper pidpersec profile: command: bcc-wrapper profile pythoncalls: command: bcc-wrapper pythoncalls pythonflow: command: bcc-wrapper pythonflow pythongc: command: bcc-wrapper pythongc pythonstat: command: bcc-wrapper pythonstat rubycalls: command: bcc-wrapper rubycalls rubyflow: command: bcc-wrapper rubyflow rubygc: command: bcc-wrapper rubygc rubyobjnew: command: bcc-wrapper rubyobjnew rubystat: command: bcc-wrapper rubystat runqlat: command: bcc-wrapper runqlat runqlen: command: bcc-wrapper runqlen slabratetop: command: bcc-wrapper slabratetop softirqs: command: bcc-wrapper softirqs solisten: command: bcc-wrapper solisten sslsniff: command: bcc-wrapper sslsniff stackcount: command: bcc-wrapper stackcount statsnoop: command: bcc-wrapper statsnoop syncsnoop: command: bcc-wrapper syncsnoop syscount: command: bcc-wrapper syscount tcpaccept: command: bcc-wrapper tcpaccept tcpconnect: command: bcc-wrapper tcpconnect tcpconnlat: command: bcc-wrapper tcpconnlat tcplife: command: bcc-wrapper tcplife tcpretrans: command: bcc-wrapper tcpretrans tcptop: command: bcc-wrapper tcptop tcptracer: command: bcc-wrapper tcptracer tplist: command: bcc-wrapper tplist trace: command: bcc-wrapper trace ttysnoop: command: bcc-wrapper ttysnoop ucalls: command: bcc-wrapper lib/ucalls uflow: command: bcc-wrapper lib/uflow ugc: command: bcc-wrapper lib/ugc uobjnew: command: bcc-wrapper lib/uobjnew ustat: command: bcc-wrapper lib/ustat uthreads: command: bcc-wrapper lib/uthreads vfscount: command: bcc-wrapper vfscount vfsstat: command: bcc-wrapper vfsstat wakeuptime: command: bcc-wrapper wakeuptime xfsdist: command: bcc-wrapper xfsdist xfsslower: command: bcc-wrapper xfsslower zfsdist: command: bcc-wrapper zfsdist zfsslower: command: bcc-wrapper zfsslower # vim: set ai et sts=4 tabstop=4 sw=4: bpfcc-0.12.0/src/000077500000000000000000000000001357404205000134365ustar00rootroot00000000000000bpfcc-0.12.0/src/CMakeLists.txt000066400000000000000000000007561357404205000162060ustar00rootroot00000000000000# Copyright (c) PLUMgrid, Inc. # Licensed under the Apache License, Version 2.0 (the "License") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") if (ENABLE_RTTI) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -frtti") else() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti") endif() include_directories(${CMAKE_CURRENT_SOURCE_DIR}) include_directories(${CMAKE_CURRENT_BINARY_DIR}) if(NOT PYTHON_ONLY) add_subdirectory(cc) endif() if(ENABLE_CLANG_JIT) add_subdirectory(python) add_subdirectory(lua) endif() bpfcc-0.12.0/src/cc/000077500000000000000000000000001357404205000140235ustar00rootroot00000000000000bpfcc-0.12.0/src/cc/CMakeLists.txt000066400000000000000000000150761357404205000165740ustar00rootroot00000000000000# Copyright (c) PLUMgrid, Inc. # Licensed under the Apache License, Version 2.0 (the "License") include_directories(${CMAKE_CURRENT_BINARY_DIR}) include_directories(${CMAKE_CURRENT_SOURCE_DIR}) # to be removed include_directories(${CMAKE_CURRENT_BINARY_DIR}/frontends/b) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/frontends/b) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/frontends/clang) include_directories(${LLVM_INCLUDE_DIRS}) include_directories(${LIBELF_INCLUDE_DIRS}) # todo: if check for kernel version include_directories(${CMAKE_CURRENT_SOURCE_DIR}/libbpf/include) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/libbpf/include/uapi) add_definitions(${LLVM_DEFINITIONS}) configure_file(libbcc.pc.in ${CMAKE_CURRENT_BINARY_DIR}/libbcc.pc @ONLY) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -DBCC_PROG_TAG_DIR='\"${BCC_PROG_TAG_DIR}\"'") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-result") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC -Wno-unused-result") if (NOT HAVE_REALLOCARRAY_SUPPORT) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DCOMPAT_NEED_REALLOCARRAY") endif() string(REGEX MATCH "^([0-9]+).*" _ ${LLVM_PACKAGE_VERSION}) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DLLVM_MAJOR_VERSION=${CMAKE_MATCH_1}") include(static_libstdc++) if(LIBBPF_FOUND) # We need to include the libbpf packaged source to get the # headers location, becaseu they are installed under 'bpf' # directory, but the libbpf code references them localy include_directories(${LIBBPF_INCLUDE_DIR}/bpf) set(extract_dir ${CMAKE_CURRENT_BINARY_DIR}/libbpf_a_extract) execute_process(COMMAND sh -c "mkdir -p ${extract_dir} && cd ${extract_dir} && ${CMAKE_AR} x ${LIBBPF_STATIC_LIBRARIES}") file(GLOB libbpf_sources "${extract_dir}/*.o") else() file(GLOB libbpf_sources "libbpf/src/*.c") endif() set(libbpf_uapi libbpf/include/uapi/linux}) add_library(bpf-static STATIC libbpf.c perf_reader.c ${libbpf_sources}) set_target_properties(bpf-static PROPERTIES OUTPUT_NAME bcc_bpf) add_library(bpf-shared SHARED libbpf.c perf_reader.c ${libbpf_sources}) set_target_properties(bpf-shared PROPERTIES VERSION ${REVISION_LAST} SOVERSION 0) set_target_properties(bpf-shared PROPERTIES OUTPUT_NAME bcc_bpf) set(bcc_common_sources bcc_common.cc bpf_module.cc bcc_btf.cc exported_files.cc) if (${LLVM_PACKAGE_VERSION} VERSION_EQUAL 6 OR ${LLVM_PACKAGE_VERSION} VERSION_GREATER 6) set(bcc_common_sources ${bcc_common_sources} bcc_debug.cc) endif() if(ENABLE_LLVM_NATIVECODEGEN) set(bcc_common_sources ${bcc_common_sources} bpf_module_rw_engine.cc) else() set(bcc_common_sources ${bcc_common_sources} bpf_module_rw_engine_disabled.cc) endif() set(bcc_table_sources table_storage.cc shared_table.cc bpffs_table.cc json_map_decl_visitor.cc) set(bcc_util_sources common.cc) set(bcc_sym_sources bcc_syms.cc bcc_elf.c bcc_perf_map.c bcc_proc.c) set(bcc_common_headers libbpf.h perf_reader.h) set(bcc_table_headers file_desc.h table_desc.h table_storage.h) set(bcc_api_headers bcc_common.h bpf_module.h bcc_exception.h bcc_syms.h bcc_proc.h bcc_elf.h) if(ENABLE_CLANG_JIT) add_library(bcc-shared SHARED link_all.cc ${bcc_common_sources} ${bcc_table_sources} ${bcc_sym_sources} ${bcc_util_sources}) set_target_properties(bcc-shared PROPERTIES VERSION ${REVISION_LAST} SOVERSION 0) set_target_properties(bcc-shared PROPERTIES OUTPUT_NAME bcc) # If there's libbpf detected we build the libbcc-no-libbpf.so library, that # dynamicaly links libbpf.so, in comparison to static link in libbcc.so. if(LIBBPF_FOUND) add_library(bcc-shared-no-libbpf SHARED link_all.cc ${bcc_common_sources} ${bcc_table_sources} ${bcc_sym_sources} ${bcc_util_sources} libbpf.c perf_reader.c) set_target_properties(bcc-shared-no-libbpf PROPERTIES VERSION ${REVISION_LAST} SOVERSION 0) set_target_properties(bcc-shared-no-libbpf PROPERTIES OUTPUT_NAME bcc-no-libbpf) endif() if(ENABLE_USDT) set(bcc_usdt_sources usdt/usdt.cc usdt/usdt_args.cc) # else undefined endif() add_library(bcc-loader-static STATIC ${bcc_sym_sources} ${bcc_util_sources}) target_link_libraries(bcc-loader-static elf) add_library(bcc-static STATIC ${bcc_common_sources} ${bcc_table_sources} ${bcc_util_sources} ${bcc_usdt_sources} ${bcc_sym_sources} ${bcc_util_sources}) set_target_properties(bcc-static PROPERTIES OUTPUT_NAME bcc) set(bcc-lua-static ${bcc_common_sources} ${bcc_table_sources} ${bcc_sym_sources} ${bcc_util_sources}) include(clang_libs) set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${clang_lib_exclude_flags}") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${llvm_lib_exclude_flags}") # bcc_common_libs_for_a for archive libraries # bcc_common_libs_for_s for shared libraries set(bcc_common_libs b_frontend clang_frontend -Wl,--whole-archive ${clang_libs} ${llvm_libs} -Wl,--no-whole-archive ${LIBELF_LIBRARIES}) set(bcc_common_libs_for_a ${bcc_common_libs} bpf-static) set(bcc_common_libs_for_s ${bcc_common_libs} bpf-static) set(bcc_common_libs_for_n ${bcc_common_libs}) set(bcc_common_libs_for_lua b_frontend clang_frontend bpf-static ${clang_libs} ${llvm_libs} ${LIBELF_LIBRARIES}) if(ENABLE_CPP_API) add_subdirectory(api) list(APPEND bcc_common_libs_for_a api-static) # Keep all API functions list(APPEND bcc_common_libs_for_s -Wl,--whole-archive api-static -Wl,--no-whole-archive) list(APPEND bcc_common_libs_for_n -Wl,--whole-archive api-static -Wl,--no-whole-archive) endif() if(ENABLE_USDT) list(APPEND bcc_api_headers bcc_usdt.h) add_subdirectory(usdt) list(APPEND bcc_common_libs_for_a usdt-static) list(APPEND bcc_common_libs_for_s usdt-static) list(APPEND bcc_common_libs_for_n usdt-static) list(APPEND bcc_common_libs_for_lua usdt-static) endif() add_subdirectory(frontends) # Link against LLVM libraries target_link_libraries(bcc-shared ${bcc_common_libs_for_s}) target_link_libraries(bcc-static ${bcc_common_libs_for_a} bcc-loader-static) set(bcc-lua-static ${bcc-lua-static} ${bcc_common_libs_for_lua}) if(LIBBPF_FOUND) target_link_libraries(bcc-shared-no-libbpf ${bcc_common_libs_for_n} ${LIBBPF_LIBRARIES}) install(TARGETS bcc-shared-no-libbpf LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) endif() install(TARGETS bcc-shared LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) install(FILES ${bcc_table_headers} DESTINATION include/bcc) install(FILES ${bcc_api_headers} DESTINATION include/bcc) install(DIRECTORY ${libbpf_uapi} DESTINATION include/bcc/compat/linux FILES_MATCHING PATTERN "*.h") install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libbcc.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) endif(ENABLE_CLANG_JIT) install(FILES ${bcc_common_headers} DESTINATION include/bcc) install(TARGETS bpf-shared LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) bpfcc-0.12.0/src/cc/README000066400000000000000000000014431357404205000147050ustar00rootroot00000000000000The libbpf directory is a git submodule for repository https://github.com/libbpf/libbpf If you have any change in libbpf directory, please upstream to linux first as libbpf repo is a mirror of linux/tools/lib/bpf directory. If any top-commit update of libbpf submodule contains a uapi header change, the following are necessary steps to sync properly with rest of bcc: 1. sync compat/linux/virtual_bpf.h with libbpf/include/uapi/linux/bpf.h as virtual_bpf.h has an extra string wrapper for bpf.h. 2. if new bpf.h has new helpers, add corresponding helper func define in bcc:src/cc/export/helpers.h and helper entry for error reporting in bcc:src/cc/libbpf.c. 3. if new bpf.h has new map types, program types, update bcc:introspection/bps.c for these new map/program types. bpfcc-0.12.0/src/cc/api/000077500000000000000000000000001357404205000145745ustar00rootroot00000000000000bpfcc-0.12.0/src/cc/api/BPF.cc000066400000000000000000000714711357404205000155240ustar00rootroot00000000000000/* * Copyright (c) 2016 Facebook, Inc. * * 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. */ #include #include #include #include #include #include #include #include #include #include #include #include "bcc_exception.h" #include "bcc_elf.h" #include "bcc_syms.h" #include "bpf_module.h" #include "common.h" #include "libbpf.h" #include "perf_reader.h" #include "syms.h" #include "table_storage.h" #include "usdt.h" #include "BPF.h" namespace ebpf { std::string uint_to_hex(uint64_t value) { std::stringstream ss; ss << std::hex << value; return ss.str(); } std::string sanitize_str(std::string str, bool (*validator)(char), char replacement = '_') { for (size_t i = 0; i < str.length(); i++) if (!validator(str[i])) str[i] = replacement; return str; } StatusTuple BPF::init_usdt(const USDT& usdt) { USDT u(usdt); StatusTuple init_stp = u.init(); if (init_stp.code() != 0) { return init_stp; } usdt_.push_back(std::move(u)); all_bpf_program_ += usdt_.back().program_text_; return StatusTuple(0); } void BPF::init_fail_reset() { usdt_.clear(); all_bpf_program_ = ""; } StatusTuple BPF::init(const std::string& bpf_program, const std::vector& cflags, const std::vector& usdt) { usdt_.reserve(usdt.size()); for (const auto& u : usdt) { StatusTuple init_stp = init_usdt(u); if (init_stp.code() != 0) { init_fail_reset(); return init_stp; } } auto flags_len = cflags.size(); const char* flags[flags_len]; for (size_t i = 0; i < flags_len; i++) flags[i] = cflags[i].c_str(); all_bpf_program_ += bpf_program; if (bpf_module_->load_string(all_bpf_program_, flags, flags_len) != 0) { init_fail_reset(); return StatusTuple(-1, "Unable to initialize BPF program"); } return StatusTuple(0); }; BPF::~BPF() { auto res = detach_all(); if (res.code() != 0) std::cerr << "Failed to detach all probes on destruction: " << std::endl << res.msg() << std::endl; bcc_free_buildsymcache(bsymcache_); bsymcache_ = NULL; } StatusTuple BPF::detach_all() { bool has_error = false; std::string error_msg; for (auto& it : kprobes_) { auto res = detach_kprobe_event(it.first, it.second); if (res.code() != 0) { error_msg += "Failed to detach kprobe event " + it.first + ": "; error_msg += res.msg() + "\n"; has_error = true; } } for (auto& it : uprobes_) { auto res = detach_uprobe_event(it.first, it.second); if (res.code() != 0) { error_msg += "Failed to detach uprobe event " + it.first + ": "; error_msg += res.msg() + "\n"; has_error = true; } } for (auto& it : tracepoints_) { auto res = detach_tracepoint_event(it.first, it.second); if (res.code() != 0) { error_msg += "Failed to detach Tracepoint " + it.first + ": "; error_msg += res.msg() + "\n"; has_error = true; } } for (auto& it : perf_buffers_) { auto res = it.second->close_all_cpu(); if (res.code() != 0) { error_msg += "Failed to close perf buffer " + it.first + ": "; error_msg += res.msg() + "\n"; has_error = true; } delete it.second; } for (auto& it : perf_event_arrays_) { auto res = it.second->close_all_cpu(); if (res.code() != 0) { error_msg += "Failed to close perf event array " + it.first + ": "; error_msg += res.msg() + "\n"; has_error = true; } delete it.second; } for (auto& it : perf_events_) { auto res = detach_perf_event_all_cpu(it.second); if (res.code() != 0) { error_msg += res.msg() + "\n"; has_error = true; } } for (auto& it : funcs_) { int res = close(it.second); if (res != 0) { error_msg += "Failed to unload BPF program for " + it.first + ": "; error_msg += std::string(std::strerror(errno)) + "\n"; has_error = true; } } if (has_error) return StatusTuple(-1, error_msg); else return StatusTuple(0); } StatusTuple BPF::attach_kprobe(const std::string& kernel_func, const std::string& probe_func, uint64_t kernel_func_offset, bpf_probe_attach_type attach_type, int maxactive) { std::string probe_event = get_kprobe_event(kernel_func, attach_type); if (kprobes_.find(probe_event) != kprobes_.end()) return StatusTuple(-1, "kprobe %s already attached", probe_event.c_str()); int probe_fd; TRY2(load_func(probe_func, BPF_PROG_TYPE_KPROBE, probe_fd)); int res_fd = bpf_attach_kprobe(probe_fd, attach_type, probe_event.c_str(), kernel_func.c_str(), kernel_func_offset, maxactive); if (res_fd < 0) { TRY2(unload_func(probe_func)); return StatusTuple(-1, "Unable to attach %skprobe for %s using %s", attach_type_debug(attach_type).c_str(), kernel_func.c_str(), probe_func.c_str()); } open_probe_t p = {}; p.perf_event_fd = res_fd; p.func = probe_func; kprobes_[probe_event] = std::move(p); return StatusTuple(0); } StatusTuple BPF::attach_uprobe(const std::string& binary_path, const std::string& symbol, const std::string& probe_func, uint64_t symbol_addr, bpf_probe_attach_type attach_type, pid_t pid, uint64_t symbol_offset) { if (symbol_addr != 0 && symbol_offset != 0) return StatusTuple(-1, "Attachng uprobe with addr %lx and offset %lx is not supported", symbol_addr, symbol_offset); std::string module; uint64_t offset; TRY2(check_binary_symbol(binary_path, symbol, symbol_addr, module, offset, symbol_offset)); std::string probe_event = get_uprobe_event(module, offset, attach_type, pid); if (uprobes_.find(probe_event) != uprobes_.end()) return StatusTuple(-1, "uprobe %s already attached", probe_event.c_str()); int probe_fd; TRY2(load_func(probe_func, BPF_PROG_TYPE_KPROBE, probe_fd)); int res_fd = bpf_attach_uprobe(probe_fd, attach_type, probe_event.c_str(), binary_path.c_str(), offset, pid); if (res_fd < 0) { TRY2(unload_func(probe_func)); return StatusTuple( -1, "Unable to attach %suprobe for binary %s symbol %s addr %lx " "offset %lx using %s\n", attach_type_debug(attach_type).c_str(), binary_path.c_str(), symbol.c_str(), symbol_addr, symbol_offset, probe_func.c_str()); } open_probe_t p = {}; p.perf_event_fd = res_fd; p.func = probe_func; uprobes_[probe_event] = std::move(p); return StatusTuple(0); } StatusTuple BPF::attach_usdt(const USDT& usdt, pid_t pid) { for (const auto& u : usdt_) { if (u == usdt) { auto& probe = *static_cast<::USDT::Probe*>(u.probe_.get()); if (!probe.enable(u.probe_func_)) return StatusTuple(-1, "Unable to enable USDT " + u.print_name()); bool failed = false; std::string err_msg; int cnt = 0; for (const auto& loc : probe.locations_) { auto res = attach_uprobe(loc.bin_path_, std::string(), u.probe_func_, loc.address_, BPF_PROBE_ENTRY, pid); if (res.code() != 0) { failed = true; err_msg += "USDT " + u.print_name() + " at " + loc.bin_path_ + " address " + std::to_string(loc.address_); err_msg += ": " + res.msg() + "\n"; break; } cnt++; } if (failed) { for (int i = 0; i < cnt; i++) { auto res = detach_uprobe(probe.locations_[i].bin_path_, std::string(), probe.locations_[i].address_, BPF_PROBE_ENTRY, pid); if (res.code() != 0) err_msg += "During clean up: " + res.msg() + "\n"; } return StatusTuple(-1, err_msg); } else { return StatusTuple(0); } } } return StatusTuple(-1, "USDT %s not found", usdt.print_name().c_str()); } StatusTuple BPF::attach_tracepoint(const std::string& tracepoint, const std::string& probe_func) { if (tracepoints_.find(tracepoint) != tracepoints_.end()) return StatusTuple(-1, "Tracepoint %s already attached", tracepoint.c_str()); auto pos = tracepoint.find(":"); if ((pos == std::string::npos) || (pos != tracepoint.rfind(":"))) return StatusTuple(-1, "Unable to parse Tracepoint %s", tracepoint.c_str()); std::string tp_category = tracepoint.substr(0, pos); std::string tp_name = tracepoint.substr(pos + 1); int probe_fd; TRY2(load_func(probe_func, BPF_PROG_TYPE_TRACEPOINT, probe_fd)); int res_fd = bpf_attach_tracepoint(probe_fd, tp_category.c_str(), tp_name.c_str()); if (res_fd < 0) { TRY2(unload_func(probe_func)); return StatusTuple(-1, "Unable to attach Tracepoint %s using %s", tracepoint.c_str(), probe_func.c_str()); } open_probe_t p = {}; p.perf_event_fd = res_fd; p.func = probe_func; tracepoints_[tracepoint] = std::move(p); return StatusTuple(0); } StatusTuple BPF::attach_perf_event(uint32_t ev_type, uint32_t ev_config, const std::string& probe_func, uint64_t sample_period, uint64_t sample_freq, pid_t pid, int cpu, int group_fd) { auto ev_pair = std::make_pair(ev_type, ev_config); if (perf_events_.find(ev_pair) != perf_events_.end()) return StatusTuple(-1, "Perf event type %d config %d already attached", ev_type, ev_config); int probe_fd; TRY2(load_func(probe_func, BPF_PROG_TYPE_PERF_EVENT, probe_fd)); std::vector cpus; if (cpu >= 0) cpus.push_back(cpu); else cpus = get_online_cpus(); auto fds = new std::vector>(); fds->reserve(cpus.size()); for (int i : cpus) { int fd = bpf_attach_perf_event(probe_fd, ev_type, ev_config, sample_period, sample_freq, pid, i, group_fd); if (fd < 0) { for (const auto& it : *fds) close(it.second); delete fds; TRY2(unload_func(probe_func)); return StatusTuple(-1, "Failed to attach perf event type %d config %d", ev_type, ev_config); } fds->emplace_back(i, fd); } open_probe_t p = {}; p.func = probe_func; p.per_cpu_fd = fds; perf_events_[ev_pair] = std::move(p); return StatusTuple(0); } StatusTuple BPF::attach_perf_event_raw(void* perf_event_attr, const std::string& probe_func, pid_t pid, int cpu, int group_fd, unsigned long extra_flags) { auto attr = static_cast(perf_event_attr); auto ev_pair = std::make_pair(attr->type, attr->config); if (perf_events_.find(ev_pair) != perf_events_.end()) return StatusTuple(-1, "Perf event type %d config %d already attached", attr->type, attr->config); int probe_fd; TRY2(load_func(probe_func, BPF_PROG_TYPE_PERF_EVENT, probe_fd)); std::vector cpus; if (cpu >= 0) cpus.push_back(cpu); else cpus = get_online_cpus(); auto fds = new std::vector>(); fds->reserve(cpus.size()); for (int i : cpus) { int fd = bpf_attach_perf_event_raw(probe_fd, attr, pid, i, group_fd, extra_flags); if (fd < 0) { for (const auto& it : *fds) close(it.second); delete fds; TRY2(unload_func(probe_func)); return StatusTuple(-1, "Failed to attach perf event type %d config %d", attr->type, attr->config); } fds->emplace_back(i, fd); } open_probe_t p = {}; p.func = probe_func; p.per_cpu_fd = fds; perf_events_[ev_pair] = std::move(p); return StatusTuple(0); } StatusTuple BPF::detach_kprobe(const std::string& kernel_func, bpf_probe_attach_type attach_type) { std::string event = get_kprobe_event(kernel_func, attach_type); auto it = kprobes_.find(event); if (it == kprobes_.end()) return StatusTuple(-1, "No open %skprobe for %s", attach_type_debug(attach_type).c_str(), kernel_func.c_str()); TRY2(detach_kprobe_event(it->first, it->second)); kprobes_.erase(it); return StatusTuple(0); } StatusTuple BPF::detach_uprobe(const std::string& binary_path, const std::string& symbol, uint64_t symbol_addr, bpf_probe_attach_type attach_type, pid_t pid, uint64_t symbol_offset) { std::string module; uint64_t offset; TRY2(check_binary_symbol(binary_path, symbol, symbol_addr, module, offset, symbol_offset)); std::string event = get_uprobe_event(module, offset, attach_type, pid); auto it = uprobes_.find(event); if (it == uprobes_.end()) return StatusTuple(-1, "No open %suprobe for binary %s symbol %s addr %lx", attach_type_debug(attach_type).c_str(), binary_path.c_str(), symbol.c_str(), symbol_addr); TRY2(detach_uprobe_event(it->first, it->second)); uprobes_.erase(it); return StatusTuple(0); } StatusTuple BPF::detach_usdt(const USDT& usdt, pid_t pid) { for (const auto& u : usdt_) { if (u == usdt) { auto& probe = *static_cast<::USDT::Probe*>(u.probe_.get()); bool failed = false; std::string err_msg; for (const auto& loc : probe.locations_) { auto res = detach_uprobe(loc.bin_path_, std::string(), loc.address_, BPF_PROBE_ENTRY, pid); if (res.code() != 0) { failed = true; err_msg += "USDT " + u.print_name() + " at " + loc.bin_path_ + " address " + std::to_string(loc.address_); err_msg += ": " + res.msg() + "\n"; } } if (!probe.disable()) { failed = true; err_msg += "Unable to disable USDT " + u.print_name(); } if (failed) return StatusTuple(-1, err_msg); else return StatusTuple(0); } } return StatusTuple(-1, "USDT %s not found", usdt.print_name().c_str()); } StatusTuple BPF::detach_tracepoint(const std::string& tracepoint) { auto it = tracepoints_.find(tracepoint); if (it == tracepoints_.end()) return StatusTuple(-1, "No open Tracepoint %s", tracepoint.c_str()); TRY2(detach_tracepoint_event(it->first, it->second)); tracepoints_.erase(it); return StatusTuple(0); } StatusTuple BPF::detach_perf_event(uint32_t ev_type, uint32_t ev_config) { auto it = perf_events_.find(std::make_pair(ev_type, ev_config)); if (it == perf_events_.end()) return StatusTuple(-1, "Perf Event type %d config %d not attached", ev_type, ev_config); TRY2(detach_perf_event_all_cpu(it->second)); perf_events_.erase(it); return StatusTuple(0); } StatusTuple BPF::detach_perf_event_raw(void* perf_event_attr) { auto attr = static_cast(perf_event_attr); return detach_perf_event(attr->type, attr->config); } StatusTuple BPF::open_perf_event(const std::string& name, uint32_t type, uint64_t config) { if (perf_event_arrays_.find(name) == perf_event_arrays_.end()) { TableStorage::iterator it; if (!bpf_module_->table_storage().Find(Path({bpf_module_->id(), name}), it)) return StatusTuple(-1, "open_perf_event: unable to find table_storage %s", name.c_str()); perf_event_arrays_[name] = new BPFPerfEventArray(it->second); } auto table = perf_event_arrays_[name]; TRY2(table->open_all_cpu(type, config)); return StatusTuple(0); } StatusTuple BPF::close_perf_event(const std::string& name) { auto it = perf_event_arrays_.find(name); if (it == perf_event_arrays_.end()) return StatusTuple(-1, "Perf Event for %s not open", name.c_str()); TRY2(it->second->close_all_cpu()); return StatusTuple(0); } StatusTuple BPF::open_perf_buffer(const std::string& name, perf_reader_raw_cb cb, perf_reader_lost_cb lost_cb, void* cb_cookie, int page_cnt) { if (perf_buffers_.find(name) == perf_buffers_.end()) { TableStorage::iterator it; if (!bpf_module_->table_storage().Find(Path({bpf_module_->id(), name}), it)) return StatusTuple(-1, "open_perf_buffer: unable to find table_storage %s", name.c_str()); perf_buffers_[name] = new BPFPerfBuffer(it->second); } if ((page_cnt & (page_cnt - 1)) != 0) return StatusTuple(-1, "open_perf_buffer page_cnt must be a power of two"); auto table = perf_buffers_[name]; TRY2(table->open_all_cpu(cb, lost_cb, cb_cookie, page_cnt)); return StatusTuple(0); } StatusTuple BPF::close_perf_buffer(const std::string& name) { auto it = perf_buffers_.find(name); if (it == perf_buffers_.end()) return StatusTuple(-1, "Perf buffer for %s not open", name.c_str()); TRY2(it->second->close_all_cpu()); return StatusTuple(0); } BPFPerfBuffer* BPF::get_perf_buffer(const std::string& name) { auto it = perf_buffers_.find(name); return (it == perf_buffers_.end()) ? nullptr : it->second; } int BPF::poll_perf_buffer(const std::string& name, int timeout_ms) { auto it = perf_buffers_.find(name); if (it == perf_buffers_.end()) return -1; return it->second->poll(timeout_ms); } StatusTuple BPF::load_func(const std::string& func_name, bpf_prog_type type, int& fd) { if (funcs_.find(func_name) != funcs_.end()) { fd = funcs_[func_name]; return StatusTuple(0); } uint8_t* func_start = bpf_module_->function_start(func_name); if (!func_start) return StatusTuple(-1, "Can't find start of function %s", func_name.c_str()); size_t func_size = bpf_module_->function_size(func_name); int log_level = 0; if (flag_ & DEBUG_BPF_REGISTER_STATE) log_level = 2; else if (flag_ & DEBUG_BPF) log_level = 1; fd = bpf_module_->bcc_func_load(type, func_name.c_str(), reinterpret_cast(func_start), func_size, bpf_module_->license(), bpf_module_->kern_version(), log_level, nullptr, 0); if (fd < 0) return StatusTuple(-1, "Failed to load %s: %d", func_name.c_str(), fd); int ret = bpf_module_->annotate_prog_tag( func_name, fd, reinterpret_cast(func_start), func_size); if (ret < 0) fprintf(stderr, "WARNING: cannot get prog tag, ignore saving source with program tag\n"); funcs_[func_name] = fd; return StatusTuple(0); } StatusTuple BPF::unload_func(const std::string& func_name) { auto it = funcs_.find(func_name); if (it == funcs_.end()) return StatusTuple(0); int res = close(it->second); if (res != 0) return StatusTuple(-1, "Can't close FD for %s: %d", it->first.c_str(), res); funcs_.erase(it); return StatusTuple(0); } std::string BPF::get_syscall_fnname(const std::string& name) { if (syscall_prefix_ == nullptr) { KSyms ksym; uint64_t addr; if (ksym.resolve_name(nullptr, "sys_bpf", &addr)) syscall_prefix_.reset(new std::string("sys_")); else if (ksym.resolve_name(nullptr, "__x64_sys_bpf", &addr)) syscall_prefix_.reset(new std::string("__x64_sys_")); else syscall_prefix_.reset(new std::string()); } return *syscall_prefix_ + name; } StatusTuple BPF::check_binary_symbol(const std::string& binary_path, const std::string& symbol, uint64_t symbol_addr, std::string& module_res, uint64_t& offset_res, uint64_t symbol_offset) { bcc_symbol output; int res = bcc_resolve_symname(binary_path.c_str(), symbol.c_str(), symbol_addr, -1, nullptr, &output); if (res < 0) return StatusTuple( -1, "Unable to find offset for binary %s symbol %s address %lx", binary_path.c_str(), symbol.c_str(), symbol_addr); if (output.module) { module_res = output.module; ::free(const_cast(output.module)); } else { module_res = ""; } offset_res = output.offset + symbol_offset; return StatusTuple(0); } std::string BPF::get_kprobe_event(const std::string& kernel_func, bpf_probe_attach_type type) { std::string res = attach_type_prefix(type) + "_"; res += sanitize_str(kernel_func, &BPF::kprobe_event_validator); return res; } BPFProgTable BPF::get_prog_table(const std::string& name) { TableStorage::iterator it; if (bpf_module_->table_storage().Find(Path({bpf_module_->id(), name}), it)) return BPFProgTable(it->second); return BPFProgTable({}); } BPFCgroupArray BPF::get_cgroup_array(const std::string& name) { TableStorage::iterator it; if (bpf_module_->table_storage().Find(Path({bpf_module_->id(), name}), it)) return BPFCgroupArray(it->second); return BPFCgroupArray({}); } BPFDevmapTable BPF::get_devmap_table(const std::string& name) { TableStorage::iterator it; if (bpf_module_->table_storage().Find(Path({bpf_module_->id(), name}), it)) return BPFDevmapTable(it->second); return BPFDevmapTable({}); } BPFStackTable BPF::get_stack_table(const std::string& name, bool use_debug_file, bool check_debug_file_crc) { TableStorage::iterator it; if (bpf_module_->table_storage().Find(Path({bpf_module_->id(), name}), it)) return BPFStackTable(it->second, use_debug_file, check_debug_file_crc); return BPFStackTable({}, use_debug_file, check_debug_file_crc); } BPFStackBuildIdTable BPF::get_stackbuildid_table(const std::string &name, bool use_debug_file, bool check_debug_file_crc) { TableStorage::iterator it; if (bpf_module_->table_storage().Find(Path({bpf_module_->id(), name}), it)) return BPFStackBuildIdTable(it->second, use_debug_file, check_debug_file_crc, get_bsymcache()); return BPFStackBuildIdTable({}, use_debug_file, check_debug_file_crc, get_bsymcache()); } BPFMapInMapTable BPF::get_map_in_map_table(const std::string& name) { TableStorage::iterator it; if (bpf_module_->table_storage().Find(Path({bpf_module_->id(), name}), it)) return BPFMapInMapTable(it->second); return BPFMapInMapTable({}); } bool BPF::add_module(std::string module) { return bcc_buildsymcache_add_module(get_bsymcache(), module.c_str()) != 0 ? false : true; } std::string BPF::get_uprobe_event(const std::string& binary_path, uint64_t offset, bpf_probe_attach_type type, pid_t pid) { std::string res = attach_type_prefix(type) + "_"; res += sanitize_str(binary_path, &BPF::uprobe_path_validator); res += "_0x" + uint_to_hex(offset); if (pid != -1) res += "_" + std::to_string(pid); return res; } StatusTuple BPF::detach_kprobe_event(const std::string& event, open_probe_t& attr) { bpf_close_perf_event_fd(attr.perf_event_fd); TRY2(unload_func(attr.func)); if (bpf_detach_kprobe(event.c_str()) < 0) return StatusTuple(-1, "Unable to detach kprobe %s", event.c_str()); return StatusTuple(0); } StatusTuple BPF::detach_uprobe_event(const std::string& event, open_probe_t& attr) { bpf_close_perf_event_fd(attr.perf_event_fd); TRY2(unload_func(attr.func)); if (bpf_detach_uprobe(event.c_str()) < 0) return StatusTuple(-1, "Unable to detach uprobe %s", event.c_str()); return StatusTuple(0); } StatusTuple BPF::detach_tracepoint_event(const std::string& tracepoint, open_probe_t& attr) { bpf_close_perf_event_fd(attr.perf_event_fd); TRY2(unload_func(attr.func)); // TODO: bpf_detach_tracepoint currently does nothing. return StatusTuple(0); } StatusTuple BPF::detach_perf_event_all_cpu(open_probe_t& attr) { bool has_error = false; std::string err_msg; for (const auto& it : *attr.per_cpu_fd) { int res = bpf_close_perf_event_fd(it.second); if (res != 0) { has_error = true; err_msg += "Failed to close perf event FD " + std::to_string(it.second) + " For CPU " + std::to_string(it.first) + ": "; err_msg += std::string(std::strerror(errno)) + "\n"; } } delete attr.per_cpu_fd; TRY2(unload_func(attr.func)); if (has_error) return StatusTuple(-1, err_msg); return StatusTuple(0); } int BPF::free_bcc_memory() { return bcc_free_memory(); } USDT::USDT(const std::string& binary_path, const std::string& provider, const std::string& name, const std::string& probe_func) : initialized_(false), binary_path_(binary_path), pid_(-1), provider_(provider), name_(name), probe_func_(probe_func), mod_match_inode_only_(0) {} USDT::USDT(pid_t pid, const std::string& provider, const std::string& name, const std::string& probe_func) : initialized_(false), binary_path_(), pid_(pid), provider_(provider), name_(name), probe_func_(probe_func), mod_match_inode_only_(0) {} USDT::USDT(const std::string& binary_path, pid_t pid, const std::string& provider, const std::string& name, const std::string& probe_func) : initialized_(false), binary_path_(binary_path), pid_(pid), provider_(provider), name_(name), probe_func_(probe_func), mod_match_inode_only_(0) {} USDT::USDT(const USDT& usdt) : initialized_(false), binary_path_(usdt.binary_path_), pid_(usdt.pid_), provider_(usdt.provider_), name_(usdt.name_), probe_func_(usdt.probe_func_), mod_match_inode_only_(usdt.mod_match_inode_only_) {} USDT::USDT(USDT&& usdt) noexcept : initialized_(usdt.initialized_), binary_path_(std::move(usdt.binary_path_)), pid_(usdt.pid_), provider_(std::move(usdt.provider_)), name_(std::move(usdt.name_)), probe_func_(std::move(usdt.probe_func_)), probe_(std::move(usdt.probe_)), program_text_(std::move(usdt.program_text_)), mod_match_inode_only_(usdt.mod_match_inode_only_) { usdt.initialized_ = false; } bool USDT::operator==(const USDT& other) const { return (provider_ == other.provider_) && (name_ == other.name_) && (binary_path_ == other.binary_path_) && (pid_ == other.pid_) && (probe_func_ == other.probe_func_); } int USDT::set_probe_matching_kludge(uint8_t kludge) { if (kludge != 0 && kludge != 1) return -1; mod_match_inode_only_ = kludge; return 0; } StatusTuple USDT::init() { std::unique_ptr<::USDT::Context> ctx; if (!binary_path_.empty() && pid_ > 0) ctx.reset(new ::USDT::Context(pid_, binary_path_, mod_match_inode_only_)); else if (!binary_path_.empty()) ctx.reset(new ::USDT::Context(binary_path_, mod_match_inode_only_)); else if (pid_ > 0) ctx.reset(new ::USDT::Context(pid_, mod_match_inode_only_)); else return StatusTuple(-1, "No valid Binary Path or PID provided"); if (!ctx->loaded()) return StatusTuple(-1, "Unable to load USDT " + print_name()); auto deleter = [](void* probe) { delete static_cast<::USDT::Probe*>(probe); }; for (auto& p : ctx->probes_) { if (p->provider_ == provider_ && p->name_ == name_) { // Take ownership of the probe that we are interested in, and avoid it // being destrcuted when we destruct the USDT::Context instance probe_ = std::unique_ptr>(p.release(), deleter); p.swap(ctx->probes_.back()); ctx->probes_.pop_back(); break; } } if (!probe_) return StatusTuple(-1, "Unable to find USDT " + print_name()); ctx.reset(nullptr); auto& probe = *static_cast<::USDT::Probe*>(probe_.get()); std::ostringstream stream; if (!probe.usdt_getarg(stream, probe_func_)) return StatusTuple( -1, "Unable to generate program text for USDT " + print_name()); program_text_ = ::USDT::USDT_PROGRAM_HEADER + stream.str(); initialized_ = true; return StatusTuple(0); } } // namespace ebpf bpfcc-0.12.0/src/cc/api/BPF.h000066400000000000000000000276031357404205000153640ustar00rootroot00000000000000/* * Copyright (c) 2016 Facebook, Inc. * * 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. */ #pragma once #include #include #include #include #include #include "BPFTable.h" #include "bcc_exception.h" #include "bcc_syms.h" #include "bpf_module.h" #include "linux/bpf.h" #include "libbpf.h" #include "table_storage.h" static const int DEFAULT_PERF_BUFFER_PAGE_CNT = 8; namespace ebpf { struct open_probe_t { int perf_event_fd; std::string func; std::vector>* per_cpu_fd; }; class USDT; class BPF { public: static const int BPF_MAX_STACK_DEPTH = 127; explicit BPF(unsigned int flag = 0, TableStorage* ts = nullptr, bool rw_engine_enabled = bpf_module_rw_engine_enabled(), const std::string &maps_ns = "", bool allow_rlimit = true) : flag_(flag), bsymcache_(NULL), bpf_module_(new BPFModule(flag, ts, rw_engine_enabled, maps_ns, allow_rlimit)) {} StatusTuple init(const std::string& bpf_program, const std::vector& cflags = {}, const std::vector& usdt = {}); StatusTuple init_usdt(const USDT& usdt); ~BPF(); StatusTuple detach_all(); StatusTuple attach_kprobe(const std::string& kernel_func, const std::string& probe_func, uint64_t kernel_func_offset = 0, bpf_probe_attach_type = BPF_PROBE_ENTRY, int maxactive = 0); StatusTuple detach_kprobe( const std::string& kernel_func, bpf_probe_attach_type attach_type = BPF_PROBE_ENTRY); StatusTuple attach_uprobe(const std::string& binary_path, const std::string& symbol, const std::string& probe_func, uint64_t symbol_addr = 0, bpf_probe_attach_type attach_type = BPF_PROBE_ENTRY, pid_t pid = -1, uint64_t symbol_offset = 0); StatusTuple detach_uprobe(const std::string& binary_path, const std::string& symbol, uint64_t symbol_addr = 0, bpf_probe_attach_type attach_type = BPF_PROBE_ENTRY, pid_t pid = -1, uint64_t symbol_offset = 0); StatusTuple attach_usdt(const USDT& usdt, pid_t pid = -1); StatusTuple detach_usdt(const USDT& usdt, pid_t pid = -1); StatusTuple attach_tracepoint(const std::string& tracepoint, const std::string& probe_func); StatusTuple detach_tracepoint(const std::string& tracepoint); StatusTuple attach_perf_event(uint32_t ev_type, uint32_t ev_config, const std::string& probe_func, uint64_t sample_period, uint64_t sample_freq, pid_t pid = -1, int cpu = -1, int group_fd = -1); StatusTuple attach_perf_event_raw(void* perf_event_attr, const std::string& probe_func, pid_t pid = -1, int cpu = -1, int group_fd = -1, unsigned long extra_flags = 0); StatusTuple detach_perf_event(uint32_t ev_type, uint32_t ev_config); StatusTuple detach_perf_event_raw(void* perf_event_attr); std::string get_syscall_fnname(const std::string& name); BPFTable get_table(const std::string& name) { TableStorage::iterator it; if (bpf_module_->table_storage().Find(Path({bpf_module_->id(), name}), it)) return BPFTable(it->second); return BPFTable({}); } template BPFArrayTable get_array_table(const std::string& name) { TableStorage::iterator it; if (bpf_module_->table_storage().Find(Path({bpf_module_->id(), name}), it)) return BPFArrayTable(it->second); return BPFArrayTable({}); } template BPFPercpuArrayTable get_percpu_array_table( const std::string& name) { TableStorage::iterator it; if (bpf_module_->table_storage().Find(Path({bpf_module_->id(), name}), it)) return BPFPercpuArrayTable(it->second); return BPFPercpuArrayTable({}); } template BPFHashTable get_hash_table(const std::string& name) { TableStorage::iterator it; if (bpf_module_->table_storage().Find(Path({bpf_module_->id(), name}), it)) return BPFHashTable(it->second); return BPFHashTable({}); } template BPFPercpuHashTable get_percpu_hash_table( const std::string& name) { TableStorage::iterator it; if (bpf_module_->table_storage().Find(Path({bpf_module_->id(), name}), it)) return BPFPercpuHashTable(it->second); return BPFPercpuHashTable({}); } void* get_bsymcache(void) { if (bsymcache_ == NULL) { bsymcache_ = bcc_buildsymcache_new(); } return bsymcache_; } BPFProgTable get_prog_table(const std::string& name); BPFCgroupArray get_cgroup_array(const std::string& name); BPFDevmapTable get_devmap_table(const std::string& name); BPFStackTable get_stack_table(const std::string& name, bool use_debug_file = true, bool check_debug_file_crc = true); BPFStackBuildIdTable get_stackbuildid_table(const std::string &name, bool use_debug_file = true, bool check_debug_file_crc = true); BPFMapInMapTable get_map_in_map_table(const std::string& name); bool add_module(std::string module); StatusTuple open_perf_event(const std::string& name, uint32_t type, uint64_t config); StatusTuple close_perf_event(const std::string& name); // Open a Perf Buffer of given name, providing callback and callback cookie // to use when polling. BPF class owns the opened Perf Buffer and will free // it on-demand or on destruction. StatusTuple open_perf_buffer(const std::string& name, perf_reader_raw_cb cb, perf_reader_lost_cb lost_cb = nullptr, void* cb_cookie = nullptr, int page_cnt = DEFAULT_PERF_BUFFER_PAGE_CNT); // Close and free the Perf Buffer of given name. StatusTuple close_perf_buffer(const std::string& name); // Obtain an pointer to the opened BPFPerfBuffer instance of given name. // Will return nullptr if such open Perf Buffer doesn't exist. BPFPerfBuffer* get_perf_buffer(const std::string& name); // Poll an opened Perf Buffer of given name with given timeout, using callback // provided when opening. Do nothing if such open Perf Buffer doesn't exist. // Returns: // -1 on error or if perf buffer with such name doesn't exist; // 0, if no data was available before timeout; // number of CPUs that have new data, otherwise. int poll_perf_buffer(const std::string& name, int timeout_ms = -1); StatusTuple load_func(const std::string& func_name, enum bpf_prog_type type, int& fd); StatusTuple unload_func(const std::string& func_name); int free_bcc_memory(); private: std::string get_kprobe_event(const std::string& kernel_func, bpf_probe_attach_type type); std::string get_uprobe_event(const std::string& binary_path, uint64_t offset, bpf_probe_attach_type type, pid_t pid); StatusTuple detach_kprobe_event(const std::string& event, open_probe_t& attr); StatusTuple detach_uprobe_event(const std::string& event, open_probe_t& attr); StatusTuple detach_tracepoint_event(const std::string& tracepoint, open_probe_t& attr); StatusTuple detach_perf_event_all_cpu(open_probe_t& attr); std::string attach_type_debug(bpf_probe_attach_type type) { switch (type) { case BPF_PROBE_ENTRY: return ""; case BPF_PROBE_RETURN: return "return "; } return "ERROR"; } std::string attach_type_prefix(bpf_probe_attach_type type) { switch (type) { case BPF_PROBE_ENTRY: return "p"; case BPF_PROBE_RETURN: return "r"; } return "ERROR"; } static bool kprobe_event_validator(char c) { return (c != '+') && (c != '.'); } static bool uprobe_path_validator(char c) { return std::isalpha(c) || std::isdigit(c) || (c == '_'); } StatusTuple check_binary_symbol(const std::string& binary_path, const std::string& symbol, uint64_t symbol_addr, std::string& module_res, uint64_t& offset_res, uint64_t symbol_offset = 0); void init_fail_reset(); int flag_; void *bsymcache_; std::unique_ptr syscall_prefix_; std::unique_ptr bpf_module_; std::map funcs_; std::vector usdt_; std::string all_bpf_program_; std::map kprobes_; std::map uprobes_; std::map tracepoints_; std::map perf_buffers_; std::map perf_event_arrays_; std::map, open_probe_t> perf_events_; }; class USDT { public: USDT(const std::string& binary_path, const std::string& provider, const std::string& name, const std::string& probe_func); USDT(pid_t pid, const std::string& provider, const std::string& name, const std::string& probe_func); USDT(const std::string& binary_path, pid_t pid, const std::string& provider, const std::string& name, const std::string& probe_func); USDT(const USDT& usdt); USDT(USDT&& usdt) noexcept; StatusTuple init(); bool operator==(const USDT& other) const; std::string print_name() const { return provider_ + ":" + name_ + " from binary " + binary_path_ + " PID " + std::to_string(pid_) + " for probe " + probe_func_; } friend std::ostream& operator<<(std::ostream& out, const USDT& usdt) { return out << usdt.provider_ << ":" << usdt.name_ << " from binary " << usdt.binary_path_ << " PID " << usdt.pid_ << " for probe " << usdt.probe_func_; } // When the kludge flag is set to 1, we will only match on inode // when searching for modules in /proc/PID/maps that might contain the // tracepoint we're looking for. Normally match is on inode and // (dev_major, dev_minor), which is a more accurate way to uniquely // identify a file. // // This hack exists because btrfs reports different device numbers for files // in /proc/PID/maps vs stat syscall. Don't use it unless you're using btrfs // // set_probe_matching_kludge(1) must be called before USDTs are submitted to // BPF::init() int set_probe_matching_kludge(uint8_t kludge); private: bool initialized_; std::string binary_path_; pid_t pid_; std::string provider_; std::string name_; std::string probe_func_; std::unique_ptr> probe_; std::string program_text_; uint8_t mod_match_inode_only_; friend class BPF; }; } // namespace ebpf bpfcc-0.12.0/src/cc/api/BPFTable.cc000066400000000000000000000514241357404205000164700ustar00rootroot00000000000000/* * Copyright (c) 2016 Facebook, Inc. * * 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. */ #include #include #include #include #include #include #include #include #include #include #include #include "BPFTable.h" #include "bcc_exception.h" #include "bcc_syms.h" #include "common.h" #include "file_desc.h" #include "libbpf.h" #include "perf_reader.h" namespace ebpf { BPFTable::BPFTable(const TableDesc& desc) : BPFTableBase(desc) {} StatusTuple BPFTable::get_value(const std::string& key_str, std::string& value_str) { char key[desc.key_size]; char value[desc.leaf_size]; StatusTuple r(0); r = string_to_key(key_str, key); if (r.code() != 0) return r; if (!lookup(key, value)) return StatusTuple(-1, "error getting value"); return leaf_to_string(value, value_str); } StatusTuple BPFTable::get_value(const std::string& key_str, std::vector& value_str) { size_t ncpus = get_possible_cpus().size(); char key[desc.key_size]; char value[desc.leaf_size * ncpus]; StatusTuple r(0); r = string_to_key(key_str, key); if (r.code() != 0) return r; if (!lookup(key, value)) return StatusTuple(-1, "error getting value"); value_str.resize(ncpus); for (size_t i = 0; i < ncpus; i++) { r = leaf_to_string(value + i * desc.leaf_size, value_str.at(i)); if (r.code() != 0) return r; } return StatusTuple(0); } StatusTuple BPFTable::update_value(const std::string& key_str, const std::string& value_str) { char key[desc.key_size]; char value[desc.leaf_size]; StatusTuple r(0); r = string_to_key(key_str, key); if (r.code() != 0) return r; r = string_to_leaf(value_str, value); if (r.code() != 0) return r; if (!update(key, value)) return StatusTuple(-1, "error updating element"); return StatusTuple(0); } StatusTuple BPFTable::update_value(const std::string& key_str, const std::vector& value_str) { size_t ncpus = get_possible_cpus().size(); char key[desc.key_size]; char value[desc.leaf_size * ncpus]; StatusTuple r(0); r = string_to_key(key_str, key); if (r.code() != 0) return r; if (value_str.size() != ncpus) return StatusTuple(-1, "bad value size"); for (size_t i = 0; i < ncpus; i++) { r = string_to_leaf(value_str.at(i), value + i * desc.leaf_size); if (r.code() != 0) return r; } if (!update(key, value)) return StatusTuple(-1, "error updating element"); return StatusTuple(0); } StatusTuple BPFTable::remove_value(const std::string& key_str) { char key[desc.key_size]; StatusTuple r(0); r = string_to_key(key_str, key); if (r.code() != 0) return r; if (!remove(key)) return StatusTuple(-1, "error removing element"); return StatusTuple(0); } StatusTuple BPFTable::clear_table_non_atomic() { if (desc.type == BPF_MAP_TYPE_HASH || desc.type == BPF_MAP_TYPE_PERCPU_HASH || desc.type == BPF_MAP_TYPE_LRU_HASH || desc.type == BPF_MAP_TYPE_PERCPU_HASH || desc.type == BPF_MAP_TYPE_HASH_OF_MAPS) { // For hash maps, use the first() interface (which uses get_next_key) to // iterate through the map and clear elements auto key = std::unique_ptr(::malloc(desc.key_size), ::free); while (this->first(key.get())) if (!this->remove(key.get())) { return StatusTuple(-1, "Failed to delete element when clearing table %s", desc.name.c_str()); } } else if (desc.type == BPF_MAP_TYPE_ARRAY || desc.type == BPF_MAP_TYPE_PERCPU_ARRAY) { return StatusTuple(-1, "Array map %s do not support clearing elements", desc.name.c_str()); } else if (desc.type == BPF_MAP_TYPE_PROG_ARRAY || desc.type == BPF_MAP_TYPE_PERF_EVENT_ARRAY || desc.type == BPF_MAP_TYPE_STACK_TRACE || desc.type == BPF_MAP_TYPE_ARRAY_OF_MAPS) { // For Stack-trace and FD arrays, just iterate over all indices for (size_t i = 0; i < desc.max_entries; i++) { this->remove(&i); } } else { return StatusTuple(-1, "Clearing for map type of %s not supported yet", desc.name.c_str()); } return StatusTuple(0); } StatusTuple BPFTable::get_table_offline( std::vector> &res) { StatusTuple r(0); int err; auto key = std::unique_ptr(::malloc(desc.key_size), ::free); auto value = std::unique_ptr(::malloc(desc.leaf_size), ::free); std::string key_str; std::string value_str; if (desc.type == BPF_MAP_TYPE_ARRAY || desc.type == BPF_MAP_TYPE_PROG_ARRAY || desc.type == BPF_MAP_TYPE_PERF_EVENT_ARRAY || desc.type == BPF_MAP_TYPE_PERCPU_ARRAY || desc.type == BPF_MAP_TYPE_CGROUP_ARRAY || desc.type == BPF_MAP_TYPE_ARRAY_OF_MAPS || desc.type == BPF_MAP_TYPE_DEVMAP || desc.type == BPF_MAP_TYPE_CPUMAP || desc.type == BPF_MAP_TYPE_REUSEPORT_SOCKARRAY) { // For arrays, just iterate over all indices for (size_t i = 0; i < desc.max_entries; i++) { err = bpf_lookup_elem(desc.fd, &i, value.get()); if (err < 0 && errno == ENOENT) { // Element is not present, skip it continue; } else if (err < 0) { // Other error, abort return StatusTuple(-1, "Error looking up value: %s", std::strerror(errno)); } r = key_to_string(&i, key_str); if (r.code() != 0) return r; r = leaf_to_string(value.get(), value_str); if (r.code() != 0) return r; res.emplace_back(key_str, value_str); } } else { res.clear(); // For other maps, try to use the first() and next() interfaces if (!this->first(key.get())) return StatusTuple(0); while (true) { if (!this->lookup(key.get(), value.get())) break; r = key_to_string(key.get(), key_str); if (r.code() != 0) return r; r = leaf_to_string(value.get(), value_str); if (r.code() != 0) return r; res.emplace_back(key_str, value_str); if (!this->next(key.get(), key.get())) break; } } return StatusTuple(0); } size_t BPFTable::get_possible_cpu_count() { return get_possible_cpus().size(); } BPFStackTable::BPFStackTable(const TableDesc& desc, bool use_debug_file, bool check_debug_file_crc) : BPFTableBase(desc) { if (desc.type != BPF_MAP_TYPE_STACK_TRACE) throw std::invalid_argument("Table '" + desc.name + "' is not a stack table"); symbol_option_ = {.use_debug_file = use_debug_file, .check_debug_file_crc = check_debug_file_crc, .lazy_symbolize = 1, .use_symbol_type = (1 << STT_FUNC) | (1 << STT_GNU_IFUNC)}; } BPFStackTable::BPFStackTable(BPFStackTable&& that) : BPFTableBase(that.desc), symbol_option_(std::move(that.symbol_option_)), pid_sym_(std::move(that.pid_sym_)) { that.pid_sym_.clear(); } BPFStackTable::~BPFStackTable() { for (auto it : pid_sym_) bcc_free_symcache(it.second, it.first); } void BPFStackTable::clear_table_non_atomic() { for (int i = 0; size_t(i) < capacity(); i++) { remove(&i); } } std::vector BPFStackTable::get_stack_addr(int stack_id) { std::vector res; stacktrace_t stack; if (stack_id < 0) return res; if (!lookup(&stack_id, &stack)) return res; for (int i = 0; (i < BPF_MAX_STACK_DEPTH) && (stack.ip[i] != 0); i++) res.push_back(stack.ip[i]); return res; } std::vector BPFStackTable::get_stack_symbol(int stack_id, int pid) { auto addresses = get_stack_addr(stack_id); std::vector res; if (addresses.empty()) return res; res.reserve(addresses.size()); if (pid < 0) pid = -1; if (pid_sym_.find(pid) == pid_sym_.end()) pid_sym_[pid] = bcc_symcache_new(pid, &symbol_option_); void* cache = pid_sym_[pid]; bcc_symbol symbol; for (auto addr : addresses) if (bcc_symcache_resolve(cache, addr, &symbol) != 0) res.emplace_back("[UNKNOWN]"); else { res.push_back(symbol.demangle_name); bcc_symbol_free_demangle_name(&symbol); } return res; } BPFStackBuildIdTable::BPFStackBuildIdTable(const TableDesc& desc, bool use_debug_file, bool check_debug_file_crc, void *bsymcache) : BPFTableBase(desc), bsymcache_(bsymcache) { if (desc.type != BPF_MAP_TYPE_STACK_TRACE) throw std::invalid_argument("Table '" + desc.name + "' is not a stack table"); symbol_option_ = {.use_debug_file = use_debug_file, .check_debug_file_crc = check_debug_file_crc, .lazy_symbolize = 1, .use_symbol_type = (1 << STT_FUNC) | (1 << STT_GNU_IFUNC)}; } void BPFStackBuildIdTable::clear_table_non_atomic() { for (int i = 0; size_t(i) < capacity(); i++) { remove(&i); } } std::vector BPFStackBuildIdTable::get_stack_addr(int stack_id) { std::vector res; struct stacktrace_buildid_t stack; if (stack_id < 0) return res; if (!lookup(&stack_id, &stack)) return res; for (int i = 0; (i < BPF_MAX_STACK_DEPTH) && \ (stack.trace[i].status == BPF_STACK_BUILD_ID_VALID); i++) { /* End of stack marker is BCC_STACK_BUILD_ID_EMPTY or * BCC_STACK_BUILD_IP(fallback) mechanism. * We do not support fallback mechanism */ res.push_back(stack.trace[i]); } return res; } std::vector BPFStackBuildIdTable::get_stack_symbol(int stack_id) { auto addresses = get_stack_addr(stack_id); std::vector res; if (addresses.empty()) return res; res.reserve(addresses.size()); bcc_symbol symbol; struct bpf_stack_build_id trace; for (auto addr : addresses) { memcpy(trace.build_id, addr.build_id, sizeof(trace.build_id)); trace.status = addr.status; trace.offset = addr.offset; if (bcc_buildsymcache_resolve(bsymcache_,&trace,&symbol) != 0) { res.emplace_back("[UNKNOWN]"); } else { res.push_back(symbol.name); bcc_symbol_free_demangle_name(&symbol); } } return res; } BPFPerfBuffer::BPFPerfBuffer(const TableDesc& desc) : BPFTableBase(desc), epfd_(-1) { if (desc.type != BPF_MAP_TYPE_PERF_EVENT_ARRAY) throw std::invalid_argument("Table '" + desc.name + "' is not a perf buffer"); } StatusTuple BPFPerfBuffer::open_on_cpu(perf_reader_raw_cb cb, perf_reader_lost_cb lost_cb, int cpu, void* cb_cookie, int page_cnt) { if (cpu_readers_.find(cpu) != cpu_readers_.end()) return StatusTuple(-1, "Perf buffer already open on CPU %d", cpu); auto reader = static_cast( bpf_open_perf_buffer(cb, lost_cb, cb_cookie, -1, cpu, page_cnt)); if (reader == nullptr) return StatusTuple(-1, "Unable to construct perf reader"); int reader_fd = perf_reader_fd(reader); if (!update(&cpu, &reader_fd)) { perf_reader_free(static_cast(reader)); return StatusTuple(-1, "Unable to open perf buffer on CPU %d: %s", cpu, std::strerror(errno)); } struct epoll_event event = {}; event.events = EPOLLIN; event.data.ptr = static_cast(reader); if (epoll_ctl(epfd_, EPOLL_CTL_ADD, reader_fd, &event) != 0) { perf_reader_free(static_cast(reader)); return StatusTuple(-1, "Unable to add perf_reader FD to epoll: %s", std::strerror(errno)); } cpu_readers_[cpu] = reader; return StatusTuple(0); } StatusTuple BPFPerfBuffer::open_all_cpu(perf_reader_raw_cb cb, perf_reader_lost_cb lost_cb, void* cb_cookie, int page_cnt) { if (cpu_readers_.size() != 0 || epfd_ != -1) return StatusTuple(-1, "Previously opened perf buffer not cleaned"); std::vector cpus = get_online_cpus(); ep_events_.reset(new epoll_event[cpus.size()]); epfd_ = epoll_create1(EPOLL_CLOEXEC); for (int i : cpus) { auto res = open_on_cpu(cb, lost_cb, i, cb_cookie, page_cnt); if (res.code() != 0) { TRY2(close_all_cpu()); return res; } } return StatusTuple(0); } StatusTuple BPFPerfBuffer::close_on_cpu(int cpu) { auto it = cpu_readers_.find(cpu); if (it == cpu_readers_.end()) return StatusTuple(0); perf_reader_free(static_cast(it->second)); if (!remove(const_cast(&(it->first)))) return StatusTuple(-1, "Unable to close perf buffer on CPU %d", it->first); cpu_readers_.erase(it); return StatusTuple(0); } StatusTuple BPFPerfBuffer::close_all_cpu() { std::string errors; bool has_error = false; if (epfd_ >= 0) { int close_res = close(epfd_); epfd_ = -1; ep_events_.reset(); if (close_res != 0) { has_error = true; errors += std::string(std::strerror(errno)) + "\n"; } } std::vector opened_cpus; for (auto it : cpu_readers_) opened_cpus.push_back(it.first); for (int i : opened_cpus) { auto res = close_on_cpu(i); if (res.code() != 0) { errors += "Failed to close CPU" + std::to_string(i) + " perf buffer: "; errors += res.msg() + "\n"; has_error = true; } } if (has_error) return StatusTuple(-1, errors); return StatusTuple(0); } int BPFPerfBuffer::poll(int timeout_ms) { if (epfd_ < 0) return -1; int cnt = epoll_wait(epfd_, ep_events_.get(), cpu_readers_.size(), timeout_ms); for (int i = 0; i < cnt; i++) perf_reader_event_read(static_cast(ep_events_[i].data.ptr)); return cnt; } BPFPerfBuffer::~BPFPerfBuffer() { auto res = close_all_cpu(); if (res.code() != 0) std::cerr << "Failed to close all perf buffer on destruction: " << res.msg() << std::endl; } BPFPerfEventArray::BPFPerfEventArray(const TableDesc& desc) : BPFTableBase(desc) { if (desc.type != BPF_MAP_TYPE_PERF_EVENT_ARRAY) throw std::invalid_argument("Table '" + desc.name + "' is not a perf event array"); } StatusTuple BPFPerfEventArray::open_all_cpu(uint32_t type, uint64_t config) { if (cpu_fds_.size() != 0) return StatusTuple(-1, "Previously opened perf event not cleaned"); std::vector cpus = get_online_cpus(); for (int i : cpus) { auto res = open_on_cpu(i, type, config); if (res.code() != 0) { TRY2(close_all_cpu()); return res; } } return StatusTuple(0); } StatusTuple BPFPerfEventArray::close_all_cpu() { std::string errors; bool has_error = false; std::vector opened_cpus; for (auto it : cpu_fds_) opened_cpus.push_back(it.first); for (int i : opened_cpus) { auto res = close_on_cpu(i); if (res.code() != 0) { errors += "Failed to close CPU" + std::to_string(i) + " perf event: "; errors += res.msg() + "\n"; has_error = true; } } if (has_error) return StatusTuple(-1, errors); return StatusTuple(0); } StatusTuple BPFPerfEventArray::open_on_cpu(int cpu, uint32_t type, uint64_t config) { if (cpu_fds_.find(cpu) != cpu_fds_.end()) return StatusTuple(-1, "Perf event already open on CPU %d", cpu); int fd = bpf_open_perf_event(type, config, -1, cpu); if (fd < 0) { return StatusTuple(-1, "Error constructing perf event %" PRIu32 ":%" PRIu64, type, config); } if (!update(&cpu, &fd)) { bpf_close_perf_event_fd(fd); return StatusTuple(-1, "Unable to open perf event on CPU %d: %s", cpu, std::strerror(errno)); } cpu_fds_[cpu] = fd; return StatusTuple(0); } StatusTuple BPFPerfEventArray::close_on_cpu(int cpu) { auto it = cpu_fds_.find(cpu); if (it == cpu_fds_.end()) { return StatusTuple(0); } bpf_close_perf_event_fd(it->second); cpu_fds_.erase(it); return StatusTuple(0); } BPFPerfEventArray::~BPFPerfEventArray() { auto res = close_all_cpu(); if (res.code() != 0) { std::cerr << "Failed to close all perf buffer on destruction: " << res.msg() << std::endl; } } BPFProgTable::BPFProgTable(const TableDesc& desc) : BPFTableBase(desc) { if (desc.type != BPF_MAP_TYPE_PROG_ARRAY) throw std::invalid_argument("Table '" + desc.name + "' is not a prog table"); } StatusTuple BPFProgTable::update_value(const int& index, const int& prog_fd) { if (!this->update(const_cast(&index), const_cast(&prog_fd))) return StatusTuple(-1, "Error updating value: %s", std::strerror(errno)); return StatusTuple(0); } StatusTuple BPFProgTable::remove_value(const int& index) { if (!this->remove(const_cast(&index))) return StatusTuple(-1, "Error removing value: %s", std::strerror(errno)); return StatusTuple(0); } BPFCgroupArray::BPFCgroupArray(const TableDesc& desc) : BPFTableBase(desc) { if (desc.type != BPF_MAP_TYPE_CGROUP_ARRAY) throw std::invalid_argument("Table '" + desc.name + "' is not a cgroup array"); } StatusTuple BPFCgroupArray::update_value(const int& index, const int& cgroup2_fd) { if (!this->update(const_cast(&index), const_cast(&cgroup2_fd))) return StatusTuple(-1, "Error updating value: %s", std::strerror(errno)); return StatusTuple(0); } StatusTuple BPFCgroupArray::update_value(const int& index, const std::string& cgroup2_path) { FileDesc f(::open(cgroup2_path.c_str(), O_RDONLY | O_CLOEXEC)); if ((int)f < 0) return StatusTuple(-1, "Unable to open %s", cgroup2_path.c_str()); TRY2(update_value(index, (int)f)); return StatusTuple(0); } StatusTuple BPFCgroupArray::remove_value(const int& index) { if (!this->remove(const_cast(&index))) return StatusTuple(-1, "Error removing value: %s", std::strerror(errno)); return StatusTuple(0); } BPFDevmapTable::BPFDevmapTable(const TableDesc& desc) : BPFTableBase(desc) { if(desc.type != BPF_MAP_TYPE_DEVMAP) throw std::invalid_argument("Table '" + desc.name + "' is not a devmap table"); } StatusTuple BPFDevmapTable::update_value(const int& index, const int& value) { if (!this->update(const_cast(&index), const_cast(&value))) return StatusTuple(-1, "Error updating value: %s", std::strerror(errno)); return StatusTuple(0); } StatusTuple BPFDevmapTable::get_value(const int& index, int& value) { if (!this->lookup(const_cast(&index), &value)) return StatusTuple(-1, "Error getting value: %s", std::strerror(errno)); return StatusTuple(0); } StatusTuple BPFDevmapTable::remove_value(const int& index) { if (!this->remove(const_cast(&index))) return StatusTuple(-1, "Error removing value: %s", std::strerror(errno)); return StatusTuple(0); } BPFMapInMapTable::BPFMapInMapTable(const TableDesc& desc) : BPFTableBase(desc) { if(desc.type != BPF_MAP_TYPE_ARRAY_OF_MAPS && desc.type != BPF_MAP_TYPE_HASH_OF_MAPS) throw std::invalid_argument("Table '" + desc.name + "' is not a map-in-map table"); } StatusTuple BPFMapInMapTable::update_value(const int& index, const int& inner_map_fd) { if (!this->update(const_cast(&index), const_cast(&inner_map_fd))) return StatusTuple(-1, "Error updating value: %s", std::strerror(errno)); return StatusTuple(0); } StatusTuple BPFMapInMapTable::remove_value(const int& index) { if (!this->remove(const_cast(&index))) return StatusTuple(-1, "Error removing value: %s", std::strerror(errno)); return StatusTuple(0); } } // namespace ebpf bpfcc-0.12.0/src/cc/api/BPFTable.h000066400000000000000000000301101357404205000163170ustar00rootroot00000000000000/* * Copyright (c) 2016 Facebook, Inc. * * 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. */ #pragma once #include #include #include #include #include #include #include #include #include #include "bcc_exception.h" #include "bcc_syms.h" #include "bpf_module.h" #include "libbpf.h" #include "perf_reader.h" #include "table_desc.h" namespace ebpf { template class BPFTableBase { public: size_t capacity() { return desc.max_entries; } StatusTuple string_to_key(const std::string& key_str, KeyType* key) { return desc.key_sscanf(key_str.c_str(), key); } StatusTuple string_to_leaf(const std::string& value_str, ValueType* value) { return desc.leaf_sscanf(value_str.c_str(), value); } StatusTuple key_to_string(const KeyType* key, std::string& key_str) { char buf[8 * desc.key_size]; StatusTuple rc = desc.key_snprintf(buf, sizeof(buf), key); if (!rc.code()) key_str.assign(buf); return rc; } StatusTuple leaf_to_string(const ValueType* value, std::string& value_str) { char buf[8 * desc.leaf_size]; StatusTuple rc = desc.leaf_snprintf(buf, sizeof(buf), value); if (!rc.code()) value_str.assign(buf); return rc; } int get_fd() { return desc.fd; } protected: explicit BPFTableBase(const TableDesc& desc) : desc(desc) {} bool lookup(void* key, void* value) { return bpf_lookup_elem(desc.fd, key, value) >= 0; } bool first(void* key) { return bpf_get_first_key(desc.fd, key, desc.key_size) >= 0; } bool next(void* key, void* next_key) { return bpf_get_next_key(desc.fd, key, next_key) >= 0; } bool update(void* key, void* value) { return bpf_update_elem(desc.fd, key, value, 0) >= 0; } bool remove(void* key) { return bpf_delete_elem(desc.fd, key) >= 0; } const TableDesc& desc; }; class BPFTable : public BPFTableBase { public: BPFTable(const TableDesc& desc); StatusTuple get_value(const std::string& key_str, std::string& value); StatusTuple get_value(const std::string& key_str, std::vector& value); StatusTuple update_value(const std::string& key_str, const std::string& value_str); StatusTuple update_value(const std::string& key_str, const std::vector& value_str); StatusTuple remove_value(const std::string& key_str); StatusTuple clear_table_non_atomic(); StatusTuple get_table_offline(std::vector> &res); static size_t get_possible_cpu_count(); }; template void* get_value_addr(ValueType& t) { return &t; } template void* get_value_addr(std::vector& t) { return t.data(); } template class BPFArrayTable : public BPFTableBase { public: BPFArrayTable(const TableDesc& desc) : BPFTableBase(desc) { if (desc.type != BPF_MAP_TYPE_ARRAY && desc.type != BPF_MAP_TYPE_PERCPU_ARRAY) throw std::invalid_argument("Table '" + desc.name + "' is not an array table"); } virtual StatusTuple get_value(const int& index, ValueType& value) { if (!this->lookup(const_cast(&index), get_value_addr(value))) return StatusTuple(-1, "Error getting value: %s", std::strerror(errno)); return StatusTuple(0); } virtual StatusTuple update_value(const int& index, const ValueType& value) { if (!this->update(const_cast(&index), get_value_addr(const_cast(value)))) return StatusTuple(-1, "Error updating value: %s", std::strerror(errno)); return StatusTuple(0); } ValueType operator[](const int& key) { ValueType value; get_value(key, value); return value; } std::vector get_table_offline() { std::vector res(this->capacity()); for (int i = 0; i < (int)this->capacity(); i++) { get_value(i, res[i]); } return res; } }; template class BPFPercpuArrayTable : public BPFArrayTable> { public: BPFPercpuArrayTable(const TableDesc& desc) : BPFArrayTable>(desc), ncpus(BPFTable::get_possible_cpu_count()) { if (desc.type != BPF_MAP_TYPE_PERCPU_ARRAY) throw std::invalid_argument("Table '" + desc.name + "' is not a percpu array table"); // leaf structures have to be aligned to 8 bytes as hardcoded in the linux // kernel. if (sizeof(ValueType) % 8) throw std::invalid_argument("leaf must be aligned to 8 bytes"); } StatusTuple get_value(const int& index, std::vector& value) { value.resize(ncpus); return BPFArrayTable>::get_value(index, value); } StatusTuple update_value(const int& index, const std::vector& value) { if (value.size() != ncpus) return StatusTuple(-1, "bad value size"); return BPFArrayTable>::update_value(index, value); } private: unsigned int ncpus; }; template class BPFHashTable : public BPFTableBase { public: explicit BPFHashTable(const TableDesc& desc) : BPFTableBase(desc) { if (desc.type != BPF_MAP_TYPE_HASH && desc.type != BPF_MAP_TYPE_PERCPU_HASH && desc.type != BPF_MAP_TYPE_LRU_HASH && desc.type != BPF_MAP_TYPE_LRU_PERCPU_HASH) throw std::invalid_argument("Table '" + desc.name + "' is not a hash table"); } virtual StatusTuple get_value(const KeyType& key, ValueType& value) { if (!this->lookup(const_cast(&key), get_value_addr(value))) return StatusTuple(-1, "Error getting value: %s", std::strerror(errno)); return StatusTuple(0); } virtual StatusTuple update_value(const KeyType& key, const ValueType& value) { if (!this->update(const_cast(&key), get_value_addr(const_cast(value)))) return StatusTuple(-1, "Error updating value: %s", std::strerror(errno)); return StatusTuple(0); } virtual StatusTuple remove_value(const KeyType& key) { if (!this->remove(const_cast(&key))) return StatusTuple(-1, "Error removing value: %s", std::strerror(errno)); return StatusTuple(0); } ValueType operator[](const KeyType& key) { ValueType value; get_value(key, value); return value; } std::vector> get_table_offline() { std::vector> res; KeyType cur; ValueType value; StatusTuple r(0); if (!this->first(&cur)) return res; while (true) { r = get_value(cur, value); if (r.code() != 0) break; res.emplace_back(cur, value); if (!this->next(&cur, &cur)) break; } return res; } StatusTuple clear_table_non_atomic() { KeyType cur; while (this->first(&cur)) TRY2(remove_value(cur)); return StatusTuple(0); } }; template class BPFPercpuHashTable : public BPFHashTable> { public: explicit BPFPercpuHashTable(const TableDesc& desc) : BPFHashTable>(desc), ncpus(BPFTable::get_possible_cpu_count()) { if (desc.type != BPF_MAP_TYPE_PERCPU_HASH && desc.type != BPF_MAP_TYPE_LRU_PERCPU_HASH) throw std::invalid_argument("Table '" + desc.name + "' is not a percpu hash table"); // leaf structures have to be aligned to 8 bytes as hardcoded in the linux // kernel. if (sizeof(ValueType) % 8) throw std::invalid_argument("leaf must be aligned to 8 bytes"); } StatusTuple get_value(const KeyType& key, std::vector& value) { value.resize(ncpus); return BPFHashTable>::get_value(key, value); } StatusTuple update_value(const KeyType& key, const std::vector& value) { if (value.size() != ncpus) return StatusTuple(-1, "bad value size"); return BPFHashTable>::update_value(key, value); } private: unsigned int ncpus; }; // From src/cc/export/helpers.h static const int BPF_MAX_STACK_DEPTH = 127; struct stacktrace_t { uintptr_t ip[BPF_MAX_STACK_DEPTH]; }; class BPFStackTable : public BPFTableBase { public: BPFStackTable(const TableDesc& desc, bool use_debug_file, bool check_debug_file_crc); BPFStackTable(BPFStackTable&& that); ~BPFStackTable(); void clear_table_non_atomic(); std::vector get_stack_addr(int stack_id); std::vector get_stack_symbol(int stack_id, int pid); private: bcc_symbol_option symbol_option_; std::map pid_sym_; }; // from src/cc/export/helpers.h struct stacktrace_buildid_t { struct bpf_stack_build_id trace[BPF_MAX_STACK_DEPTH]; }; class BPFStackBuildIdTable : public BPFTableBase { public: BPFStackBuildIdTable(const TableDesc& desc, bool use_debug_file, bool check_debug_file_crc, void *bsymcache); ~BPFStackBuildIdTable() = default; void clear_table_non_atomic(); std::vector get_stack_addr(int stack_id); std::vector get_stack_symbol(int stack_id); private: void *bsymcache_; bcc_symbol_option symbol_option_; }; class BPFPerfBuffer : public BPFTableBase { public: BPFPerfBuffer(const TableDesc& desc); ~BPFPerfBuffer(); StatusTuple open_all_cpu(perf_reader_raw_cb cb, perf_reader_lost_cb lost_cb, void* cb_cookie, int page_cnt); StatusTuple close_all_cpu(); int poll(int timeout_ms); private: StatusTuple open_on_cpu(perf_reader_raw_cb cb, perf_reader_lost_cb lost_cb, int cpu, void* cb_cookie, int page_cnt); StatusTuple close_on_cpu(int cpu); std::map cpu_readers_; int epfd_; std::unique_ptr ep_events_; }; class BPFPerfEventArray : public BPFTableBase { public: BPFPerfEventArray(const TableDesc& desc); ~BPFPerfEventArray(); StatusTuple open_all_cpu(uint32_t type, uint64_t config); StatusTuple close_all_cpu(); private: StatusTuple open_on_cpu(int cpu, uint32_t type, uint64_t config); StatusTuple close_on_cpu(int cpu); std::map cpu_fds_; }; class BPFProgTable : public BPFTableBase { public: BPFProgTable(const TableDesc& desc); StatusTuple update_value(const int& index, const int& prog_fd); StatusTuple remove_value(const int& index); }; class BPFCgroupArray : public BPFTableBase { public: BPFCgroupArray(const TableDesc& desc); StatusTuple update_value(const int& index, const int& cgroup2_fd); StatusTuple update_value(const int& index, const std::string& cgroup2_path); StatusTuple remove_value(const int& index); }; class BPFDevmapTable : public BPFTableBase { public: BPFDevmapTable(const TableDesc& desc); StatusTuple update_value(const int& index, const int& value); StatusTuple get_value(const int& index, int& value); StatusTuple remove_value(const int& index); }; class BPFMapInMapTable : public BPFTableBase { public: BPFMapInMapTable(const TableDesc& desc); StatusTuple update_value(const int& index, const int& inner_map_fd); StatusTuple remove_value(const int& index); }; } // namespace ebpf bpfcc-0.12.0/src/cc/api/CMakeLists.txt000066400000000000000000000002431357404205000173330ustar00rootroot00000000000000set(bcc_api_sources BPF.cc BPFTable.cc) add_library(api-static STATIC ${bcc_api_sources}) install(FILES BPF.h BPFTable.h COMPONENT libbcc DESTINATION include/bcc) bpfcc-0.12.0/src/cc/bcc_btf.cc000066400000000000000000000251241357404205000157200ustar00rootroot00000000000000/* * Copyright (c) 2019 Facebook, Inc. * * 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. */ #include "bcc_btf.h" #include #include #include "linux/btf.h" #include "libbpf.h" #include "libbpf/src/libbpf.h" #include "libbpf/src/btf.h" #include #define BCC_MAX_ERRNO 4095 #define BCC_IS_ERR_VALUE(x) ((x) >= (unsigned long)-BCC_MAX_ERRNO) #define BCC_IS_ERR(ptr) BCC_IS_ERR_VALUE((unsigned long)ptr) namespace ebpf { int32_t BTFStringTable::addString(std::string S) { // Check whether the string already exists. for (auto &OffsetM : OffsetToIdMap) { if (Table[OffsetM.second] == S) return OffsetM.first; } // Make sure we do not overflow the string table. if (OrigTblLen + Size + S.size() + 1 >= BTF_MAX_NAME_OFFSET) return -1; // Not find, add to the string table. uint32_t Offset = Size; OffsetToIdMap[Offset] = Table.size(); Table.push_back(S); Size += S.size() + 1; return Offset; } BTF::BTF(bool debug, sec_map_def §ions) : debug_(debug), btf_(nullptr), btf_ext_(nullptr), sections_(sections) { if (!debug) libbpf_set_print(NULL); } BTF::~BTF() { btf__free(btf_); btf_ext__free(btf_ext_); } void BTF::warning(const char *format, ...) { va_list args; if (!debug_) return; va_start(args, format); vfprintf(stderr, format, args); va_end(args); } // Adjust datasec types. The compiler is not able to determine // the datasec size, so we need to set the value properly based // on actual section size. void BTF::fixup_datasec(uint8_t *type_sec, uintptr_t type_sec_size, char *strings) { uint8_t *next_type = type_sec; uint8_t *end_type = type_sec + type_sec_size; int base_size = sizeof(struct btf_type); char *secname; while (next_type < end_type) { struct btf_type *t = (struct btf_type *)next_type; unsigned short vlen = BTF_INFO_VLEN(t->info); next_type += base_size; switch(BTF_INFO_KIND(t->info)) { case BTF_KIND_FWD: case BTF_KIND_CONST: case BTF_KIND_VOLATILE: case BTF_KIND_RESTRICT: case BTF_KIND_PTR: case BTF_KIND_TYPEDEF: case BTF_KIND_FUNC: break; case BTF_KIND_INT: next_type += sizeof(uint32_t); break; case BTF_KIND_ENUM: next_type += vlen * sizeof(struct btf_enum); break; case BTF_KIND_ARRAY: next_type += sizeof(struct btf_array); break; case BTF_KIND_STRUCT: case BTF_KIND_UNION: next_type += vlen * sizeof(struct btf_member); break; case BTF_KIND_FUNC_PROTO: next_type += vlen * sizeof(struct btf_param); break; case BTF_KIND_VAR: next_type += sizeof(struct btf_var); break; case BTF_KIND_DATASEC: { secname = strings + t->name_off; if (sections_.find(secname) != sections_.end()) { t->size = std::get<1>(sections_[secname]); } // For section name, the kernel does not accept '/'. // So change DataSec name here to please the kenerl // as we do not really use DataSec yet. // This change can be removed if the kernel is // changed to accpet '/' in section names. if (strncmp(strings + t->name_off, "maps/", 5) == 0) t->name_off += 5; // fill in the in-section offset as JIT did not // do relocations. Did not cross-verify correctness with // symbol table as we do not use it yet. struct btf_var_secinfo *var_info = (struct btf_var_secinfo *)next_type; uint32_t offset = 0; for (int index = 0; index < vlen; index++) { var_info->offset = offset; offset += var_info->size; var_info++; } next_type += vlen * sizeof(struct btf_var_secinfo); break; } default: // Something not understood return; } } } // The compiler doesn't have source code for remapped files. // So we modify .BTF and .BTF.ext sections here to add these // missing line source codes. // The .BTF and .BTF.ext ELF section specification can be // found at linux repo: linux/Documentation/bpf/btf.rst. void BTF::adjust(uint8_t *btf_sec, uintptr_t btf_sec_size, uint8_t *btf_ext_sec, uintptr_t btf_ext_sec_size, std::map &remapped_sources, uint8_t **new_btf_sec, uintptr_t *new_btf_sec_size) { // Line cache for remapped files std::map> LineCaches; for (auto it = remapped_sources.begin(); it != remapped_sources.end(); ++it) { size_t FileBufSize = it->second.size(); std::vector LineCache; for (uint32_t start = 0, end = start; end < FileBufSize; end++) { if (it->second[end] == '\n' || end == FileBufSize - 1 || (it->second[end] == '\r' && it->second[end + 1] == '\n')) { // Not including the endline LineCache.push_back(std::string(it->second.substr(start, end - start))); if (it->second[end] == '\r') end++; start = end + 1; } } LineCaches[it->first] = std::move(LineCache); } struct btf_header *hdr = (struct btf_header *)btf_sec; struct btf_ext_header *ehdr = (struct btf_ext_header *)btf_ext_sec; // Fixup datasec. fixup_datasec(btf_sec + hdr->hdr_len + hdr->type_off, hdr->type_len, (char *)(btf_sec + hdr->hdr_len + hdr->str_off)); // Check the LineInfo table and add missing lines char *strings = (char *)(btf_sec + hdr->hdr_len + hdr->str_off); unsigned orig_strings_len = hdr->str_len; unsigned *linfo_s = (unsigned *)(btf_ext_sec + ehdr->hdr_len + ehdr->line_info_off); unsigned lrec_size = *linfo_s; linfo_s++; unsigned linfo_len = ehdr->line_info_len - 4; // Go through all line info. For any line number whose line is in the LineCaches, // Correct the line_off and record the corresponding source line in BTFStringTable, // which later will be merged into .BTF string section. BTFStringTable new_strings(orig_strings_len); bool overflow = false; while (!overflow && linfo_len) { unsigned num_recs = linfo_s[1]; linfo_s += 2; for (unsigned i = 0; !overflow && i < num_recs; i++) { struct bpf_line_info *linfo = (struct bpf_line_info *)linfo_s; if (linfo->line_off == 0) { for (auto it = LineCaches.begin(); it != LineCaches.end(); ++it) { if (strcmp(strings + linfo->file_name_off, it->first.c_str()) == 0) { unsigned line_num = BPF_LINE_INFO_LINE_NUM(linfo->line_col); if (line_num > 0 && line_num <= it->second.size()) { int offset = new_strings.addString(it->second[line_num - 1]); if (offset < 0) { overflow = true; warning(".BTF string table overflowed, some lines missing\n"); break; } linfo->line_off = orig_strings_len + offset; } } } } linfo_s += lrec_size >> 2; } linfo_len -= 8 + num_recs * lrec_size; } // If any new source lines need to be recorded, do not touch the original section, // allocate a new section. The original section is allocated through llvm infra. if (new_strings.getSize() > 0) { // LLVM generated .BTF layout always has type_sec followed by str_sec without holes, // so we can just append the new strings to the end and adjust str_sec size. unsigned tmp_sec_size = btf_sec_size + new_strings.getSize(); uint8_t *tmp_sec = new uint8_t[tmp_sec_size]; memcpy(tmp_sec, btf_sec, btf_sec_size); struct btf_header *nhdr = (struct btf_header *)tmp_sec; nhdr->str_len += new_strings.getSize(); // Populate new strings to the string table. uint8_t *new_str = tmp_sec + nhdr->hdr_len + nhdr->str_off + orig_strings_len; std::vector &Table = new_strings.getTable(); for (unsigned i = 0; i < Table.size(); i++) { strcpy((char *)new_str, Table[i].c_str()); new_str += Table[i].size() + 1; } *new_btf_sec = tmp_sec; *new_btf_sec_size = tmp_sec_size; } } int BTF::load(uint8_t *btf_sec, uintptr_t btf_sec_size, uint8_t *btf_ext_sec, uintptr_t btf_ext_sec_size, std::map &remapped_sources) { struct btf *btf; struct btf_ext *btf_ext; uint8_t *new_btf_sec = NULL; uintptr_t new_btf_sec_size = 0; adjust(btf_sec, btf_sec_size, btf_ext_sec, btf_ext_sec_size, remapped_sources, &new_btf_sec, &new_btf_sec_size); if (new_btf_sec) { btf = btf__new(new_btf_sec, new_btf_sec_size); delete[] new_btf_sec; } else { btf = btf__new(btf_sec, btf_sec_size); } if (BCC_IS_ERR(btf)) { warning("Processing .BTF section failed\n"); return -1; } if (btf__load(btf)) { btf__free(btf); warning("Loading .BTF section failed\n"); return -1; } btf_ext = btf_ext__new(btf_ext_sec, btf_ext_sec_size); if (BCC_IS_ERR(btf_ext)) { btf__free(btf); warning("Processing .BTF.ext section failed\n"); return -1; } btf_ = btf; btf_ext_ = btf_ext; return 0; } int BTF::get_fd() { return btf__fd(btf_); } int BTF::get_btf_info(const char *fname, void **func_info, unsigned *func_info_cnt, unsigned *finfo_rec_size, void **line_info, unsigned *line_info_cnt, unsigned *linfo_rec_size) { int ret; *func_info = *line_info = NULL; *func_info_cnt = *line_info_cnt = 0; *finfo_rec_size = btf_ext__func_info_rec_size(btf_ext_); *linfo_rec_size = btf_ext__line_info_rec_size(btf_ext_); ret = btf_ext__reloc_func_info(btf_, btf_ext_, fname, 0, func_info, func_info_cnt); if (ret) { warning(".BTF.ext reloc func_info failed\n"); return ret; } ret = btf_ext__reloc_line_info(btf_, btf_ext_, fname, 0, line_info, line_info_cnt); if (ret) { warning(".BTF.ext reloc line_info failed\n"); return ret; } return 0; } int BTF::get_map_tids(std::string map_name, unsigned expected_ksize, unsigned expected_vsize, unsigned *key_tid, unsigned *value_tid) { return btf__get_map_kv_tids(btf_, map_name.c_str(), expected_ksize, expected_vsize, key_tid, value_tid); } } // namespace ebpf bpfcc-0.12.0/src/cc/bcc_btf.h000066400000000000000000000045131357404205000155610ustar00rootroot00000000000000/* * Copyright (c) 2019 Facebook, Inc. * * 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. */ #ifndef BCC_BTF_H #define BCC_BTF_H #include #include #include #include #include #include "bpf_module.h" struct btf; struct btf_ext; namespace ebpf { class BTFStringTable { private: uint32_t Size; uint32_t OrigTblLen; std::map OffsetToIdMap; std::vector Table; public: BTFStringTable(uint32_t TblLen): Size(0), OrigTblLen(TblLen) {} uint32_t getSize() { return Size; } std::vector &getTable() { return Table; } int32_t addString(std::string Str); }; class BTF { public: BTF(bool debug, sec_map_def §ions); ~BTF(); int load(uint8_t *btf_sec, uintptr_t btf_sec_size, uint8_t *btf_ext_sec, uintptr_t btf_ext_sec_size, std::map &remapped_sources); int get_fd(); int get_btf_info(const char *fname, void **func_info, unsigned *func_info_cnt, unsigned *finfo_rec_size, void **line_info, unsigned *line_info_cnt, unsigned *linfo_rec_size); int get_map_tids(std::string map_name, unsigned expected_ksize, unsigned expected_vsize, unsigned *key_tid, unsigned *value_tid); private: void fixup_datasec(uint8_t *type_sec, uintptr_t type_sec_size, char *strings); void adjust(uint8_t *btf_sec, uintptr_t btf_sec_size, uint8_t *btf_ext_sec, uintptr_t btf_ext_sec_size, std::map &remapped_sources, uint8_t **new_btf_sec, uintptr_t *new_btf_sec_size); void warning(const char *format, ...); private: bool debug_; struct btf *btf_; struct btf_ext *btf_ext_; sec_map_def §ions_; }; } // namespace ebpf #endif bpfcc-0.12.0/src/cc/bcc_common.cc000066400000000000000000000203571357404205000164400ustar00rootroot00000000000000/* * Copyright (c) 2015 PLUMgrid, Inc. * * 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. */ #include "bcc_common.h" #include "bpf_module.h" extern "C" { void * bpf_module_create_b(const char *filename, const char *proto_filename, unsigned flags, const char *dev_name) { auto mod = new ebpf::BPFModule(flags, nullptr, true, "", true, dev_name); if (mod->load_b(filename, proto_filename) != 0) { delete mod; return nullptr; } return mod; } void * bpf_module_create_c(const char *filename, unsigned flags, const char *cflags[], int ncflags, bool allow_rlimit, const char *dev_name) { auto mod = new ebpf::BPFModule(flags, nullptr, true, "", allow_rlimit, dev_name); if (mod->load_c(filename, cflags, ncflags) != 0) { delete mod; return nullptr; } return mod; } void * bpf_module_create_c_from_string(const char *text, unsigned flags, const char *cflags[], int ncflags, bool allow_rlimit, const char *dev_name) { auto mod = new ebpf::BPFModule(flags, nullptr, true, "", allow_rlimit, dev_name); if (mod->load_string(text, cflags, ncflags) != 0) { delete mod; return nullptr; } return mod; } void bpf_module_destroy(void *program) { auto mod = static_cast(program); if (!mod) return; delete mod; } size_t bpf_num_functions(void *program) { auto mod = static_cast(program); if (!mod) return 0; return mod->num_functions(); } const char * bpf_function_name(void *program, size_t id) { auto mod = static_cast(program); if (!mod) return nullptr; return mod->function_name(id); } void * bpf_function_start(void *program, const char *name) { auto mod = static_cast(program); if (!mod) return nullptr; return mod->function_start(name); } void * bpf_function_start_id(void *program, size_t id) { auto mod = static_cast(program); if (!mod) return nullptr; return mod->function_start(id); } size_t bpf_function_size(void *program, const char *name) { auto mod = static_cast(program); if (!mod) return 0; return mod->function_size(name); } size_t bpf_function_size_id(void *program, size_t id) { auto mod = static_cast(program); if (!mod) return 0; return mod->function_size(id); } char * bpf_module_license(void *program) { auto mod = static_cast(program); if (!mod) return nullptr; return mod->license(); } unsigned bpf_module_kern_version(void *program) { auto mod = static_cast(program); if (!mod) return 0; return mod->kern_version(); } size_t bpf_num_tables(void *program) { auto mod = static_cast(program); if (!mod) return -1; return mod->num_tables(); } size_t bpf_table_id(void *program, const char *table_name) { auto mod = static_cast(program); if (!mod) return ~0ull; return mod->table_id(table_name); } int bpf_table_fd(void *program, const char *table_name) { auto mod = static_cast(program); if (!mod) return -1; return mod->table_fd(table_name); } int bpf_table_fd_id(void *program, size_t id) { auto mod = static_cast(program); if (!mod) return -1; return mod->table_fd(id); } int bpf_table_type(void *program, const char *table_name) { auto mod = static_cast(program); if (!mod) return -1; return mod->table_type(table_name); } int bpf_table_type_id(void *program, size_t id) { auto mod = static_cast(program); if (!mod) return -1; return mod->table_type(id); } size_t bpf_table_max_entries(void *program, const char *table_name) { auto mod = static_cast(program); if (!mod) return 0; return mod->table_max_entries(table_name); } size_t bpf_table_max_entries_id(void *program, size_t id) { auto mod = static_cast(program); if (!mod) return 0; return mod->table_max_entries(id); } int bpf_table_flags(void *program, const char *table_name) { auto mod = static_cast(program); if (!mod) return -1; return mod->table_flags(table_name); } int bpf_table_flags_id(void *program, size_t id) { auto mod = static_cast(program); if (!mod) return -1; return mod->table_flags(id); } const char * bpf_table_name(void *program, size_t id) { auto mod = static_cast(program); if (!mod) return nullptr; return mod->table_name(id); } const char * bpf_table_key_desc(void *program, const char *table_name) { auto mod = static_cast(program); if (!mod) return nullptr; return mod->table_key_desc(table_name); } const char * bpf_table_key_desc_id(void *program, size_t id) { auto mod = static_cast(program); if (!mod) return nullptr; return mod->table_key_desc(id); } const char * bpf_table_leaf_desc(void *program, const char *table_name) { auto mod = static_cast(program); if (!mod) return nullptr; return mod->table_leaf_desc(table_name); } const char * bpf_table_leaf_desc_id(void *program, size_t id) { auto mod = static_cast(program); if (!mod) return nullptr; return mod->table_leaf_desc(id); } size_t bpf_table_key_size(void *program, const char *table_name) { auto mod = static_cast(program); if (!mod) return 0; return mod->table_key_size(table_name); } size_t bpf_table_key_size_id(void *program, size_t id) { auto mod = static_cast(program); if (!mod) return 0; return mod->table_key_size(id); } size_t bpf_table_leaf_size(void *program, const char *table_name) { auto mod = static_cast(program); if (!mod) return 0; return mod->table_leaf_size(table_name); } size_t bpf_table_leaf_size_id(void *program, size_t id) { auto mod = static_cast(program); if (!mod) return 0; return mod->table_leaf_size(id); } int bpf_table_key_snprintf(void *program, size_t id, char *buf, size_t buflen, const void *key) { auto mod = static_cast(program); if (!mod) return -1; return mod->table_key_printf(id, buf, buflen, key); } int bpf_table_leaf_snprintf(void *program, size_t id, char *buf, size_t buflen, const void *leaf) { auto mod = static_cast(program); if (!mod) return -1; return mod->table_leaf_printf(id, buf, buflen, leaf); } int bpf_table_key_sscanf(void *program, size_t id, const char *buf, void *key) { auto mod = static_cast(program); if (!mod) return -1; return mod->table_key_scanf(id, buf, key); } int bpf_table_leaf_sscanf(void *program, size_t id, const char *buf, void *leaf) { auto mod = static_cast(program); if (!mod) return -1; return mod->table_leaf_scanf(id, buf, leaf); } int bcc_func_load(void *program, int prog_type, const char *name, const struct bpf_insn *insns, int prog_len, const char *license, unsigned kern_version, int log_level, char *log_buf, unsigned log_buf_size, const char *dev_name) { auto mod = static_cast(program); if (!mod) return -1; return mod->bcc_func_load(prog_type, name, insns, prog_len, license, kern_version, log_level, log_buf, log_buf_size, dev_name); } size_t bpf_perf_event_fields(void *program, const char *event) { auto mod = static_cast(program); if (!mod) return 0; return mod->perf_event_fields(event); } const char * bpf_perf_event_field(void *program, const char *event, size_t i) { auto mod = static_cast(program); if (!mod) return nullptr; return mod->perf_event_field(event, i); } } bpfcc-0.12.0/src/cc/bcc_common.h000066400000000000000000000072111357404205000162740ustar00rootroot00000000000000/* * Copyright (c) 2015 PLUMgrid, Inc. * * 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. */ #ifndef BCC_COMMON_H #define BCC_COMMON_H #include #include #include #ifdef __cplusplus extern "C" { #endif void * bpf_module_create_b(const char *filename, const char *proto_filename, unsigned flags, const char *dev_name); void * bpf_module_create_c(const char *filename, unsigned flags, const char *cflags[], int ncflags, bool allow_rlimit, const char *dev_name); void * bpf_module_create_c_from_string(const char *text, unsigned flags, const char *cflags[], int ncflags, bool allow_rlimit, const char *dev_name); void bpf_module_destroy(void *program); char * bpf_module_license(void *program); unsigned bpf_module_kern_version(void *program); size_t bpf_num_functions(void *program); const char * bpf_function_name(void *program, size_t id); void * bpf_function_start_id(void *program, size_t id); void * bpf_function_start(void *program, const char *name); size_t bpf_function_size_id(void *program, size_t id); size_t bpf_function_size(void *program, const char *name); size_t bpf_num_tables(void *program); size_t bpf_table_id(void *program, const char *table_name); int bpf_table_fd(void *program, const char *table_name); int bpf_table_fd_id(void *program, size_t id); int bpf_table_type(void *program, const char *table_name); int bpf_table_type_id(void *program, size_t id); size_t bpf_table_max_entries(void *program, const char *table_name); size_t bpf_table_max_entries_id(void *program, size_t id); int bpf_table_flags(void *program, const char *table_name); int bpf_table_flags_id(void *program, size_t id); const char * bpf_table_name(void *program, size_t id); const char * bpf_table_key_desc(void *program, const char *table_name); const char * bpf_table_key_desc_id(void *program, size_t id); const char * bpf_table_leaf_desc(void *program, const char *table_name); const char * bpf_table_leaf_desc_id(void *program, size_t id); size_t bpf_table_key_size(void *program, const char *table_name); size_t bpf_table_key_size_id(void *program, size_t id); size_t bpf_table_leaf_size(void *program, const char *table_name); size_t bpf_table_leaf_size_id(void *program, size_t id); int bpf_table_key_snprintf(void *program, size_t id, char *buf, size_t buflen, const void *key); int bpf_table_leaf_snprintf(void *program, size_t id, char *buf, size_t buflen, const void *leaf); int bpf_table_key_sscanf(void *program, size_t id, const char *buf, void *key); int bpf_table_leaf_sscanf(void *program, size_t id, const char *buf, void *leaf); size_t bpf_perf_event_fields(void *program, const char *event); const char * bpf_perf_event_field(void *program, const char *event, size_t i); struct bpf_insn; int bcc_func_load(void *program, int prog_type, const char *name, const struct bpf_insn *insns, int prog_len, const char *license, unsigned kern_version, int log_level, char *log_buf, unsigned log_buf_size, const char *dev_name); #ifdef __cplusplus } #endif #endif bpfcc-0.12.0/src/cc/bcc_debug.cc000066400000000000000000000171161357404205000162350ustar00rootroot00000000000000/* * Copyright (c) 2017 Facebook, Inc. * * 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "bcc_debug.h" namespace ebpf { // ld_pseudo can only be disassembled properly // in llvm 6.0, so having this workaround now // until disto llvm versions catch up #define WORKAROUND_FOR_LD_PSEUDO using std::get; using std::map; using std::string; using std::tuple; using std::vector; using namespace llvm; using DWARFLineTable = DWARFDebugLine::LineTable; void SourceDebugger::adjustInstSize(uint64_t &Size, uint8_t byte0, uint8_t byte1) { #ifdef WORKAROUND_FOR_LD_PSEUDO bool isLittleEndian = mod_->getDataLayout().isLittleEndian(); if (byte0 == 0x18 && ((isLittleEndian && (byte1 & 0xf) == 0x1) || (!isLittleEndian && (byte1 & 0xf0) == 0x10))) Size = 16; #endif } vector SourceDebugger::buildLineCache() { vector LineCache; size_t FileBufSize = mod_src_.size(); for (uint32_t start = 0, end = start; end < FileBufSize; end++) if (mod_src_[end] == '\n' || end == FileBufSize - 1 || (mod_src_[end] == '\r' && mod_src_[end + 1] == '\n')) { // Not including the endline LineCache.push_back(string(mod_src_.substr(start, end - start))); if (mod_src_[end] == '\r') end++; start = end + 1; } return LineCache; } void SourceDebugger::dumpSrcLine(const vector &LineCache, const string &FileName, uint32_t Line, uint32_t &CurrentSrcLine, llvm::raw_ostream &os) { if (Line != 0 && Line != CurrentSrcLine && Line < LineCache.size() && FileName == mod_->getSourceFileName()) { os << "; " << StringRef(LineCache[Line - 1]).ltrim() << format( " // Line" "%4" PRIu64 "\n", Line); CurrentSrcLine = Line; } } void SourceDebugger::getDebugSections( StringMap> &DebugSections) { for (auto section : sections_) { if (strncmp(section.first.c_str(), ".debug", 6) == 0) { StringRef SecData(reinterpret_cast(get<0>(section.second)), get<1>(section.second)); DebugSections[section.first.substr(1)] = MemoryBuffer::getMemBufferCopy(SecData); } } } void SourceDebugger::dump() { string Error; string TripleStr(mod_->getTargetTriple()); Triple TheTriple(TripleStr); const Target *T = TargetRegistry::lookupTarget(TripleStr, Error); if (!T) { errs() << "Debug Error: cannot get target\n"; return; } std::unique_ptr MRI(T->createMCRegInfo(TripleStr)); if (!MRI) { errs() << "Debug Error: cannot get register info\n"; return; } #if LLVM_MAJOR_VERSION >= 10 MCTargetOptions MCOptions; std::unique_ptr MAI(T->createMCAsmInfo(*MRI, TripleStr, MCOptions)); #else std::unique_ptr MAI(T->createMCAsmInfo(*MRI, TripleStr)); #endif if (!MAI) { errs() << "Debug Error: cannot get assembly info\n"; return; } MCObjectFileInfo MOFI; MCContext Ctx(MAI.get(), MRI.get(), &MOFI, nullptr); MOFI.InitMCObjectFileInfo(TheTriple, false, Ctx, false); std::unique_ptr STI( T->createMCSubtargetInfo(TripleStr, "", "")); std::unique_ptr MCII(T->createMCInstrInfo()); MCInstPrinter *IP = T->createMCInstPrinter(TheTriple, 0, *MAI, *MCII, *MRI); if (!IP) { errs() << "Debug Error: unable to create instruction printer\n"; return; } std::unique_ptr DisAsm( T->createMCDisassembler(*STI, Ctx)); if (!DisAsm) { errs() << "Debug Error: no disassembler\n"; return; } // Set up the dwarf debug context StringMap> DebugSections; getDebugSections(DebugSections); std::unique_ptr DwarfCtx = DWARFContext::create(DebugSections, 8); if (!DwarfCtx) { errs() << "Debug Error: dwarf context creation failed\n"; return; } // bcc has only one compilation unit // getCompileUnitAtIndex() was gone in llvm 8.0 (https://reviews.llvm.org/D49741) #if LLVM_MAJOR_VERSION >= 8 DWARFCompileUnit *CU = cast(DwarfCtx->getUnitAtIndex(0)); #else DWARFCompileUnit *CU = DwarfCtx->getCompileUnitAtIndex(0); #endif if (!CU) { errs() << "Debug Error: dwarf context failed to get compile unit\n"; return; } const DWARFLineTable *LineTable = DwarfCtx->getLineTableForUnit(CU); if (!LineTable) { errs() << "Debug Error: dwarf context failed to get line table\n"; return; } // Build LineCache for later source code printing vector LineCache = buildLineCache(); // Start to disassemble with source code annotation section by section for (auto section : sections_) if (!strncmp(fn_prefix_.c_str(), section.first.c_str(), fn_prefix_.size())) { MCDisassembler::DecodeStatus S; MCInst Inst; uint64_t Size; uint8_t *FuncStart = get<0>(section.second); uint64_t FuncSize = get<1>(section.second); #if LLVM_MAJOR_VERSION >= 9 unsigned SectionID = get<2>(section.second); #endif ArrayRef Data(FuncStart, FuncSize); uint32_t CurrentSrcLine = 0; string func_name = section.first.substr(fn_prefix_.size()); errs() << "Disassembly of section " << section.first << ":\n" << func_name << ":\n"; string src_dbg_str; llvm::raw_string_ostream os(src_dbg_str); for (uint64_t Index = 0; Index < FuncSize; Index += Size) { S = DisAsm->getInstruction(Inst, Size, Data.slice(Index), Index, nulls(), nulls()); if (S != MCDisassembler::Success) { os << "Debug Error: disassembler failed: " << std::to_string(S) << '\n'; break; } else { DILineInfo LineInfo; LineTable->getFileLineInfoForAddress( #if LLVM_MAJOR_VERSION >= 9 {(uint64_t)FuncStart + Index, SectionID}, #else (uint64_t)FuncStart + Index, #endif CU->getCompilationDir(), DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, LineInfo); adjustInstSize(Size, Data[Index], Data[Index + 1]); dumpSrcLine(LineCache, LineInfo.FileName, LineInfo.Line, CurrentSrcLine, os); os << format("%4" PRIu64 ":", Index >> 3) << '\t'; dumpBytes(Data.slice(Index, Size), os); IP->printInst(&Inst, os, "", *STI); os << '\n'; } } os.flush(); errs() << src_dbg_str << '\n'; src_dbg_fmap_[func_name] = src_dbg_str; } } } // namespace ebpf bpfcc-0.12.0/src/cc/bcc_debug.h000066400000000000000000000036731357404205000161020ustar00rootroot00000000000000/* * Copyright (c) 2017 Facebook, Inc. * * 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. */ #include "bpf_module.h" namespace ebpf { class SourceDebugger { public: SourceDebugger( llvm::Module *mod, sec_map_def §ions, const std::string &fn_prefix, const std::string &mod_src, std::map &src_dbg_fmap) : mod_(mod), sections_(sections), fn_prefix_(fn_prefix), mod_src_(mod_src), src_dbg_fmap_(src_dbg_fmap) {} // Only support dump for llvm 6.x and later. // // The llvm 5.x, but not earlier versions, also supports create // a dwarf context for source debugging based // on a set of in-memory sections with slightly different interfaces. // FIXME: possibly to support 5.x // #if LLVM_MAJOR_VERSION >= 6 void dump(); private: void adjustInstSize(uint64_t &Size, uint8_t byte0, uint8_t byte1); std::vector buildLineCache(); void dumpSrcLine(const std::vector &LineCache, const std::string &FileName, uint32_t Line, uint32_t &CurrentSrcLine, llvm::raw_ostream &os); void getDebugSections( llvm::StringMap> &DebugSections); #else void dump() { } #endif private: llvm::Module *mod_; const sec_map_def §ions_; const std::string &fn_prefix_; const std::string &mod_src_; std::map &src_dbg_fmap_; }; } // namespace ebpf bpfcc-0.12.0/src/cc/bcc_elf.c000066400000000000000000000712431357404205000155530ustar00rootroot00000000000000/* * Copyright (c) 2016 GitHub, Inc. * * 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "bcc_elf.h" #include "bcc_proc.h" #include "bcc_syms.h" #define NT_STAPSDT 3 #define ELF_ST_TYPE(x) (((uint32_t) x) & 0xf) static int openelf_fd(int fd, Elf **elf_out) { if (elf_version(EV_CURRENT) == EV_NONE) return -1; *elf_out = elf_begin(fd, ELF_C_READ, 0); if (*elf_out == NULL) return -1; return 0; } static int openelf(const char *path, Elf **elf_out, int *fd_out) { *fd_out = open(path, O_RDONLY); if (*fd_out < 0) return -1; if (openelf_fd(*fd_out, elf_out) == -1) { close(*fd_out); return -1; } return 0; } static const char *parse_stapsdt_note(struct bcc_elf_usdt *probe, const char *desc, int elf_class) { if (elf_class == ELFCLASS32) { probe->pc = *((uint32_t *)(desc)); probe->base_addr = *((uint32_t *)(desc + 4)); probe->semaphore = *((uint32_t *)(desc + 8)); desc = desc + 12; } else { probe->pc = *((uint64_t *)(desc)); probe->base_addr = *((uint64_t *)(desc + 8)); probe->semaphore = *((uint64_t *)(desc + 16)); desc = desc + 24; } probe->provider = desc; desc += strlen(desc) + 1; probe->name = desc; desc += strlen(desc) + 1; probe->arg_fmt = desc; desc += strlen(desc) + 1; return desc; } static int do_note_segment(Elf_Scn *section, int elf_class, bcc_elf_probecb callback, const char *binpath, uint64_t first_inst_offset, void *payload) { Elf_Data *data = NULL; while ((data = elf_getdata(section, data)) != 0) { size_t offset = 0; GElf_Nhdr hdr; size_t name_off, desc_off; while ((offset = gelf_getnote(data, offset, &hdr, &name_off, &desc_off)) != 0) { const char *desc, *desc_end; struct bcc_elf_usdt probe; if (hdr.n_type != NT_STAPSDT) continue; if (hdr.n_namesz != 8) continue; if (memcmp((const char *)data->d_buf + name_off, "stapsdt", 8) != 0) continue; desc = (const char *)data->d_buf + desc_off; desc_end = desc + hdr.n_descsz; if (parse_stapsdt_note(&probe, desc, elf_class) == desc_end) { if (probe.pc < first_inst_offset) fprintf(stderr, "WARNING: invalid address 0x%lx for probe (%s,%s) in binary %s\n", probe.pc, probe.provider, probe.name, binpath); else callback(binpath, &probe, payload); } } } return 0; } static int listprobes(Elf *e, bcc_elf_probecb callback, const char *binpath, void *payload) { Elf_Scn *section = NULL; size_t stridx; int elf_class = gelf_getclass(e); uint64_t first_inst_offset = 0; if (elf_getshdrstrndx(e, &stridx) != 0) return -1; // Get the offset to the first instruction while ((section = elf_nextscn(e, section)) != 0) { GElf_Shdr header; if (!gelf_getshdr(section, &header)) continue; // The elf file section layout is based on increasing virtual address, // getting the first section with SHF_EXECINSTR is enough. if (header.sh_flags & SHF_EXECINSTR) { first_inst_offset = header.sh_addr; break; } } while ((section = elf_nextscn(e, section)) != 0) { GElf_Shdr header; char *name; if (!gelf_getshdr(section, &header)) continue; if (header.sh_type != SHT_NOTE) continue; name = elf_strptr(e, stridx, header.sh_name); if (name && !strcmp(name, ".note.stapsdt")) { if (do_note_segment(section, elf_class, callback, binpath, first_inst_offset, payload) < 0) return -1; } } return 0; } int bcc_elf_foreach_usdt(const char *path, bcc_elf_probecb callback, void *payload) { Elf *e; int fd, res; if (openelf(path, &e, &fd) < 0) return -1; res = listprobes(e, callback, path, payload); elf_end(e); close(fd); return res; } static Elf_Scn * get_section(Elf *e, const char *section_name, GElf_Shdr *section_hdr, size_t *section_idx) { Elf_Scn *section = NULL; GElf_Shdr header; char *name; size_t stridx; if (elf_getshdrstrndx(e, &stridx) != 0) return NULL; size_t index; for (index = 1; (section = elf_nextscn(e, section)) != 0; index++) { if (!gelf_getshdr(section, &header)) continue; name = elf_strptr(e, stridx, header.sh_name); if (name && !strcmp(name, section_name)) { if (section_hdr) *section_hdr = header; if (section_idx) *section_idx = index; return section; } } return NULL; } static int list_in_scn(Elf *e, Elf_Scn *section, size_t stridx, size_t symsize, struct bcc_symbol_option *option, bcc_elf_symcb callback, bcc_elf_symcb_lazy callback_lazy, void *payload, bool debugfile) { Elf_Data *data = NULL; #if defined(__powerpc64__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ size_t opdidx = 0; Elf_Scn *opdsec = NULL; GElf_Shdr opdshdr = {}; Elf_Data *opddata = NULL; opdsec = get_section(e, ".opd", &opdshdr, &opdidx); if (opdsec && opdshdr.sh_type == SHT_PROGBITS) opddata = elf_getdata(opdsec, NULL); #endif while ((data = elf_getdata(section, data)) != 0) { size_t i, symcount = data->d_size / symsize; if (data->d_size % symsize) return -1; for (i = 0; i < symcount; ++i) { GElf_Sym sym; const char *name; size_t name_len; if (!gelf_getsym(data, (int)i, &sym)) continue; if ((name = elf_strptr(e, stridx, sym.st_name)) == NULL) continue; if (name[0] == 0) continue; name_len = strlen(name); if (sym.st_value == 0) continue; uint32_t st_type = ELF_ST_TYPE(sym.st_info); if (!(option->use_symbol_type & (1 << st_type))) continue; #ifdef __powerpc64__ #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ if (opddata && sym.st_shndx == opdidx) { size_t offset = sym.st_value - opdshdr.sh_addr; /* Find the function descriptor */ uint64_t *descr = opddata->d_buf + offset; /* Read the actual entry point address from the descriptor */ sym.st_value = *descr; } #elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ if (option->use_symbol_type & (1 << STT_PPC64LE_SYM_LEP)) { /* * The PowerPC 64-bit ELF v2 ABI says that the 3 most significant bits * in the st_other field of the symbol table specifies the number of * instructions between a function's Global Entry Point (GEP) and Local * Entry Point (LEP). */ switch (sym.st_other >> 5) { /* GEP and LEP are the same for 0 or 1, usage is reserved for 7 */ /* If 2, LEP is 1 instruction past the GEP */ case 2: sym.st_value += 4; break; /* If 3, LEP is 2 instructions past the GEP */ case 3: sym.st_value += 8; break; /* If 4, LEP is 4 instructions past the GEP */ case 4: sym.st_value += 16; break; /* If 5, LEP is 8 instructions past the GEP */ case 5: sym.st_value += 32; break; /* If 6, LEP is 16 instructions past the GEP */ case 6: sym.st_value += 64; break; } } #endif #endif int ret; if (option->lazy_symbolize) ret = callback_lazy(stridx, sym.st_name, name_len, sym.st_value, sym.st_size, debugfile, payload); else ret = callback(name, sym.st_value, sym.st_size, payload); if (ret < 0) return 1; // signal termination to caller } } return 0; } static int listsymbols(Elf *e, bcc_elf_symcb callback, bcc_elf_symcb_lazy callback_lazy, void *payload, struct bcc_symbol_option *option, bool debugfile) { Elf_Scn *section = NULL; while ((section = elf_nextscn(e, section)) != 0) { GElf_Shdr header; if (!gelf_getshdr(section, &header)) continue; if (header.sh_type != SHT_SYMTAB && header.sh_type != SHT_DYNSYM) continue; int rc = list_in_scn(e, section, header.sh_link, header.sh_entsize, option, callback, callback_lazy, payload, debugfile); if (rc == 1) break; // callback signaled termination if (rc < 0) return rc; } return 0; } static Elf_Data * get_section_elf_data(Elf *e, const char *section_name) { Elf_Scn *section = get_section(e, section_name, NULL, NULL); if (section) return elf_getdata(section, NULL); return NULL; } static int find_debuglink(Elf *e, char **debug_file, unsigned int *crc) { Elf_Data *data = NULL; *debug_file = NULL; *crc = 0; data = get_section_elf_data(e, ".gnu_debuglink"); if (!data || data->d_size <= 5) return 0; *debug_file = (char *)data->d_buf; *crc = *(unsigned int*)((char *)data->d_buf + data->d_size - 4); return *debug_file ? 1 : 0; } static int find_buildid(Elf *e, char *buildid) { Elf_Data *data = get_section_elf_data(e, ".note.gnu.build-id"); if (!data || data->d_size <= 16 || strcmp((char *)data->d_buf + 12, "GNU")) return 0; char *buf = (char *)data->d_buf + 16; size_t length = data->d_size - 16; size_t i = 0; for (i = 0; i < length; ++i) { sprintf(buildid + (i * 2), "%02hhx", buf[i]); } return 1; } // The CRC algorithm used by GNU debuglink. Taken from: // https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html static unsigned int gnu_debuglink_crc32(unsigned int crc, char *buf, size_t len) { static const unsigned int crc32_table[256] = { 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d }; char *end; crc = ~crc & 0xffffffff; for (end = buf + len; buf < end; ++buf) crc = crc32_table[(crc ^ *buf) & 0xff] ^ (crc >> 8); return ~crc & 0xffffffff; } static int verify_checksum(const char *file, unsigned int crc) { struct stat st; int fd; void *buf; unsigned int actual; fd = open(file, O_RDONLY); if (fd < 0) return 0; if (fstat(fd, &st) < 0) { close(fd); return 0; } buf = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); if (!buf) { close(fd); return 0; } actual = gnu_debuglink_crc32(0, buf, st.st_size); munmap(buf, st.st_size); close(fd); return actual == crc; } // Check if two filenames point to the same file, including hard or soft links. static bool same_file(char *a, const char *b) { struct stat stat_a, stat_b; if (stat(a, &stat_a) || stat(b, &stat_b)) return false; if ((stat_a.st_dev == stat_b.st_dev) && (stat_a.st_ino == stat_b.st_ino)) return true; else return false; } static char *find_debug_via_debuglink(Elf *e, const char *binpath, int check_crc) { char fullpath[PATH_MAX]; char *tmppath; char *bindir = NULL; char *res = NULL; unsigned int crc; char *name; // the name of the debuginfo file if (!find_debuglink(e, &name, &crc)) return NULL; tmppath = strdup(binpath); bindir = dirname(tmppath); // Search for the file in 'binpath', but ignore the file we find if it // matches the binary itself: the binary will always be probed later on, // and it might contain poorer symbols (e.g. stripped or partial symbols) // than the external debuginfo that might be available elsewhere. snprintf(fullpath, sizeof(fullpath),"%s/%s", bindir, name); if (same_file(fullpath, binpath) != true && access(fullpath, F_OK) != -1) { res = strdup(fullpath); goto DONE; } // Search for the file in 'binpath'/.debug snprintf(fullpath, sizeof(fullpath), "%s/.debug/%s", bindir, name); if (access(fullpath, F_OK) != -1) { res = strdup(fullpath); goto DONE; } // Search for the file in the global debug directory /usr/lib/debug/'binpath' snprintf(fullpath, sizeof(fullpath), "/usr/lib/debug%s/%s", bindir, name); if (access(fullpath, F_OK) != -1) { res = strdup(fullpath); goto DONE; } DONE: free(tmppath); if (res && check_crc && !verify_checksum(res, crc)) { free(res); return NULL; } return res; } static char *find_debug_via_buildid(Elf *e) { char fullpath[PATH_MAX]; char buildid[128]; // currently 40 seems to be default, let's be safe if (!find_buildid(e, buildid)) return NULL; // Search for the file in the global debug directory with a sub-path: // mm/nnnnnn...nnnn.debug // Where mm are the first two characters of the buildid, and nnnn are the // rest of the build id, followed by .debug. snprintf(fullpath, sizeof(fullpath), "/usr/lib/debug/.build-id/%c%c/%s.debug", buildid[0], buildid[1], buildid + 2); if (access(fullpath, F_OK) != -1) { return strdup(fullpath); } return NULL; } static char *find_debug_via_symfs(Elf *e, const char* path) { char fullpath[PATH_MAX]; char buildid[128]; char symfs_buildid[128]; int check_build_id; char *symfs; Elf *symfs_e = NULL; int symfs_fd = -1; char *result = NULL; symfs = getenv("BCC_SYMFS"); if (!symfs || !*symfs) goto out; check_build_id = find_buildid(e, buildid); snprintf(fullpath, sizeof(fullpath), "%s/%s", symfs, path); if (access(fullpath, F_OK) == -1) goto out; if (openelf(fullpath, &symfs_e, &symfs_fd) < 0) { symfs_e = NULL; symfs_fd = -1; goto out; } if (check_build_id) { if (!find_buildid(symfs_e, symfs_buildid)) goto out; if (strncmp(buildid, symfs_buildid, sizeof(buildid))) goto out; } result = strdup(fullpath); out: if (symfs_e) { elf_end(symfs_e); } if (symfs_fd != -1) { close(symfs_fd); } return result; } static int foreach_sym_core(const char *path, bcc_elf_symcb callback, bcc_elf_symcb_lazy callback_lazy, struct bcc_symbol_option *option, void *payload, int is_debug_file) { Elf *e; int fd, res; char *debug_file; if (!option) return -1; if (openelf(path, &e, &fd) < 0) return -1; // If there is a separate debuginfo file, try to locate and read it, first // using symfs, then using the build-id section, finally using the debuglink // section. These rules are what perf and gdb follow. // See: // - https://github.com/torvalds/linux/blob/v5.2/tools/perf/Documentation/perf-report.txt#L325 // - https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html if (option->use_debug_file && !is_debug_file) { // The is_debug_file argument helps avoid infinitely resolving debuginfo // files for debuginfo files and so on. debug_file = find_debug_via_symfs(e, path); if (!debug_file) debug_file = find_debug_via_buildid(e); if (!debug_file) debug_file = find_debug_via_debuglink(e, path, option->check_debug_file_crc); if (debug_file) { foreach_sym_core(debug_file, callback, callback_lazy, option, payload, 1); free(debug_file); } } res = listsymbols(e, callback, callback_lazy, payload, option, is_debug_file); elf_end(e); close(fd); return res; } int bcc_elf_foreach_sym(const char *path, bcc_elf_symcb callback, void *option, void *payload) { struct bcc_symbol_option *o = option; o->lazy_symbolize = 0; return foreach_sym_core(path, callback, NULL, o, payload, 0); } int bcc_elf_foreach_sym_lazy(const char *path, bcc_elf_symcb_lazy callback, void *option, void *payload) { struct bcc_symbol_option *o = option; o->lazy_symbolize = 1; return foreach_sym_core(path, NULL, callback, o, payload, 0); } int bcc_elf_get_text_scn_info(const char *path, uint64_t *addr, uint64_t *offset) { Elf *e = NULL; int fd = -1, err; Elf_Scn *section = NULL; GElf_Shdr header; size_t stridx; char *name; if ((err = openelf(path, &e, &fd)) < 0 || (err = elf_getshdrstrndx(e, &stridx)) < 0) goto exit; err = -1; while ((section = elf_nextscn(e, section)) != 0) { if (!gelf_getshdr(section, &header)) continue; name = elf_strptr(e, stridx, header.sh_name); if (name && !strcmp(name, ".text")) { *addr = (uint64_t)header.sh_addr; *offset = (uint64_t)header.sh_offset; err = 0; break; } } exit: if (e) elf_end(e); if (fd >= 0) close(fd); return err; } int bcc_elf_foreach_load_section(const char *path, bcc_elf_load_sectioncb callback, void *payload) { Elf *e = NULL; int fd = -1, err = -1, res; size_t nhdrs, i; if (openelf(path, &e, &fd) < 0) goto exit; if (elf_getphdrnum(e, &nhdrs) != 0) goto exit; GElf_Phdr header; for (i = 0; i < nhdrs; i++) { if (!gelf_getphdr(e, (int)i, &header)) continue; if (header.p_type != PT_LOAD || !(header.p_flags & PF_X)) continue; res = callback(header.p_vaddr, header.p_memsz, header.p_offset, payload); if (res < 0) { err = 1; goto exit; } } err = 0; exit: if (e) elf_end(e); if (fd >= 0) close(fd); return err; } int bcc_elf_get_type(const char *path) { Elf *e; GElf_Ehdr hdr; int fd; void* res = NULL; if (openelf(path, &e, &fd) < 0) return -1; res = (void*)gelf_getehdr(e, &hdr); elf_end(e); close(fd); if (!res) return -1; else return hdr.e_type; } int bcc_elf_is_exe(const char *path) { return (bcc_elf_get_type(path) != -1) && (access(path, X_OK) == 0); } int bcc_elf_is_shared_obj(const char *path) { return bcc_elf_get_type(path) == ET_DYN; } int bcc_elf_is_vdso(const char *name) { return strcmp(name, "[vdso]") == 0; } // -2: Failed // -1: Not initialized // >0: Initialized static int vdso_image_fd = -1; static int find_vdso(struct mod_info *info, int enter_ns, void *payload) { int fd; char tmpfile[128]; if (!bcc_elf_is_vdso(info->name)) return 0; uint64_t sz = info->end_addr - info->start_addr; void *image = malloc(sz); if (!image) goto on_error; memcpy(image, (void *)info->start_addr, sz); snprintf(tmpfile, sizeof(tmpfile), "/tmp/bcc_%d_vdso_image_XXXXXX", getpid()); fd = mkostemp(tmpfile, O_CLOEXEC); if (fd < 0) { fprintf(stderr, "Unable to create temp file: %s\n", strerror(errno)); goto on_error; } // Unlink the file to avoid leaking if (unlink(tmpfile) == -1) fprintf(stderr, "Unlink %s failed: %s\n", tmpfile, strerror(errno)); if (write(fd, image, sz) == -1) { fprintf(stderr, "Failed to write to vDSO image: %s\n", strerror(errno)); close(fd); goto on_error; } vdso_image_fd = fd; on_error: if (image) free(image); // Always stop the iteration return -1; } int bcc_elf_foreach_vdso_sym(bcc_elf_symcb callback, void *payload) { Elf *elf; static struct bcc_symbol_option default_option = { .use_debug_file = 0, .check_debug_file_crc = 0, .use_symbol_type = (1 << STT_FUNC) | (1 << STT_GNU_IFUNC) }; if (vdso_image_fd == -1) { vdso_image_fd = -2; bcc_procutils_each_module(getpid(), &find_vdso, NULL); } if (vdso_image_fd == -2) return -1; if (openelf_fd(vdso_image_fd, &elf) == -1) return -1; return listsymbols(elf, callback, NULL, payload, &default_option, 0); } // return value: 0 : success // < 0 : error and no bcc lib found // > 0 : error and bcc lib found static int bcc_free_memory_with_file(const char *path) { unsigned long sym_addr = 0, sym_shndx; Elf_Scn *section = NULL; int fd = -1, err; GElf_Shdr header; Elf *e = NULL; if ((err = openelf(path, &e, &fd)) < 0) goto exit; // get symbol address of "bcc_free_memory", which // will be used to calculate runtime .text address // range, esp. for shared libraries. err = -1; while ((section = elf_nextscn(e, section)) != 0) { Elf_Data *data = NULL; size_t symsize; if (!gelf_getshdr(section, &header)) continue; if (header.sh_type != SHT_SYMTAB && header.sh_type != SHT_DYNSYM) continue; /* iterate all symbols */ symsize = header.sh_entsize; while ((data = elf_getdata(section, data)) != 0) { size_t i, symcount = data->d_size / symsize; for (i = 0; i < symcount; ++i) { GElf_Sym sym; if (!gelf_getsym(data, (int)i, &sym)) continue; if (GELF_ST_TYPE(sym.st_info) != STT_FUNC) continue; const char *name; if ((name = elf_strptr(e, header.sh_link, sym.st_name)) == NULL) continue; if (strcmp(name, "bcc_free_memory") == 0) { sym_addr = sym.st_value; sym_shndx = sym.st_shndx; break; } } } } // Didn't find bcc_free_memory in the ELF file. if (sym_addr == 0) goto exit; int sh_idx = 0; section = NULL; err = 1; while ((section = elf_nextscn(e, section)) != 0) { sh_idx++; if (!gelf_getshdr(section, &header)) continue; if (sh_idx == sym_shndx) { unsigned long saddr, saddr_n, eaddr; long page_size = sysconf(_SC_PAGESIZE); saddr = (unsigned long)bcc_free_memory - sym_addr + header.sh_addr; eaddr = saddr + header.sh_size; // adjust saddr and eaddr, start addr needs to be page aligned saddr_n = (saddr + page_size - 1) & ~(page_size - 1); eaddr -= saddr_n - saddr; if (madvise((void *)saddr_n, eaddr - saddr_n, MADV_DONTNEED)) { fprintf(stderr, "madvise failed, saddr %lx, eaddr %lx\n", saddr, eaddr); goto exit; } err = 0; break; } } exit: if (e) elf_end(e); if (fd >= 0) close(fd); return err; } // Free bcc mmemory // // The main purpose of this function is to free llvm/clang text memory // through madvise MADV_DONTNEED. // // bcc could be linked statically or dynamically into the application. // If it is static linking, there is no easy way to know which region // inside .text section belongs to llvm/clang, so the whole .text section // is freed. Otherwise, the process map is searched to find libbcc.so // library and the whole .text section for that shared library is // freed. // // Note that the text memory used by bcc (mainly llvm/clang) is reclaimable // in the kernel as it is file backed. But the reclaim process // may take some time if no memory pressure. So this API is mostly // used for application who needs to immediately lowers its RssFile // metric right after loading BPF program. int bcc_free_memory() { int err; // First try whether bcc is statically linked or not err = bcc_free_memory_with_file("/proc/self/exe"); if (err >= 0) return -err; // Not statically linked, let us find the libbcc.so FILE *maps = fopen("/proc/self/maps", "r"); if (!maps) return -1; char *line = NULL; size_t size; while (getline(&line, &size, maps) > 0) { char *libbcc = strstr(line, "libbcc.so"); if (!libbcc) continue; // Parse the line and get the full libbcc.so path unsigned long addr_start, addr_end, offset, inode; int path_start = 0, path_end = 0; unsigned int devmajor, devminor; char perms[8]; if (sscanf(line, "%lx-%lx %7s %lx %x:%x %lu %n%*[^\n]%n", &addr_start, &addr_end, perms, &offset, &devmajor, &devminor, &inode, &path_start, &path_end) < 7) break; // Free the text in the bcc dynamic library. char libbcc_path[4096]; memcpy(libbcc_path, line + path_start, path_end - path_start); libbcc_path[path_end - path_start] = '\0'; err = bcc_free_memory_with_file(libbcc_path); err = (err <= 0) ? err : -err; } fclose(maps); free(line); return err; } int bcc_elf_get_buildid(const char *path, char *buildid) { Elf *e; int fd; if (openelf(path, &e, &fd) < 0) return -1; if (!find_buildid(e, buildid)) return -1; return 0; } int bcc_elf_symbol_str(const char *path, size_t section_idx, size_t str_table_idx, char *out, size_t len, int debugfile) { Elf *e = NULL, *d = NULL; int fd = -1, dfd = -1, err = 0; const char *name; char *debug_file = NULL; if (!out) return -1; if (openelf(path, &e, &fd) < 0) return -1; if (debugfile) { debug_file = find_debug_via_buildid(e); if (!debug_file) debug_file = find_debug_via_debuglink(e, path, 0); // No crc for speed if (!debug_file) { err = -1; goto exit; } if (openelf(debug_file, &d, &dfd) < 0) { err = -1; goto exit; } if ((name = elf_strptr(d, section_idx, str_table_idx)) == NULL) { err = -1; goto exit; } } else { if ((name = elf_strptr(e, section_idx, str_table_idx)) == NULL) { err = -1; goto exit; } } strncpy(out, name, len); exit: if (debug_file) free(debug_file); if (e) elf_end(e); if (d) elf_end(d); if (fd >= 0) close(fd); if (dfd >= 0) close(dfd); return err; } #if 0 #include int main(int argc, char *argv[]) { uint64_t addr; if (bcc_elf_findsym(argv[1], argv[2], -1, STT_FUNC, &addr) < 0) return -1; printf("%s: %p\n", argv[2], (void *)addr); return 0; } #endif bpfcc-0.12.0/src/cc/bcc_elf.h000066400000000000000000000066511357404205000155610ustar00rootroot00000000000000/* * Copyright (c) 2016 GitHub, Inc. * * 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. */ #ifndef LIBBCC_ELF_H #define LIBBCC_ELF_H #ifdef __cplusplus extern "C" { #endif #include #include struct bcc_elf_usdt { uint64_t pc; uint64_t base_addr; uint64_t semaphore; const char *provider; const char *name; const char *arg_fmt; }; // Binary module path, bcc_elf_usdt struct, payload typedef void (*bcc_elf_probecb)(const char *, const struct bcc_elf_usdt *, void *); // Symbol name, start address, length, payload // Callback returning a negative value indicates to stop the iteration typedef int (*bcc_elf_symcb)(const char *, uint64_t, uint64_t, void *); // Section idx, str table idx, str length, start address, length, payload typedef int (*bcc_elf_symcb_lazy)(size_t, size_t, size_t, uint64_t, uint64_t, int, void *); // Segment virtual address, memory size, file offset, payload // Callback returning a negative value indicates to stop the iteration typedef int (*bcc_elf_load_sectioncb)(uint64_t, uint64_t, uint64_t, void *); // Iterate over all USDT probes noted in a binary module // Returns -1 on error, and 0 on success int bcc_elf_foreach_usdt(const char *path, bcc_elf_probecb callback, void *payload); // Iterate over all executable load sections of an ELF // Returns -1 on error, 1 if stopped by callback, and 0 on success int bcc_elf_foreach_load_section(const char *path, bcc_elf_load_sectioncb callback, void *payload); // Iterate over symbol table of a binary module // Parameter "option" points to a bcc_symbol_option struct to indicate wheather // and how to use debuginfo file, and what types of symbols to load. // Returns -1 on error, and 0 on success or stopped by callback int bcc_elf_foreach_sym(const char *path, bcc_elf_symcb callback, void *option, void *payload); // Similar to bcc_elf_foreach_sym, but pass reference to symbolized string along // with symbolized string length int bcc_elf_foreach_sym_lazy(const char *path, bcc_elf_symcb_lazy callback, void *option, void *payload); // Iterate over all symbols from current system's vDSO // Returns -1 on error, and 0 on success or stopped by callback int bcc_elf_foreach_vdso_sym(bcc_elf_symcb callback, void *payload); int bcc_elf_get_text_scn_info(const char *path, uint64_t *addr, uint64_t *offset); int bcc_elf_get_type(const char *path); int bcc_elf_is_shared_obj(const char *path); int bcc_elf_is_exe(const char *path); int bcc_elf_is_vdso(const char *name); int bcc_free_memory(); int bcc_elf_get_buildid(const char *path, char *buildid); int bcc_elf_symbol_str(const char *path, size_t section_idx, size_t str_table_idx, char *out, size_t len, int debugfile); #ifdef __cplusplus } #endif #endif bpfcc-0.12.0/src/cc/bcc_exception.h000066400000000000000000000030071357404205000170010ustar00rootroot00000000000000/* * Copyright (c) 2015 PLUMgrid, Inc. * * 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. */ #pragma once #include #include namespace ebpf { class StatusTuple { public: StatusTuple(int ret) : ret_(ret) {} StatusTuple(int ret, const char *msg) : ret_(ret), msg_(msg) {} StatusTuple(int ret, const std::string &msg) : ret_(ret), msg_(msg) {} template StatusTuple(int ret, const char *fmt, Args... args) : ret_(ret) { char buf[2048]; snprintf(buf, sizeof(buf), fmt, args...); msg_ = std::string(buf); } void append_msg(const std::string& msg) { msg_ += msg; } int code() const { return ret_; } const std::string& msg() const { return msg_; } private: int ret_; std::string msg_; }; #define TRY2(CMD) \ do { \ ebpf::StatusTuple __stp = (CMD); \ if (__stp.code() != 0) { \ return __stp; \ } \ } while (0) } // namespace ebpf bpfcc-0.12.0/src/cc/bcc_perf_map.c000066400000000000000000000062221357404205000165710ustar00rootroot00000000000000/* * Copyright (c) 2016 Facebook, Inc. * * 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. */ #include #include #include #include #include #include #include "bcc_perf_map.h" bool bcc_is_perf_map(const char *path) { char* pos = strstr(path, ".map"); // Path ends with ".map" return (pos != NULL) && (*(pos + 4)== 0); } bool bcc_is_valid_perf_map(const char *path) { return bcc_is_perf_map(path) && (access(path, R_OK) == 0); } int bcc_perf_map_nstgid(int pid) { char status_path[64]; FILE *status; snprintf(status_path, sizeof(status_path), "/proc/%d/status", pid); status = fopen(status_path, "r"); if (!status) return -1; // return the original PID if we fail to work out the TGID int nstgid = pid; size_t size = 0; char *line = NULL; while (getline(&line, &size, status) != -1) { // check Tgid line first in case CONFIG_PID_NS is off if (strstr(line, "Tgid:") != NULL) nstgid = (int)strtol(strrchr(line, '\t'), NULL, 10); if (strstr(line, "NStgid:") != NULL) // PID namespaces can be nested -- last number is innermost PID nstgid = (int)strtol(strrchr(line, '\t'), NULL, 10); } free(line); fclose(status); return nstgid; } bool bcc_perf_map_path(char *map_path, size_t map_len, int pid) { char source[64]; snprintf(source, sizeof(source), "/proc/%d/root", pid); char target[4096]; ssize_t target_len = readlink(source, target, sizeof(target) - 1); if (target_len == -1) return false; target[target_len] = '\0'; if (strcmp(target, "/") == 0) target[0] = '\0'; int nstgid = bcc_perf_map_nstgid(pid); snprintf(map_path, map_len, "%s/tmp/perf-%d.map", target, nstgid); return true; } int bcc_perf_map_foreach_sym(const char *path, bcc_perf_map_symcb callback, void* payload) { FILE* file = fopen(path, "r"); if (!file) return -1; char *line = NULL; size_t size = 0; long long begin, len; while (getline(&line, &size, file) != -1) { char *cursor = line; char *newline, *sep; begin = strtoull(cursor, &sep, 16); if (begin == 0 || *sep != ' ' || (begin == ULLONG_MAX && errno == ERANGE)) continue; cursor = sep; while (*cursor && isspace(*cursor)) cursor++; len = strtoull(cursor, &sep, 16); if (*sep != ' ' || (sep == cursor && len == 0) || (len == ULLONG_MAX && errno == ERANGE)) continue; cursor = sep; while (*cursor && isspace(*cursor)) cursor++; newline = strchr(cursor, '\n'); if (newline) newline[0] = '\0'; callback(cursor, begin, len, payload); } free(line); fclose(file); return 0; } bpfcc-0.12.0/src/cc/bcc_perf_map.h000066400000000000000000000023051357404205000165740ustar00rootroot00000000000000/* * Copyright (c) 2016 Facebook, Inc. * * 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. */ #ifndef LIBBCC_PERF_MAP_H #define LIBBCC_PERF_MAP_H #ifdef __cplusplus extern "C" { #endif #include #include #include // Symbol name, start address, length, payload typedef int (*bcc_perf_map_symcb)(const char *, uint64_t, uint64_t, void *); bool bcc_is_perf_map(const char *path); bool bcc_is_valid_perf_map(const char *path); int bcc_perf_map_nstgid(int pid); bool bcc_perf_map_path(char *map_path, size_t map_len, int pid); int bcc_perf_map_foreach_sym(const char *path, bcc_perf_map_symcb callback, void* payload); #ifdef __cplusplus } #endif #endif bpfcc-0.12.0/src/cc/bcc_proc.c000066400000000000000000000343451357404205000157520ustar00rootroot00000000000000/* * Copyright (c) 2016 GitHub, Inc. * * 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "bcc_perf_map.h" #include "bcc_proc.h" #include "bcc_elf.h" #ifdef __x86_64__ // https://www.kernel.org/doc/Documentation/x86/x86_64/mm.txt const unsigned long long kernelAddrSpace = 0x00ffffffffffffff; #else const unsigned long long kernelAddrSpace = 0x0; #endif char *bcc_procutils_which(const char *binpath) { char buffer[4096]; const char *PATH; if (strchr(binpath, '/')) return bcc_elf_is_exe(binpath) ? strdup(binpath) : 0; if (!(PATH = getenv("PATH"))) return 0; while (PATH) { const char *next = strchr(PATH, ':') ?: strchr(PATH, '\0'); const size_t path_len = next - PATH; if (path_len) { int ret = snprintf(buffer, sizeof(buffer), "%.*s/%s", (int)path_len, PATH, binpath); if (ret < 0 || ret >= sizeof(buffer)) return 0; if (bcc_elf_is_exe(buffer)) return strdup(buffer); } PATH = *next ? (next + 1) : 0; } return 0; } #define STARTS_WITH(mapname, prefix) (!strncmp(mapname, prefix, sizeof(prefix)-1)) int bcc_mapping_is_file_backed(const char *mapname) { return mapname[0] && !( STARTS_WITH(mapname, "//anon") || STARTS_WITH(mapname, "/dev/zero") || STARTS_WITH(mapname, "/anon_hugepage") || STARTS_WITH(mapname, "[stack") || STARTS_WITH(mapname, "/SYSV") || STARTS_WITH(mapname, "[heap]") || STARTS_WITH(mapname, "[vsyscall]")); } /* Finds a file descriptor for a given inode if it's a memory-backed fd. */ static char *_procutils_memfd_path(const int pid, const uint64_t inum) { char path_buffer[PATH_MAX + 1]; char *path = NULL; char *dirstr; DIR *dirstream; struct stat sb; struct dirent *dent; snprintf(path_buffer, (PATH_MAX + 1), "/proc/%d/fd", pid); dirstr = malloc(strlen(path_buffer) + 1); strcpy(dirstr, path_buffer); dirstream = opendir(dirstr); if (dirstream == NULL) { free(dirstr); return NULL; } while (path == NULL && (dent = readdir(dirstream)) != NULL) { snprintf(path_buffer, (PATH_MAX + 1), "%s/%s", dirstr, dent->d_name); if (stat(path_buffer, &sb) == -1) continue; if (sb.st_ino == inum) { char *pid_fd_path = malloc(strlen(path_buffer) + 1); strcpy(pid_fd_path, path_buffer); path = pid_fd_path; } } closedir(dirstream); free(dirstr); return path; } // return: 0 -> callback returned < 0, stopped iterating // -1 -> callback never indicated to stop int _procfs_maps_each_module(FILE *procmap, int pid, bcc_procutils_modulecb callback, void *payload) { char buf[PATH_MAX + 1], perm[5]; char *name; mod_info mod; uint8_t enter_ns; while (true) { enter_ns = 1; buf[0] = '\0'; // From fs/proc/task_mmu.c:show_map_vma if (fscanf(procmap, "%lx-%lx %4s %llx %lx:%lx %lu%[^\n]", &mod.start_addr, &mod.end_addr, perm, &mod.file_offset, &mod.dev_major, &mod.dev_minor, &mod.inode, buf) != 8) break; if (perm[2] != 'x') continue; name = buf; while (isspace(*name)) name++; mod.name = name; if (!bcc_mapping_is_file_backed(name)) continue; if (strstr(name, "/memfd:")) { char *memfd_name = _procutils_memfd_path(pid, mod.inode); if (memfd_name != NULL) { strcpy(buf, memfd_name); free(memfd_name); mod.name = buf; enter_ns = 0; } } if (callback(&mod, enter_ns, payload) < 0) return 0; } return -1; } int bcc_procutils_each_module(int pid, bcc_procutils_modulecb callback, void *payload) { char procmap_filename[128]; FILE *procmap; snprintf(procmap_filename, sizeof(procmap_filename), "/proc/%ld/maps", (long)pid); procmap = fopen(procmap_filename, "r"); if (!procmap) return -1; _procfs_maps_each_module(procmap, pid, callback, payload); // Address mapping for the entire address space maybe in /tmp/perf-.map // This will be used if symbols aren't resolved in an earlier mapping. char map_path[4096]; // Try perf-.map path with process's mount namespace, chroot and NSPID, // in case it is generated by the process itself. mod_info mod; memset(&mod, 0, sizeof(mod_info)); if (bcc_perf_map_path(map_path, sizeof(map_path), pid)) { mod.name = map_path; mod.end_addr = -1; if (callback(&mod, 1, payload) < 0) goto done; } // Try perf-.map path with global root and PID, in case it is generated // by other Process. Avoid checking mount namespace for this. memset(&mod, 0, sizeof(mod_info)); int res = snprintf(map_path, 4096, "/tmp/perf-%d.map", pid); if (res > 0 && res < 4096) { mod.name = map_path; mod.end_addr = -1; if (callback(&mod, 0, payload) < 0) goto done; } done: fclose(procmap); return 0; } int bcc_procutils_each_ksym(bcc_procutils_ksymcb callback, void *payload) { char line[2048]; char *symname, *endsym, *modname, *endmod = NULL; FILE *kallsyms; unsigned long long addr; /* root is needed to list ksym addresses */ if (geteuid() != 0) return -1; kallsyms = fopen("/proc/kallsyms", "r"); if (!kallsyms) return -1; while (fgets(line, sizeof(line), kallsyms)) { addr = strtoull(line, &symname, 16); if (addr == 0 || addr == ULLONG_MAX) continue; if (addr < kernelAddrSpace) continue; symname++; // Ignore data symbols if (*symname == 'b' || *symname == 'B' || *symname == 'd' || *symname == 'D' || *symname == 'r' || *symname =='R') continue; endsym = (symname = symname + 2); while (*endsym && !isspace(*endsym)) endsym++; *endsym = '\0'; // Parse module name if it's available modname = endsym + 1; while (*modname && isspace(*endsym)) modname++; if (*modname && *modname == '[') { endmod = ++modname; while (*endmod && *endmod != ']') endmod++; if (*endmod) *(endmod) = '\0'; else endmod = NULL; } if (!endmod) modname = "kernel"; callback(symname, modname, addr, payload); } fclose(kallsyms); return 0; } #define CACHE1_HEADER "ld.so-1.7.0" #define CACHE1_HEADER_LEN (sizeof(CACHE1_HEADER) - 1) #define CACHE2_HEADER "glibc-ld.so.cache" #define CACHE2_HEADER_LEN (sizeof(CACHE2_HEADER) - 1) #define CACHE2_VERSION "1.1" struct ld_cache1_entry { int32_t flags; uint32_t key; uint32_t value; }; struct ld_cache1 { char header[CACHE1_HEADER_LEN]; uint32_t entry_count; struct ld_cache1_entry entries[0]; }; struct ld_cache2_entry { int32_t flags; uint32_t key; uint32_t value; uint32_t pad1_; uint64_t pad2_; }; struct ld_cache2 { char header[CACHE2_HEADER_LEN]; char version[3]; uint32_t entry_count; uint32_t string_table_len; uint32_t pad_[5]; struct ld_cache2_entry entries[0]; }; static int lib_cache_count; static struct ld_lib { char *libname; char *path; int flags; } * lib_cache; static int read_cache1(const char *ld_map) { struct ld_cache1 *ldcache = (struct ld_cache1 *)ld_map; const char *ldstrings = (const char *)(ldcache->entries + ldcache->entry_count); uint32_t i; lib_cache = (struct ld_lib *)malloc(ldcache->entry_count * sizeof(struct ld_lib)); lib_cache_count = (int)ldcache->entry_count; for (i = 0; i < ldcache->entry_count; ++i) { const char *key = ldstrings + ldcache->entries[i].key; const char *val = ldstrings + ldcache->entries[i].value; const int flags = ldcache->entries[i].flags; lib_cache[i].libname = strdup(key); lib_cache[i].path = strdup(val); lib_cache[i].flags = flags; } return 0; } static int read_cache2(const char *ld_map) { struct ld_cache2 *ldcache = (struct ld_cache2 *)ld_map; uint32_t i; if (memcmp(ld_map, CACHE2_HEADER, CACHE2_HEADER_LEN)) return -1; lib_cache = (struct ld_lib *)malloc(ldcache->entry_count * sizeof(struct ld_lib)); lib_cache_count = (int)ldcache->entry_count; for (i = 0; i < ldcache->entry_count; ++i) { const char *key = ld_map + ldcache->entries[i].key; const char *val = ld_map + ldcache->entries[i].value; const int flags = ldcache->entries[i].flags; lib_cache[i].libname = strdup(key); lib_cache[i].path = strdup(val); lib_cache[i].flags = flags; } return 0; } static int load_ld_cache(const char *cache_path) { struct stat st; size_t ld_size; const char *ld_map; int ret, fd = open(cache_path, O_RDONLY); if (fd < 0) return -1; if (fstat(fd, &st) < 0 || st.st_size < sizeof(struct ld_cache1)) { close(fd); return -1; } ld_size = st.st_size; ld_map = (const char *)mmap(NULL, ld_size, PROT_READ, MAP_PRIVATE, fd, 0); if (ld_map == MAP_FAILED) { close(fd); return -1; } if (memcmp(ld_map, CACHE1_HEADER, CACHE1_HEADER_LEN) == 0) { const struct ld_cache1 *cache1 = (struct ld_cache1 *)ld_map; size_t cache1_len = sizeof(struct ld_cache1) + (cache1->entry_count * sizeof(struct ld_cache1_entry)); cache1_len = (cache1_len + 0x7) & ~0x7ULL; if (ld_size > (cache1_len + sizeof(struct ld_cache2))) ret = read_cache2(ld_map + cache1_len); else ret = read_cache1(ld_map); } else { ret = read_cache2(ld_map); } munmap((void *)ld_map, ld_size); close(fd); return ret; } #define LD_SO_CACHE "/etc/ld.so.cache" #define FLAG_TYPE_MASK 0x00ff #define TYPE_ELF_LIBC6 0x0003 #define FLAG_ABI_MASK 0xff00 #define ABI_SPARC_LIB64 0x0100 #define ABI_IA64_LIB64 0x0200 #define ABI_X8664_LIB64 0x0300 #define ABI_S390_LIB64 0x0400 #define ABI_POWERPC_LIB64 0x0500 #define ABI_AARCH64_LIB64 0x0a00 static bool match_so_flags(int flags) { if ((flags & FLAG_TYPE_MASK) != TYPE_ELF_LIBC6) return false; switch (flags & FLAG_ABI_MASK) { case ABI_SPARC_LIB64: case ABI_IA64_LIB64: case ABI_X8664_LIB64: case ABI_S390_LIB64: case ABI_POWERPC_LIB64: case ABI_AARCH64_LIB64: return (sizeof(void *) == 8); } return sizeof(void *) == 4; } static bool which_so_in_process(const char* libname, int pid, char* libpath) { int ret, found = false; char endline[4096], *mapname = NULL, *newline; char mappings_file[128]; const size_t search_len = strlen(libname) + strlen("/lib."); char search1[search_len + 1]; char search2[search_len + 1]; snprintf(mappings_file, sizeof(mappings_file), "/proc/%ld/maps", (long)pid); FILE *fp = fopen(mappings_file, "r"); if (!fp) return NULL; snprintf(search1, search_len + 1, "/lib%s.", libname); snprintf(search2, search_len + 1, "/lib%s-", libname); do { ret = fscanf(fp, "%*x-%*x %*s %*x %*s %*d"); if (!fgets(endline, sizeof(endline), fp)) break; mapname = endline; newline = strchr(endline, '\n'); if (newline) newline[0] = '\0'; while (isspace(mapname[0])) mapname++; if (strstr(mapname, ".so") && (strstr(mapname, search1) || strstr(mapname, search2))) { found = true; memcpy(libpath, mapname, strlen(mapname) + 1); break; } } while (ret != EOF); fclose(fp); return found; } char *bcc_procutils_which_so(const char *libname, int pid) { const size_t soname_len = strlen(libname) + strlen("lib.so"); char soname[soname_len + 1]; char libpath[4096]; int i; if (strchr(libname, '/')) return strdup(libname); if (pid && which_so_in_process(libname, pid, libpath)) return strdup(libpath); if (lib_cache_count < 0) return NULL; if (!lib_cache_count && load_ld_cache(LD_SO_CACHE) < 0) { lib_cache_count = -1; return NULL; } snprintf(soname, soname_len + 1, "lib%s.so", libname); for (i = 0; i < lib_cache_count; ++i) { if (!strncmp(lib_cache[i].libname, soname, soname_len) && match_so_flags(lib_cache[i].flags)) { return strdup(lib_cache[i].path); } } return NULL; } void bcc_procutils_free(const char *ptr) { free((void *)ptr); } /* Detects the following languages + C. */ const char *languages[] = {"java", "node", "perl", "php", "python", "ruby"}; const char *language_c = "c"; const int nb_languages = 6; const char *bcc_procutils_language(int pid) { char procfilename[24], line[4096], pathname[32], *str; FILE *procfile; int i, ret; /* Look for clues in the absolute path to the executable. */ snprintf(procfilename, sizeof(procfilename), "/proc/%ld/exe", (long)pid); if (realpath(procfilename, line)) { for (i = 0; i < nb_languages; i++) if (strstr(line, languages[i])) return languages[i]; } snprintf(procfilename, sizeof(procfilename), "/proc/%ld/maps", (long)pid); procfile = fopen(procfilename, "r"); if (!procfile) return NULL; /* Look for clues in memory mappings. */ bool libc = false; do { char perm[8], dev[8]; long long begin, end, size, inode; ret = fscanf(procfile, "%llx-%llx %s %llx %s %lld", &begin, &end, perm, &size, dev, &inode); if (!fgets(line, sizeof(line), procfile)) break; if (ret == 6) { char *mapname = line; char *newline = strchr(line, '\n'); if (newline) newline[0] = '\0'; while (isspace(mapname[0])) mapname++; for (i = 0; i < nb_languages; i++) { snprintf(pathname, sizeof(pathname), "/lib%s", languages[i]); if (strstr(mapname, pathname)) { fclose(procfile); return languages[i]; } if ((str = strstr(mapname, "libc")) && (str[4] == '-' || str[4] == '.')) libc = true; } } } while (ret && ret != EOF); fclose(procfile); /* Return C as the language if libc was found and nothing else. */ return libc ? language_c : NULL; } bpfcc-0.12.0/src/cc/bcc_proc.h000066400000000000000000000042371357404205000157540ustar00rootroot00000000000000/* * Copyright (c) 2016 GitHub, Inc. * * 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. */ #ifndef LIBBCC_PROC_H #define LIBBCC_PROC_H #include "bcc_syms.h" #ifdef __cplusplus extern "C" { #endif #include #include #include typedef struct mod_info { char *name; uint64_t start_addr; uint64_t end_addr; long long unsigned int file_offset; uint64_t dev_major; uint64_t dev_minor; uint64_t inode; } mod_info; // Module info, whether to check mount namespace, payload // Callback returning a negative value indicates to stop the iteration typedef int (*bcc_procutils_modulecb)(mod_info *, int, void *); // Symbol name, address, payload typedef void (*bcc_procutils_ksymcb)(const char *, const char *, uint64_t, void *); char *bcc_procutils_which_so(const char *libname, int pid); char *bcc_procutils_which(const char *binpath); int bcc_mapping_is_file_backed(const char *mapname); // Iterate over all executable memory mapping sections of a Process. // All anonymous and non-file-backed mapping sections, namely those // listed in bcc_mapping_is_file_backed, will be ignored. // Returns -1 on error, and 0 on success int bcc_procutils_each_module(int pid, bcc_procutils_modulecb callback, void *payload); int _procfs_maps_each_module(FILE *procmaps, int pid, bcc_procutils_modulecb callback, void *payload); // Iterate over all non-data Kernel symbols. // Returns -1 on error, and 0 on success int bcc_procutils_each_ksym(bcc_procutils_ksymcb callback, void *payload); void bcc_procutils_free(const char *ptr); const char *bcc_procutils_language(int pid); #ifdef __cplusplus } #endif #endif bpfcc-0.12.0/src/cc/bcc_syms.cc000066400000000000000000000546261357404205000161510ustar00rootroot00000000000000/* * Copyright (c) 2016 GitHub, Inc. * * 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. */ #include #include #include #include #include #include #include #include #include #include #include "bcc_elf.h" #include "bcc_perf_map.h" #include "bcc_proc.h" #include "bcc_syms.h" #include "common.h" #include "vendor/tinyformat.hpp" #include "syms.h" ino_t ProcStat::getinode_() { struct stat s; return (!stat(procfs_.c_str(), &s)) ? s.st_ino : -1; } bool ProcStat::is_stale() { ino_t cur_inode = getinode_(); return (cur_inode > 0) && (cur_inode != inode_); } ProcStat::ProcStat(int pid) : procfs_(tfm::format("/proc/%d/exe", pid)), inode_(getinode_()) {} void KSyms::_add_symbol(const char *symname, const char *modname, uint64_t addr, void *p) { KSyms *ks = static_cast(p); ks->syms_.emplace_back(symname, modname, addr); } void KSyms::refresh() { if (syms_.empty()) { bcc_procutils_each_ksym(_add_symbol, this); std::sort(syms_.begin(), syms_.end()); } } bool KSyms::resolve_addr(uint64_t addr, struct bcc_symbol *sym, bool demangle) { refresh(); std::vector::iterator it; if (syms_.empty()) goto unknown_symbol; it = std::upper_bound(syms_.begin(), syms_.end(), Symbol("", "", addr)); if (it != syms_.begin()) { it--; sym->name = (*it).name.c_str(); if (demangle) sym->demangle_name = sym->name; sym->module = (*it).mod.c_str(); sym->offset = addr - (*it).addr; return true; } unknown_symbol: memset(sym, 0, sizeof(struct bcc_symbol)); return false; } bool KSyms::resolve_name(const char *_unused, const char *name, uint64_t *addr) { refresh(); if (syms_.size() != symnames_.size()) { symnames_.clear(); for (Symbol &sym : syms_) { symnames_[sym.name] = sym.addr; } } auto it = symnames_.find(name); if (it == symnames_.end()) return false; *addr = it->second; return true; } ProcSyms::ProcSyms(int pid, struct bcc_symbol_option *option) : pid_(pid), procstat_(pid) { if (option) std::memcpy(&symbol_option_, option, sizeof(bcc_symbol_option)); else symbol_option_ = { .use_debug_file = 1, .check_debug_file_crc = 1, .lazy_symbolize = 1, .use_symbol_type = (1 << STT_FUNC) | (1 << STT_GNU_IFUNC) }; load_modules(); } int ProcSyms::_add_load_sections(uint64_t v_addr, uint64_t mem_sz, uint64_t file_offset, void *payload) { auto module = static_cast(payload); module->ranges_.emplace_back(v_addr, v_addr + mem_sz, file_offset); return 0; } void ProcSyms::load_exe() { std::string exe = ebpf::get_pid_exe(pid_); Module module(exe.c_str(), exe.c_str(), &symbol_option_); if (module.type_ != ModuleType::EXEC) return; bcc_elf_foreach_load_section(exe.c_str(), &_add_load_sections, &module); if (!module.ranges_.empty()) modules_.emplace_back(std::move(module)); } void ProcSyms::load_modules() { load_exe(); bcc_procutils_each_module(pid_, _add_module, this); } void ProcSyms::refresh() { modules_.clear(); load_modules(); procstat_.reset(); } int ProcSyms::_add_module(mod_info *mod, int enter_ns, void *payload) { ProcSyms *ps = static_cast(payload); std::string ns_relative_path = tfm::format("/proc/%d/root%s", ps->pid_, mod->name); const char *modpath = enter_ns && ps->pid_ != -1 ? ns_relative_path.c_str() : mod->name; auto it = std::find_if( ps->modules_.begin(), ps->modules_.end(), [=](const ProcSyms::Module &m) { return m.name_ == mod->name; }); if (it == ps->modules_.end()) { auto module = Module( mod->name, modpath, &ps->symbol_option_); // pid/maps doesn't account for file_offset of text within the ELF. // It only gives the mmap offset. We need the real offset for symbol // lookup. if (module.type_ == ModuleType::SO) { if (bcc_elf_get_text_scn_info(modpath, &module.elf_so_addr_, &module.elf_so_offset_) < 0) { fprintf(stderr, "WARNING: Couldn't find .text section in %s\n", modpath); fprintf(stderr, "WARNING: BCC can't handle sym look ups for %s", modpath); } } if (!bcc_is_perf_map(modpath) || module.type_ != ModuleType::UNKNOWN) // Always add the module even if we can't read it, so that we could // report correct module name. Unless it's a perf map that we only // add readable ones. it = ps->modules_.insert(ps->modules_.end(), std::move(module)); else return 0; } it->ranges_.emplace_back(mod->start_addr, mod->end_addr, mod->file_offset); // perf-PID map is added last. We try both inside the Process's mount // namespace + chroot, and in global /tmp. Make sure we only add one. if (it->type_ == ModuleType::PERF_MAP) return -1; return 0; } bool ProcSyms::resolve_addr(uint64_t addr, struct bcc_symbol *sym, bool demangle) { if (procstat_.is_stale()) refresh(); memset(sym, 0, sizeof(struct bcc_symbol)); const char *original_module = nullptr; uint64_t offset; bool only_perf_map = false; for (Module &mod : modules_) { if (only_perf_map && (mod.type_ != ModuleType::PERF_MAP)) continue; if (mod.contains(addr, offset)) { if (mod.find_addr(offset, sym)) { if (demangle) { if (sym->name && (!strncmp(sym->name, "_Z", 2) || !strncmp(sym->name, "___Z", 4))) sym->demangle_name = abi::__cxa_demangle(sym->name, nullptr, nullptr, nullptr); if (!sym->demangle_name) sym->demangle_name = sym->name; } return true; } else if (mod.type_ != ModuleType::PERF_MAP) { // In this case, we found the address in the range of a module, but // not able to find a symbol of that address in the module. // Thus, we would try to find the address in perf map, and // save the module's name in case we will need it later. original_module = mod.name_.c_str(); only_perf_map = true; } } } // If we didn't find the symbol anywhere, the module name is probably // set to be the perf map's name as it would be the last we tried. // In this case, if we have found the address previously in a module, // report the saved original module name instead. if (original_module) sym->module = original_module; return false; } bool ProcSyms::resolve_name(const char *module, const char *name, uint64_t *addr) { if (procstat_.is_stale()) refresh(); for (Module &mod : modules_) { if (mod.name_ == module) return mod.find_name(name, addr); } return false; } ProcSyms::Module::Module(const char *name, const char *path, struct bcc_symbol_option *option) : name_(name), path_(path), loaded_(false), symbol_option_(option), type_(ModuleType::UNKNOWN) { int elf_type = bcc_elf_get_type(path_.c_str()); // The Module is an ELF file if (elf_type >= 0) { if (elf_type == ET_EXEC) type_ = ModuleType::EXEC; else if (elf_type == ET_DYN) type_ = ModuleType::SO; return; } // Other symbol files if (bcc_is_valid_perf_map(path_.c_str()) == 1) type_ = ModuleType::PERF_MAP; else if (bcc_elf_is_vdso(path_.c_str()) == 1) type_ = ModuleType::VDSO; // Will be stored later elf_so_offset_ = 0; elf_so_addr_ = 0; } int ProcSyms::Module::_add_symbol(const char *symname, uint64_t start, uint64_t size, void *p) { Module *m = static_cast(p); auto res = m->symnames_.emplace(symname); m->syms_.emplace_back(&*(res.first), start, size); return 0; } int ProcSyms::Module::_add_symbol_lazy(size_t section_idx, size_t str_table_idx, size_t str_len, uint64_t start, uint64_t size, int debugfile, void *p) { Module *m = static_cast(p); m->syms_.emplace_back( section_idx, str_table_idx, str_len, start, size, debugfile); return 0; } void ProcSyms::Module::load_sym_table() { if (loaded_) return; loaded_ = true; if (type_ == ModuleType::UNKNOWN) return; if (type_ == ModuleType::PERF_MAP) bcc_perf_map_foreach_sym(path_.c_str(), _add_symbol, this); if (type_ == ModuleType::EXEC || type_ == ModuleType::SO) { if (symbol_option_->lazy_symbolize) bcc_elf_foreach_sym_lazy(path_.c_str(), _add_symbol_lazy, symbol_option_, this); else bcc_elf_foreach_sym(path_.c_str(), _add_symbol, symbol_option_, this); } if (type_ == ModuleType::VDSO) bcc_elf_foreach_vdso_sym(_add_symbol, this); std::sort(syms_.begin(), syms_.end()); } bool ProcSyms::Module::contains(uint64_t addr, uint64_t &offset) const { for (const auto &range : ranges_) { if (addr >= range.start && addr < range.end) { if (type_ == ModuleType::SO || type_ == ModuleType::VDSO) { // Offset within the mmap offset = addr - range.start + range.file_offset; // Offset within the ELF for SO symbol lookup offset += (elf_so_addr_ - elf_so_offset_); } else { offset = addr; } return true; } } return false; } bool ProcSyms::Module::find_name(const char *symname, uint64_t *addr) { struct Payload { const char *symname; uint64_t *out; bool found; }; Payload payload; payload.symname = symname; payload.out = addr; payload.found = false; auto cb = [](const char *name, uint64_t start, uint64_t size, void *p) { Payload *payload = static_cast(p); if (!strcmp(payload->symname, name)) { payload->found = true; *(payload->out) = start; return -1; // Stop iteration } return 0; }; if (type_ == ModuleType::PERF_MAP) bcc_perf_map_foreach_sym(path_.c_str(), cb, &payload); if (type_ == ModuleType::EXEC || type_ == ModuleType::SO) bcc_elf_foreach_sym(path_.c_str(), cb, symbol_option_, &payload); if (type_ == ModuleType::VDSO) bcc_elf_foreach_vdso_sym(cb, &payload); if (!payload.found) return false; if (type_ == ModuleType::SO) *(payload.out) += start(); return true; } bool ProcSyms::Module::find_addr(uint64_t offset, struct bcc_symbol *sym) { load_sym_table(); sym->module = name_.c_str(); sym->offset = offset; auto it = std::upper_bound(syms_.begin(), syms_.end(), Symbol(nullptr, offset, 0)); if (it == syms_.begin()) return false; // 'it' points to the symbol whose start address is strictly greater than // the address we're looking for. Start stepping backwards as long as the // current symbol is still below the desired address, and see if the end // of the current symbol (start + size) is above the desired address. Once // we have a matching symbol, return it. Note that simply looking at '--it' // is not enough, because symbols can be nested. For example, we could be // looking for offset 0x12 with the following symbols available: // SYMBOL START SIZE END // goo 0x0 0x6 0x0 + 0x6 = 0x6 // foo 0x6 0x10 0x6 + 0x10 = 0x16 // bar 0x8 0x4 0x8 + 0x4 = 0xc // baz 0x16 0x10 0x16 + 0x10 = 0x26 // The upper_bound lookup will return baz, and then going one symbol back // brings us to bar, which does not contain offset 0x12 and is nested inside // foo. Going back one more symbol brings us to foo, which contains 0x12 // and is a match. // However, we also don't want to walk through the entire symbol list for // unknown / missing symbols. So we will break if we reach a function that // doesn't cover the function immediately before 'it', which means it is // not possibly a nested function containing the address we're looking for. --it; uint64_t limit = it->start; for (; offset >= it->start; --it) { if (offset < it->start + it->size) { // Resolve and cache the symbol name if necessary if (!it->is_name_resolved) { std::string sym_name(it->data.name_idx.str_len + 1, '\0'); if (bcc_elf_symbol_str(path_.c_str(), it->data.name_idx.section_idx, it->data.name_idx.str_table_idx, &sym_name[0], sym_name.size(), it->data.name_idx.debugfile)) break; it->data.name = &*(symnames_.emplace(std::move(sym_name)).first); it->is_name_resolved = true; } sym->name = it->data.name->c_str(); sym->offset = (offset - it->start); return true; } if (limit > it->start + it->size) break; // But don't step beyond begin()! if (it == syms_.begin()) break; } return false; } bool BuildSyms::Module::load_sym_table() { if (loaded_) return true; symbol_option_ = { .use_debug_file = 1, .check_debug_file_crc = 1, .lazy_symbolize = 1, .use_symbol_type = (1 << STT_FUNC) | (1 << STT_GNU_IFUNC) }; bcc_elf_foreach_sym(module_name_.c_str(), _add_symbol, &symbol_option_, this); std::sort(syms_.begin(), syms_.end()); for(std::vector::iterator it = syms_.begin(); it != syms_.end(); ++it++) { } loaded_ = true; return true; } int BuildSyms::Module::_add_symbol(const char *symname, uint64_t start, uint64_t size, void *p) { BuildSyms::Module *m = static_cast (p); auto res = m->symnames_.emplace(symname); m->syms_.emplace_back(&*(res.first), start, size); return 0; } bool BuildSyms::Module::resolve_addr(uint64_t offset, struct bcc_symbol* sym, bool demangle) { std::vector::iterator it; load_sym_table(); if (syms_.empty()) goto unknown_symbol; it = std::upper_bound(syms_.begin(), syms_.end(), Symbol(nullptr, offset, 0)); if (it != syms_.begin()) { it--; sym->name = (*it).name->c_str(); if (demangle) sym->demangle_name = sym->name; sym->offset = offset - (*it).start; sym->module = module_name_.c_str(); return true; } unknown_symbol: memset(sym, 0, sizeof(struct bcc_symbol)); return false; } bool BuildSyms::add_module(const std::string module_name) { struct stat s; char buildid[BPF_BUILD_ID_SIZE*2+1]; if (stat(module_name.c_str(), &s) < 0) return false; if (bcc_elf_get_buildid(module_name.c_str(), buildid) < 0) return false; std::string elf_buildid(buildid); std::unique_ptr ptr(new BuildSyms::Module(module_name.c_str())); buildmap_[elf_buildid] = std::move(ptr); return true; } bool BuildSyms::resolve_addr(std::string build_id, uint64_t offset, struct bcc_symbol *sym, bool demangle) { std::unordered_map >::iterator it; it = buildmap_.find(build_id); if (it == buildmap_.end()) /*build-id not added to the BuildSym*/ return false; BuildSyms::Module *mod = it->second.get(); return mod->resolve_addr(offset, sym, demangle); } extern "C" { void *bcc_symcache_new(int pid, struct bcc_symbol_option *option) { if (pid < 0) return static_cast(new KSyms()); return static_cast(new ProcSyms(pid, option)); } void bcc_free_symcache(void *symcache, int pid) { if (pid < 0) delete static_cast(symcache); else delete static_cast(symcache); } void bcc_symbol_free_demangle_name(struct bcc_symbol *sym) { if (sym->demangle_name && (sym->demangle_name != sym->name)) free(const_cast(sym->demangle_name)); } int bcc_symcache_resolve(void *resolver, uint64_t addr, struct bcc_symbol *sym) { SymbolCache *cache = static_cast(resolver); return cache->resolve_addr(addr, sym) ? 0 : -1; } int bcc_symcache_resolve_no_demangle(void *resolver, uint64_t addr, struct bcc_symbol *sym) { SymbolCache *cache = static_cast(resolver); return cache->resolve_addr(addr, sym, false) ? 0 : -1; } int bcc_symcache_resolve_name(void *resolver, const char *module, const char *name, uint64_t *addr) { SymbolCache *cache = static_cast(resolver); return cache->resolve_name(module, name, addr) ? 0 : -1; } void bcc_symcache_refresh(void *resolver) { SymbolCache *cache = static_cast(resolver); cache->refresh(); } void *bcc_buildsymcache_new(void) { return static_cast(new BuildSyms()); } void bcc_free_buildsymcache(void *symcache) { delete static_cast(symcache); } int bcc_buildsymcache_add_module(void *resolver, const char *module_name) { BuildSyms *bsym = static_cast(resolver); return bsym->add_module(module_name) ? 0 : -1; } int bcc_buildsymcache_resolve(void *resolver, struct bpf_stack_build_id *trace, struct bcc_symbol *sym) { std::string build_id; unsigned char *c = &trace->build_id[0]; int idx = 0; /*cannot resolve in case of fallback*/ if (trace->status == BPF_STACK_BUILD_ID_EMPTY || trace->status == BPF_STACK_BUILD_ID_IP) return 0; while( idx < 20) { int nib1 = (c[idx]&0xf0)>>4; int nib2 = (c[idx]&0x0f); build_id += "0123456789abcdef"[nib1]; build_id += "0123456789abcdef"[nib2]; idx++; } BuildSyms *bsym = static_cast(resolver); return bsym->resolve_addr(build_id, trace->offset, sym) ? 0 : -1; } struct mod_search { const char *name; uint64_t inode; uint64_t dev_major; uint64_t dev_minor; uint64_t addr; uint8_t inode_match_only; uint64_t start; uint64_t file_offset; }; int _bcc_syms_find_module(mod_info *info, int enter_ns, void *p) { struct mod_search *mod = (struct mod_search *)p; // use inode + dev to determine match if inode set if (mod->inode) { if (mod->inode != info->inode) return 0; // look at comment on USDT::set_probe_matching_kludge // in api/BPF.h for explanation of why this might be // necessary if (mod->inode_match_only) goto file_match; if(mod->dev_major == info->dev_major && mod->dev_minor == info->dev_minor) goto file_match; return 0; } // fallback to name match if (!strcmp(info->name, mod->name)) goto file_match; return 0; file_match: mod->start = info->start_addr; mod->file_offset = info->file_offset; return -1; } int bcc_resolve_global_addr(int pid, const char *module, const uint64_t address, uint8_t inode_match_only, uint64_t *global) { struct stat s; if (stat(module, &s)) return -1; struct mod_search mod = {module, s.st_ino, major(s.st_dev), minor(s.st_dev), address, inode_match_only, 0x0, 0x0}; if (bcc_procutils_each_module(pid, _bcc_syms_find_module, &mod) < 0 || mod.start == 0x0) return -1; *global = mod.start - mod.file_offset + address; return 0; } static int _sym_cb_wrapper(const char *symname, uint64_t addr, uint64_t, void *payload) { SYM_CB cb = (SYM_CB) payload; return cb(symname, addr); } int bcc_foreach_function_symbol(const char *module, SYM_CB cb) { if (module == 0 || cb == 0) return -1; static struct bcc_symbol_option default_option = { .use_debug_file = 1, .check_debug_file_crc = 1, .lazy_symbolize = 1, .use_symbol_type = (1 << STT_FUNC) | (1 << STT_GNU_IFUNC) }; return bcc_elf_foreach_sym( module, _sym_cb_wrapper, &default_option, (void *)cb); } static int _find_sym(const char *symname, uint64_t addr, uint64_t, void *payload) { struct bcc_symbol *sym = (struct bcc_symbol *)payload; if (!strcmp(sym->name, symname)) { sym->offset = addr; return -1; } return 0; } struct load_addr_t { uint64_t target_addr; uint64_t binary_addr; }; int _find_load(uint64_t v_addr, uint64_t mem_sz, uint64_t file_offset, void *payload) { struct load_addr_t *addr = static_cast(payload); if (addr->target_addr >= v_addr && addr->target_addr < (v_addr + mem_sz)) { addr->binary_addr = addr->target_addr - v_addr + file_offset; return -1; } return 0; } int bcc_resolve_symname(const char *module, const char *symname, const uint64_t addr, int pid, struct bcc_symbol_option *option, struct bcc_symbol *sym) { static struct bcc_symbol_option default_option = { .use_debug_file = 1, .check_debug_file_crc = 1, .lazy_symbolize = 1, #if defined(__powerpc64__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ .use_symbol_type = BCC_SYM_ALL_TYPES | (1 << STT_PPC64LE_SYM_LEP), #else .use_symbol_type = BCC_SYM_ALL_TYPES, #endif }; if (module == NULL) return -1; memset(sym, 0, sizeof(bcc_symbol)); if (strchr(module, '/')) { sym->module = strdup(module); } else { sym->module = bcc_procutils_which_so(module, pid); } if (sym->module == NULL) return -1; if (pid != 0 && pid != -1) { char *temp = (char*)sym->module; sym->module = strdup(tfm::format("/proc/%d/root%s", pid, sym->module).c_str()); free(temp); } sym->name = symname; sym->offset = addr; if (option == NULL) option = &default_option; if (sym->name && sym->offset == 0x0) if (bcc_elf_foreach_sym(sym->module, _find_sym, option, sym) < 0) goto invalid_module; if (sym->offset == 0x0) goto invalid_module; // For executable (ET_EXEC) binaries, translate the virtual address // to physical address in the binary file. // For shared object binaries (ET_DYN), the address from symbol table should // already be physical address in the binary file. if (bcc_elf_get_type(sym->module) == ET_EXEC) { struct load_addr_t addr = { .target_addr = sym->offset, .binary_addr = 0x0, }; if (bcc_elf_foreach_load_section(sym->module, &_find_load, &addr) < 0) goto invalid_module; if (!addr.binary_addr) goto invalid_module; sym->offset = addr.binary_addr; } return 0; invalid_module: if (sym->module) { ::free(const_cast(sym->module)); sym->module = NULL; } return -1; } } bpfcc-0.12.0/src/cc/bcc_syms.h000066400000000000000000000076751357404205000160150ustar00rootroot00000000000000/* * Copyright (c) 2016 GitHub, Inc. * * 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. */ #ifndef LIBBCC_SYMS_H #define LIBBCC_SYMS_H #ifdef __cplusplus extern "C" { #endif #include #include "linux/bpf.h" #include "bcc_proc.h" struct bcc_symbol { const char *name; const char *demangle_name; const char *module; uint64_t offset; }; typedef int (*SYM_CB)(const char *symname, uint64_t addr); struct mod_info; #ifndef STT_GNU_IFUNC #define STT_GNU_IFUNC 10 #endif #if defined(__powerpc64__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ // Indicate if the Local Entry Point (LEP) should be used as a symbol's // start address #define STT_PPC64LE_SYM_LEP 31 #endif static const uint32_t BCC_SYM_ALL_TYPES = 65535; struct bcc_symbol_option { int use_debug_file; int check_debug_file_crc; // Symbolize on-demand or symbolize everything ahead of time int lazy_symbolize; // Bitmask flags indicating what types of ELF symbols to use uint32_t use_symbol_type; }; void *bcc_symcache_new(int pid, struct bcc_symbol_option *option); void bcc_free_symcache(void *symcache, int pid); // The demangle_name pointer in bcc_symbol struct is returned from the // __cxa_demangle function call, which is supposed to be freed by caller. Call // this function after done using returned result of bcc_symcache_resolve. void bcc_symbol_free_demangle_name(struct bcc_symbol *sym); int bcc_symcache_resolve(void *symcache, uint64_t addr, struct bcc_symbol *sym); int bcc_symcache_resolve_no_demangle(void *symcache, uint64_t addr, struct bcc_symbol *sym); int bcc_symcache_resolve_name(void *resolver, const char *module, const char *name, uint64_t *addr); void bcc_symcache_refresh(void *resolver); int _bcc_syms_find_module(struct mod_info *info, int enter_ns, void *p); int bcc_resolve_global_addr(int pid, const char *module, const uint64_t address, uint8_t inode_match_only, uint64_t *global); /*bcc APIs for build_id stackmap support*/ void *bcc_buildsymcache_new(void); void bcc_free_buildsymcache(void *symcache); int bcc_buildsymcache_add_module(void *resolver, const char *module_name); int bcc_buildsymcache_resolve(void *resolver, struct bpf_stack_build_id *trace, struct bcc_symbol *sym); // Call cb on every function symbol in the specified module. Uses simpler // SYM_CB callback mainly for easier to use in Python API. // Will prefer use debug file and check debug file CRC when reading the module. int bcc_foreach_function_symbol(const char *module, SYM_CB cb); // Find the offset of a symbol in a module binary. If addr is not zero, will // calculate the offset using the provided addr and the module's load address. // // If pid is provided, will use it to help lookup the module in the Process and // enter the Process's mount Namespace. // // If option is not NULL, will respect the specified options for lookup. // Otherwise default option will apply, which is to use debug file, verify // checksum, and try all types of symbols. // // Return 0 on success and -1 on failure. Output will be write to sym. After // use, sym->module need to be freed if it's not empty. int bcc_resolve_symname(const char *module, const char *symname, const uint64_t addr, int pid, struct bcc_symbol_option* option, struct bcc_symbol *sym); #ifdef __cplusplus } #endif #endif bpfcc-0.12.0/src/cc/bcc_usdt.h000066400000000000000000000053321357404205000157650ustar00rootroot00000000000000/* * Copyright (c) 2016 GitHub, Inc. * * 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. */ #ifndef LIBBCC_USDT_H #define LIBBCC_USDT_H #ifdef __cplusplus extern "C" { #endif #include void *bcc_usdt_new_frompid(int pid, const char *path); void *bcc_usdt_new_frompath(const char *path); void bcc_usdt_close(void *usdt); struct bcc_usdt { const char *provider; const char *name; const char *bin_path; uint64_t semaphore; int num_locations; int num_arguments; }; struct bcc_usdt_location { uint64_t address; const char *bin_path; }; #define BCC_USDT_ARGUMENT_NONE 0x0 #define BCC_USDT_ARGUMENT_CONSTANT 0x1 #define BCC_USDT_ARGUMENT_DEREF_OFFSET 0x2 #define BCC_USDT_ARGUMENT_DEREF_IDENT 0x4 #define BCC_USDT_ARGUMENT_BASE_REGISTER_NAME 0x8 #define BCC_USDT_ARGUMENT_INDEX_REGISTER_NAME 0x10 #define BCC_USDT_ARGUMENT_SCALE 0x20 struct bcc_usdt_argument { int size; int valid; int constant; int deref_offset; const char *deref_ident; const char *base_register_name; const char *index_register_name; int scale; }; typedef void (*bcc_usdt_cb)(struct bcc_usdt *); void bcc_usdt_foreach(void *usdt, bcc_usdt_cb callback); int bcc_usdt_get_location(void *usdt, const char *provider_name, const char *probe_name, int index, struct bcc_usdt_location *location); int bcc_usdt_get_argument(void *usdt, const char *provider_name, const char *probe_name, int location_index, int argument_index, struct bcc_usdt_argument *argument); int bcc_usdt_enable_probe(void *, const char *, const char *); #define BCC_USDT_HAS_FULLY_SPECIFIED_PROBE int bcc_usdt_enable_fully_specified_probe(void *, const char *, const char *, const char *); const char *bcc_usdt_genargs(void **ctx_array, int len); const char *bcc_usdt_get_probe_argctype( void *ctx, const char* probe_name, const int arg_index ); typedef void (*bcc_usdt_uprobe_cb)(const char *, const char *, uint64_t, int); void bcc_usdt_foreach_uprobe(void *usdt, bcc_usdt_uprobe_cb callback); #ifdef __cplusplus } #endif #endif bpfcc-0.12.0/src/cc/bpf_module.cc000066400000000000000000000655341357404205000164630ustar00rootroot00000000000000/* * Copyright (c) 2015 PLUMgrid, Inc. * * 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "common.h" #include "bcc_debug.h" #include "bcc_elf.h" #include "frontends/b/loader.h" #include "frontends/clang/loader.h" #include "frontends/clang/b_frontend_action.h" #include "bpf_module.h" #include "exported_files.h" #include "libbpf.h" #include "bcc_btf.h" #include "libbpf/src/bpf.h" namespace ebpf { using std::get; using std::make_tuple; using std::map; using std::move; using std::string; using std::tuple; using std::unique_ptr; using std::vector; using namespace llvm; const string BPFModule::FN_PREFIX = BPF_FN_PREFIX; // Snooping class to remember the sections as the JIT creates them class MyMemoryManager : public SectionMemoryManager { public: explicit MyMemoryManager(sec_map_def *sections) : sections_(sections) { } virtual ~MyMemoryManager() {} uint8_t *allocateCodeSection(uintptr_t Size, unsigned Alignment, unsigned SectionID, StringRef SectionName) override { // The programs need to change from fake fd to real map fd, so not allocate ReadOnly regions. uint8_t *Addr = SectionMemoryManager::allocateDataSection(Size, Alignment, SectionID, SectionName, false); //printf("allocateDataSection: %s Addr %p Size %ld Alignment %d SectionID %d\n", // SectionName.str().c_str(), (void *)Addr, Size, Alignment, SectionID); (*sections_)[SectionName.str()] = make_tuple(Addr, Size, SectionID); return Addr; } uint8_t *allocateDataSection(uintptr_t Size, unsigned Alignment, unsigned SectionID, StringRef SectionName, bool isReadOnly) override { // The lines in .BTF.ext line_info, if corresponding to remapped files, will have empty source line. // The line_info will be fixed in place, so not allocate ReadOnly regions. uint8_t *Addr = SectionMemoryManager::allocateDataSection(Size, Alignment, SectionID, SectionName, false); //printf("allocateDataSection: %s Addr %p Size %ld Alignment %d SectionID %d\n", // SectionName.str().c_str(), (void *)Addr, Size, Alignment, SectionID); (*sections_)[SectionName.str()] = make_tuple(Addr, Size, SectionID); return Addr; } sec_map_def *sections_; }; BPFModule::BPFModule(unsigned flags, TableStorage *ts, bool rw_engine_enabled, const std::string &maps_ns, bool allow_rlimit, const char *dev_name) : flags_(flags), rw_engine_enabled_(rw_engine_enabled && bpf_module_rw_engine_enabled()), used_b_loader_(false), allow_rlimit_(allow_rlimit), ctx_(new LLVMContext), id_(std::to_string((uintptr_t)this)), maps_ns_(maps_ns), ts_(ts), btf_(nullptr) { ifindex_ = dev_name ? if_nametoindex(dev_name) : 0; initialize_rw_engine(); LLVMInitializeBPFTarget(); LLVMInitializeBPFTargetMC(); LLVMInitializeBPFTargetInfo(); LLVMInitializeBPFAsmPrinter(); #if LLVM_MAJOR_VERSION >= 6 LLVMInitializeBPFAsmParser(); if (flags & DEBUG_SOURCE) LLVMInitializeBPFDisassembler(); #endif LLVMLinkInMCJIT(); /* call empty function to force linking of MCJIT */ if (!ts_) { local_ts_ = createSharedTableStorage(); ts_ = &*local_ts_; } func_src_ = ebpf::make_unique(); } static StatusTuple unimplemented_sscanf(const char *, void *) { return StatusTuple(-1, "sscanf unimplemented"); } static StatusTuple unimplemented_snprintf(char *, size_t, const void *) { return StatusTuple(-1, "snprintf unimplemented"); } BPFModule::~BPFModule() { for (auto &v : tables_) { v->key_sscanf = unimplemented_sscanf; v->leaf_sscanf = unimplemented_sscanf; v->key_snprintf = unimplemented_snprintf; v->leaf_snprintf = unimplemented_snprintf; } if (!rw_engine_enabled_) { for (auto section : sections_) delete[] get<0>(section.second); } engine_.reset(); cleanup_rw_engine(); ctx_.reset(); func_src_.reset(); if (btf_) delete btf_; ts_->DeletePrefix(Path({id_})); } int BPFModule::free_bcc_memory() { return bcc_free_memory(); } // load an entire c file as a module int BPFModule::load_cfile(const string &file, bool in_memory, const char *cflags[], int ncflags) { ClangLoader clang_loader(&*ctx_, flags_); if (clang_loader.parse(&mod_, *ts_, file, in_memory, cflags, ncflags, id_, *func_src_, mod_src_, maps_ns_, fake_fd_map_, perf_events_)) return -1; return 0; } // NOTE: this is a duplicate of the above, but planning to deprecate if we // settle on clang as the frontend // Load in a pre-built list of functions into the initial Module object, then // build an ExecutionEngine. int BPFModule::load_includes(const string &text) { ClangLoader clang_loader(&*ctx_, flags_); if (clang_loader.parse(&mod_, *ts_, text, true, nullptr, 0, "", *func_src_, mod_src_, "", fake_fd_map_, perf_events_)) return -1; return 0; } void BPFModule::annotate_light() { for (auto fn = mod_->getFunctionList().begin(); fn != mod_->getFunctionList().end(); ++fn) if (!fn->hasFnAttribute(Attribute::NoInline)) fn->addFnAttr(Attribute::AlwaysInline); size_t id = 0; Path path({id_}); for (auto it = ts_->lower_bound(path), up = ts_->upper_bound(path); it != up; ++it) { TableDesc &table = it->second; tables_.push_back(&it->second); table_names_[table.name] = id++; } } void BPFModule::dump_ir(Module &mod) { legacy::PassManager PM; PM.add(createPrintModulePass(errs())); PM.run(mod); } int BPFModule::run_pass_manager(Module &mod) { if (verifyModule(mod, &errs())) { if (flags_ & DEBUG_LLVM_IR) dump_ir(mod); return -1; } legacy::PassManager PM; PassManagerBuilder PMB; PMB.OptLevel = 3; 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); if (flags_ & DEBUG_LLVM_IR) PM.add(createPrintModulePass(outs())); PM.run(mod); return 0; } void BPFModule::load_btf(sec_map_def §ions) { uint8_t *btf_sec = nullptr, *btf_ext_sec = nullptr; uintptr_t btf_sec_size = 0, btf_ext_sec_size = 0; for (auto section: sections) { auto sname = section.first; uint8_t *addr = get<0>(section.second); uintptr_t size = get<1>(section.second); if (strcmp(".BTF", sname.c_str()) == 0) { btf_sec = addr; btf_sec_size = size; } if (strcmp(".BTF.ext", sname.c_str()) == 0) { btf_ext_sec = addr; btf_ext_sec_size = size; } } if (btf_sec == nullptr || btf_ext_sec == nullptr) return; // Both .BTF and .BTF.ext ELF sections are present. // The remapped files (the main file and /virtual/include/bcc/helpers.h) // will provide missing source codes in the .BTF.ext line_info table. auto helpers_h = ExportedFiles::headers().find("/virtual/include/bcc/helpers.h"); if (helpers_h == ExportedFiles::headers().end()) { fprintf(stderr, "Internal error: missing bcc/helpers.h"); return; } std::map remapped_sources; remapped_sources["/virtual/main.c"] = mod_src_; remapped_sources["/virtual/include/bcc/helpers.h"] = helpers_h->second; BTF *btf = new BTF(flags_ & DEBUG_BTF, sections); int ret = btf->load(btf_sec, btf_sec_size, btf_ext_sec, btf_ext_sec_size, remapped_sources); if (ret) { delete btf; return; } btf_ = btf; } int BPFModule::create_maps(std::map> &map_tids, std::map &map_fds, std::map &inner_map_fds, bool for_inner_map) { std::set inner_maps; if (for_inner_map) { for (auto map : fake_fd_map_) { std::string inner_map_name = get<7>(map.second); if (inner_map_name.size()) inner_maps.insert(inner_map_name); } } for (auto map : fake_fd_map_) { int fd, fake_fd, map_type, key_size, value_size, max_entries, map_flags; const char *map_name; unsigned int pinned_id; std::string inner_map_name; int inner_map_fd = 0; fake_fd = map.first; map_type = get<0>(map.second); map_name = get<1>(map.second).c_str(); key_size = get<2>(map.second); value_size = get<3>(map.second); max_entries = get<4>(map.second); map_flags = get<5>(map.second); pinned_id = get<6>(map.second); inner_map_name = get<7>(map.second); if (for_inner_map) { if (inner_maps.find(map_name) == inner_maps.end()) continue; if (inner_map_name.size()) { fprintf(stderr, "inner map %s has inner map %s\n", map_name, inner_map_name.c_str()); return -1; } } else { if (inner_map_fds.find(map_name) != inner_map_fds.end()) continue; if (inner_map_name.size()) inner_map_fd = inner_map_fds[inner_map_name]; } if (pinned_id) { fd = bpf_map_get_fd_by_id(pinned_id); } else { struct bpf_create_map_attr attr = {}; attr.map_type = (enum bpf_map_type)map_type; attr.name = map_name; attr.key_size = key_size; attr.value_size = value_size; attr.max_entries = max_entries; attr.map_flags = map_flags; attr.map_ifindex = ifindex_; attr.inner_map_fd = inner_map_fd; if (map_tids.find(map_name) != map_tids.end()) { attr.btf_fd = btf_->get_fd(); attr.btf_key_type_id = map_tids[map_name].first; attr.btf_value_type_id = map_tids[map_name].second; } fd = bcc_create_map_xattr(&attr, allow_rlimit_); } if (fd < 0) { fprintf(stderr, "could not open bpf map: %s, error: %s\n", map_name, strerror(errno)); return -1; } if (for_inner_map) inner_map_fds[map_name] = fd; map_fds[fake_fd] = fd; } return 0; } int BPFModule::load_maps(sec_map_def §ions) { // find .maps. sections and retrieve all map key/value type id's std::map> map_tids; if (btf_) { for (auto section : sections) { auto sec_name = section.first; if (strncmp(".maps.", sec_name.c_str(), 6) == 0) { std::string map_name = sec_name.substr(6); unsigned key_tid = 0, value_tid = 0; unsigned expected_ksize = 0, expected_vsize = 0; // skip extern maps, which won't be in fake_fd_map_ as they do not // require explicit bpf_create_map. bool is_extern = false; for (auto &t : tables_) { if (t->name == map_name) { is_extern = t->is_extern; break; } } if (is_extern) continue; for (auto map : fake_fd_map_) { std::string name; name = get<1>(map.second); if (map_name == name) { expected_ksize = get<2>(map.second); expected_vsize = get<3>(map.second); break; } } int ret = btf_->get_map_tids(map_name, expected_ksize, expected_vsize, &key_tid, &value_tid); if (ret) continue; map_tids[map_name] = std::make_pair(key_tid, value_tid); } } } // create maps std::map inner_map_fds; std::map map_fds; if (create_maps(map_tids, map_fds, inner_map_fds, true) < 0) return -1; if (create_maps(map_tids, map_fds, inner_map_fds, false) < 0) return -1; // update map table fd's for (auto it = ts_->begin(), up = ts_->end(); it != up; ++it) { TableDesc &table = it->second; if (map_fds.find(table.fake_fd) != map_fds.end()) { table.fd = map_fds[table.fake_fd]; table.fake_fd = 0; } } // update instructions for (auto section : sections) { auto sec_name = section.first; if (strncmp(".bpf.fn.", sec_name.c_str(), 8) == 0) { uint8_t *addr = get<0>(section.second); uintptr_t size = get<1>(section.second); struct bpf_insn *insns = (struct bpf_insn *)addr; int i, num_insns; num_insns = size/sizeof(struct bpf_insn); for (i = 0; i < num_insns; i++) { if (insns[i].code == (BPF_LD | BPF_DW | BPF_IMM)) { // change map_fd is it is a ld_pseudo */ if (insns[i].src_reg == BPF_PSEUDO_MAP_FD && map_fds.find(insns[i].imm) != map_fds.end()) insns[i].imm = map_fds[insns[i].imm]; i++; } } } } return 0; } int BPFModule::finalize() { Module *mod = &*mod_; sec_map_def tmp_sections, *sections_p; mod->setTargetTriple("bpf-pc-linux"); #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ mod->setDataLayout("e-m:e-p:64:64-i64:64-n32:64-S128"); #else mod->setDataLayout("E-m:e-p:64:64-i64:64-n32:64-S128"); #endif sections_p = rw_engine_enabled_ ? §ions_ : &tmp_sections; string err; EngineBuilder builder(move(mod_)); builder.setErrorStr(&err); builder.setMCJITMemoryManager(ebpf::make_unique(sections_p)); builder.setMArch("bpf"); builder.setUseOrcMCJITReplacement(false); engine_ = unique_ptr(builder.create()); if (!engine_) { fprintf(stderr, "Could not create ExecutionEngine: %s\n", err.c_str()); return -1; } #if LLVM_MAJOR_VERSION >= 9 engine_->setProcessAllSections(true); #else if (flags_ & DEBUG_SOURCE) engine_->setProcessAllSections(true); #endif if (int rc = run_pass_manager(*mod)) return rc; engine_->finalizeObject(); if (flags_ & DEBUG_SOURCE) { SourceDebugger src_debugger(mod, *sections_p, FN_PREFIX, mod_src_, src_dbg_fmap_); src_debugger.dump(); } load_btf(*sections_p); if (load_maps(*sections_p)) return -1; if (!rw_engine_enabled_) { // Setup sections_ correctly and then free llvm internal memory for (auto section : tmp_sections) { auto fname = section.first; uintptr_t size = get<1>(section.second); uint8_t *tmp_p = NULL; // Only copy data for non-map sections if (strncmp("maps/", section.first.c_str(), 5)) { uint8_t *addr = get<0>(section.second); tmp_p = new uint8_t[size]; memcpy(tmp_p, addr, size); } sections_[fname] = make_tuple(tmp_p, size, get<2>(section.second)); } engine_.reset(); ctx_.reset(); } // give functions an id for (auto section : sections_) if (!strncmp(FN_PREFIX.c_str(), section.first.c_str(), FN_PREFIX.size())) function_names_.push_back(section.first); return 0; } size_t BPFModule::num_functions() const { return function_names_.size(); } const char * BPFModule::function_name(size_t id) const { if (id >= function_names_.size()) return nullptr; return function_names_[id].c_str() + FN_PREFIX.size(); } uint8_t * BPFModule::function_start(size_t id) const { if (id >= function_names_.size()) return nullptr; auto section = sections_.find(function_names_[id]); if (section == sections_.end()) return nullptr; return get<0>(section->second); } uint8_t * BPFModule::function_start(const string &name) const { auto section = sections_.find(FN_PREFIX + name); if (section == sections_.end()) return nullptr; return get<0>(section->second); } const char * BPFModule::function_source(const string &name) const { return func_src_->src(name); } const char * BPFModule::function_source_rewritten(const string &name) const { return func_src_->src_rewritten(name); } int BPFModule::annotate_prog_tag(const string &name, int prog_fd, struct bpf_insn *insns, int prog_len) { unsigned long long tag1, tag2; int err; err = bpf_prog_compute_tag(insns, prog_len, &tag1); if (err) return err; err = bpf_prog_get_tag(prog_fd, &tag2); if (err) return err; if (tag1 != tag2) { fprintf(stderr, "prog tag mismatch %llx %llx\n", tag1, tag2); return -1; } err = mkdir(BCC_PROG_TAG_DIR, 0777); if (err && errno != EEXIST) { fprintf(stderr, "cannot create " BCC_PROG_TAG_DIR "\n"); return -1; } char buf[128]; ::snprintf(buf, sizeof(buf), BCC_PROG_TAG_DIR "/bpf_prog_%llx", tag1); err = mkdir(buf, 0777); if (err && errno != EEXIST) { fprintf(stderr, "cannot create %s\n", buf); return -1; } ::snprintf(buf, sizeof(buf), BCC_PROG_TAG_DIR "/bpf_prog_%llx/%s.c", tag1, name.data()); FileDesc fd(open(buf, O_CREAT | O_WRONLY | O_TRUNC, 0644)); if (fd < 0) { fprintf(stderr, "cannot create %s\n", buf); return -1; } const char *src = function_source(name); write(fd, src, strlen(src)); ::snprintf(buf, sizeof(buf), BCC_PROG_TAG_DIR "/bpf_prog_%llx/%s.rewritten.c", tag1, name.data()); fd = open(buf, O_CREAT | O_WRONLY | O_TRUNC, 0644); if (fd < 0) { fprintf(stderr, "cannot create %s\n", buf); return -1; } src = function_source_rewritten(name); write(fd, src, strlen(src)); if (!src_dbg_fmap_[name].empty()) { ::snprintf(buf, sizeof(buf), BCC_PROG_TAG_DIR "/bpf_prog_%llx/%s.dis.txt", tag1, name.data()); fd = open(buf, O_CREAT | O_WRONLY | O_TRUNC, 0644); if (fd < 0) { fprintf(stderr, "cannot create %s\n", buf); return -1; } const char *src = src_dbg_fmap_[name].c_str(); write(fd, src, strlen(src)); } return 0; } size_t BPFModule::function_size(size_t id) const { if (id >= function_names_.size()) return 0; auto section = sections_.find(function_names_[id]); if (section == sections_.end()) return 0; return get<1>(section->second); } size_t BPFModule::function_size(const string &name) const { auto section = sections_.find(FN_PREFIX + name); if (section == sections_.end()) return 0; return get<1>(section->second); } char * BPFModule::license() const { auto section = sections_.find("license"); if (section == sections_.end()) return nullptr; return (char *)get<0>(section->second); } unsigned BPFModule::kern_version() const { auto section = sections_.find("version"); if (section == sections_.end()) return 0; return *(unsigned *)get<0>(section->second); } size_t BPFModule::num_tables() const { return tables_.size(); } size_t BPFModule::perf_event_fields(const char *event) const { auto it = perf_events_.find(event); if (it == perf_events_.end()) return 0; return it->second.size(); } const char * BPFModule::perf_event_field(const char *event, size_t i) const { auto it = perf_events_.find(event); if (it == perf_events_.end() || i >= it->second.size()) return nullptr; return it->second[i].c_str(); } size_t BPFModule::table_id(const string &name) const { auto it = table_names_.find(name); if (it == table_names_.end()) return ~0ull; return it->second; } int BPFModule::table_fd(const string &name) const { return table_fd(table_id(name)); } int BPFModule::table_fd(size_t id) const { if (id >= tables_.size()) return -1; return tables_[id]->fd; } int BPFModule::table_type(const string &name) const { return table_type(table_id(name)); } int BPFModule::table_type(size_t id) const { if (id >= tables_.size()) return -1; return tables_[id]->type; } size_t BPFModule::table_max_entries(const string &name) const { return table_max_entries(table_id(name)); } size_t BPFModule::table_max_entries(size_t id) const { if (id >= tables_.size()) return 0; return tables_[id]->max_entries; } int BPFModule::table_flags(const string &name) const { return table_flags(table_id(name)); } int BPFModule::table_flags(size_t id) const { if (id >= tables_.size()) return -1; return tables_[id]->flags; } const char * BPFModule::table_name(size_t id) const { if (id >= tables_.size()) return nullptr; return tables_[id]->name.c_str(); } const char * BPFModule::table_key_desc(size_t id) const { if (used_b_loader_) return nullptr; if (id >= tables_.size()) return nullptr; return tables_[id]->key_desc.c_str(); } const char * BPFModule::table_key_desc(const string &name) const { return table_key_desc(table_id(name)); } const char * BPFModule::table_leaf_desc(size_t id) const { if (used_b_loader_) return nullptr; if (id >= tables_.size()) return nullptr; return tables_[id]->leaf_desc.c_str(); } const char * BPFModule::table_leaf_desc(const string &name) const { return table_leaf_desc(table_id(name)); } size_t BPFModule::table_key_size(size_t id) const { if (id >= tables_.size()) return 0; return tables_[id]->key_size; } size_t BPFModule::table_key_size(const string &name) const { return table_key_size(table_id(name)); } size_t BPFModule::table_leaf_size(size_t id) const { if (id >= tables_.size()) return 0; return tables_[id]->leaf_size; } size_t BPFModule::table_leaf_size(const string &name) const { return table_leaf_size(table_id(name)); } struct TableIterator { TableIterator(size_t key_size, size_t leaf_size) : key(new uint8_t[key_size]), leaf(new uint8_t[leaf_size]) { } unique_ptr key; unique_ptr leaf; uint8_t keyb[512]; }; int BPFModule::table_key_printf(size_t id, char *buf, size_t buflen, const void *key) { if (id >= tables_.size()) return -1; const TableDesc &desc = *tables_[id]; StatusTuple rc = desc.key_snprintf(buf, buflen, key); if (rc.code() < 0) { fprintf(stderr, "%s\n", rc.msg().c_str()); return -1; } return 0; } int BPFModule::table_leaf_printf(size_t id, char *buf, size_t buflen, const void *leaf) { if (id >= tables_.size()) return -1; const TableDesc &desc = *tables_[id]; StatusTuple rc = desc.leaf_snprintf(buf, buflen, leaf); if (rc.code() < 0) { fprintf(stderr, "%s\n", rc.msg().c_str()); return -1; } return 0; } int BPFModule::table_key_scanf(size_t id, const char *key_str, void *key) { if (id >= tables_.size()) return -1; const TableDesc &desc = *tables_[id]; StatusTuple rc = desc.key_sscanf(key_str, key); if (rc.code() < 0) { fprintf(stderr, "%s\n", rc.msg().c_str()); return -1; } return 0; } int BPFModule::table_leaf_scanf(size_t id, const char *leaf_str, void *leaf) { if (id >= tables_.size()) return -1; const TableDesc &desc = *tables_[id]; StatusTuple rc = desc.leaf_sscanf(leaf_str, leaf); if (rc.code() < 0) { fprintf(stderr, "%s\n", rc.msg().c_str()); return -1; } return 0; } // load a B file, which comes in two parts int BPFModule::load_b(const string &filename, const string &proto_filename) { if (!sections_.empty()) { fprintf(stderr, "Program already initialized\n"); return -1; } if (filename.empty() || proto_filename.empty()) { fprintf(stderr, "Invalid filenames\n"); return -1; } // Helpers are inlined in the following file (C). Load the definitions and // pass the partially compiled module to the B frontend to continue with. auto helpers_h = ExportedFiles::headers().find("/virtual/include/bcc/helpers.h"); if (helpers_h == ExportedFiles::headers().end()) { fprintf(stderr, "Internal error: missing bcc/helpers.h"); return -1; } if (int rc = load_includes(helpers_h->second)) return rc; BLoader b_loader(flags_); used_b_loader_ = true; if (int rc = b_loader.parse(&*mod_, filename, proto_filename, *ts_, id_, maps_ns_)) return rc; if (rw_engine_enabled_) { if (int rc = annotate()) return rc; } else { annotate_light(); } if (int rc = finalize()) return rc; return 0; } // load a C file int BPFModule::load_c(const string &filename, const char *cflags[], int ncflags) { if (!sections_.empty()) { fprintf(stderr, "Program already initialized\n"); return -1; } if (filename.empty()) { fprintf(stderr, "Invalid filename\n"); return -1; } if (int rc = load_cfile(filename, false, cflags, ncflags)) return rc; if (rw_engine_enabled_) { if (int rc = annotate()) return rc; } else { annotate_light(); } if (int rc = finalize()) return rc; return 0; } // load a C text string int BPFModule::load_string(const string &text, const char *cflags[], int ncflags) { if (!sections_.empty()) { fprintf(stderr, "Program already initialized\n"); return -1; } if (int rc = load_cfile(text, true, cflags, ncflags)) return rc; if (rw_engine_enabled_) { if (int rc = annotate()) return rc; } else { annotate_light(); } if (int rc = finalize()) return rc; return 0; } int BPFModule::bcc_func_load(int prog_type, const char *name, const struct bpf_insn *insns, int prog_len, const char *license, unsigned kern_version, int log_level, char *log_buf, unsigned log_buf_size, const char *dev_name) { struct bpf_load_program_attr attr = {}; unsigned func_info_cnt, line_info_cnt, finfo_rec_size, linfo_rec_size; void *func_info = NULL, *line_info = NULL; int ret; attr.prog_type = (enum bpf_prog_type)prog_type; attr.name = name; attr.insns = insns; attr.license = license; attr.kern_version = kern_version; attr.log_level = log_level; if (dev_name) attr.prog_ifindex = if_nametoindex(dev_name); if (btf_) { int btf_fd = btf_->get_fd(); char secname[256]; ::snprintf(secname, sizeof(secname), ".bpf.fn.%s", name); ret = btf_->get_btf_info(secname, &func_info, &func_info_cnt, &finfo_rec_size, &line_info, &line_info_cnt, &linfo_rec_size); if (!ret) { attr.prog_btf_fd = btf_fd; attr.func_info = func_info; attr.func_info_cnt = func_info_cnt; attr.func_info_rec_size = finfo_rec_size; attr.line_info = line_info; attr.line_info_cnt = line_info_cnt; attr.line_info_rec_size = linfo_rec_size; } } ret = bcc_prog_load_xattr(&attr, prog_len, log_buf, log_buf_size, allow_rlimit_); if (btf_) { free(func_info); free(line_info); } return ret; } } // namespace ebpf bpfcc-0.12.0/src/cc/bpf_module.h000066400000000000000000000152421357404205000163140ustar00rootroot00000000000000/* * Copyright (c) 2015 PLUMgrid, Inc. * * 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. */ #pragma once #include #include #include #include #include #include "bcc_exception.h" #include "table_storage.h" namespace llvm { class ExecutionEngine; class Function; class LLVMContext; class Module; class Type; } struct bpf_insn; namespace ebpf { typedef std::map> sec_map_def; // Options to enable different debug logging. enum { // Debug output compiled LLVM IR. DEBUG_LLVM_IR = 0x1, // Debug output loaded BPF bytecode and register state on branches. DEBUG_BPF = 0x2, // Debug output pre-processor result. DEBUG_PREPROCESSOR = 0x4, // Debug output ASM instructions embedded with source. DEBUG_SOURCE = 0x8, // Debug output register state on all instructions in addition to DEBUG_BPF. DEBUG_BPF_REGISTER_STATE = 0x10, // Debug BTF. DEBUG_BTF = 0x20, }; class TableDesc; class TableStorage; class BLoader; class ClangLoader; class FuncSource; class BTF; bool bpf_module_rw_engine_enabled(void); class BPFModule { private: static const std::string FN_PREFIX; int init_engine(); void initialize_rw_engine(); void cleanup_rw_engine(); int parse(llvm::Module *mod); int finalize(); int annotate(); void annotate_light(); std::unique_ptr finalize_rw(std::unique_ptr mod); std::string make_reader(llvm::Module *mod, llvm::Type *type); std::string make_writer(llvm::Module *mod, llvm::Type *type); void dump_ir(llvm::Module &mod); int load_file_module(std::unique_ptr *mod, const std::string &file, bool in_memory); int load_includes(const std::string &text); int load_cfile(const std::string &file, bool in_memory, const char *cflags[], int ncflags); int kbuild_flags(const char *uname_release, std::vector *cflags); int run_pass_manager(llvm::Module &mod); StatusTuple sscanf(std::string fn_name, const char *str, void *val); StatusTuple snprintf(std::string fn_name, char *str, size_t sz, const void *val); void load_btf(sec_map_def §ions); int load_maps(sec_map_def §ions); int create_maps(std::map> &map_tids, std::map &map_fds, std::map &inner_map_fds, bool for_inner_map); public: BPFModule(unsigned flags, TableStorage *ts = nullptr, bool rw_engine_enabled = true, const std::string &maps_ns = "", bool allow_rlimit = true, const char *dev_name = nullptr); ~BPFModule(); int free_bcc_memory(); int load_b(const std::string &filename, const std::string &proto_filename); int load_c(const std::string &filename, const char *cflags[], int ncflags); int load_string(const std::string &text, const char *cflags[], int ncflags); std::string id() const { return id_; } std::string maps_ns() const { return maps_ns_; } size_t num_functions() const; uint8_t * function_start(size_t id) const; uint8_t * function_start(const std::string &name) const; const char * function_source(const std::string &name) const; const char * function_source_rewritten(const std::string &name) const; int annotate_prog_tag(const std::string &name, int fd, struct bpf_insn *insn, int prog_len); const char * function_name(size_t id) const; size_t function_size(size_t id) const; size_t function_size(const std::string &name) const; size_t num_tables() const; size_t table_id(const std::string &name) const; int table_fd(size_t id) const; int table_fd(const std::string &name) const; const char * table_name(size_t id) const; int table_type(const std::string &name) const; int table_type(size_t id) const; size_t table_max_entries(const std::string &name) const; size_t table_max_entries(size_t id) const; int table_flags(const std::string &name) const; int table_flags(size_t id) const; const char * table_key_desc(size_t id) const; const char * table_key_desc(const std::string &name) const; size_t table_key_size(size_t id) const; size_t table_key_size(const std::string &name) const; int table_key_printf(size_t id, char *buf, size_t buflen, const void *key); int table_key_scanf(size_t id, const char *buf, void *key); const char * table_leaf_desc(size_t id) const; const char * table_leaf_desc(const std::string &name) const; size_t table_leaf_size(size_t id) const; size_t table_leaf_size(const std::string &name) const; int table_leaf_printf(size_t id, char *buf, size_t buflen, const void *leaf); int table_leaf_scanf(size_t id, const char *buf, void *leaf); char * license() const; unsigned kern_version() const; TableStorage &table_storage() { return *ts_; } int bcc_func_load(int prog_type, const char *name, const struct bpf_insn *insns, int prog_len, const char *license, unsigned kern_version, int log_level, char *log_buf, unsigned log_buf_size, const char *dev_name = nullptr); size_t perf_event_fields(const char *) const; const char * perf_event_field(const char *, size_t i) const; private: unsigned flags_; // 0x1 for printing bool rw_engine_enabled_; bool used_b_loader_; bool allow_rlimit_; std::string filename_; std::string proto_filename_; std::unique_ptr ctx_; std::unique_ptr engine_; std::unique_ptr rw_engine_; std::unique_ptr mod_; std::unique_ptr func_src_; sec_map_def sections_; std::vector tables_; std::map table_names_; std::vector function_names_; std::map readers_; std::map writers_; std::string id_; std::string maps_ns_; std::string mod_src_; std::map src_dbg_fmap_; TableStorage *ts_; std::unique_ptr local_ts_; BTF *btf_; fake_fd_map_def fake_fd_map_; unsigned int ifindex_; // map of events -- key: event name, value: event fields std::map> perf_events_; }; } // namespace ebpf bpfcc-0.12.0/src/cc/bpf_module_rw_engine.cc000066400000000000000000000361521357404205000205120ustar00rootroot00000000000000/* * Copyright (c) 2015 PLUMgrid, Inc. * * 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. */ #include #include #include #include #include #include #include "common.h" #include "bpf_module.h" #include "table_storage.h" namespace ebpf { using std::map; using std::move; using std::string; using std::unique_ptr; using std::vector; using namespace llvm; bool bpf_module_rw_engine_enabled(void) { return true; } void BPFModule::initialize_rw_engine() { InitializeNativeTarget(); InitializeNativeTargetAsmPrinter(); } void BPFModule::cleanup_rw_engine() { rw_engine_.reset(); } static void debug_printf(Module *mod, IRBuilder<> &B, const string &fmt, vector args) { GlobalVariable *fmt_gvar = B.CreateGlobalString(fmt, "fmt"); args.insert(args.begin(), B.CreateInBoundsGEP(fmt_gvar, vector({B.getInt64(0), B.getInt64(0)}))); args.insert(args.begin(), B.getInt64((uintptr_t)stderr)); Function *fprintf_fn = mod->getFunction("fprintf"); if (!fprintf_fn) { vector fprintf_fn_args({B.getInt64Ty(), B.getInt8PtrTy()}); FunctionType *fprintf_fn_type = FunctionType::get(B.getInt32Ty(), fprintf_fn_args, /*isvarArg=*/true); fprintf_fn = Function::Create(fprintf_fn_type, GlobalValue::ExternalLinkage, "fprintf", mod); fprintf_fn->setCallingConv(CallingConv::C); fprintf_fn->addFnAttr(Attribute::NoUnwind); } B.CreateCall(fprintf_fn, args); } static void finish_sscanf(IRBuilder<> &B, vector *args, string *fmt, const map &locals, bool exact_args) { // fmt += "%n"; // int nread = 0; // int n = sscanf(s, fmt, args..., &nread); // if (n < 0) return -1; // s = &s[nread]; Value *sptr = locals.at("sptr"); Value *nread = locals.at("nread"); Function *cur_fn = B.GetInsertBlock()->getParent(); Function *sscanf_fn = B.GetInsertBlock()->getModule()->getFunction("sscanf"); *fmt += "%n"; B.CreateStore(B.getInt32(0), nread); GlobalVariable *fmt_gvar = B.CreateGlobalString(*fmt, "fmt"); (*args)[1] = B.CreateInBoundsGEP(fmt_gvar, {B.getInt64(0), B.getInt64(0)}); (*args)[0] = B.CreateLoad(sptr); args->push_back(nread); CallInst *call = B.CreateCall(sscanf_fn, *args); call->setTailCall(true); BasicBlock *label_true = BasicBlock::Create(B.getContext(), "", cur_fn); BasicBlock *label_false = BasicBlock::Create(B.getContext(), "", cur_fn); // exact_args means fail if don't consume exact number of "%" inputs // exact_args is disabled for string parsing (empty case) Value *cond = exact_args ? B.CreateICmpNE(call, B.getInt32(args->size() - 3)) : B.CreateICmpSLT(call, B.getInt32(0)); B.CreateCondBr(cond, label_true, label_false); B.SetInsertPoint(label_true); B.CreateRet(B.getInt32(-1)); B.SetInsertPoint(label_false); // s = &s[nread]; B.CreateStore( B.CreateInBoundsGEP(B.CreateLoad(sptr), B.CreateLoad(nread, true)), sptr); args->resize(2); fmt->clear(); } // recursive helper to capture the arguments static void parse_type(IRBuilder<> &B, vector *args, string *fmt, Type *type, Value *out, const map &locals, bool is_writer) { if (StructType *st = dyn_cast(type)) { *fmt += "{ "; unsigned idx = 0; for (auto field : st->elements()) { parse_type(B, args, fmt, field, B.CreateStructGEP(type, out, idx++), locals, is_writer); *fmt += " "; } *fmt += "}"; } else if (ArrayType *at = dyn_cast(type)) { if (at->getElementType() == B.getInt8Ty()) { // treat i8[] as a char string instead of as an array of u8's if (is_writer) { *fmt += "\"%s\""; args->push_back(out); } else { // When reading strings, scanf doesn't support empty "", so we need to // break this up into multiple scanf calls. To understand it, let's take // an example: // struct Event { // u32 a; // struct { // char x[64]; // int y; // } b[2]; // u32 c; // }; // The writer string would look like: // "{ 0x%x [ { \"%s\" 0x%x } { \"%s\" 0x%x } ] 0x%x }" // But the reader string needs to restart at each \"\". // reader0(const char *s, struct Event *val) { // int nread, rc; // nread = 0; // rc = sscanf(s, "{ %i [ { \"%n", &val->a, &nread); // if (rc != 1) return -1; // s += nread; nread = 0; // rc = sscanf(s, "%[^\"]%n", &val->b[0].x, &nread); // if (rc < 0) return -1; // s += nread; nread = 0; // rc = sscanf(s, "\" %i } { \"%n", &val->b[0].y, &nread); // if (rc != 1) return -1; // s += nread; nread = 0; // rc = sscanf(s, "%[^\"]%n", &val->b[1].x, &nread); // if (rc < 0) return -1; // s += nread; nread = 0; // rc = sscanf(s, "\" %i } ] %i }%n", &val->b[1].y, &val->c, &nread); // if (rc != 2) return -1; // s += nread; nread = 0; // return 0; // } *fmt += "\""; finish_sscanf(B, args, fmt, locals, true); *fmt = "%[^\"]"; args->push_back(out); finish_sscanf(B, args, fmt, locals, false); *fmt = "\""; } } else { *fmt += "[ "; for (size_t i = 0; i < at->getNumElements(); ++i) { parse_type(B, args, fmt, at->getElementType(), B.CreateStructGEP(type, out, i), locals, is_writer); *fmt += " "; } *fmt += "]"; } } else if (isa(type)) { *fmt += "0xl"; if (is_writer) *fmt += "x"; else *fmt += "i"; } else if (IntegerType *it = dyn_cast(type)) { if (is_writer) *fmt += "0x"; if (it->getBitWidth() <= 8) *fmt += "%hh"; else if (it->getBitWidth() <= 16) *fmt += "%h"; else if (it->getBitWidth() <= 32) *fmt += "%"; else *fmt += "%l"; if (is_writer) *fmt += "x"; else *fmt += "i"; args->push_back(is_writer ? B.CreateLoad(out) : out); } } // make_reader generates a dynamic function in the instruction set of the host // (not bpf) that is able to convert c-strings in the pretty-print format of // make_writer back into binary representations. The encoding of the string // takes the llvm ir structure format, which closely maps the c structure but // not exactly (no support for unions for instance). // The general algorithm is: // pod types (u8..u64) <= %i // array types // u8[] no nested quotes :( <= "..." // !u8[] <= [ %i %i ... ] // struct types // struct { u8 a; u64 b; } <= { %i %i } // nesting is supported // struct { struct { u8 a[]; }; } <= { "" } // struct { struct { u64 a[]; }; } <= { [ %i %i .. ] } string BPFModule::make_reader(Module *mod, Type *type) { auto fn_it = readers_.find(type); if (fn_it != readers_.end()) return fn_it->second; // int read(const char *in, Type *out) { // int n = sscanf(in, "{ %i ... }", &out->field1, ...); // if (n != num_fields) return -1; // return 0; // } IRBuilder<> B(*ctx_); FunctionType *sscanf_fn_type = FunctionType::get( B.getInt32Ty(), {B.getInt8PtrTy(), B.getInt8PtrTy()}, /*isVarArg=*/true); Function *sscanf_fn = mod->getFunction("sscanf"); if (!sscanf_fn) { sscanf_fn = Function::Create(sscanf_fn_type, GlobalValue::ExternalLinkage, "sscanf", mod); sscanf_fn->setCallingConv(CallingConv::C); sscanf_fn->addFnAttr(Attribute::NoUnwind); } string name = "reader" + std::to_string(readers_.size()); vector fn_args({B.getInt8PtrTy(), PointerType::getUnqual(type)}); FunctionType *fn_type = FunctionType::get(B.getInt32Ty(), fn_args, /*isVarArg=*/false); Function *fn = Function::Create(fn_type, GlobalValue::ExternalLinkage, name, mod); auto arg_it = fn->arg_begin(); Argument *arg_in = &*arg_it; ++arg_it; arg_in->setName("in"); Argument *arg_out = &*arg_it; ++arg_it; arg_out->setName("out"); BasicBlock *label_entry = BasicBlock::Create(*ctx_, "entry", fn); B.SetInsertPoint(label_entry); Value *nread = B.CreateAlloca(B.getInt32Ty()); Value *sptr = B.CreateAlloca(B.getInt8PtrTy()); map locals{{"nread", nread}, {"sptr", sptr}}; B.CreateStore(arg_in, sptr); vector args({nullptr, nullptr}); string fmt; parse_type(B, &args, &fmt, type, arg_out, locals, false); if (0) debug_printf(mod, B, "%p %p\n", vector({arg_in, arg_out})); finish_sscanf(B, &args, &fmt, locals, true); B.CreateRet(B.getInt32(0)); readers_[type] = name; return name; } // make_writer generates a dynamic function in the instruction set of the host // (not bpf) that is able to pretty-print key/leaf entries as a c-string. The // encoding of the string takes the llvm ir structure format, which closely maps // the c structure but not exactly (no support for unions for instance). // The general algorithm is: // pod types (u8..u64) => 0x%x // array types // u8[] => "..." // !u8[] => [ 0x%x 0x%x ... ] // struct types // struct { u8 a; u64 b; } => { 0x%x 0x%x } // nesting is supported // struct { struct { u8 a[]; }; } => { "" } // struct { struct { u64 a[]; }; } => { [ 0x%x 0x%x .. ] } string BPFModule::make_writer(Module *mod, Type *type) { auto fn_it = writers_.find(type); if (fn_it != writers_.end()) return fn_it->second; // int write(int len, char *out, Type *in) { // return snprintf(out, len, "{ %i ... }", out->field1, ...); // } IRBuilder<> B(*ctx_); string name = "writer" + std::to_string(writers_.size()); vector fn_args({B.getInt8PtrTy(), B.getInt64Ty(), PointerType::getUnqual(type)}); FunctionType *fn_type = FunctionType::get(B.getInt32Ty(), fn_args, /*isVarArg=*/false); Function *fn = Function::Create(fn_type, GlobalValue::ExternalLinkage, name, mod); auto arg_it = fn->arg_begin(); Argument *arg_out = &*arg_it; ++arg_it; arg_out->setName("out"); Argument *arg_len = &*arg_it; ++arg_it; arg_len->setName("len"); Argument *arg_in = &*arg_it; ++arg_it; arg_in->setName("in"); BasicBlock *label_entry = BasicBlock::Create(*ctx_, "entry", fn); B.SetInsertPoint(label_entry); map locals{ {"nread", B.CreateAlloca(B.getInt64Ty())}, }; vector args({arg_out, B.CreateZExt(arg_len, B.getInt64Ty()), nullptr}); string fmt; parse_type(B, &args, &fmt, type, arg_in, locals, true); GlobalVariable *fmt_gvar = B.CreateGlobalString(fmt, "fmt"); args[2] = B.CreateInBoundsGEP(fmt_gvar, vector({B.getInt64(0), B.getInt64(0)})); if (0) debug_printf(mod, B, "%d %p %p\n", vector({arg_len, arg_out, arg_in})); vector snprintf_fn_args({B.getInt8PtrTy(), B.getInt64Ty(), B.getInt8PtrTy()}); FunctionType *snprintf_fn_type = FunctionType::get(B.getInt32Ty(), snprintf_fn_args, /*isVarArg=*/true); Function *snprintf_fn = mod->getFunction("snprintf"); if (!snprintf_fn) snprintf_fn = Function::Create(snprintf_fn_type, GlobalValue::ExternalLinkage, "snprintf", mod); snprintf_fn->setCallingConv(CallingConv::C); snprintf_fn->addFnAttr(Attribute::NoUnwind); CallInst *call = B.CreateCall(snprintf_fn, args); call->setTailCall(true); B.CreateRet(call); writers_[type] = name; return name; } unique_ptr BPFModule::finalize_rw(unique_ptr m) { Module *mod = &*m; run_pass_manager(*mod); string err; EngineBuilder builder(move(m)); builder.setErrorStr(&err); builder.setUseOrcMCJITReplacement(false); auto engine = unique_ptr(builder.create()); if (!engine) fprintf(stderr, "Could not create ExecutionEngine: %s\n", err.c_str()); return engine; } int BPFModule::annotate() { for (auto fn = mod_->getFunctionList().begin(); fn != mod_->getFunctionList().end(); ++fn) if (!fn->hasFnAttribute(Attribute::NoInline)) fn->addFnAttr(Attribute::AlwaysInline); // separate module to hold the reader functions auto m = ebpf::make_unique("sscanf", *ctx_); size_t id = 0; Path path({id_}); for (auto it = ts_->lower_bound(path), up = ts_->upper_bound(path); it != up; ++it) { TableDesc &table = it->second; tables_.push_back(&it->second); table_names_[table.name] = id++; GlobalValue *gvar = mod_->getNamedValue(table.name); if (!gvar) continue; if (PointerType *pt = dyn_cast(gvar->getType())) { if (StructType *st = dyn_cast(pt->getElementType())) { if (st->getNumElements() < 2) continue; Type *key_type = st->elements()[0]; Type *leaf_type = st->elements()[1]; using std::placeholders::_1; using std::placeholders::_2; using std::placeholders::_3; table.key_sscanf = std::bind(&BPFModule::sscanf, this, make_reader(&*m, key_type), _1, _2); table.leaf_sscanf = std::bind(&BPFModule::sscanf, this, make_reader(&*m, leaf_type), _1, _2); table.key_snprintf = std::bind(&BPFModule::snprintf, this, make_writer(&*m, key_type), _1, _2, _3); table.leaf_snprintf = std::bind(&BPFModule::snprintf, this, make_writer(&*m, leaf_type), _1, _2, _3); } } } rw_engine_ = finalize_rw(move(m)); if (!rw_engine_) return -1; return 0; } StatusTuple BPFModule::sscanf(string fn_name, const char *str, void *val) { if (!rw_engine_enabled_) return StatusTuple(-1, "rw_engine not enabled"); auto fn = (int (*)(const char *, void *))rw_engine_->getFunctionAddress(fn_name); if (!fn) return StatusTuple(-1, "sscanf not available"); int rc = fn(str, val); if (rc < 0) return StatusTuple(rc, "error in sscanf: %s", std::strerror(errno)); return StatusTuple(rc); } StatusTuple BPFModule::snprintf(string fn_name, char *str, size_t sz, const void *val) { if (!rw_engine_enabled_) return StatusTuple(-1, "rw_engine not enabled"); auto fn = (int (*)(char *, size_t, const void *))rw_engine_->getFunctionAddress(fn_name); if (!fn) return StatusTuple(-1, "snprintf not available"); int rc = fn(str, sz, val); if (rc < 0) return StatusTuple(rc, "error in snprintf: %s", std::strerror(errno)); if ((size_t)rc == sz) return StatusTuple(-1, "buffer of size %zd too small", sz); return StatusTuple(0); } } // namespace ebpf bpfcc-0.12.0/src/cc/bpf_module_rw_engine_disabled.cc000066400000000000000000000015241357404205000223340ustar00rootroot00000000000000/* * Copyright (c) 2015 PLUMgrid, Inc. * * 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. */ #include "bpf_module.h" namespace ebpf { bool bpf_module_rw_engine_enabled(void) { return false; } void BPFModule::initialize_rw_engine() { } void BPFModule::cleanup_rw_engine() { } int BPFModule::annotate() { return -1; } } // namespace ebpf bpfcc-0.12.0/src/cc/bpffs_table.cc000066400000000000000000000053411357404205000166040ustar00rootroot00000000000000/* * Copyright (c) 2017 VMware, Inc. * * 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. */ #include "common.h" #include "table_storage_impl.h" namespace ebpf { using std::string; using std::unique_ptr; /// A filesystem backed table storage class BpfFsTableStorage : public TableStorageImpl { public: class iterator : public TableStorageIteratorImpl { public: virtual ~iterator() {} virtual unique_ptr clone() const override; virtual self_type &operator++() override; virtual value_type &operator*() const override; virtual pointer operator->() const override; }; virtual ~BpfFsTableStorage() {} virtual bool Find(const string &name, TableStorage::iterator &result) const override; virtual bool Insert(const string &name, TableDesc &&desc) override; virtual bool Delete(const string &name) override; virtual unique_ptr begin() override; virtual unique_ptr end() override; virtual unique_ptr lower_bound(const string &k) override; virtual unique_ptr upper_bound(const string &k) override; virtual unique_ptr erase(const TableStorageIteratorImpl &it) override; private: }; bool BpfFsTableStorage::Find(const string &name, TableStorage::iterator &result) const { return false; } bool BpfFsTableStorage::Insert(const string &name, TableDesc &&desc) { return false; } bool BpfFsTableStorage::Delete(const string &name) { return false; } unique_ptr BpfFsTableStorage::begin() { return unique_ptr(); } unique_ptr BpfFsTableStorage::end() { return unique_ptr(); } unique_ptr BpfFsTableStorage::lower_bound(const string &k) { return unique_ptr(); } unique_ptr BpfFsTableStorage::upper_bound(const string &k) { return unique_ptr(); } unique_ptr BpfFsTableStorage::erase(const TableStorageIteratorImpl &it) { return unique_ptr(); } unique_ptr createBpfFsTableStorage() { auto t = make_unique(); t->Init(make_unique()); return t; } } // namespace ebpf bpfcc-0.12.0/src/cc/clang/000077500000000000000000000000001357404205000151075ustar00rootroot00000000000000bpfcc-0.12.0/src/cc/clang/include/000077500000000000000000000000001357404205000165325ustar00rootroot00000000000000bpfcc-0.12.0/src/cc/clang/include/stdarg.h000066400000000000000000000041051357404205000201670ustar00rootroot00000000000000R"********( /*===---- 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 /* Hack required to make standard headers work, at least on Ubuntu */ #ifndef __GNUC_VA_LIST #define __GNUC_VA_LIST 1 #endif typedef __builtin_va_list __gnuc_va_list; #endif /* __STDARG_H */ )********" bpfcc-0.12.0/src/cc/common.cc000066400000000000000000000035121357404205000156230ustar00rootroot00000000000000/* * Copyright (c) 2016 Catalysts GmbH * * 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. */ #include #include #include "common.h" #include "vendor/tinyformat.hpp" namespace ebpf { 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 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::string get_pid_exe(pid_t pid) { char exe_path[4096]; int res; std::string exe_link = tfm::format("/proc/%d/exe", pid); res = readlink(exe_link.c_str(), exe_path, sizeof(exe_path)); if (res == -1) return ""; if (res >= static_cast(sizeof(exe_path))) res = sizeof(exe_path) - 1; exe_path[res] = '\0'; return std::string(exe_path); } } // namespace ebpf bpfcc-0.12.0/src/cc/common.h000066400000000000000000000021331357404205000154630ustar00rootroot00000000000000/* * Copyright (c) 2015 PLUMgrid, Inc. * * 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. */ #pragma once #include #include #include #include namespace ebpf { #ifdef __cpp_lib_make_unique using std::make_unique; #else template typename std::enable_if::value, std::unique_ptr>::type make_unique(Args &&... args) { return std::unique_ptr(new T(std::forward(args)...)); } #endif std::vector get_online_cpus(); std::vector get_possible_cpus(); std::string get_pid_exe(pid_t pid); } // namespace ebpf bpfcc-0.12.0/src/cc/compat/000077500000000000000000000000001357404205000153065ustar00rootroot00000000000000bpfcc-0.12.0/src/cc/compat/linux/000077500000000000000000000000001357404205000164455ustar00rootroot00000000000000bpfcc-0.12.0/src/cc/compat/linux/virtual_bpf.h000066400000000000000000004240201357404205000211350ustar00rootroot00000000000000R"********( /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ /* Copyright (c) 2011-2014 PLUMgrid, http://plumgrid.com * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. */ #ifndef _UAPI__LINUX_BPF_H__ #define _UAPI__LINUX_BPF_H__ #include #include /* Extended instruction set based on top of classic BPF */ /* instruction classes */ #define BPF_JMP32 0x06 /* jmp mode in word width */ #define BPF_ALU64 0x07 /* alu mode in double word width */ /* ld/ldx fields */ #define BPF_DW 0x18 /* double word (64-bit) */ #define BPF_XADD 0xc0 /* exclusive add */ /* alu/jmp fields */ #define BPF_MOV 0xb0 /* mov reg to reg */ #define BPF_ARSH 0xc0 /* sign extending arithmetic shift right */ /* change endianness of a register */ #define BPF_END 0xd0 /* flags for endianness conversion: */ #define BPF_TO_LE 0x00 /* convert to little-endian */ #define BPF_TO_BE 0x08 /* convert to big-endian */ #define BPF_FROM_LE BPF_TO_LE #define BPF_FROM_BE BPF_TO_BE /* jmp encodings */ #define BPF_JNE 0x50 /* jump != */ #define BPF_JLT 0xa0 /* LT is unsigned, '<' */ #define BPF_JLE 0xb0 /* LE is unsigned, '<=' */ #define BPF_JSGT 0x60 /* SGT is signed '>', GT in x86 */ #define BPF_JSGE 0x70 /* SGE is signed '>=', GE in x86 */ #define BPF_JSLT 0xc0 /* SLT is signed, '<' */ #define BPF_JSLE 0xd0 /* SLE is signed, '<=' */ #define BPF_CALL 0x80 /* function call */ #define BPF_EXIT 0x90 /* function return */ /* Register numbers */ enum { BPF_REG_0 = 0, BPF_REG_1, BPF_REG_2, BPF_REG_3, BPF_REG_4, BPF_REG_5, BPF_REG_6, BPF_REG_7, BPF_REG_8, BPF_REG_9, BPF_REG_10, __MAX_BPF_REG, }; /* BPF has 10 general purpose 64-bit registers and stack frame. */ #define MAX_BPF_REG __MAX_BPF_REG struct bpf_insn { __u8 code; /* opcode */ __u8 dst_reg:4; /* dest register */ __u8 src_reg:4; /* source register */ __s16 off; /* signed offset */ __s32 imm; /* signed immediate constant */ }; /* Key of an a BPF_MAP_TYPE_LPM_TRIE entry */ struct bpf_lpm_trie_key { __u32 prefixlen; /* up to 32 for AF_INET, 128 for AF_INET6 */ __u8 data[0]; /* Arbitrary size */ }; struct bpf_cgroup_storage_key { __u64 cgroup_inode_id; /* cgroup inode id */ __u32 attach_type; /* program attach type */ }; /* BPF syscall commands, see bpf(2) man-page for details. */ enum bpf_cmd { BPF_MAP_CREATE, BPF_MAP_LOOKUP_ELEM, BPF_MAP_UPDATE_ELEM, BPF_MAP_DELETE_ELEM, BPF_MAP_GET_NEXT_KEY, BPF_PROG_LOAD, BPF_OBJ_PIN, BPF_OBJ_GET, BPF_PROG_ATTACH, BPF_PROG_DETACH, BPF_PROG_TEST_RUN, BPF_PROG_GET_NEXT_ID, BPF_MAP_GET_NEXT_ID, BPF_PROG_GET_FD_BY_ID, BPF_MAP_GET_FD_BY_ID, BPF_OBJ_GET_INFO_BY_FD, BPF_PROG_QUERY, BPF_RAW_TRACEPOINT_OPEN, BPF_BTF_LOAD, BPF_BTF_GET_FD_BY_ID, BPF_TASK_FD_QUERY, BPF_MAP_LOOKUP_AND_DELETE_ELEM, BPF_MAP_FREEZE, BPF_BTF_GET_NEXT_ID, }; 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, }; /* Note that tracing related programs such as * BPF_PROG_TYPE_{KPROBE,TRACEPOINT,PERF_EVENT,RAW_TRACEPOINT} * are not subject to a stable API since kernel internal data * structures can change from release to release and may * therefore break existing tracing BPF programs. Tracing BPF * programs correspond to /a/ specific kernel which is to be * analyzed, and not /a/ specific kernel /and/ all future ones. */ enum bpf_prog_type { BPF_PROG_TYPE_UNSPEC, BPF_PROG_TYPE_SOCKET_FILTER, BPF_PROG_TYPE_KPROBE, BPF_PROG_TYPE_SCHED_CLS, BPF_PROG_TYPE_SCHED_ACT, BPF_PROG_TYPE_TRACEPOINT, BPF_PROG_TYPE_XDP, BPF_PROG_TYPE_PERF_EVENT, BPF_PROG_TYPE_CGROUP_SKB, BPF_PROG_TYPE_CGROUP_SOCK, BPF_PROG_TYPE_LWT_IN, BPF_PROG_TYPE_LWT_OUT, BPF_PROG_TYPE_LWT_XMIT, BPF_PROG_TYPE_SOCK_OPS, BPF_PROG_TYPE_SK_SKB, BPF_PROG_TYPE_CGROUP_DEVICE, BPF_PROG_TYPE_SK_MSG, BPF_PROG_TYPE_RAW_TRACEPOINT, BPF_PROG_TYPE_CGROUP_SOCK_ADDR, BPF_PROG_TYPE_LWT_SEG6LOCAL, BPF_PROG_TYPE_LIRC_MODE2, BPF_PROG_TYPE_SK_REUSEPORT, BPF_PROG_TYPE_FLOW_DISSECTOR, BPF_PROG_TYPE_CGROUP_SYSCTL, BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE, BPF_PROG_TYPE_CGROUP_SOCKOPT, BPF_PROG_TYPE_TRACING, }; 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, __MAX_BPF_ATTACH_TYPE }; #define MAX_BPF_ATTACH_TYPE __MAX_BPF_ATTACH_TYPE /* cgroup-bpf attach flags used in BPF_PROG_ATTACH command * * NONE(default): No further bpf programs allowed in the subtree. * * BPF_F_ALLOW_OVERRIDE: If a sub-cgroup installs some bpf program, * the program in this cgroup yields to sub-cgroup program. * * BPF_F_ALLOW_MULTI: If a sub-cgroup installs some bpf program, * that cgroup program gets run in addition to the program in this cgroup. * * Only one program is allowed to be attached to a cgroup with * NONE or BPF_F_ALLOW_OVERRIDE flag. * Attaching another program on top of NONE or BPF_F_ALLOW_OVERRIDE will * release old program and attach the new one. Attach flags has to match. * * Multiple programs are allowed to be attached to a cgroup with * BPF_F_ALLOW_MULTI flag. They are executed in FIFO order * (those that were attached first, run first) * The programs of sub-cgroup are executed first, then programs of * this cgroup and then programs of parent cgroup. * When children program makes decision (like picking TCP CA or sock bind) * parent program has a chance to override it. * * A cgroup with MULTI or OVERRIDE flag allows any attach flags in sub-cgroups. * A cgroup with NONE doesn't allow any programs in sub-cgroups. * Ex1: * cgrp1 (MULTI progs A, B) -> * cgrp2 (OVERRIDE prog C) -> * cgrp3 (MULTI prog D) -> * cgrp4 (OVERRIDE prog E) -> * cgrp5 (NONE prog F) * the event in cgrp5 triggers execution of F,D,A,B in that order. * if prog F is detached, the execution is E,D,A,B * if prog F and D are detached, the execution is E,A,B * if prog F, E and D are detached, the execution is C,A,B * * All eligible programs are executed regardless of return code from * earlier programs. */ #define BPF_F_ALLOW_OVERRIDE (1U << 0) #define BPF_F_ALLOW_MULTI (1U << 1) /* If BPF_F_STRICT_ALIGNMENT is used in BPF_PROG_LOAD command, the * verifier will perform strict alignment checking as if the kernel * has been built with CONFIG_EFFICIENT_UNALIGNED_ACCESS not set, * and NET_IP_ALIGN defined to 2. */ #define BPF_F_STRICT_ALIGNMENT (1U << 0) /* If BPF_F_ANY_ALIGNMENT is used in BPF_PROF_LOAD command, the * verifier will allow any alignment whatsoever. On platforms * with strict alignment requirements for loads ands stores (such * as sparc and mips) the verifier validates that all loads and * stores provably follow this requirement. This flag turns that * checking and enforcement off. * * It is mostly used for testing when we want to validate the * context and memory access aspects of the verifier, but because * of an unaligned access the alignment check would trigger before * the one we are interested in. */ #define BPF_F_ANY_ALIGNMENT (1U << 1) /* BPF_F_TEST_RND_HI32 is used in BPF_PROG_LOAD command for testing purpose. * Verifier does sub-register def/use analysis and identifies instructions whose * def only matters for low 32-bit, high 32-bit is never referenced later * through implicit zero extension. Therefore verifier notifies JIT back-ends * that it is safe to ignore clearing high 32-bit for these instructions. This * saves some back-ends a lot of code-gen. However such optimization is not * necessary on some arches, for example x86_64, arm64 etc, whose JIT back-ends * hence hasn't used verifier's analysis result. But, we really want to have a * way to be able to verify the correctness of the described optimization on * x86_64 on which testsuites are frequently exercised. * * So, this flag is introduced. Once it is set, verifier will randomize high * 32-bit for those instructions who has been identified as safe to ignore them. * Then, if verifier is not doing correct analysis, such randomization will * regress tests to expose bugs. */ #define BPF_F_TEST_RND_HI32 (1U << 2) /* The verifier internal test flag. Behavior is undefined */ #define BPF_F_TEST_STATE_FREQ (1U << 3) /* When BPF ldimm64's insn[0].src_reg != 0 then this can have * two extensions: * * insn[0].src_reg: BPF_PSEUDO_MAP_FD BPF_PSEUDO_MAP_VALUE * insn[0].imm: map fd map fd * insn[1].imm: 0 offset into value * insn[0].off: 0 0 * insn[1].off: 0 0 * ldimm64 rewrite: address of map address of map[0]+offset * verifier type: CONST_PTR_TO_MAP PTR_TO_MAP_VALUE */ #define BPF_PSEUDO_MAP_FD 1 #define BPF_PSEUDO_MAP_VALUE 2 /* when bpf_call->src_reg == BPF_PSEUDO_CALL, bpf_call->imm == pc-relative * offset to another bpf function */ #define BPF_PSEUDO_CALL 1 /* flags for BPF_MAP_UPDATE_ELEM command */ #define BPF_ANY 0 /* create new element or update existing */ #define BPF_NOEXIST 1 /* create new element if it didn't exist */ #define BPF_EXIST 2 /* update existing element */ #define BPF_F_LOCK 4 /* spin_lock-ed map_lookup/map_update */ /* flags for BPF_MAP_CREATE command */ #define BPF_F_NO_PREALLOC (1U << 0) /* Instead of having one common LRU list in the * BPF_MAP_TYPE_LRU_[PERCPU_]HASH map, use a percpu LRU list * which can scale and perform better. * Note, the LRU nodes (including free nodes) cannot be moved * across different LRU lists. */ #define BPF_F_NO_COMMON_LRU (1U << 1) /* Specify numa node during map creation */ #define BPF_F_NUMA_NODE (1U << 2) #define BPF_OBJ_NAME_LEN 16U /* Flags for accessing BPF object from syscall side. */ #define BPF_F_RDONLY (1U << 3) #define BPF_F_WRONLY (1U << 4) /* Flag for stack_map, store build_id+offset instead of pointer */ #define BPF_F_STACK_BUILD_ID (1U << 5) /* Zero-initialize hash function seed. This should only be used for testing. */ #define BPF_F_ZERO_SEED (1U << 6) /* Flags for accessing BPF object from program side. */ #define BPF_F_RDONLY_PROG (1U << 7) #define BPF_F_WRONLY_PROG (1U << 8) /* Clone map from listener for newly accepted socket */ #define BPF_F_CLONE (1U << 9) /* Enable memory-mapping BPF map */ #define BPF_F_MMAPABLE (1U << 10) /* flags for BPF_PROG_QUERY */ #define BPF_F_QUERY_EFFECTIVE (1U << 0) enum bpf_stack_build_id_status { /* user space need an empty entry to identify end of a trace */ BPF_STACK_BUILD_ID_EMPTY = 0, /* with valid build_id and offset */ BPF_STACK_BUILD_ID_VALID = 1, /* couldn't get build_id, fallback to ip */ BPF_STACK_BUILD_ID_IP = 2, }; #define BPF_BUILD_ID_SIZE 20 struct bpf_stack_build_id { __s32 status; unsigned char build_id[BPF_BUILD_ID_SIZE]; union { __u64 offset; __u64 ip; }; }; union bpf_attr { struct { /* anonymous struct used by BPF_MAP_CREATE command */ __u32 map_type; /* one of enum bpf_map_type */ __u32 key_size; /* size of key in bytes */ __u32 value_size; /* size of value in bytes */ __u32 max_entries; /* max number of entries in a map */ __u32 map_flags; /* BPF_MAP_CREATE related * flags defined above. */ __u32 inner_map_fd; /* fd pointing to the inner map */ __u32 numa_node; /* numa node (effective only if * BPF_F_NUMA_NODE is set). */ char map_name[BPF_OBJ_NAME_LEN]; __u32 map_ifindex; /* ifindex of netdev to create on */ __u32 btf_fd; /* fd pointing to a BTF type data */ __u32 btf_key_type_id; /* BTF type_id of the key */ __u32 btf_value_type_id; /* BTF type_id of the value */ }; struct { /* anonymous struct used by BPF_MAP_*_ELEM commands */ __u32 map_fd; __aligned_u64 key; union { __aligned_u64 value; __aligned_u64 next_key; }; __u64 flags; }; struct { /* anonymous struct used by BPF_PROG_LOAD command */ __u32 prog_type; /* one of enum bpf_prog_type */ __u32 insn_cnt; __aligned_u64 insns; __aligned_u64 license; __u32 log_level; /* verbosity level of verifier */ __u32 log_size; /* size of user buffer */ __aligned_u64 log_buf; /* user supplied buffer */ __u32 kern_version; /* not used */ __u32 prog_flags; char prog_name[BPF_OBJ_NAME_LEN]; __u32 prog_ifindex; /* ifindex of netdev to prep for */ /* For some prog types expected attach type must be known at * load time to verify attach type specific parts of prog * (context accesses, allowed helpers, etc). */ __u32 expected_attach_type; __u32 prog_btf_fd; /* fd pointing to BTF type data */ __u32 func_info_rec_size; /* userspace bpf_func_info size */ __aligned_u64 func_info; /* func info */ __u32 func_info_cnt; /* number of bpf_func_info records */ __u32 line_info_rec_size; /* userspace bpf_line_info size */ __aligned_u64 line_info; /* line info */ __u32 line_info_cnt; /* number of bpf_line_info records */ __u32 attach_btf_id; /* in-kernel BTF type id to attach to */ __u32 attach_prog_fd; /* 0 to attach to vmlinux */ }; struct { /* anonymous struct used by BPF_OBJ_* commands */ __aligned_u64 pathname; __u32 bpf_fd; __u32 file_flags; }; struct { /* anonymous struct used by BPF_PROG_ATTACH/DETACH commands */ __u32 target_fd; /* container object to attach to */ __u32 attach_bpf_fd; /* eBPF program to attach */ __u32 attach_type; __u32 attach_flags; }; struct { /* anonymous struct used by BPF_PROG_TEST_RUN command */ __u32 prog_fd; __u32 retval; __u32 data_size_in; /* input: len of data_in */ __u32 data_size_out; /* input/output: len of data_out * returns ENOSPC if data_out * is too small. */ __aligned_u64 data_in; __aligned_u64 data_out; __u32 repeat; __u32 duration; __u32 ctx_size_in; /* input: len of ctx_in */ __u32 ctx_size_out; /* input/output: len of ctx_out * returns ENOSPC if ctx_out * is too small. */ __aligned_u64 ctx_in; __aligned_u64 ctx_out; } test; struct { /* anonymous struct used by BPF_*_GET_*_ID */ union { __u32 start_id; __u32 prog_id; __u32 map_id; __u32 btf_id; }; __u32 next_id; __u32 open_flags; }; struct { /* anonymous struct used by BPF_OBJ_GET_INFO_BY_FD */ __u32 bpf_fd; __u32 info_len; __aligned_u64 info; } info; struct { /* anonymous struct used by BPF_PROG_QUERY command */ __u32 target_fd; /* container object to query */ __u32 attach_type; __u32 query_flags; __u32 attach_flags; __aligned_u64 prog_ids; __u32 prog_cnt; } query; struct { __u64 name; __u32 prog_fd; } raw_tracepoint; struct { /* anonymous struct for BPF_BTF_LOAD */ __aligned_u64 btf; __aligned_u64 btf_log_buf; __u32 btf_size; __u32 btf_log_size; __u32 btf_log_level; }; struct { __u32 pid; /* input: pid */ __u32 fd; /* input: fd */ __u32 flags; /* input: flags */ __u32 buf_len; /* input/output: buf len */ __aligned_u64 buf; /* input/output: * tp_name for tracepoint * symbol for kprobe * filename for uprobe */ __u32 prog_id; /* output: prod_id */ __u32 fd_type; /* output: BPF_FD_TYPE_* */ __u64 probe_offset; /* output: probe_offset */ __u64 probe_addr; /* output: probe_addr */ } task_fd_query; } __attribute__((aligned(8))); /* The description below is an attempt at providing documentation to eBPF * developers about the multiple available eBPF helper functions. It can be * parsed and used to produce a manual page. The workflow is the following, * and requires the rst2man utility: * * $ ./scripts/bpf_helpers_doc.py \ * --filename include/uapi/linux/bpf.h > /tmp/bpf-helpers.rst * $ rst2man /tmp/bpf-helpers.rst > /tmp/bpf-helpers.7 * $ man /tmp/bpf-helpers.7 * * Note that in order to produce this external documentation, some RST * formatting is used in the descriptions to get "bold" and "italics" in * manual pages. Also note that the few trailing white spaces are * intentional, removing them would break paragraphs for rst2man. * * Start of BPF helper function descriptions: * * void *bpf_map_lookup_elem(struct bpf_map *map, const void *key) * Description * Perform a lookup in *map* for an entry associated to *key*. * Return * Map value associated to *key*, or **NULL** if no entry was * found. * * int bpf_map_update_elem(struct bpf_map *map, const void *key, const void *value, u64 flags) * Description * Add or update the value of the entry associated to *key* in * *map* with *value*. *flags* is one of: * * **BPF_NOEXIST** * The entry for *key* must not exist in the map. * **BPF_EXIST** * The entry for *key* must already exist in the map. * **BPF_ANY** * No condition on the existence of the entry for *key*. * * Flag value **BPF_NOEXIST** cannot be used for maps of types * **BPF_MAP_TYPE_ARRAY** or **BPF_MAP_TYPE_PERCPU_ARRAY** (all * elements always exist), the helper would return an error. * Return * 0 on success, or a negative error in case of failure. * * int bpf_map_delete_elem(struct bpf_map *map, const void *key) * Description * Delete entry with *key* from *map*. * Return * 0 on success, or a negative error in case of failure. * * int bpf_probe_read(void *dst, u32 size, const void *unsafe_ptr) * Description * For tracing programs, safely attempt to read *size* bytes from * kernel space address *unsafe_ptr* and store the data in *dst*. * * Generally, use bpf_probe_read_user() or bpf_probe_read_kernel() * instead. * Return * 0 on success, or a negative error in case of failure. * * u64 bpf_ktime_get_ns(void) * Description * Return the time elapsed since system boot, in nanoseconds. * Return * Current *ktime*. * * int bpf_trace_printk(const char *fmt, u32 fmt_size, ...) * Description * This helper is a "printk()-like" facility for debugging. It * prints a message defined by format *fmt* (of size *fmt_size*) * to file *\/sys/kernel/debug/tracing/trace* from DebugFS, if * available. It can take up to three additional **u64** * arguments (as an eBPF helpers, the total number of arguments is * limited to five). * * Each time the helper is called, it appends a line to the trace. * Lines are discarded while *\/sys/kernel/debug/tracing/trace* is * open, use *\/sys/kernel/debug/tracing/trace_pipe* to avoid this. * The format of the trace is customizable, and the exact output * one will get depends on the options set in * *\/sys/kernel/debug/tracing/trace_options* (see also the * *README* file under the same directory). However, it usually * defaults to something like: * * :: * * telnet-470 [001] .N.. 419421.045894: 0x00000001: * * In the above: * * * ``telnet`` is the name of the current task. * * ``470`` is the PID of the current task. * * ``001`` is the CPU number on which the task is * running. * * In ``.N..``, each character refers to a set of * options (whether irqs are enabled, scheduling * options, whether hard/softirqs are running, level of * preempt_disabled respectively). **N** means that * **TIF_NEED_RESCHED** and **PREEMPT_NEED_RESCHED** * are set. * * ``419421.045894`` is a timestamp. * * ``0x00000001`` is a fake value used by BPF for the * instruction pointer register. * * ```` is the message formatted with * *fmt*. * * The conversion specifiers supported by *fmt* are similar, but * more limited than for printk(). They are **%d**, **%i**, * **%u**, **%x**, **%ld**, **%li**, **%lu**, **%lx**, **%lld**, * **%lli**, **%llu**, **%llx**, **%p**, **%s**. No modifier (size * of field, padding with zeroes, etc.) is available, and the * helper will return **-EINVAL** (but print nothing) if it * encounters an unknown specifier. * * Also, note that **bpf_trace_printk**\ () is slow, and should * only be used for debugging purposes. For this reason, a notice * bloc (spanning several lines) is printed to kernel logs and * states that the helper should not be used "for production use" * the first time this helper is used (or more precisely, when * **trace_printk**\ () buffers are allocated). For passing values * to user space, perf events should be preferred. * Return * The number of bytes written to the buffer, or a negative error * in case of failure. * * u32 bpf_get_prandom_u32(void) * Description * Get a pseudo-random number. * * From a security point of view, this helper uses its own * pseudo-random internal state, and cannot be used to infer the * seed of other random functions in the kernel. However, it is * essential to note that the generator used by the helper is not * cryptographically secure. * Return * A random 32-bit unsigned value. * * u32 bpf_get_smp_processor_id(void) * Description * Get the SMP (symmetric multiprocessing) processor id. Note that * all programs run with preemption disabled, which means that the * SMP processor id is stable during all the execution of the * program. * Return * The SMP id of the processor running the program. * * int bpf_skb_store_bytes(struct sk_buff *skb, u32 offset, const void *from, u32 len, u64 flags) * Description * Store *len* bytes from address *from* into the packet * associated to *skb*, at *offset*. *flags* are a combination of * **BPF_F_RECOMPUTE_CSUM** (automatically recompute the * checksum for the packet after storing the bytes) and * **BPF_F_INVALIDATE_HASH** (set *skb*\ **->hash**, *skb*\ * **->swhash** and *skb*\ **->l4hash** to 0). * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * Return * 0 on success, or a negative error in case of failure. * * int bpf_l3_csum_replace(struct sk_buff *skb, u32 offset, u64 from, u64 to, u64 size) * Description * Recompute the layer 3 (e.g. IP) checksum for the packet * associated to *skb*. Computation is incremental, so the helper * must know the former value of the header field that was * modified (*from*), the new value of this field (*to*), and the * number of bytes (2 or 4) for this field, stored in *size*. * Alternatively, it is possible to store the difference between * the previous and the new values of the header field in *to*, by * setting *from* and *size* to 0. For both methods, *offset* * indicates the location of the IP checksum within the packet. * * This helper works in combination with **bpf_csum_diff**\ (), * which does not update the checksum in-place, but offers more * flexibility and can handle sizes larger than 2 or 4 for the * checksum to update. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * Return * 0 on success, or a negative error in case of failure. * * int bpf_l4_csum_replace(struct sk_buff *skb, u32 offset, u64 from, u64 to, u64 flags) * Description * Recompute the layer 4 (e.g. TCP, UDP or ICMP) checksum for the * packet associated to *skb*. Computation is incremental, so the * helper must know the former value of the header field that was * modified (*from*), the new value of this field (*to*), and the * number of bytes (2 or 4) for this field, stored on the lowest * four bits of *flags*. Alternatively, it is possible to store * the difference between the previous and the new values of the * header field in *to*, by setting *from* and the four lowest * bits of *flags* to 0. For both methods, *offset* indicates the * location of the IP checksum within the packet. In addition to * the size of the field, *flags* can be added (bitwise OR) actual * flags. With **BPF_F_MARK_MANGLED_0**, a null checksum is left * untouched (unless **BPF_F_MARK_ENFORCE** is added as well), and * for updates resulting in a null checksum the value is set to * **CSUM_MANGLED_0** instead. Flag **BPF_F_PSEUDO_HDR** indicates * the checksum is to be computed against a pseudo-header. * * This helper works in combination with **bpf_csum_diff**\ (), * which does not update the checksum in-place, but offers more * flexibility and can handle sizes larger than 2 or 4 for the * checksum to update. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * Return * 0 on success, or a negative error in case of failure. * * int bpf_tail_call(void *ctx, struct bpf_map *prog_array_map, u32 index) * Description * This special helper is used to trigger a "tail call", or in * other words, to jump into another eBPF program. The same stack * frame is used (but values on stack and in registers for the * caller are not accessible to the callee). This mechanism allows * for program chaining, either for raising the maximum number of * available eBPF instructions, or to execute given programs in * conditional blocks. For security reasons, there is an upper * limit to the number of successive tail calls that can be * performed. * * Upon call of this helper, the program attempts to jump into a * program referenced at index *index* in *prog_array_map*, a * special map of type **BPF_MAP_TYPE_PROG_ARRAY**, and passes * *ctx*, a pointer to the context. * * If the call succeeds, the kernel immediately runs the first * instruction of the new program. This is not a function call, * and it never returns to the previous program. If the call * fails, then the helper has no effect, and the caller continues * to run its subsequent instructions. A call can fail if the * destination program for the jump does not exist (i.e. *index* * is superior to the number of entries in *prog_array_map*), or * if the maximum number of tail calls has been reached for this * chain of programs. This limit is defined in the kernel by the * macro **MAX_TAIL_CALL_CNT** (not accessible to user space), * which is currently set to 32. * Return * 0 on success, or a negative error in case of failure. * * int bpf_clone_redirect(struct sk_buff *skb, u32 ifindex, u64 flags) * Description * Clone and redirect the packet associated to *skb* to another * net device of index *ifindex*. Both ingress and egress * interfaces can be used for redirection. The **BPF_F_INGRESS** * value in *flags* is used to make the distinction (ingress path * is selected if the flag is present, egress path otherwise). * This is the only flag supported for now. * * In comparison with **bpf_redirect**\ () helper, * **bpf_clone_redirect**\ () has the associated cost of * duplicating the packet buffer, but this can be executed out of * the eBPF program. Conversely, **bpf_redirect**\ () is more * efficient, but it is handled through an action code where the * redirection happens only after the eBPF program has returned. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * Return * 0 on success, or a negative error in case of failure. * * u64 bpf_get_current_pid_tgid(void) * Return * A 64-bit integer containing the current tgid and pid, and * created as such: * *current_task*\ **->tgid << 32 \|** * *current_task*\ **->pid**. * * u64 bpf_get_current_uid_gid(void) * Return * A 64-bit integer containing the current GID and UID, and * created as such: *current_gid* **<< 32 \|** *current_uid*. * * int bpf_get_current_comm(void *buf, u32 size_of_buf) * Description * Copy the **comm** attribute of the current task into *buf* of * *size_of_buf*. The **comm** attribute contains the name of * the executable (excluding the path) for the current task. The * *size_of_buf* must be strictly positive. On success, the * helper makes sure that the *buf* is NUL-terminated. On failure, * it is filled with zeroes. * Return * 0 on success, or a negative error in case of failure. * * u32 bpf_get_cgroup_classid(struct sk_buff *skb) * Description * Retrieve the classid for the current task, i.e. for the net_cls * cgroup to which *skb* belongs. * * This helper can be used on TC egress path, but not on ingress. * * The net_cls cgroup provides an interface to tag network packets * based on a user-provided identifier for all traffic coming from * the tasks belonging to the related cgroup. See also the related * kernel documentation, available from the Linux sources in file * *Documentation/admin-guide/cgroup-v1/net_cls.rst*. * * The Linux kernel has two versions for cgroups: there are * cgroups v1 and cgroups v2. Both are available to users, who can * use a mixture of them, but note that the net_cls cgroup is for * cgroup v1 only. This makes it incompatible with BPF programs * run on cgroups, which is a cgroup-v2-only feature (a socket can * only hold data for one version of cgroups at a time). * * This helper is only available is the kernel was compiled with * the **CONFIG_CGROUP_NET_CLASSID** configuration option set to * "**y**" or to "**m**". * Return * The classid, or 0 for the default unconfigured classid. * * int bpf_skb_vlan_push(struct sk_buff *skb, __be16 vlan_proto, u16 vlan_tci) * Description * Push a *vlan_tci* (VLAN tag control information) of protocol * *vlan_proto* to the packet associated to *skb*, then update * the checksum. Note that if *vlan_proto* is different from * **ETH_P_8021Q** and **ETH_P_8021AD**, it is considered to * be **ETH_P_8021Q**. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * Return * 0 on success, or a negative error in case of failure. * * int bpf_skb_vlan_pop(struct sk_buff *skb) * Description * Pop a VLAN header from the packet associated to *skb*. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * Return * 0 on success, or a negative error in case of failure. * * int bpf_skb_get_tunnel_key(struct sk_buff *skb, struct bpf_tunnel_key *key, u32 size, u64 flags) * Description * Get tunnel metadata. This helper takes a pointer *key* to an * empty **struct bpf_tunnel_key** of **size**, that will be * filled with tunnel metadata for the packet associated to *skb*. * The *flags* can be set to **BPF_F_TUNINFO_IPV6**, which * indicates that the tunnel is based on IPv6 protocol instead of * IPv4. * * The **struct bpf_tunnel_key** is an object that generalizes the * principal parameters used by various tunneling protocols into a * single struct. This way, it can be used to easily make a * decision based on the contents of the encapsulation header, * "summarized" in this struct. In particular, it holds the IP * address of the remote end (IPv4 or IPv6, depending on the case) * in *key*\ **->remote_ipv4** or *key*\ **->remote_ipv6**. Also, * this struct exposes the *key*\ **->tunnel_id**, which is * generally mapped to a VNI (Virtual Network Identifier), making * it programmable together with the **bpf_skb_set_tunnel_key**\ * () helper. * * Let's imagine that the following code is part of a program * attached to the TC ingress interface, on one end of a GRE * tunnel, and is supposed to filter out all messages coming from * remote ends with IPv4 address other than 10.0.0.1: * * :: * * int ret; * struct bpf_tunnel_key key = {}; * * ret = bpf_skb_get_tunnel_key(skb, &key, sizeof(key), 0); * if (ret < 0) * return TC_ACT_SHOT; // drop packet * * if (key.remote_ipv4 != 0x0a000001) * return TC_ACT_SHOT; // drop packet * * return TC_ACT_OK; // accept packet * * This interface can also be used with all encapsulation devices * that can operate in "collect metadata" mode: instead of having * one network device per specific configuration, the "collect * metadata" mode only requires a single device where the * configuration can be extracted from this helper. * * This can be used together with various tunnels such as VXLan, * Geneve, GRE or IP in IP (IPIP). * Return * 0 on success, or a negative error in case of failure. * * int bpf_skb_set_tunnel_key(struct sk_buff *skb, struct bpf_tunnel_key *key, u32 size, u64 flags) * Description * Populate tunnel metadata for packet associated to *skb.* The * tunnel metadata is set to the contents of *key*, of *size*. The * *flags* can be set to a combination of the following values: * * **BPF_F_TUNINFO_IPV6** * Indicate that the tunnel is based on IPv6 protocol * instead of IPv4. * **BPF_F_ZERO_CSUM_TX** * For IPv4 packets, add a flag to tunnel metadata * indicating that checksum computation should be skipped * and checksum set to zeroes. * **BPF_F_DONT_FRAGMENT** * Add a flag to tunnel metadata indicating that the * packet should not be fragmented. * **BPF_F_SEQ_NUMBER** * Add a flag to tunnel metadata indicating that a * sequence number should be added to tunnel header before * sending the packet. This flag was added for GRE * encapsulation, but might be used with other protocols * as well in the future. * * Here is a typical usage on the transmit path: * * :: * * struct bpf_tunnel_key key; * populate key ... * bpf_skb_set_tunnel_key(skb, &key, sizeof(key), 0); * bpf_clone_redirect(skb, vxlan_dev_ifindex, 0); * * See also the description of the **bpf_skb_get_tunnel_key**\ () * helper for additional information. * Return * 0 on success, or a negative error in case of failure. * * u64 bpf_perf_event_read(struct bpf_map *map, u64 flags) * Description * Read the value of a perf event counter. This helper relies on a * *map* of type **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. The nature of * the perf event counter is selected when *map* is updated with * perf event file descriptors. The *map* is an array whose size * is the number of available CPUs, and each cell contains a value * relative to one CPU. The value to retrieve is indicated by * *flags*, that contains the index of the CPU to look up, masked * with **BPF_F_INDEX_MASK**. Alternatively, *flags* can be set to * **BPF_F_CURRENT_CPU** to indicate that the value for the * current CPU should be retrieved. * * Note that before Linux 4.13, only hardware perf event can be * retrieved. * * Also, be aware that the newer helper * **bpf_perf_event_read_value**\ () is recommended over * **bpf_perf_event_read**\ () in general. The latter has some ABI * quirks where error and counter value are used as a return code * (which is wrong to do since ranges may overlap). This issue is * fixed with **bpf_perf_event_read_value**\ (), which at the same * time provides more features over the **bpf_perf_event_read**\ * () interface. Please refer to the description of * **bpf_perf_event_read_value**\ () for details. * Return * The value of the perf event counter read from the map, or a * negative error code in case of failure. * * int bpf_redirect(u32 ifindex, u64 flags) * Description * Redirect the packet to another net device of index *ifindex*. * This helper is somewhat similar to **bpf_clone_redirect**\ * (), except that the packet is not cloned, which provides * increased performance. * * Except for XDP, both ingress and egress interfaces can be used * for redirection. The **BPF_F_INGRESS** value in *flags* is used * to make the distinction (ingress path is selected if the flag * is present, egress path otherwise). Currently, XDP only * supports redirection to the egress interface, and accepts no * flag at all. * * The same effect can be attained with the more generic * **bpf_redirect_map**\ (), which requires specific maps to be * used but offers better performance. * Return * For XDP, the helper returns **XDP_REDIRECT** on success or * **XDP_ABORTED** on error. For other program types, the values * are **TC_ACT_REDIRECT** on success or **TC_ACT_SHOT** on * error. * * u32 bpf_get_route_realm(struct sk_buff *skb) * Description * Retrieve the realm or the route, that is to say the * **tclassid** field of the destination for the *skb*. The * indentifier retrieved is a user-provided tag, similar to the * one used with the net_cls cgroup (see description for * **bpf_get_cgroup_classid**\ () helper), but here this tag is * held by a route (a destination entry), not by a task. * * Retrieving this identifier works with the clsact TC egress hook * (see also **tc-bpf(8)**), or alternatively on conventional * classful egress qdiscs, but not on TC ingress path. In case of * clsact TC egress hook, this has the advantage that, internally, * the destination entry has not been dropped yet in the transmit * path. Therefore, the destination entry does not need to be * artificially held via **netif_keep_dst**\ () for a classful * qdisc until the *skb* is freed. * * This helper is available only if the kernel was compiled with * **CONFIG_IP_ROUTE_CLASSID** configuration option. * Return * The realm of the route for the packet associated to *skb*, or 0 * if none was found. * * int bpf_perf_event_output(void *ctx, struct bpf_map *map, u64 flags, void *data, u64 size) * Description * Write raw *data* blob into a special BPF perf event held by * *map* of type **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. This perf * event must have the following attributes: **PERF_SAMPLE_RAW** * as **sample_type**, **PERF_TYPE_SOFTWARE** as **type**, and * **PERF_COUNT_SW_BPF_OUTPUT** as **config**. * * The *flags* are used to indicate the index in *map* for which * the value must be put, masked with **BPF_F_INDEX_MASK**. * Alternatively, *flags* can be set to **BPF_F_CURRENT_CPU** * to indicate that the index of the current CPU core should be * used. * * The value to write, of *size*, is passed through eBPF stack and * pointed by *data*. * * The context of the program *ctx* needs also be passed to the * helper. * * On user space, a program willing to read the values needs to * call **perf_event_open**\ () on the perf event (either for * one or for all CPUs) and to store the file descriptor into the * *map*. This must be done before the eBPF program can send data * into it. An example is available in file * *samples/bpf/trace_output_user.c* in the Linux kernel source * tree (the eBPF program counterpart is in * *samples/bpf/trace_output_kern.c*). * * **bpf_perf_event_output**\ () achieves better performance * than **bpf_trace_printk**\ () for sharing data with user * space, and is much better suitable for streaming data from eBPF * programs. * * Note that this helper is not restricted to tracing use cases * and can be used with programs attached to TC or XDP as well, * where it allows for passing data to user space listeners. Data * can be: * * * Only custom structs, * * Only the packet payload, or * * A combination of both. * Return * 0 on success, or a negative error in case of failure. * * int bpf_skb_load_bytes(const void *skb, u32 offset, void *to, u32 len) * Description * This helper was provided as an easy way to load data from a * packet. It can be used to load *len* bytes from *offset* from * the packet associated to *skb*, into the buffer pointed by * *to*. * * Since Linux 4.7, usage of this helper has mostly been replaced * by "direct packet access", enabling packet data to be * manipulated with *skb*\ **->data** and *skb*\ **->data_end** * pointing respectively to the first byte of packet data and to * the byte after the last byte of packet data. However, it * remains useful if one wishes to read large quantities of data * at once from a packet into the eBPF stack. * Return * 0 on success, or a negative error in case of failure. * * int bpf_get_stackid(void *ctx, struct bpf_map *map, u64 flags) * Description * Walk a user or a kernel stack and return its id. To achieve * this, the helper needs *ctx*, which is a pointer to the context * on which the tracing program is executed, and a pointer to a * *map* of type **BPF_MAP_TYPE_STACK_TRACE**. * * The last argument, *flags*, holds the number of stack frames to * skip (from 0 to 255), masked with * **BPF_F_SKIP_FIELD_MASK**. The next bits can be used to set * a combination of the following flags: * * **BPF_F_USER_STACK** * Collect a user space stack instead of a kernel stack. * **BPF_F_FAST_STACK_CMP** * Compare stacks by hash only. * **BPF_F_REUSE_STACKID** * If two different stacks hash into the same *stackid*, * discard the old one. * * The stack id retrieved is a 32 bit long integer handle which * can be further combined with other data (including other stack * ids) and used as a key into maps. This can be useful for * generating a variety of graphs (such as flame graphs or off-cpu * graphs). * * For walking a stack, this helper is an improvement over * **bpf_probe_read**\ (), which can be used with unrolled loops * but is not efficient and consumes a lot of eBPF instructions. * Instead, **bpf_get_stackid**\ () can collect up to * **PERF_MAX_STACK_DEPTH** both kernel and user frames. Note that * this limit can be controlled with the **sysctl** program, and * that it should be manually increased in order to profile long * user stacks (such as stacks for Java programs). To do so, use: * * :: * * # sysctl kernel.perf_event_max_stack= * Return * The positive or null stack id on success, or a negative error * in case of failure. * * s64 bpf_csum_diff(__be32 *from, u32 from_size, __be32 *to, u32 to_size, __wsum seed) * Description * Compute a checksum difference, from the raw buffer pointed by * *from*, of length *from_size* (that must be a multiple of 4), * towards the raw buffer pointed by *to*, of size *to_size* * (same remark). An optional *seed* can be added to the value * (this can be cascaded, the seed may come from a previous call * to the helper). * * This is flexible enough to be used in several ways: * * * With *from_size* == 0, *to_size* > 0 and *seed* set to * checksum, it can be used when pushing new data. * * With *from_size* > 0, *to_size* == 0 and *seed* set to * checksum, it can be used when removing data from a packet. * * With *from_size* > 0, *to_size* > 0 and *seed* set to 0, it * can be used to compute a diff. Note that *from_size* and * *to_size* do not need to be equal. * * This helper can be used in combination with * **bpf_l3_csum_replace**\ () and **bpf_l4_csum_replace**\ (), to * which one can feed in the difference computed with * **bpf_csum_diff**\ (). * Return * The checksum result, or a negative error code in case of * failure. * * int bpf_skb_get_tunnel_opt(struct sk_buff *skb, void *opt, u32 size) * Description * Retrieve tunnel options metadata for the packet associated to * *skb*, and store the raw tunnel option data to the buffer *opt* * of *size*. * * This helper can be used with encapsulation devices that can * operate in "collect metadata" mode (please refer to the related * note in the description of **bpf_skb_get_tunnel_key**\ () for * more details). A particular example where this can be used is * in combination with the Geneve encapsulation protocol, where it * allows for pushing (with **bpf_skb_get_tunnel_opt**\ () helper) * and retrieving arbitrary TLVs (Type-Length-Value headers) from * the eBPF program. This allows for full customization of these * headers. * Return * The size of the option data retrieved. * * int bpf_skb_set_tunnel_opt(struct sk_buff *skb, void *opt, u32 size) * Description * Set tunnel options metadata for the packet associated to *skb* * to the option data contained in the raw buffer *opt* of *size*. * * See also the description of the **bpf_skb_get_tunnel_opt**\ () * helper for additional information. * Return * 0 on success, or a negative error in case of failure. * * int bpf_skb_change_proto(struct sk_buff *skb, __be16 proto, u64 flags) * Description * Change the protocol of the *skb* to *proto*. Currently * supported are transition from IPv4 to IPv6, and from IPv6 to * IPv4. The helper takes care of the groundwork for the * transition, including resizing the socket buffer. The eBPF * program is expected to fill the new headers, if any, via * **skb_store_bytes**\ () and to recompute the checksums with * **bpf_l3_csum_replace**\ () and **bpf_l4_csum_replace**\ * (). The main case for this helper is to perform NAT64 * operations out of an eBPF program. * * Internally, the GSO type is marked as dodgy so that headers are * checked and segments are recalculated by the GSO/GRO engine. * The size for GSO target is adapted as well. * * All values for *flags* are reserved for future usage, and must * be left at zero. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * Return * 0 on success, or a negative error in case of failure. * * int bpf_skb_change_type(struct sk_buff *skb, u32 type) * Description * Change the packet type for the packet associated to *skb*. This * comes down to setting *skb*\ **->pkt_type** to *type*, except * the eBPF program does not have a write access to *skb*\ * **->pkt_type** beside this helper. Using a helper here allows * for graceful handling of errors. * * The major use case is to change incoming *skb*s to * **PACKET_HOST** in a programmatic way instead of having to * recirculate via **redirect**\ (..., **BPF_F_INGRESS**), for * example. * * Note that *type* only allows certain values. At this time, they * are: * * **PACKET_HOST** * Packet is for us. * **PACKET_BROADCAST** * Send packet to all. * **PACKET_MULTICAST** * Send packet to group. * **PACKET_OTHERHOST** * Send packet to someone else. * Return * 0 on success, or a negative error in case of failure. * * int bpf_skb_under_cgroup(struct sk_buff *skb, struct bpf_map *map, u32 index) * Description * Check whether *skb* is a descendant of the cgroup2 held by * *map* of type **BPF_MAP_TYPE_CGROUP_ARRAY**, at *index*. * Return * The return value depends on the result of the test, and can be: * * * 0, if the *skb* failed the cgroup2 descendant test. * * 1, if the *skb* succeeded the cgroup2 descendant test. * * A negative error code, if an error occurred. * * u32 bpf_get_hash_recalc(struct sk_buff *skb) * Description * Retrieve the hash of the packet, *skb*\ **->hash**. If it is * not set, in particular if the hash was cleared due to mangling, * recompute this hash. Later accesses to the hash can be done * directly with *skb*\ **->hash**. * * Calling **bpf_set_hash_invalid**\ (), changing a packet * prototype with **bpf_skb_change_proto**\ (), or calling * **bpf_skb_store_bytes**\ () with the * **BPF_F_INVALIDATE_HASH** are actions susceptible to clear * the hash and to trigger a new computation for the next call to * **bpf_get_hash_recalc**\ (). * Return * The 32-bit hash. * * u64 bpf_get_current_task(void) * Return * A pointer to the current task struct. * * int bpf_probe_write_user(void *dst, const void *src, u32 len) * Description * Attempt in a safe way to write *len* bytes from the buffer * *src* to *dst* in memory. It only works for threads that are in * user context, and *dst* must be a valid user space address. * * This helper should not be used to implement any kind of * security mechanism because of TOC-TOU attacks, but rather to * debug, divert, and manipulate execution of semi-cooperative * processes. * * Keep in mind that this feature is meant for experiments, and it * has a risk of crashing the system and running programs. * Therefore, when an eBPF program using this helper is attached, * a warning including PID and process name is printed to kernel * logs. * Return * 0 on success, or a negative error in case of failure. * * int bpf_current_task_under_cgroup(struct bpf_map *map, u32 index) * Description * Check whether the probe is being run is the context of a given * subset of the cgroup2 hierarchy. The cgroup2 to test is held by * *map* of type **BPF_MAP_TYPE_CGROUP_ARRAY**, at *index*. * Return * The return value depends on the result of the test, and can be: * * * 0, if the *skb* task belongs to the cgroup2. * * 1, if the *skb* task does not belong to the cgroup2. * * A negative error code, if an error occurred. * * int bpf_skb_change_tail(struct sk_buff *skb, u32 len, u64 flags) * Description * Resize (trim or grow) the packet associated to *skb* to the * new *len*. The *flags* are reserved for future usage, and must * be left at zero. * * The basic idea is that the helper performs the needed work to * change the size of the packet, then the eBPF program rewrites * the rest via helpers like **bpf_skb_store_bytes**\ (), * **bpf_l3_csum_replace**\ (), **bpf_l3_csum_replace**\ () * and others. This helper is a slow path utility intended for * replies with control messages. And because it is targeted for * slow path, the helper itself can afford to be slow: it * implicitly linearizes, unclones and drops offloads from the * *skb*. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * Return * 0 on success, or a negative error in case of failure. * * int bpf_skb_pull_data(struct sk_buff *skb, u32 len) * Description * Pull in non-linear data in case the *skb* is non-linear and not * all of *len* are part of the linear section. Make *len* bytes * from *skb* readable and writable. If a zero value is passed for * *len*, then the whole length of the *skb* is pulled. * * This helper is only needed for reading and writing with direct * packet access. * * For direct packet access, testing that offsets to access * are within packet boundaries (test on *skb*\ **->data_end**) is * susceptible to fail if offsets are invalid, or if the requested * data is in non-linear parts of the *skb*. On failure the * program can just bail out, or in the case of a non-linear * buffer, use a helper to make the data available. The * **bpf_skb_load_bytes**\ () helper is a first solution to access * the data. Another one consists in using **bpf_skb_pull_data** * to pull in once the non-linear parts, then retesting and * eventually access the data. * * At the same time, this also makes sure the *skb* is uncloned, * which is a necessary condition for direct write. As this needs * to be an invariant for the write part only, the verifier * detects writes and adds a prologue that is calling * **bpf_skb_pull_data()** to effectively unclone the *skb* from * the very beginning in case it is indeed cloned. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * Return * 0 on success, or a negative error in case of failure. * * s64 bpf_csum_update(struct sk_buff *skb, __wsum csum) * Description * Add the checksum *csum* into *skb*\ **->csum** in case the * driver has supplied a checksum for the entire packet into that * field. Return an error otherwise. This helper is intended to be * used in combination with **bpf_csum_diff**\ (), in particular * when the checksum needs to be updated after data has been * written into the packet through direct packet access. * Return * The checksum on success, or a negative error code in case of * failure. * * void bpf_set_hash_invalid(struct sk_buff *skb) * Description * Invalidate the current *skb*\ **->hash**. It can be used after * mangling on headers through direct packet access, in order to * indicate that the hash is outdated and to trigger a * recalculation the next time the kernel tries to access this * hash or when the **bpf_get_hash_recalc**\ () helper is called. * * int bpf_get_numa_node_id(void) * Description * Return the id of the current NUMA node. The primary use case * for this helper is the selection of sockets for the local NUMA * node, when the program is attached to sockets using the * **SO_ATTACH_REUSEPORT_EBPF** option (see also **socket(7)**), * but the helper is also available to other eBPF program types, * similarly to **bpf_get_smp_processor_id**\ (). * Return * The id of current NUMA node. * * int bpf_skb_change_head(struct sk_buff *skb, u32 len, u64 flags) * Description * Grows headroom of packet associated to *skb* and adjusts the * offset of the MAC header accordingly, adding *len* bytes of * space. It automatically extends and reallocates memory as * required. * * This helper can be used on a layer 3 *skb* to push a MAC header * for redirection into a layer 2 device. * * All values for *flags* are reserved for future usage, and must * be left at zero. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * Return * 0 on success, or a negative error in case of failure. * * int bpf_xdp_adjust_head(struct xdp_buff *xdp_md, int delta) * Description * Adjust (move) *xdp_md*\ **->data** by *delta* bytes. Note that * it is possible to use a negative value for *delta*. This helper * can be used to prepare the packet for pushing or popping * headers. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * Return * 0 on success, or a negative error in case of failure. * * int bpf_probe_read_str(void *dst, u32 size, const void *unsafe_ptr) * Description * Copy a NUL terminated string from an unsafe kernel address * *unsafe_ptr* to *dst*. See bpf_probe_read_kernel_str() for * more details. * * Generally, use bpf_probe_read_user_str() or bpf_probe_read_kernel_str() * instead. * Return * On success, the strictly positive length of the string, * including the trailing NUL character. On error, a negative * value. * * u64 bpf_get_socket_cookie(struct sk_buff *skb) * Description * If the **struct sk_buff** pointed by *skb* has a known socket, * retrieve the cookie (generated by the kernel) of this socket. * If no cookie has been set yet, generate a new cookie. Once * generated, the socket cookie remains stable for the life of the * socket. This helper can be useful for monitoring per socket * networking traffic statistics as it provides a global socket * identifier that can be assumed unique. * Return * A 8-byte long non-decreasing number on success, or 0 if the * socket field is missing inside *skb*. * * u64 bpf_get_socket_cookie(struct bpf_sock_addr *ctx) * Description * Equivalent to bpf_get_socket_cookie() helper that accepts * *skb*, but gets socket from **struct bpf_sock_addr** context. * Return * A 8-byte long non-decreasing number. * * u64 bpf_get_socket_cookie(struct bpf_sock_ops *ctx) * Description * Equivalent to bpf_get_socket_cookie() helper that accepts * *skb*, but gets socket from **struct bpf_sock_ops** context. * Return * A 8-byte long non-decreasing number. * * u32 bpf_get_socket_uid(struct sk_buff *skb) * Return * The owner UID of the socket associated to *skb*. If the socket * is **NULL**, or if it is not a full socket (i.e. if it is a * time-wait or a request socket instead), **overflowuid** value * is returned (note that **overflowuid** might also be the actual * UID value for the socket). * * u32 bpf_set_hash(struct sk_buff *skb, u32 hash) * Description * Set the full hash for *skb* (set the field *skb*\ **->hash**) * to value *hash*. * Return * 0 * * int bpf_setsockopt(struct bpf_sock_ops *bpf_socket, int level, int optname, void *optval, int optlen) * Description * Emulate a call to **setsockopt()** on the socket associated to * *bpf_socket*, which must be a full socket. The *level* at * which the option resides and the name *optname* of the option * must be specified, see **setsockopt(2)** for more information. * The option value of length *optlen* is pointed by *optval*. * * This helper actually implements a subset of **setsockopt()**. * It supports the following *level*\ s: * * * **SOL_SOCKET**, which supports the following *optname*\ s: * **SO_RCVBUF**, **SO_SNDBUF**, **SO_MAX_PACING_RATE**, * **SO_PRIORITY**, **SO_RCVLOWAT**, **SO_MARK**. * * **IPPROTO_TCP**, which supports the following *optname*\ s: * **TCP_CONGESTION**, **TCP_BPF_IW**, * **TCP_BPF_SNDCWND_CLAMP**. * * **IPPROTO_IP**, which supports *optname* **IP_TOS**. * * **IPPROTO_IPV6**, which supports *optname* **IPV6_TCLASS**. * Return * 0 on success, or a negative error in case of failure. * * int bpf_skb_adjust_room(struct sk_buff *skb, s32 len_diff, u32 mode, u64 flags) * Description * Grow or shrink the room for data in the packet associated to * *skb* by *len_diff*, and according to the selected *mode*. * * There are two supported modes at this time: * * * **BPF_ADJ_ROOM_MAC**: Adjust room at the mac layer * (room space is added or removed below the layer 2 header). * * * **BPF_ADJ_ROOM_NET**: Adjust room at the network layer * (room space is added or removed below the layer 3 header). * * The following flags are supported at this time: * * * **BPF_F_ADJ_ROOM_FIXED_GSO**: Do not adjust gso_size. * Adjusting mss in this way is not allowed for datagrams. * * * **BPF_F_ADJ_ROOM_ENCAP_L3_IPV4**, * **BPF_F_ADJ_ROOM_ENCAP_L3_IPV6**: * Any new space is reserved to hold a tunnel header. * Configure skb offsets and other fields accordingly. * * * **BPF_F_ADJ_ROOM_ENCAP_L4_GRE**, * **BPF_F_ADJ_ROOM_ENCAP_L4_UDP**: * Use with ENCAP_L3 flags to further specify the tunnel type. * * * **BPF_F_ADJ_ROOM_ENCAP_L2**\ (*len*): * Use with ENCAP_L3/L4 flags to further specify the tunnel * type; *len* is the length of the inner MAC header. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * Return * 0 on success, or a negative error in case of failure. * * int bpf_redirect_map(struct bpf_map *map, u32 key, u64 flags) * Description * Redirect the packet to the endpoint referenced by *map* at * index *key*. Depending on its type, this *map* can contain * references to net devices (for forwarding packets through other * ports), or to CPUs (for redirecting XDP frames to another CPU; * but this is only implemented for native XDP (with driver * support) as of this writing). * * The lower two bits of *flags* are used as the return code if * the map lookup fails. This is so that the return value can be * one of the XDP program return codes up to XDP_TX, as chosen by * the caller. Any higher bits in the *flags* argument must be * unset. * * When used to redirect packets to net devices, this helper * provides a high performance increase over **bpf_redirect**\ (). * This is due to various implementation details of the underlying * mechanisms, one of which is the fact that **bpf_redirect_map**\ * () tries to send packet as a "bulk" to the device. * Return * **XDP_REDIRECT** on success, or **XDP_ABORTED** on error. * * int bpf_sk_redirect_map(struct sk_buff *skb, struct bpf_map *map, u32 key, u64 flags) * Description * Redirect the packet to the socket referenced by *map* (of type * **BPF_MAP_TYPE_SOCKMAP**) at index *key*. Both ingress and * egress interfaces can be used for redirection. The * **BPF_F_INGRESS** value in *flags* is used to make the * distinction (ingress path is selected if the flag is present, * egress path otherwise). This is the only flag supported for now. * Return * **SK_PASS** on success, or **SK_DROP** on error. * * int bpf_sock_map_update(struct bpf_sock_ops *skops, struct bpf_map *map, void *key, u64 flags) * Description * Add an entry to, or update a *map* referencing sockets. The * *skops* is used as a new value for the entry associated to * *key*. *flags* is one of: * * **BPF_NOEXIST** * The entry for *key* must not exist in the map. * **BPF_EXIST** * The entry for *key* must already exist in the map. * **BPF_ANY** * No condition on the existence of the entry for *key*. * * If the *map* has eBPF programs (parser and verdict), those will * be inherited by the socket being added. If the socket is * already attached to eBPF programs, this results in an error. * Return * 0 on success, or a negative error in case of failure. * * int bpf_xdp_adjust_meta(struct xdp_buff *xdp_md, int delta) * Description * Adjust the address pointed by *xdp_md*\ **->data_meta** by * *delta* (which can be positive or negative). Note that this * operation modifies the address stored in *xdp_md*\ **->data**, * so the latter must be loaded only after the helper has been * called. * * The use of *xdp_md*\ **->data_meta** is optional and programs * are not required to use it. The rationale is that when the * packet is processed with XDP (e.g. as DoS filter), it is * possible to push further meta data along with it before passing * to the stack, and to give the guarantee that an ingress eBPF * program attached as a TC classifier on the same device can pick * this up for further post-processing. Since TC works with socket * buffers, it remains possible to set from XDP the **mark** or * **priority** pointers, or other pointers for the socket buffer. * Having this scratch space generic and programmable allows for * more flexibility as the user is free to store whatever meta * data they need. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * Return * 0 on success, or a negative error in case of failure. * * int bpf_perf_event_read_value(struct bpf_map *map, u64 flags, struct bpf_perf_event_value *buf, u32 buf_size) * Description * Read the value of a perf event counter, and store it into *buf* * of size *buf_size*. This helper relies on a *map* of type * **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. The nature of the perf event * counter is selected when *map* is updated with perf event file * descriptors. The *map* is an array whose size is the number of * available CPUs, and each cell contains a value relative to one * CPU. The value to retrieve is indicated by *flags*, that * contains the index of the CPU to look up, masked with * **BPF_F_INDEX_MASK**. Alternatively, *flags* can be set to * **BPF_F_CURRENT_CPU** to indicate that the value for the * current CPU should be retrieved. * * This helper behaves in a way close to * **bpf_perf_event_read**\ () helper, save that instead of * just returning the value observed, it fills the *buf* * structure. This allows for additional data to be retrieved: in * particular, the enabled and running times (in *buf*\ * **->enabled** and *buf*\ **->running**, respectively) are * copied. In general, **bpf_perf_event_read_value**\ () is * recommended over **bpf_perf_event_read**\ (), which has some * ABI issues and provides fewer functionalities. * * These values are interesting, because hardware PMU (Performance * Monitoring Unit) counters are limited resources. When there are * more PMU based perf events opened than available counters, * kernel will multiplex these events so each event gets certain * percentage (but not all) of the PMU time. In case that * multiplexing happens, the number of samples or counter value * will not reflect the case compared to when no multiplexing * occurs. This makes comparison between different runs difficult. * Typically, the counter value should be normalized before * comparing to other experiments. The usual normalization is done * as follows. * * :: * * normalized_counter = counter * t_enabled / t_running * * Where t_enabled is the time enabled for event and t_running is * the time running for event since last normalization. The * enabled and running times are accumulated since the perf event * open. To achieve scaling factor between two invocations of an * eBPF program, users can can use CPU id as the key (which is * typical for perf array usage model) to remember the previous * value and do the calculation inside the eBPF program. * Return * 0 on success, or a negative error in case of failure. * * int bpf_perf_prog_read_value(struct bpf_perf_event_data *ctx, struct bpf_perf_event_value *buf, u32 buf_size) * Description * For en eBPF program attached to a perf event, retrieve the * value of the event counter associated to *ctx* and store it in * the structure pointed by *buf* and of size *buf_size*. Enabled * and running times are also stored in the structure (see * description of helper **bpf_perf_event_read_value**\ () for * more details). * Return * 0 on success, or a negative error in case of failure. * * int bpf_getsockopt(struct bpf_sock_ops *bpf_socket, int level, int optname, void *optval, int optlen) * Description * Emulate a call to **getsockopt()** on the socket associated to * *bpf_socket*, which must be a full socket. The *level* at * which the option resides and the name *optname* of the option * must be specified, see **getsockopt(2)** for more information. * The retrieved value is stored in the structure pointed by * *opval* and of length *optlen*. * * This helper actually implements a subset of **getsockopt()**. * It supports the following *level*\ s: * * * **IPPROTO_TCP**, which supports *optname* * **TCP_CONGESTION**. * * **IPPROTO_IP**, which supports *optname* **IP_TOS**. * * **IPPROTO_IPV6**, which supports *optname* **IPV6_TCLASS**. * Return * 0 on success, or a negative error in case of failure. * * int bpf_override_return(struct pt_regs *regs, u64 rc) * Description * Used for error injection, this helper uses kprobes to override * the return value of the probed function, and to set it to *rc*. * The first argument is the context *regs* on which the kprobe * works. * * This helper works by setting setting the PC (program counter) * to an override function which is run in place of the original * probed function. This means the probed function is not run at * all. The replacement function just returns with the required * value. * * This helper has security implications, and thus is subject to * restrictions. It is only available if the kernel was compiled * with the **CONFIG_BPF_KPROBE_OVERRIDE** configuration * option, and in this case it only works on functions tagged with * **ALLOW_ERROR_INJECTION** in the kernel code. * * Also, the helper is only available for the architectures having * the CONFIG_FUNCTION_ERROR_INJECTION option. As of this writing, * x86 architecture is the only one to support this feature. * Return * 0 * * int bpf_sock_ops_cb_flags_set(struct bpf_sock_ops *bpf_sock, int argval) * Description * Attempt to set the value of the **bpf_sock_ops_cb_flags** field * for the full TCP socket associated to *bpf_sock_ops* to * *argval*. * * The primary use of this field is to determine if there should * be calls to eBPF programs of type * **BPF_PROG_TYPE_SOCK_OPS** at various points in the TCP * code. A program of the same type can change its value, per * connection and as necessary, when the connection is * established. This field is directly accessible for reading, but * this helper must be used for updates in order to return an * error if an eBPF program tries to set a callback that is not * supported in the current kernel. * * *argval* is a flag array which can combine these flags: * * * **BPF_SOCK_OPS_RTO_CB_FLAG** (retransmission time out) * * **BPF_SOCK_OPS_RETRANS_CB_FLAG** (retransmission) * * **BPF_SOCK_OPS_STATE_CB_FLAG** (TCP state change) * * **BPF_SOCK_OPS_RTT_CB_FLAG** (every RTT) * * Therefore, this function can be used to clear a callback flag by * setting the appropriate bit to zero. e.g. to disable the RTO * callback: * * **bpf_sock_ops_cb_flags_set(bpf_sock,** * **bpf_sock->bpf_sock_ops_cb_flags & ~BPF_SOCK_OPS_RTO_CB_FLAG)** * * Here are some examples of where one could call such eBPF * program: * * * When RTO fires. * * When a packet is retransmitted. * * When the connection terminates. * * When a packet is sent. * * When a packet is received. * Return * Code **-EINVAL** if the socket is not a full TCP socket; * otherwise, a positive number containing the bits that could not * be set is returned (which comes down to 0 if all bits were set * as required). * * int bpf_msg_redirect_map(struct sk_msg_buff *msg, struct bpf_map *map, u32 key, u64 flags) * Description * This helper is used in programs implementing policies at the * socket level. If the message *msg* is allowed to pass (i.e. if * the verdict eBPF program returns **SK_PASS**), redirect it to * the socket referenced by *map* (of type * **BPF_MAP_TYPE_SOCKMAP**) at index *key*. Both ingress and * egress interfaces can be used for redirection. The * **BPF_F_INGRESS** value in *flags* is used to make the * distinction (ingress path is selected if the flag is present, * egress path otherwise). This is the only flag supported for now. * Return * **SK_PASS** on success, or **SK_DROP** on error. * * int bpf_msg_apply_bytes(struct sk_msg_buff *msg, u32 bytes) * Description * For socket policies, apply the verdict of the eBPF program to * the next *bytes* (number of bytes) of message *msg*. * * For example, this helper can be used in the following cases: * * * A single **sendmsg**\ () or **sendfile**\ () system call * contains multiple logical messages that the eBPF program is * supposed to read and for which it should apply a verdict. * * An eBPF program only cares to read the first *bytes* of a * *msg*. If the message has a large payload, then setting up * and calling the eBPF program repeatedly for all bytes, even * though the verdict is already known, would create unnecessary * overhead. * * When called from within an eBPF program, the helper sets a * counter internal to the BPF infrastructure, that is used to * apply the last verdict to the next *bytes*. If *bytes* is * smaller than the current data being processed from a * **sendmsg**\ () or **sendfile**\ () system call, the first * *bytes* will be sent and the eBPF program will be re-run with * the pointer for start of data pointing to byte number *bytes* * **+ 1**. If *bytes* is larger than the current data being * processed, then the eBPF verdict will be applied to multiple * **sendmsg**\ () or **sendfile**\ () calls until *bytes* are * consumed. * * Note that if a socket closes with the internal counter holding * a non-zero value, this is not a problem because data is not * being buffered for *bytes* and is sent as it is received. * Return * 0 * * int bpf_msg_cork_bytes(struct sk_msg_buff *msg, u32 bytes) * Description * For socket policies, prevent the execution of the verdict eBPF * program for message *msg* until *bytes* (byte number) have been * accumulated. * * This can be used when one needs a specific number of bytes * before a verdict can be assigned, even if the data spans * multiple **sendmsg**\ () or **sendfile**\ () calls. The extreme * case would be a user calling **sendmsg**\ () repeatedly with * 1-byte long message segments. Obviously, this is bad for * performance, but it is still valid. If the eBPF program needs * *bytes* bytes to validate a header, this helper can be used to * prevent the eBPF program to be called again until *bytes* have * been accumulated. * Return * 0 * * int bpf_msg_pull_data(struct sk_msg_buff *msg, u32 start, u32 end, u64 flags) * Description * For socket policies, pull in non-linear data from user space * for *msg* and set pointers *msg*\ **->data** and *msg*\ * **->data_end** to *start* and *end* bytes offsets into *msg*, * respectively. * * If a program of type **BPF_PROG_TYPE_SK_MSG** is run on a * *msg* it can only parse data that the (**data**, **data_end**) * pointers have already consumed. For **sendmsg**\ () hooks this * is likely the first scatterlist element. But for calls relying * on the **sendpage** handler (e.g. **sendfile**\ ()) this will * be the range (**0**, **0**) because the data is shared with * user space and by default the objective is to avoid allowing * user space to modify data while (or after) eBPF verdict is * being decided. This helper can be used to pull in data and to * set the start and end pointer to given values. Data will be * copied if necessary (i.e. if data was not linear and if start * and end pointers do not point to the same chunk). * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * * All values for *flags* are reserved for future usage, and must * be left at zero. * Return * 0 on success, or a negative error in case of failure. * * int bpf_bind(struct bpf_sock_addr *ctx, struct sockaddr *addr, int addr_len) * Description * Bind the socket associated to *ctx* to the address pointed by * *addr*, of length *addr_len*. This allows for making outgoing * connection from the desired IP address, which can be useful for * example when all processes inside a cgroup should use one * single IP address on a host that has multiple IP configured. * * This helper works for IPv4 and IPv6, TCP and UDP sockets. The * domain (*addr*\ **->sa_family**) must be **AF_INET** (or * **AF_INET6**). Looking for a free port to bind to can be * expensive, therefore binding to port is not permitted by the * helper: *addr*\ **->sin_port** (or **sin6_port**, respectively) * must be set to zero. * Return * 0 on success, or a negative error in case of failure. * * int bpf_xdp_adjust_tail(struct xdp_buff *xdp_md, int delta) * Description * Adjust (move) *xdp_md*\ **->data_end** by *delta* bytes. It is * only possible to shrink the packet as of this writing, * therefore *delta* must be a negative integer. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * Return * 0 on success, or a negative error in case of failure. * * int bpf_skb_get_xfrm_state(struct sk_buff *skb, u32 index, struct bpf_xfrm_state *xfrm_state, u32 size, u64 flags) * Description * Retrieve the XFRM state (IP transform framework, see also * **ip-xfrm(8)**) at *index* in XFRM "security path" for *skb*. * * The retrieved value is stored in the **struct bpf_xfrm_state** * pointed by *xfrm_state* and of length *size*. * * All values for *flags* are reserved for future usage, and must * be left at zero. * * This helper is available only if the kernel was compiled with * **CONFIG_XFRM** configuration option. * Return * 0 on success, or a negative error in case of failure. * * int bpf_get_stack(void *ctx, void *buf, u32 size, u64 flags) * Description * Return a user or a kernel stack in bpf program provided buffer. * To achieve this, the helper needs *ctx*, which is a pointer * to the context on which the tracing program is executed. * To store the stacktrace, the bpf program provides *buf* with * a nonnegative *size*. * * The last argument, *flags*, holds the number of stack frames to * skip (from 0 to 255), masked with * **BPF_F_SKIP_FIELD_MASK**. The next bits can be used to set * the following flags: * * **BPF_F_USER_STACK** * Collect a user space stack instead of a kernel stack. * **BPF_F_USER_BUILD_ID** * Collect buildid+offset instead of ips for user stack, * only valid if **BPF_F_USER_STACK** is also specified. * * **bpf_get_stack**\ () can collect up to * **PERF_MAX_STACK_DEPTH** both kernel and user frames, subject * to sufficient large buffer size. Note that * this limit can be controlled with the **sysctl** program, and * that it should be manually increased in order to profile long * user stacks (such as stacks for Java programs). To do so, use: * * :: * * # sysctl kernel.perf_event_max_stack= * Return * A non-negative value equal to or less than *size* on success, * or a negative error in case of failure. * * int bpf_skb_load_bytes_relative(const void *skb, u32 offset, void *to, u32 len, u32 start_header) * Description * This helper is similar to **bpf_skb_load_bytes**\ () in that * it provides an easy way to load *len* bytes from *offset* * from the packet associated to *skb*, into the buffer pointed * by *to*. The difference to **bpf_skb_load_bytes**\ () is that * a fifth argument *start_header* exists in order to select a * base offset to start from. *start_header* can be one of: * * **BPF_HDR_START_MAC** * Base offset to load data from is *skb*'s mac header. * **BPF_HDR_START_NET** * Base offset to load data from is *skb*'s network header. * * In general, "direct packet access" is the preferred method to * access packet data, however, this helper is in particular useful * in socket filters where *skb*\ **->data** does not always point * to the start of the mac header and where "direct packet access" * is not available. * Return * 0 on success, or a negative error in case of failure. * * int bpf_fib_lookup(void *ctx, struct bpf_fib_lookup *params, int plen, u32 flags) * Description * Do FIB lookup in kernel tables using parameters in *params*. * If lookup is successful and result shows packet is to be * forwarded, the neighbor tables are searched for the nexthop. * If successful (ie., FIB lookup shows forwarding and nexthop * is resolved), the nexthop address is returned in ipv4_dst * or ipv6_dst based on family, smac is set to mac address of * egress device, dmac is set to nexthop mac address, rt_metric * is set to metric from route (IPv4/IPv6 only), and ifindex * is set to the device index of the nexthop from the FIB lookup. * * *plen* argument is the size of the passed in struct. * *flags* argument can be a combination of one or more of the * following values: * * **BPF_FIB_LOOKUP_DIRECT** * Do a direct table lookup vs full lookup using FIB * rules. * **BPF_FIB_LOOKUP_OUTPUT** * Perform lookup from an egress perspective (default is * ingress). * * *ctx* is either **struct xdp_md** for XDP programs or * **struct sk_buff** tc cls_act programs. * Return * * < 0 if any input argument is invalid * * 0 on success (packet is forwarded, nexthop neighbor exists) * * > 0 one of **BPF_FIB_LKUP_RET_** codes explaining why the * packet is not forwarded or needs assist from full stack * * int bpf_sock_hash_update(struct bpf_sock_ops *skops, struct bpf_map *map, void *key, u64 flags) * Description * Add an entry to, or update a sockhash *map* referencing sockets. * The *skops* is used as a new value for the entry associated to * *key*. *flags* is one of: * * **BPF_NOEXIST** * The entry for *key* must not exist in the map. * **BPF_EXIST** * The entry for *key* must already exist in the map. * **BPF_ANY** * No condition on the existence of the entry for *key*. * * If the *map* has eBPF programs (parser and verdict), those will * be inherited by the socket being added. If the socket is * already attached to eBPF programs, this results in an error. * Return * 0 on success, or a negative error in case of failure. * * int bpf_msg_redirect_hash(struct sk_msg_buff *msg, struct bpf_map *map, void *key, u64 flags) * Description * This helper is used in programs implementing policies at the * socket level. If the message *msg* is allowed to pass (i.e. if * the verdict eBPF program returns **SK_PASS**), redirect it to * the socket referenced by *map* (of type * **BPF_MAP_TYPE_SOCKHASH**) using hash *key*. Both ingress and * egress interfaces can be used for redirection. The * **BPF_F_INGRESS** value in *flags* is used to make the * distinction (ingress path is selected if the flag is present, * egress path otherwise). This is the only flag supported for now. * Return * **SK_PASS** on success, or **SK_DROP** on error. * * int bpf_sk_redirect_hash(struct sk_buff *skb, struct bpf_map *map, void *key, u64 flags) * Description * This helper is used in programs implementing policies at the * skb socket level. If the sk_buff *skb* is allowed to pass (i.e. * if the verdeict eBPF program returns **SK_PASS**), redirect it * to the socket referenced by *map* (of type * **BPF_MAP_TYPE_SOCKHASH**) using hash *key*. Both ingress and * egress interfaces can be used for redirection. The * **BPF_F_INGRESS** value in *flags* is used to make the * distinction (ingress path is selected if the flag is present, * egress otherwise). This is the only flag supported for now. * Return * **SK_PASS** on success, or **SK_DROP** on error. * * int bpf_lwt_push_encap(struct sk_buff *skb, u32 type, void *hdr, u32 len) * Description * Encapsulate the packet associated to *skb* within a Layer 3 * protocol header. This header is provided in the buffer at * address *hdr*, with *len* its size in bytes. *type* indicates * the protocol of the header and can be one of: * * **BPF_LWT_ENCAP_SEG6** * IPv6 encapsulation with Segment Routing Header * (**struct ipv6_sr_hdr**). *hdr* only contains the SRH, * the IPv6 header is computed by the kernel. * **BPF_LWT_ENCAP_SEG6_INLINE** * Only works if *skb* contains an IPv6 packet. Insert a * Segment Routing Header (**struct ipv6_sr_hdr**) inside * the IPv6 header. * **BPF_LWT_ENCAP_IP** * IP encapsulation (GRE/GUE/IPIP/etc). The outer header * must be IPv4 or IPv6, followed by zero or more * additional headers, up to **LWT_BPF_MAX_HEADROOM** * total bytes in all prepended headers. Please note that * if **skb_is_gso**\ (*skb*) is true, no more than two * headers can be prepended, and the inner header, if * present, should be either GRE or UDP/GUE. * * **BPF_LWT_ENCAP_SEG6**\ \* types can be called by BPF programs * of type **BPF_PROG_TYPE_LWT_IN**; **BPF_LWT_ENCAP_IP** type can * be called by bpf programs of types **BPF_PROG_TYPE_LWT_IN** and * **BPF_PROG_TYPE_LWT_XMIT**. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * Return * 0 on success, or a negative error in case of failure. * * int bpf_lwt_seg6_store_bytes(struct sk_buff *skb, u32 offset, const void *from, u32 len) * Description * Store *len* bytes from address *from* into the packet * associated to *skb*, at *offset*. Only the flags, tag and TLVs * inside the outermost IPv6 Segment Routing Header can be * modified through this helper. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * Return * 0 on success, or a negative error in case of failure. * * int bpf_lwt_seg6_adjust_srh(struct sk_buff *skb, u32 offset, s32 delta) * Description * Adjust the size allocated to TLVs in the outermost IPv6 * Segment Routing Header contained in the packet associated to * *skb*, at position *offset* by *delta* bytes. Only offsets * after the segments are accepted. *delta* can be as well * positive (growing) as negative (shrinking). * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * Return * 0 on success, or a negative error in case of failure. * * int bpf_lwt_seg6_action(struct sk_buff *skb, u32 action, void *param, u32 param_len) * Description * Apply an IPv6 Segment Routing action of type *action* to the * packet associated to *skb*. Each action takes a parameter * contained at address *param*, and of length *param_len* bytes. * *action* can be one of: * * **SEG6_LOCAL_ACTION_END_X** * End.X action: Endpoint with Layer-3 cross-connect. * Type of *param*: **struct in6_addr**. * **SEG6_LOCAL_ACTION_END_T** * End.T action: Endpoint with specific IPv6 table lookup. * Type of *param*: **int**. * **SEG6_LOCAL_ACTION_END_B6** * End.B6 action: Endpoint bound to an SRv6 policy. * Type of *param*: **struct ipv6_sr_hdr**. * **SEG6_LOCAL_ACTION_END_B6_ENCAP** * End.B6.Encap action: Endpoint bound to an SRv6 * encapsulation policy. * Type of *param*: **struct ipv6_sr_hdr**. * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be * performed again, if the helper is used in combination with * direct packet access. * Return * 0 on success, or a negative error in case of failure. * * int bpf_rc_repeat(void *ctx) * Description * This helper is used in programs implementing IR decoding, to * report a successfully decoded repeat key message. This delays * the generation of a key up event for previously generated * key down event. * * Some IR protocols like NEC have a special IR message for * repeating last button, for when a button is held down. * * The *ctx* should point to the lirc sample as passed into * the program. * * This helper is only available is the kernel was compiled with * the **CONFIG_BPF_LIRC_MODE2** configuration option set to * "**y**". * Return * 0 * * int bpf_rc_keydown(void *ctx, u32 protocol, u64 scancode, u32 toggle) * Description * This helper is used in programs implementing IR decoding, to * report a successfully decoded key press with *scancode*, * *toggle* value in the given *protocol*. The scancode will be * translated to a keycode using the rc keymap, and reported as * an input key down event. After a period a key up event is * generated. This period can be extended by calling either * **bpf_rc_keydown**\ () again with the same values, or calling * **bpf_rc_repeat**\ (). * * Some protocols include a toggle bit, in case the button was * released and pressed again between consecutive scancodes. * * The *ctx* should point to the lirc sample as passed into * the program. * * The *protocol* is the decoded protocol number (see * **enum rc_proto** for some predefined values). * * This helper is only available is the kernel was compiled with * the **CONFIG_BPF_LIRC_MODE2** configuration option set to * "**y**". * Return * 0 * * u64 bpf_skb_cgroup_id(struct sk_buff *skb) * Description * Return the cgroup v2 id of the socket associated with the *skb*. * This is roughly similar to the **bpf_get_cgroup_classid**\ () * helper for cgroup v1 by providing a tag resp. identifier that * can be matched on or used for map lookups e.g. to implement * policy. The cgroup v2 id of a given path in the hierarchy is * exposed in user space through the f_handle API in order to get * to the same 64-bit id. * * This helper can be used on TC egress path, but not on ingress, * and is available only if the kernel was compiled with the * **CONFIG_SOCK_CGROUP_DATA** configuration option. * Return * The id is returned or 0 in case the id could not be retrieved. * * u64 bpf_get_current_cgroup_id(void) * Return * A 64-bit integer containing the current cgroup id based * on the cgroup within which the current task is running. * * void *bpf_get_local_storage(void *map, u64 flags) * Description * Get the pointer to the local storage area. * The type and the size of the local storage is defined * by the *map* argument. * The *flags* meaning is specific for each map type, * and has to be 0 for cgroup local storage. * * Depending on the BPF program type, a local storage area * can be shared between multiple instances of the BPF program, * running simultaneously. * * A user should care about the synchronization by himself. * For example, by using the **BPF_STX_XADD** instruction to alter * the shared data. * Return * A pointer to the local storage area. * * int bpf_sk_select_reuseport(struct sk_reuseport_md *reuse, struct bpf_map *map, void *key, u64 flags) * Description * Select a **SO_REUSEPORT** socket from a * **BPF_MAP_TYPE_REUSEPORT_ARRAY** *map*. * It checks the selected socket is matching the incoming * request in the socket buffer. * Return * 0 on success, or a negative error in case of failure. * * u64 bpf_skb_ancestor_cgroup_id(struct sk_buff *skb, int ancestor_level) * Description * Return id of cgroup v2 that is ancestor of cgroup associated * with the *skb* at the *ancestor_level*. The root cgroup is at * *ancestor_level* zero and each step down the hierarchy * increments the level. If *ancestor_level* == level of cgroup * associated with *skb*, then return value will be same as that * of **bpf_skb_cgroup_id**\ (). * * The helper is useful to implement policies based on cgroups * that are upper in hierarchy than immediate cgroup associated * with *skb*. * * The format of returned id and helper limitations are same as in * **bpf_skb_cgroup_id**\ (). * Return * The id is returned or 0 in case the id could not be retrieved. * * struct bpf_sock *bpf_sk_lookup_tcp(void *ctx, struct bpf_sock_tuple *tuple, u32 tuple_size, u64 netns, u64 flags) * Description * Look for TCP socket matching *tuple*, optionally in a child * network namespace *netns*. The return value must be checked, * and if non-**NULL**, released via **bpf_sk_release**\ (). * * The *ctx* should point to the context of the program, such as * the skb or socket (depending on the hook in use). This is used * to determine the base network namespace for the lookup. * * *tuple_size* must be one of: * * **sizeof**\ (*tuple*\ **->ipv4**) * Look for an IPv4 socket. * **sizeof**\ (*tuple*\ **->ipv6**) * Look for an IPv6 socket. * * If the *netns* is a negative signed 32-bit integer, then the * socket lookup table in the netns associated with the *ctx* will * will be used. For the TC hooks, this is the netns of the device * in the skb. For socket hooks, this is the netns of the socket. * If *netns* is any other signed 32-bit value greater than or * equal to zero then it specifies the ID of the netns relative to * the netns associated with the *ctx*. *netns* values beyond the * range of 32-bit integers are reserved for future use. * * All values for *flags* are reserved for future usage, and must * be left at zero. * * This helper is available only if the kernel was compiled with * **CONFIG_NET** configuration option. * Return * Pointer to **struct bpf_sock**, or **NULL** in case of failure. * For sockets with reuseport option, the **struct bpf_sock** * result is from *reuse*\ **->socks**\ [] using the hash of the * tuple. * * struct bpf_sock *bpf_sk_lookup_udp(void *ctx, struct bpf_sock_tuple *tuple, u32 tuple_size, u64 netns, u64 flags) * Description * Look for UDP socket matching *tuple*, optionally in a child * network namespace *netns*. The return value must be checked, * and if non-**NULL**, released via **bpf_sk_release**\ (). * * The *ctx* should point to the context of the program, such as * the skb or socket (depending on the hook in use). This is used * to determine the base network namespace for the lookup. * * *tuple_size* must be one of: * * **sizeof**\ (*tuple*\ **->ipv4**) * Look for an IPv4 socket. * **sizeof**\ (*tuple*\ **->ipv6**) * Look for an IPv6 socket. * * If the *netns* is a negative signed 32-bit integer, then the * socket lookup table in the netns associated with the *ctx* will * will be used. For the TC hooks, this is the netns of the device * in the skb. For socket hooks, this is the netns of the socket. * If *netns* is any other signed 32-bit value greater than or * equal to zero then it specifies the ID of the netns relative to * the netns associated with the *ctx*. *netns* values beyond the * range of 32-bit integers are reserved for future use. * * All values for *flags* are reserved for future usage, and must * be left at zero. * * This helper is available only if the kernel was compiled with * **CONFIG_NET** configuration option. * Return * Pointer to **struct bpf_sock**, or **NULL** in case of failure. * For sockets with reuseport option, the **struct bpf_sock** * result is from *reuse*\ **->socks**\ [] using the hash of the * tuple. * * int bpf_sk_release(struct bpf_sock *sock) * Description * Release the reference held by *sock*. *sock* must be a * non-**NULL** pointer that was returned from * **bpf_sk_lookup_xxx**\ (). * Return * 0 on success, or a negative error in case of failure. * * int bpf_map_push_elem(struct bpf_map *map, const void *value, u64 flags) * Description * Push an element *value* in *map*. *flags* is one of: * * **BPF_EXIST** * If the queue/stack is full, the oldest element is * removed to make room for this. * Return * 0 on success, or a negative error in case of failure. * * int bpf_map_pop_elem(struct bpf_map *map, void *value) * Description * Pop an element from *map*. * Return * 0 on success, or a negative error in case of failure. * * int bpf_map_peek_elem(struct bpf_map *map, void *value) * Description * Get an element from *map* without removing it. * Return * 0 on success, or a negative error in case of failure. * * int bpf_msg_push_data(struct sk_msg_buff *msg, u32 start, u32 len, u64 flags) * Description * For socket policies, insert *len* bytes into *msg* at offset * *start*. * * If a program of type **BPF_PROG_TYPE_SK_MSG** is run on a * *msg* it may want to insert metadata or options into the *msg*. * This can later be read and used by any of the lower layer BPF * hooks. * * This helper may fail if under memory pressure (a malloc * fails) in these cases BPF programs will get an appropriate * error and BPF programs will need to handle them. * Return * 0 on success, or a negative error in case of failure. * * int bpf_msg_pop_data(struct sk_msg_buff *msg, u32 start, u32 len, u64 flags) * Description * Will remove *len* bytes from a *msg* starting at byte *start*. * This may result in **ENOMEM** errors under certain situations if * an allocation and copy are required due to a full ring buffer. * However, the helper will try to avoid doing the allocation * if possible. Other errors can occur if input parameters are * invalid either due to *start* byte not being valid part of *msg* * payload and/or *pop* value being to large. * Return * 0 on success, or a negative error in case of failure. * * int bpf_rc_pointer_rel(void *ctx, s32 rel_x, s32 rel_y) * Description * This helper is used in programs implementing IR decoding, to * report a successfully decoded pointer movement. * * The *ctx* should point to the lirc sample as passed into * the program. * * This helper is only available is the kernel was compiled with * the **CONFIG_BPF_LIRC_MODE2** configuration option set to * "**y**". * Return * 0 * * int bpf_spin_lock(struct bpf_spin_lock *lock) * Description * Acquire a spinlock represented by the pointer *lock*, which is * stored as part of a value of a map. Taking the lock allows to * safely update the rest of the fields in that value. The * spinlock can (and must) later be released with a call to * **bpf_spin_unlock**\ (\ *lock*\ ). * * Spinlocks in BPF programs come with a number of restrictions * and constraints: * * * **bpf_spin_lock** objects are only allowed inside maps of * types **BPF_MAP_TYPE_HASH** and **BPF_MAP_TYPE_ARRAY** (this * list could be extended in the future). * * BTF description of the map is mandatory. * * The BPF program can take ONE lock at a time, since taking two * or more could cause dead locks. * * Only one **struct bpf_spin_lock** is allowed per map element. * * When the lock is taken, calls (either BPF to BPF or helpers) * are not allowed. * * The **BPF_LD_ABS** and **BPF_LD_IND** instructions are not * allowed inside a spinlock-ed region. * * The BPF program MUST call **bpf_spin_unlock**\ () to release * the lock, on all execution paths, before it returns. * * The BPF program can access **struct bpf_spin_lock** only via * the **bpf_spin_lock**\ () and **bpf_spin_unlock**\ () * helpers. Loading or storing data into the **struct * bpf_spin_lock** *lock*\ **;** field of a map is not allowed. * * To use the **bpf_spin_lock**\ () helper, the BTF description * of the map value must be a struct and have **struct * bpf_spin_lock** *anyname*\ **;** field at the top level. * Nested lock inside another struct is not allowed. * * The **struct bpf_spin_lock** *lock* field in a map value must * be aligned on a multiple of 4 bytes in that value. * * Syscall with command **BPF_MAP_LOOKUP_ELEM** does not copy * the **bpf_spin_lock** field to user space. * * Syscall with command **BPF_MAP_UPDATE_ELEM**, or update from * a BPF program, do not update the **bpf_spin_lock** field. * * **bpf_spin_lock** cannot be on the stack or inside a * networking packet (it can only be inside of a map values). * * **bpf_spin_lock** is available to root only. * * Tracing programs and socket filter programs cannot use * **bpf_spin_lock**\ () due to insufficient preemption checks * (but this may change in the future). * * **bpf_spin_lock** is not allowed in inner maps of map-in-map. * Return * 0 * * int bpf_spin_unlock(struct bpf_spin_lock *lock) * Description * Release the *lock* previously locked by a call to * **bpf_spin_lock**\ (\ *lock*\ ). * Return * 0 * * struct bpf_sock *bpf_sk_fullsock(struct bpf_sock *sk) * Description * This helper gets a **struct bpf_sock** pointer such * that all the fields in this **bpf_sock** can be accessed. * Return * A **struct bpf_sock** pointer on success, or **NULL** in * case of failure. * * struct bpf_tcp_sock *bpf_tcp_sock(struct bpf_sock *sk) * Description * This helper gets a **struct bpf_tcp_sock** pointer from a * **struct bpf_sock** pointer. * Return * A **struct bpf_tcp_sock** pointer on success, or **NULL** in * case of failure. * * int bpf_skb_ecn_set_ce(struct sk_buff *skb) * Description * Set ECN (Explicit Congestion Notification) field of IP header * to **CE** (Congestion Encountered) if current value is **ECT** * (ECN Capable Transport). Otherwise, do nothing. Works with IPv6 * and IPv4. * Return * 1 if the **CE** flag is set (either by the current helper call * or because it was already present), 0 if it is not set. * * struct bpf_sock *bpf_get_listener_sock(struct bpf_sock *sk) * Description * Return a **struct bpf_sock** pointer in **TCP_LISTEN** state. * **bpf_sk_release**\ () is unnecessary and not allowed. * Return * A **struct bpf_sock** pointer on success, or **NULL** in * case of failure. * * struct bpf_sock *bpf_skc_lookup_tcp(void *ctx, struct bpf_sock_tuple *tuple, u32 tuple_size, u64 netns, u64 flags) * Description * Look for TCP socket matching *tuple*, optionally in a child * network namespace *netns*. The return value must be checked, * and if non-**NULL**, released via **bpf_sk_release**\ (). * * This function is identical to **bpf_sk_lookup_tcp**\ (), except * that it also returns timewait or request sockets. Use * **bpf_sk_fullsock**\ () or **bpf_tcp_sock**\ () to access the * full structure. * * This helper is available only if the kernel was compiled with * **CONFIG_NET** configuration option. * Return * Pointer to **struct bpf_sock**, or **NULL** in case of failure. * For sockets with reuseport option, the **struct bpf_sock** * result is from *reuse*\ **->socks**\ [] using the hash of the * tuple. * * int bpf_tcp_check_syncookie(struct bpf_sock *sk, void *iph, u32 iph_len, struct tcphdr *th, u32 th_len) * Description * Check whether *iph* and *th* contain a valid SYN cookie ACK for * the listening socket in *sk*. * * *iph* points to the start of the IPv4 or IPv6 header, while * *iph_len* contains **sizeof**\ (**struct iphdr**) or * **sizeof**\ (**struct ip6hdr**). * * *th* points to the start of the TCP header, while *th_len* * contains **sizeof**\ (**struct tcphdr**). * * Return * 0 if *iph* and *th* are a valid SYN cookie ACK, or a negative * error otherwise. * * int bpf_sysctl_get_name(struct bpf_sysctl *ctx, char *buf, size_t buf_len, u64 flags) * Description * Get name of sysctl in /proc/sys/ and copy it into provided by * program buffer *buf* of size *buf_len*. * * The buffer is always NUL terminated, unless it's zero-sized. * * If *flags* is zero, full name (e.g. "net/ipv4/tcp_mem") is * copied. Use **BPF_F_SYSCTL_BASE_NAME** flag to copy base name * only (e.g. "tcp_mem"). * Return * Number of character copied (not including the trailing NUL). * * **-E2BIG** if the buffer wasn't big enough (*buf* will contain * truncated name in this case). * * int bpf_sysctl_get_current_value(struct bpf_sysctl *ctx, char *buf, size_t buf_len) * Description * Get current value of sysctl as it is presented in /proc/sys * (incl. newline, etc), and copy it as a string into provided * by program buffer *buf* of size *buf_len*. * * The whole value is copied, no matter what file position user * space issued e.g. sys_read at. * * The buffer is always NUL terminated, unless it's zero-sized. * Return * Number of character copied (not including the trailing NUL). * * **-E2BIG** if the buffer wasn't big enough (*buf* will contain * truncated name in this case). * * **-EINVAL** if current value was unavailable, e.g. because * sysctl is uninitialized and read returns -EIO for it. * * int bpf_sysctl_get_new_value(struct bpf_sysctl *ctx, char *buf, size_t buf_len) * Description * Get new value being written by user space to sysctl (before * the actual write happens) and copy it as a string into * provided by program buffer *buf* of size *buf_len*. * * User space may write new value at file position > 0. * * The buffer is always NUL terminated, unless it's zero-sized. * Return * Number of character copied (not including the trailing NUL). * * **-E2BIG** if the buffer wasn't big enough (*buf* will contain * truncated name in this case). * * **-EINVAL** if sysctl is being read. * * int bpf_sysctl_set_new_value(struct bpf_sysctl *ctx, const char *buf, size_t buf_len) * Description * Override new value being written by user space to sysctl with * value provided by program in buffer *buf* of size *buf_len*. * * *buf* should contain a string in same form as provided by user * space on sysctl write. * * User space may write new value at file position > 0. To override * the whole sysctl value file position should be set to zero. * Return * 0 on success. * * **-E2BIG** if the *buf_len* is too big. * * **-EINVAL** if sysctl is being read. * * int bpf_strtol(const char *buf, size_t buf_len, u64 flags, long *res) * Description * Convert the initial part of the string from buffer *buf* of * size *buf_len* to a long integer according to the given base * and save the result in *res*. * * The string may begin with an arbitrary amount of white space * (as determined by **isspace**\ (3)) followed by a single * optional '**-**' sign. * * Five least significant bits of *flags* encode base, other bits * are currently unused. * * Base must be either 8, 10, 16 or 0 to detect it automatically * similar to user space **strtol**\ (3). * Return * Number of characters consumed on success. Must be positive but * no more than *buf_len*. * * **-EINVAL** if no valid digits were found or unsupported base * was provided. * * **-ERANGE** if resulting value was out of range. * * int bpf_strtoul(const char *buf, size_t buf_len, u64 flags, unsigned long *res) * Description * Convert the initial part of the string from buffer *buf* of * size *buf_len* to an unsigned long integer according to the * given base and save the result in *res*. * * The string may begin with an arbitrary amount of white space * (as determined by **isspace**\ (3)). * * Five least significant bits of *flags* encode base, other bits * are currently unused. * * Base must be either 8, 10, 16 or 0 to detect it automatically * similar to user space **strtoul**\ (3). * Return * Number of characters consumed on success. Must be positive but * no more than *buf_len*. * * **-EINVAL** if no valid digits were found or unsupported base * was provided. * * **-ERANGE** if resulting value was out of range. * * void *bpf_sk_storage_get(struct bpf_map *map, struct bpf_sock *sk, void *value, u64 flags) * Description * Get a bpf-local-storage from a *sk*. * * Logically, it could be thought of getting the value from * a *map* with *sk* as the **key**. From this * perspective, the usage is not much different from * **bpf_map_lookup_elem**\ (*map*, **&**\ *sk*) except this * helper enforces the key must be a full socket and the map must * be a **BPF_MAP_TYPE_SK_STORAGE** also. * * Underneath, the value is stored locally at *sk* instead of * the *map*. The *map* is used as the bpf-local-storage * "type". The bpf-local-storage "type" (i.e. the *map*) is * searched against all bpf-local-storages residing at *sk*. * * An optional *flags* (**BPF_SK_STORAGE_GET_F_CREATE**) can be * used such that a new bpf-local-storage will be * created if one does not exist. *value* can be used * together with **BPF_SK_STORAGE_GET_F_CREATE** to specify * the initial value of a bpf-local-storage. If *value* is * **NULL**, the new bpf-local-storage will be zero initialized. * Return * A bpf-local-storage pointer is returned on success. * * **NULL** if not found or there was an error in adding * a new bpf-local-storage. * * int bpf_sk_storage_delete(struct bpf_map *map, struct bpf_sock *sk) * Description * Delete a bpf-local-storage from a *sk*. * Return * 0 on success. * * **-ENOENT** if the bpf-local-storage cannot be found. * * int bpf_send_signal(u32 sig) * Description * Send signal *sig* to the current task. * Return * 0 on success or successfully queued. * * **-EBUSY** if work queue under nmi is full. * * **-EINVAL** if *sig* is invalid. * * **-EPERM** if no permission to send the *sig*. * * **-EAGAIN** if bpf program can try again. * * s64 bpf_tcp_gen_syncookie(struct bpf_sock *sk, void *iph, u32 iph_len, struct tcphdr *th, u32 th_len) * Description * Try to issue a SYN cookie for the packet with corresponding * IP/TCP headers, *iph* and *th*, on the listening socket in *sk*. * * *iph* points to the start of the IPv4 or IPv6 header, while * *iph_len* contains **sizeof**\ (**struct iphdr**) or * **sizeof**\ (**struct ip6hdr**). * * *th* points to the start of the TCP header, while *th_len* * contains the length of the TCP header. * * Return * On success, lower 32 bits hold the generated SYN cookie in * followed by 16 bits which hold the MSS value for that cookie, * and the top 16 bits are unused. * * On failure, the returned value is one of the following: * * **-EINVAL** SYN cookie cannot be issued due to error * * **-ENOENT** SYN cookie should not be issued (no SYN flood) * * **-EOPNOTSUPP** kernel configuration does not enable SYN cookies * * **-EPROTONOSUPPORT** IP packet version is not 4 or 6 * * int bpf_skb_output(void *ctx, struct bpf_map *map, u64 flags, void *data, u64 size) * Description * Write raw *data* blob into a special BPF perf event held by * *map* of type **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. This perf * event must have the following attributes: **PERF_SAMPLE_RAW** * as **sample_type**, **PERF_TYPE_SOFTWARE** as **type**, and * **PERF_COUNT_SW_BPF_OUTPUT** as **config**. * * The *flags* are used to indicate the index in *map* for which * the value must be put, masked with **BPF_F_INDEX_MASK**. * Alternatively, *flags* can be set to **BPF_F_CURRENT_CPU** * to indicate that the index of the current CPU core should be * used. * * The value to write, of *size*, is passed through eBPF stack and * pointed by *data*. * * *ctx* is a pointer to in-kernel struct sk_buff. * * This helper is similar to **bpf_perf_event_output**\ () but * restricted to raw_tracepoint bpf programs. * Return * 0 on success, or a negative error in case of failure. * * int bpf_probe_read_user(void *dst, u32 size, const void *unsafe_ptr) * Description * Safely attempt to read *size* bytes from user space address * *unsafe_ptr* and store the data in *dst*. * Return * 0 on success, or a negative error in case of failure. * * int bpf_probe_read_kernel(void *dst, u32 size, const void *unsafe_ptr) * Description * Safely attempt to read *size* bytes from kernel space address * *unsafe_ptr* and store the data in *dst*. * Return * 0 on success, or a negative error in case of failure. * * int bpf_probe_read_user_str(void *dst, u32 size, const void *unsafe_ptr) * Description * Copy a NUL terminated string from an unsafe user address * *unsafe_ptr* to *dst*. The *size* should include the * terminating NUL byte. In case the string length is smaller than * *size*, the target is not padded with further NUL bytes. If the * string length is larger than *size*, just *size*-1 bytes are * copied and the last byte is set to NUL. * * On success, the length of the copied string is returned. This * makes this helper useful in tracing programs for reading * strings, and more importantly to get its length at runtime. See * the following snippet: * * :: * * SEC("kprobe/sys_open") * void bpf_sys_open(struct pt_regs *ctx) * { * char buf[PATHLEN]; // PATHLEN is defined to 256 * int res = bpf_probe_read_user_str(buf, sizeof(buf), * ctx->di); * * // Consume buf, for example push it to * // userspace via bpf_perf_event_output(); we * // can use res (the string length) as event * // size, after checking its boundaries. * } * * In comparison, using **bpf_probe_read_user()** helper here * instead to read the string would require to estimate the length * at compile time, and would often result in copying more memory * than necessary. * * Another useful use case is when parsing individual process * arguments or individual environment variables navigating * *current*\ **->mm->arg_start** and *current*\ * **->mm->env_start**: using this helper and the return value, * one can quickly iterate at the right offset of the memory area. * Return * On success, the strictly positive length of the string, * including the trailing NUL character. On error, a negative * value. * * int bpf_probe_read_kernel_str(void *dst, u32 size, const void *unsafe_ptr) * Description * Copy a NUL terminated string from an unsafe kernel address *unsafe_ptr* * to *dst*. Same semantics as with bpf_probe_read_user_str() apply. * Return * On success, the strictly positive length of the string, including * the trailing NUL character. On error, a negative value. */ #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), /* 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 /* All flags used by eBPF helper functions, placed here. */ /* BPF_FUNC_skb_store_bytes flags. */ #define BPF_F_RECOMPUTE_CSUM (1ULL << 0) #define BPF_F_INVALIDATE_HASH (1ULL << 1) /* BPF_FUNC_l3_csum_replace and BPF_FUNC_l4_csum_replace flags. * First 4 bits are for passing the header field size. */ #define BPF_F_HDR_FIELD_MASK 0xfULL /* BPF_FUNC_l4_csum_replace flags. */ #define BPF_F_PSEUDO_HDR (1ULL << 4) #define BPF_F_MARK_MANGLED_0 (1ULL << 5) #define BPF_F_MARK_ENFORCE (1ULL << 6) /* BPF_FUNC_clone_redirect and BPF_FUNC_redirect flags. */ #define BPF_F_INGRESS (1ULL << 0) /* BPF_FUNC_skb_set_tunnel_key and BPF_FUNC_skb_get_tunnel_key flags. */ #define BPF_F_TUNINFO_IPV6 (1ULL << 0) /* flags for both BPF_FUNC_get_stackid and BPF_FUNC_get_stack. */ #define BPF_F_SKIP_FIELD_MASK 0xffULL #define BPF_F_USER_STACK (1ULL << 8) /* flags used by BPF_FUNC_get_stackid only. */ #define BPF_F_FAST_STACK_CMP (1ULL << 9) #define BPF_F_REUSE_STACKID (1ULL << 10) /* flags used by BPF_FUNC_get_stack only. */ #define BPF_F_USER_BUILD_ID (1ULL << 11) /* BPF_FUNC_skb_set_tunnel_key flags. */ #define BPF_F_ZERO_CSUM_TX (1ULL << 1) #define BPF_F_DONT_FRAGMENT (1ULL << 2) #define BPF_F_SEQ_NUMBER (1ULL << 3) /* BPF_FUNC_perf_event_output, BPF_FUNC_perf_event_read and * BPF_FUNC_perf_event_read_value flags. */ #define BPF_F_INDEX_MASK 0xffffffffULL #define BPF_F_CURRENT_CPU BPF_F_INDEX_MASK /* BPF_FUNC_perf_event_output for sk_buff input context. */ #define BPF_F_CTXLEN_MASK (0xfffffULL << 32) /* Current network namespace */ #define BPF_F_CURRENT_NETNS (-1L) /* BPF_FUNC_skb_adjust_room flags. */ #define BPF_F_ADJ_ROOM_FIXED_GSO (1ULL << 0) #define BPF_ADJ_ROOM_ENCAP_L2_MASK 0xff #define BPF_ADJ_ROOM_ENCAP_L2_SHIFT 56 #define BPF_F_ADJ_ROOM_ENCAP_L3_IPV4 (1ULL << 1) #define BPF_F_ADJ_ROOM_ENCAP_L3_IPV6 (1ULL << 2) #define BPF_F_ADJ_ROOM_ENCAP_L4_GRE (1ULL << 3) #define BPF_F_ADJ_ROOM_ENCAP_L4_UDP (1ULL << 4) #define BPF_F_ADJ_ROOM_ENCAP_L2(len) (((__u64)len & \ BPF_ADJ_ROOM_ENCAP_L2_MASK) \ << BPF_ADJ_ROOM_ENCAP_L2_SHIFT) /* BPF_FUNC_sysctl_get_name flags. */ #define BPF_F_SYSCTL_BASE_NAME (1ULL << 0) /* BPF_FUNC_sk_storage_get flags */ #define BPF_SK_STORAGE_GET_F_CREATE (1ULL << 0) /* Mode for BPF_FUNC_skb_adjust_room helper. */ enum bpf_adj_room_mode { BPF_ADJ_ROOM_NET, BPF_ADJ_ROOM_MAC, }; /* Mode for BPF_FUNC_skb_load_bytes_relative helper. */ enum bpf_hdr_start_off { BPF_HDR_START_MAC, BPF_HDR_START_NET, }; /* Encapsulation type for BPF_FUNC_lwt_push_encap helper. */ enum bpf_lwt_encap_mode { BPF_LWT_ENCAP_SEG6, BPF_LWT_ENCAP_SEG6_INLINE, BPF_LWT_ENCAP_IP, }; #define __bpf_md_ptr(type, name) \ union { \ type name; \ __u64 :64; \ } __attribute__((aligned(8))) /* user accessible mirror of in-kernel sk_buff. * new fields can only be added to the end of this structure */ struct __sk_buff { __u32 len; __u32 pkt_type; __u32 mark; __u32 queue_mapping; __u32 protocol; __u32 vlan_present; __u32 vlan_tci; __u32 vlan_proto; __u32 priority; __u32 ingress_ifindex; __u32 ifindex; __u32 tc_index; __u32 cb[5]; __u32 hash; __u32 tc_classid; __u32 data; __u32 data_end; __u32 napi_id; /* Accessed by BPF_PROG_TYPE_sk_skb types from here to ... */ __u32 family; __u32 remote_ip4; /* Stored in network byte order */ __u32 local_ip4; /* Stored in network byte order */ __u32 remote_ip6[4]; /* Stored in network byte order */ __u32 local_ip6[4]; /* Stored in network byte order */ __u32 remote_port; /* Stored in network byte order */ __u32 local_port; /* stored in host byte order */ /* ... here. */ __u32 data_meta; __bpf_md_ptr(struct bpf_flow_keys *, flow_keys); __u64 tstamp; __u32 wire_len; __u32 gso_segs; __bpf_md_ptr(struct bpf_sock *, sk); }; struct bpf_tunnel_key { __u32 tunnel_id; union { __u32 remote_ipv4; __u32 remote_ipv6[4]; }; __u8 tunnel_tos; __u8 tunnel_ttl; __u16 tunnel_ext; /* Padding, future use. */ __u32 tunnel_label; }; /* user accessible mirror of in-kernel xfrm_state. * new fields can only be added to the end of this structure */ struct bpf_xfrm_state { __u32 reqid; __u32 spi; /* Stored in network byte order */ __u16 family; __u16 ext; /* Padding, future use. */ union { __u32 remote_ipv4; /* Stored in network byte order */ __u32 remote_ipv6[4]; /* Stored in network byte order */ }; }; /* Generic BPF return codes which all BPF program types may support. * The values are binary compatible with their TC_ACT_* counter-part to * provide backwards compatibility with existing SCHED_CLS and SCHED_ACT * programs. * * XDP is handled seprately, see XDP_*. */ enum bpf_ret_code { BPF_OK = 0, /* 1 reserved */ BPF_DROP = 2, /* 3-6 reserved */ BPF_REDIRECT = 7, /* >127 are reserved for prog type specific return codes. * * BPF_LWT_REROUTE: used by BPF_PROG_TYPE_LWT_IN and * BPF_PROG_TYPE_LWT_XMIT to indicate that skb had been * changed and should be routed based on its new L3 header. * (This is an L3 redirect, as opposed to L2 redirect * represented by BPF_REDIRECT above). */ BPF_LWT_REROUTE = 128, }; struct bpf_sock { __u32 bound_dev_if; __u32 family; __u32 type; __u32 protocol; __u32 mark; __u32 priority; /* IP address also allows 1 and 2 bytes access */ __u32 src_ip4; __u32 src_ip6[4]; __u32 src_port; /* host byte order */ __u32 dst_port; /* network byte order */ __u32 dst_ip4; __u32 dst_ip6[4]; __u32 state; }; struct bpf_tcp_sock { __u32 snd_cwnd; /* Sending congestion window */ __u32 srtt_us; /* smoothed round trip time << 3 in usecs */ __u32 rtt_min; __u32 snd_ssthresh; /* Slow start size threshold */ __u32 rcv_nxt; /* What we want to receive next */ __u32 snd_nxt; /* Next sequence we send */ __u32 snd_una; /* First byte we want an ack for */ __u32 mss_cache; /* Cached effective mss, not including SACKS */ __u32 ecn_flags; /* ECN status bits. */ __u32 rate_delivered; /* saved rate sample: packets delivered */ __u32 rate_interval_us; /* saved rate sample: time elapsed */ __u32 packets_out; /* Packets which are "in flight" */ __u32 retrans_out; /* Retransmitted packets out */ __u32 total_retrans; /* Total retransmits for entire connection */ __u32 segs_in; /* RFC4898 tcpEStatsPerfSegsIn * total number of segments in. */ __u32 data_segs_in; /* RFC4898 tcpEStatsPerfDataSegsIn * total number of data segments in. */ __u32 segs_out; /* RFC4898 tcpEStatsPerfSegsOut * The total number of segments sent. */ __u32 data_segs_out; /* RFC4898 tcpEStatsPerfDataSegsOut * total number of data segments sent. */ __u32 lost_out; /* Lost packets */ __u32 sacked_out; /* SACK'd packets */ __u64 bytes_received; /* RFC4898 tcpEStatsAppHCThruOctetsReceived * sum(delta(rcv_nxt)), or how many bytes * were acked. */ __u64 bytes_acked; /* RFC4898 tcpEStatsAppHCThruOctetsAcked * sum(delta(snd_una)), or how many bytes * were acked. */ __u32 dsack_dups; /* RFC4898 tcpEStatsStackDSACKDups * total number of DSACK blocks received */ __u32 delivered; /* Total data packets delivered incl. rexmits */ __u32 delivered_ce; /* Like the above but only ECE marked packets */ __u32 icsk_retransmits; /* Number of unrecovered [RTO] timeouts */ }; struct bpf_sock_tuple { union { struct { __be32 saddr; __be32 daddr; __be16 sport; __be16 dport; } ipv4; struct { __be32 saddr[4]; __be32 daddr[4]; __be16 sport; __be16 dport; } ipv6; }; }; struct bpf_xdp_sock { __u32 queue_id; }; #define XDP_PACKET_HEADROOM 256 /* User return codes for XDP prog type. * A valid XDP program must return one of these defined values. All other * return codes are reserved for future use. Unknown return codes will * result in packet drops and a warning via bpf_warn_invalid_xdp_action(). */ enum xdp_action { XDP_ABORTED = 0, XDP_DROP, XDP_PASS, XDP_TX, XDP_REDIRECT, }; /* user accessible metadata for XDP packet hook * new fields must be added to the end of this structure */ struct xdp_md { __u32 data; __u32 data_end; __u32 data_meta; /* Below access go through struct xdp_rxq_info */ __u32 ingress_ifindex; /* rxq->dev->ifindex */ __u32 rx_queue_index; /* rxq->queue_index */ }; enum sk_action { SK_DROP = 0, SK_PASS, }; /* user accessible metadata for SK_MSG packet hook, new fields must * be added to the end of this structure */ struct sk_msg_md { __bpf_md_ptr(void *, data); __bpf_md_ptr(void *, data_end); __u32 family; __u32 remote_ip4; /* Stored in network byte order */ __u32 local_ip4; /* Stored in network byte order */ __u32 remote_ip6[4]; /* Stored in network byte order */ __u32 local_ip6[4]; /* Stored in network byte order */ __u32 remote_port; /* Stored in network byte order */ __u32 local_port; /* stored in host byte order */ __u32 size; /* Total size of sk_msg */ }; struct sk_reuseport_md { /* * Start of directly accessible data. It begins from * the tcp/udp header. */ __bpf_md_ptr(void *, data); /* End of directly accessible data */ __bpf_md_ptr(void *, data_end); /* * Total length of packet (starting from the tcp/udp header). * Note that the directly accessible bytes (data_end - data) * could be less than this "len". Those bytes could be * indirectly read by a helper "bpf_skb_load_bytes()". */ __u32 len; /* * Eth protocol in the mac header (network byte order). e.g. * ETH_P_IP(0x0800) and ETH_P_IPV6(0x86DD) */ __u32 eth_protocol; __u32 ip_protocol; /* IP protocol. e.g. IPPROTO_TCP, IPPROTO_UDP */ __u32 bind_inany; /* Is sock bound to an INANY address? */ __u32 hash; /* A hash of the packet 4 tuples */ }; #define BPF_TAG_SIZE 8 struct bpf_prog_info { __u32 type; __u32 id; __u8 tag[BPF_TAG_SIZE]; __u32 jited_prog_len; __u32 xlated_prog_len; __aligned_u64 jited_prog_insns; __aligned_u64 xlated_prog_insns; __u64 load_time; /* ns since boottime */ __u32 created_by_uid; __u32 nr_map_ids; __aligned_u64 map_ids; char name[BPF_OBJ_NAME_LEN]; __u32 ifindex; __u32 gpl_compatible:1; __u32 :31; /* alignment pad */ __u64 netns_dev; __u64 netns_ino; __u32 nr_jited_ksyms; __u32 nr_jited_func_lens; __aligned_u64 jited_ksyms; __aligned_u64 jited_func_lens; __u32 btf_id; __u32 func_info_rec_size; __aligned_u64 func_info; __u32 nr_func_info; __u32 nr_line_info; __aligned_u64 line_info; __aligned_u64 jited_line_info; __u32 nr_jited_line_info; __u32 line_info_rec_size; __u32 jited_line_info_rec_size; __u32 nr_prog_tags; __aligned_u64 prog_tags; __u64 run_time_ns; __u64 run_cnt; } __attribute__((aligned(8))); struct bpf_map_info { __u32 type; __u32 id; __u32 key_size; __u32 value_size; __u32 max_entries; __u32 map_flags; char name[BPF_OBJ_NAME_LEN]; __u32 ifindex; __u32 :32; __u64 netns_dev; __u64 netns_ino; __u32 btf_id; __u32 btf_key_type_id; __u32 btf_value_type_id; } __attribute__((aligned(8))); struct bpf_btf_info { __aligned_u64 btf; __u32 btf_size; __u32 id; } __attribute__((aligned(8))); /* User bpf_sock_addr struct to access socket fields and sockaddr struct passed * by user and intended to be used by socket (e.g. to bind to, depends on * attach attach type). */ struct bpf_sock_addr { __u32 user_family; /* Allows 4-byte read, but no write. */ __u32 user_ip4; /* Allows 1,2,4-byte read and 4-byte write. * Stored in network byte order. */ __u32 user_ip6[4]; /* Allows 1,2,4,8-byte read and 4,8-byte write. * Stored in network byte order. */ __u32 user_port; /* Allows 4-byte read and write. * Stored in network byte order */ __u32 family; /* Allows 4-byte read, but no write */ __u32 type; /* Allows 4-byte read, but no write */ __u32 protocol; /* Allows 4-byte read, but no write */ __u32 msg_src_ip4; /* Allows 1,2,4-byte read and 4-byte write. * Stored in network byte order. */ __u32 msg_src_ip6[4]; /* Allows 1,2,4,8-byte read and 4,8-byte write. * Stored in network byte order. */ __bpf_md_ptr(struct bpf_sock *, sk); }; /* User bpf_sock_ops struct to access socket values and specify request ops * and their replies. * Some of this fields are in network (bigendian) byte order and may need * to be converted before use (bpf_ntohl() defined in samples/bpf/bpf_endian.h). * New fields can only be added at the end of this structure */ struct bpf_sock_ops { __u32 op; union { __u32 args[4]; /* Optionally passed to bpf program */ __u32 reply; /* Returned by bpf program */ __u32 replylong[4]; /* Optionally returned by bpf prog */ }; __u32 family; __u32 remote_ip4; /* Stored in network byte order */ __u32 local_ip4; /* Stored in network byte order */ __u32 remote_ip6[4]; /* Stored in network byte order */ __u32 local_ip6[4]; /* Stored in network byte order */ __u32 remote_port; /* Stored in network byte order */ __u32 local_port; /* stored in host byte order */ __u32 is_fullsock; /* Some TCP fields are only valid if * there is a full socket. If not, the * fields read as zero. */ __u32 snd_cwnd; __u32 srtt_us; /* Averaged RTT << 3 in usecs */ __u32 bpf_sock_ops_cb_flags; /* flags defined in uapi/linux/tcp.h */ __u32 state; __u32 rtt_min; __u32 snd_ssthresh; __u32 rcv_nxt; __u32 snd_nxt; __u32 snd_una; __u32 mss_cache; __u32 ecn_flags; __u32 rate_delivered; __u32 rate_interval_us; __u32 packets_out; __u32 retrans_out; __u32 total_retrans; __u32 segs_in; __u32 data_segs_in; __u32 segs_out; __u32 data_segs_out; __u32 lost_out; __u32 sacked_out; __u32 sk_txhash; __u64 bytes_received; __u64 bytes_acked; __bpf_md_ptr(struct bpf_sock *, sk); }; /* Definitions for bpf_sock_ops_cb_flags */ #define BPF_SOCK_OPS_RTO_CB_FLAG (1<<0) #define BPF_SOCK_OPS_RETRANS_CB_FLAG (1<<1) #define BPF_SOCK_OPS_STATE_CB_FLAG (1<<2) #define BPF_SOCK_OPS_RTT_CB_FLAG (1<<3) #define BPF_SOCK_OPS_ALL_CB_FLAGS 0xF /* Mask of all currently * supported cb flags */ /* List of known BPF sock_ops operators. * New entries can only be added at the end */ enum { BPF_SOCK_OPS_VOID, BPF_SOCK_OPS_TIMEOUT_INIT, /* Should return SYN-RTO value to use or * -1 if default value should be used */ BPF_SOCK_OPS_RWND_INIT, /* Should return initial advertized * window (in packets) or -1 if default * value should be used */ BPF_SOCK_OPS_TCP_CONNECT_CB, /* Calls BPF program right before an * active connection is initialized */ BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB, /* Calls BPF program when an * active connection is * established */ BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB, /* Calls BPF program when a * passive connection is * established */ BPF_SOCK_OPS_NEEDS_ECN, /* If connection's congestion control * needs ECN */ BPF_SOCK_OPS_BASE_RTT, /* Get base RTT. The correct value is * based on the path and may be * dependent on the congestion control * algorithm. In general it indicates * a congestion threshold. RTTs above * this indicate congestion */ BPF_SOCK_OPS_RTO_CB, /* Called when an RTO has triggered. * Arg1: value of icsk_retransmits * Arg2: value of icsk_rto * Arg3: whether RTO has expired */ BPF_SOCK_OPS_RETRANS_CB, /* Called when skb is retransmitted. * Arg1: sequence number of 1st byte * Arg2: # segments * Arg3: return value of * tcp_transmit_skb (0 => success) */ BPF_SOCK_OPS_STATE_CB, /* Called when TCP changes state. * Arg1: old_state * Arg2: new_state */ BPF_SOCK_OPS_TCP_LISTEN_CB, /* Called on listen(2), right after * socket transition to LISTEN state. */ BPF_SOCK_OPS_RTT_CB, /* Called on every RTT. */ }; /* List of TCP states. There is a build check in net/ipv4/tcp.c to detect * changes between the TCP and BPF versions. Ideally this should never happen. * If it does, we need to add code to convert them before calling * the BPF sock_ops function. */ enum { BPF_TCP_ESTABLISHED = 1, BPF_TCP_SYN_SENT, BPF_TCP_SYN_RECV, BPF_TCP_FIN_WAIT1, BPF_TCP_FIN_WAIT2, BPF_TCP_TIME_WAIT, BPF_TCP_CLOSE, BPF_TCP_CLOSE_WAIT, BPF_TCP_LAST_ACK, BPF_TCP_LISTEN, BPF_TCP_CLOSING, /* Now a valid state */ BPF_TCP_NEW_SYN_RECV, BPF_TCP_MAX_STATES /* Leave at the end! */ }; #define TCP_BPF_IW 1001 /* Set TCP initial congestion window */ #define TCP_BPF_SNDCWND_CLAMP 1002 /* Set sndcwnd_clamp */ struct bpf_perf_event_value { __u64 counter; __u64 enabled; __u64 running; }; #define BPF_DEVCG_ACC_MKNOD (1ULL << 0) #define BPF_DEVCG_ACC_READ (1ULL << 1) #define BPF_DEVCG_ACC_WRITE (1ULL << 2) #define BPF_DEVCG_DEV_BLOCK (1ULL << 0) #define BPF_DEVCG_DEV_CHAR (1ULL << 1) struct bpf_cgroup_dev_ctx { /* access_type encoded as (BPF_DEVCG_ACC_* << 16) | BPF_DEVCG_DEV_* */ __u32 access_type; __u32 major; __u32 minor; }; struct bpf_raw_tracepoint_args { __u64 args[0]; }; /* DIRECT: Skip the FIB rules and go to FIB table associated with device * OUTPUT: Do lookup from egress perspective; default is ingress */ #define BPF_FIB_LOOKUP_DIRECT (1U << 0) #define BPF_FIB_LOOKUP_OUTPUT (1U << 1) enum { BPF_FIB_LKUP_RET_SUCCESS, /* lookup successful */ BPF_FIB_LKUP_RET_BLACKHOLE, /* dest is blackholed; can be dropped */ BPF_FIB_LKUP_RET_UNREACHABLE, /* dest is unreachable; can be dropped */ BPF_FIB_LKUP_RET_PROHIBIT, /* dest not allowed; can be dropped */ BPF_FIB_LKUP_RET_NOT_FWDED, /* packet is not forwarded */ BPF_FIB_LKUP_RET_FWD_DISABLED, /* fwding is not enabled on ingress */ BPF_FIB_LKUP_RET_UNSUPP_LWT, /* fwd requires encapsulation */ BPF_FIB_LKUP_RET_NO_NEIGH, /* no neighbor entry for nh */ BPF_FIB_LKUP_RET_FRAG_NEEDED, /* fragmentation required to fwd */ }; struct bpf_fib_lookup { /* input: network family for lookup (AF_INET, AF_INET6) * output: network family of egress nexthop */ __u8 family; /* set if lookup is to consider L4 data - e.g., FIB rules */ __u8 l4_protocol; __be16 sport; __be16 dport; /* total length of packet from network header - used for MTU check */ __u16 tot_len; /* input: L3 device index for lookup * output: device index from FIB lookup */ __u32 ifindex; union { /* inputs to lookup */ __u8 tos; /* AF_INET */ __be32 flowinfo; /* AF_INET6, flow_label + priority */ /* output: metric of fib result (IPv4/IPv6 only) */ __u32 rt_metric; }; union { __be32 ipv4_src; __u32 ipv6_src[4]; /* in6_addr; network order */ }; /* input to bpf_fib_lookup, ipv{4,6}_dst is destination address in * network header. output: bpf_fib_lookup sets to gateway address * if FIB lookup returns gateway route */ union { __be32 ipv4_dst; __u32 ipv6_dst[4]; /* in6_addr; network order */ }; /* output */ __be16 h_vlan_proto; __be16 h_vlan_TCI; __u8 smac[6]; /* ETH_ALEN */ __u8 dmac[6]; /* ETH_ALEN */ }; enum bpf_task_fd_type { BPF_FD_TYPE_RAW_TRACEPOINT, /* tp name */ BPF_FD_TYPE_TRACEPOINT, /* tp name */ BPF_FD_TYPE_KPROBE, /* (symbol + offset) or addr */ BPF_FD_TYPE_KRETPROBE, /* (symbol + offset) or addr */ BPF_FD_TYPE_UPROBE, /* filename + offset */ BPF_FD_TYPE_URETPROBE, /* filename + offset */ }; #define BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG (1U << 0) #define BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL (1U << 1) #define BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP (1U << 2) struct bpf_flow_keys { __u16 nhoff; __u16 thoff; __u16 addr_proto; /* ETH_P_* of valid addrs */ __u8 is_frag; __u8 is_first_frag; __u8 is_encap; __u8 ip_proto; __be16 n_proto; __be16 sport; __be16 dport; union { struct { __be32 ipv4_src; __be32 ipv4_dst; }; struct { __u32 ipv6_src[4]; /* in6_addr; network order */ __u32 ipv6_dst[4]; /* in6_addr; network order */ }; }; __u32 flags; __be32 flow_label; }; struct bpf_func_info { __u32 insn_off; __u32 type_id; }; #define BPF_LINE_INFO_LINE_NUM(line_col) ((line_col) >> 10) #define BPF_LINE_INFO_LINE_COL(line_col) ((line_col) & 0x3ff) struct bpf_line_info { __u32 insn_off; __u32 file_name_off; __u32 line_off; __u32 line_col; }; struct bpf_spin_lock { __u32 val; }; struct bpf_sysctl { __u32 write; /* Sysctl is being read (= 0) or written (= 1). * Allows 1,2,4-byte read, but no write. */ __u32 file_pos; /* Sysctl file position to read from, write to. * Allows 1,2,4-byte read an 4-byte write. */ }; struct bpf_sockopt { __bpf_md_ptr(struct bpf_sock *, sk); __bpf_md_ptr(void *, optval); __bpf_md_ptr(void *, optval_end); __s32 level; __s32 optname; __s32 optlen; __s32 retval; }; #endif /* _UAPI__LINUX_BPF_H__ */ )********" bpfcc-0.12.0/src/cc/export/000077500000000000000000000000001357404205000153445ustar00rootroot00000000000000bpfcc-0.12.0/src/cc/export/footer.h000066400000000000000000000016201357404205000170120ustar00rootroot00000000000000R"********( /* * Copyright (c) 2018 Clevernet, Inc. * * 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. */ #ifndef BPF_LICENSE /* No license defined, using GPL * You can define your own BPF_LICENSE in your C code */ #define BPF_LICENSE GPL #endif #define ___LICENSE(s) #s #define __LICENSE(s) ___LICENSE(s) #define _LICENSE __LICENSE(BPF_LICENSE) char _license[] BCC_SEC("license") = _LICENSE; )********" bpfcc-0.12.0/src/cc/export/helpers.h000066400000000000000000001071571357404205000171720ustar00rootroot00000000000000R"********( /* * Copyright (c) 2015 PLUMgrid, Inc. * * 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. */ #ifndef __BPF_HELPERS_H #define __BPF_HELPERS_H /* In Linux 5.4 asm_inline was introduced, but it's not supported by clang. * Redefine it to just asm to enable successful compilation. */ #ifdef asm_inline #undef asm_inline #define asm_inline asm #endif /* Before bpf_helpers.h is included, uapi bpf.h has been * included, which references linux/types.h. This may 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. * If asm_volatile_goto is actually used by the bpf program, * a compilation error will appear. */ #ifdef asm_volatile_goto #undef asm_volatile_goto #endif #define asm_volatile_goto(x...) asm volatile("invalid use of asm_volatile_goto") #include #include #include #include #ifndef CONFIG_BPF_SYSCALL #error "CONFIG_BPF_SYSCALL is undefined, please check your .config or ask your Linux distro to enable this feature" #endif #ifdef PERF_MAX_STACK_DEPTH #define BPF_MAX_STACK_DEPTH PERF_MAX_STACK_DEPTH #else #define BPF_MAX_STACK_DEPTH 127 #endif /* helper macro to place programs, maps, license in * different sections in elf_bpf file. Section names * are interpreted by elf_bpf loader */ #define BCC_SEC(NAME) __attribute__((section(NAME), used)) // Associate map with its key/value types #define BPF_ANNOTATE_KV_PAIR(name, type_key, type_val) \ struct ____btf_map_##name { \ type_key key; \ type_val value; \ }; \ struct ____btf_map_##name \ __attribute__ ((section(".maps." #name), used)) \ ____btf_map_##name = { } // Changes to the macro require changes in BFrontendAction classes #define BPF_F_TABLE(_table_type, _key_type, _leaf_type, _name, _max_entries, _flags) \ struct _name##_table_t { \ _key_type key; \ _leaf_type leaf; \ _leaf_type * (*lookup) (_key_type *); \ _leaf_type * (*lookup_or_init) (_key_type *, _leaf_type *); \ _leaf_type * (*lookup_or_try_init) (_key_type *, _leaf_type *); \ int (*update) (_key_type *, _leaf_type *); \ int (*insert) (_key_type *, _leaf_type *); \ int (*delete) (_key_type *); \ void (*call) (void *, int index); \ void (*increment) (_key_type, ...); \ int (*get_stackid) (void *, u64); \ u32 max_entries; \ int flags; \ }; \ __attribute__((section("maps/" _table_type))) \ struct _name##_table_t _name = { .flags = (_flags), .max_entries = (_max_entries) }; \ BPF_ANNOTATE_KV_PAIR(_name, _key_type, _leaf_type) #define BPF_TABLE(_table_type, _key_type, _leaf_type, _name, _max_entries) \ BPF_F_TABLE(_table_type, _key_type, _leaf_type, _name, _max_entries, 0) #define BPF_TABLE_PINNED(_table_type, _key_type, _leaf_type, _name, _max_entries, _pinned) \ BPF_TABLE(_table_type ":" _pinned, _key_type, _leaf_type, _name, _max_entries) // define a table same as above but allow it to be referenced by other modules #define BPF_TABLE_PUBLIC(_table_type, _key_type, _leaf_type, _name, _max_entries) \ BPF_TABLE(_table_type, _key_type, _leaf_type, _name, _max_entries); \ __attribute__((section("maps/export"))) \ struct _name##_table_t __##_name // define a table that is shared accross the programs in the same namespace #define BPF_TABLE_SHARED(_table_type, _key_type, _leaf_type, _name, _max_entries) \ BPF_TABLE(_table_type, _key_type, _leaf_type, _name, _max_entries); \ __attribute__((section("maps/shared"))) \ struct _name##_table_t __##_name // Identifier for current CPU used in perf_submit and perf_read // Prefer BPF_F_CURRENT_CPU flag, falls back to call helper for older kernel // Can be overridden from BCC #ifndef CUR_CPU_IDENTIFIER #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0) #define CUR_CPU_IDENTIFIER BPF_F_CURRENT_CPU #else #define CUR_CPU_IDENTIFIER bpf_get_smp_processor_id() #endif #endif // Table for pushing custom events to userspace via ring buffer #define BPF_PERF_OUTPUT(_name) \ struct _name##_table_t { \ int key; \ u32 leaf; \ /* map.perf_submit(ctx, data, data_size) */ \ int (*perf_submit) (void *, void *, u32); \ int (*perf_submit_skb) (void *, u32, void *, u32); \ u32 max_entries; \ }; \ __attribute__((section("maps/perf_output"))) \ struct _name##_table_t _name = { .max_entries = 0 } // Table for reading hw perf cpu counters #define BPF_PERF_ARRAY(_name, _max_entries) \ struct _name##_table_t { \ int key; \ u32 leaf; \ /* counter = map.perf_read(index) */ \ u64 (*perf_read) (int); \ int (*perf_counter_value) (int, void *, u32); \ u32 max_entries; \ }; \ __attribute__((section("maps/perf_array"))) \ struct _name##_table_t _name = { .max_entries = (_max_entries) } // Table for cgroup file descriptors #define BPF_CGROUP_ARRAY(_name, _max_entries) \ struct _name##_table_t { \ int key; \ u32 leaf; \ int (*check_current_task) (int); \ u32 max_entries; \ }; \ __attribute__((section("maps/cgroup_array"))) \ struct _name##_table_t _name = { .max_entries = (_max_entries) } #define BPF_HASH1(_name) \ BPF_TABLE("hash", u64, u64, _name, 10240) #define BPF_HASH2(_name, _key_type) \ BPF_TABLE("hash", _key_type, u64, _name, 10240) #define BPF_HASH3(_name, _key_type, _leaf_type) \ BPF_TABLE("hash", _key_type, _leaf_type, _name, 10240) #define BPF_HASH4(_name, _key_type, _leaf_type, _size) \ BPF_TABLE("hash", _key_type, _leaf_type, _name, _size) // helper for default-variable macro function #define BPF_HASHX(_1, _2, _3, _4, NAME, ...) NAME // Define a hash function, some arguments optional // BPF_HASH(name, key_type=u64, leaf_type=u64, size=10240) #define BPF_HASH(...) \ BPF_HASHX(__VA_ARGS__, BPF_HASH4, BPF_HASH3, BPF_HASH2, BPF_HASH1)(__VA_ARGS__) #define BPF_ARRAY1(_name) \ BPF_TABLE("array", int, u64, _name, 10240) #define BPF_ARRAY2(_name, _leaf_type) \ BPF_TABLE("array", int, _leaf_type, _name, 10240) #define BPF_ARRAY3(_name, _leaf_type, _size) \ BPF_TABLE("array", int, _leaf_type, _name, _size) // helper for default-variable macro function #define BPF_ARRAYX(_1, _2, _3, NAME, ...) NAME // Define an array function, some arguments optional // BPF_ARRAY(name, leaf_type=u64, size=10240) #define BPF_ARRAY(...) \ BPF_ARRAYX(__VA_ARGS__, BPF_ARRAY3, BPF_ARRAY2, BPF_ARRAY1)(__VA_ARGS__) #define BPF_PERCPU_ARRAY1(_name) \ BPF_TABLE("percpu_array", int, u64, _name, 10240) #define BPF_PERCPU_ARRAY2(_name, _leaf_type) \ BPF_TABLE("percpu_array", int, _leaf_type, _name, 10240) #define BPF_PERCPU_ARRAY3(_name, _leaf_type, _size) \ BPF_TABLE("percpu_array", int, _leaf_type, _name, _size) // helper for default-variable macro function #define BPF_PERCPU_ARRAYX(_1, _2, _3, NAME, ...) NAME // Define an array function (per CPU), some arguments optional // BPF_PERCPU_ARRAY(name, leaf_type=u64, size=10240) #define BPF_PERCPU_ARRAY(...) \ BPF_PERCPU_ARRAYX( \ __VA_ARGS__, BPF_PERCPU_ARRAY3, BPF_PERCPU_ARRAY2, BPF_PERCPU_ARRAY1) \ (__VA_ARGS__) #define BPF_HIST1(_name) \ BPF_TABLE("histogram", int, u64, _name, 64) #define BPF_HIST2(_name, _key_type) \ BPF_TABLE("histogram", _key_type, u64, _name, 64) #define BPF_HIST3(_name, _key_type, _size) \ BPF_TABLE("histogram", _key_type, u64, _name, _size) #define BPF_HISTX(_1, _2, _3, NAME, ...) NAME // Define a histogram, some arguments optional // BPF_HISTOGRAM(name, key_type=int, size=64) #define BPF_HISTOGRAM(...) \ BPF_HISTX(__VA_ARGS__, BPF_HIST3, BPF_HIST2, BPF_HIST1)(__VA_ARGS__) #define BPF_LPM_TRIE1(_name) \ BPF_F_TABLE("lpm_trie", u64, u64, _name, 10240, BPF_F_NO_PREALLOC) #define BPF_LPM_TRIE2(_name, _key_type) \ BPF_F_TABLE("lpm_trie", _key_type, u64, _name, 10240, BPF_F_NO_PREALLOC) #define BPF_LPM_TRIE3(_name, _key_type, _leaf_type) \ BPF_F_TABLE("lpm_trie", _key_type, _leaf_type, _name, 10240, BPF_F_NO_PREALLOC) #define BPF_LPM_TRIE4(_name, _key_type, _leaf_type, _size) \ BPF_F_TABLE("lpm_trie", _key_type, _leaf_type, _name, _size, BPF_F_NO_PREALLOC) #define BPF_LPM_TRIEX(_1, _2, _3, _4, NAME, ...) NAME // Define a LPM trie function, some arguments optional // BPF_LPM_TRIE(name, key_type=u64, leaf_type=u64, size=10240) #define BPF_LPM_TRIE(...) \ BPF_LPM_TRIEX(__VA_ARGS__, BPF_LPM_TRIE4, BPF_LPM_TRIE3, BPF_LPM_TRIE2, BPF_LPM_TRIE1)(__VA_ARGS__) struct bpf_stacktrace { u64 ip[BPF_MAX_STACK_DEPTH]; }; struct bpf_stacktrace_buildid { struct bpf_stack_build_id trace[BPF_MAX_STACK_DEPTH]; }; #define BPF_STACK_TRACE(_name, _max_entries) \ BPF_TABLE("stacktrace", int, struct bpf_stacktrace, _name, roundup_pow_of_two(_max_entries)) #define BPF_STACK_TRACE_BUILDID(_name, _max_entries) \ BPF_F_TABLE("stacktrace", int, struct bpf_stacktrace_buildid, _name, roundup_pow_of_two(_max_entries), BPF_F_STACK_BUILD_ID) #define BPF_PROG_ARRAY(_name, _max_entries) \ BPF_TABLE("prog", u32, u32, _name, _max_entries) #define BPF_XDP_REDIRECT_MAP(_table_type, _leaf_type, _name, _max_entries) \ struct _name##_table_t { \ u32 key; \ _leaf_type leaf; \ /* xdp_act = map.redirect_map(index, flag) */ \ u64 (*redirect_map) (int, int); \ u32 max_entries; \ }; \ __attribute__((section("maps/"_table_type))) \ struct _name##_table_t _name = { .max_entries = (_max_entries) } #define BPF_DEVMAP(_name, _max_entries) \ BPF_XDP_REDIRECT_MAP("devmap", int, _name, _max_entries) #define BPF_CPUMAP(_name, _max_entries) \ BPF_XDP_REDIRECT_MAP("cpumap", u32, _name, _max_entries) #define BPF_ARRAY_OF_MAPS(_name, _inner_map_name, _max_entries) \ BPF_TABLE("array_of_maps$" _inner_map_name, int, int, _name, _max_entries) #define BPF_HASH_OF_MAPS(_name, _inner_map_name, _max_entries) \ BPF_TABLE("hash_of_maps$" _inner_map_name, int, int, _name, _max_entries) // packet parsing state machine helpers #define cursor_advance(_cursor, _len) \ ({ void *_tmp = _cursor; _cursor += _len; _tmp; }) #ifdef LINUX_VERSION_CODE_OVERRIDE unsigned _version BCC_SEC("version") = LINUX_VERSION_CODE_OVERRIDE; #else unsigned _version BCC_SEC("version") = LINUX_VERSION_CODE; #endif /* helper functions called from eBPF programs written in C */ static void *(*bpf_map_lookup_elem)(void *map, void *key) = (void *) BPF_FUNC_map_lookup_elem; static int (*bpf_map_update_elem)(void *map, void *key, void *value, u64 flags) = (void *) BPF_FUNC_map_update_elem; static int (*bpf_map_delete_elem)(void *map, void *key) = (void *) BPF_FUNC_map_delete_elem; static int (*bpf_probe_read)(void *dst, u64 size, const void *unsafe_ptr) = (void *) BPF_FUNC_probe_read; static u64 (*bpf_ktime_get_ns)(void) = (void *) BPF_FUNC_ktime_get_ns; static u32 (*bpf_get_prandom_u32)(void) = (void *) BPF_FUNC_get_prandom_u32; static int (*bpf_trace_printk_)(const char *fmt, u64 fmt_size, ...) = (void *) BPF_FUNC_trace_printk; static int (*bpf_probe_read_str)(void *dst, u64 size, const void *unsafe_ptr) = (void *) BPF_FUNC_probe_read_str; int bpf_trace_printk(const char *fmt, ...) asm("llvm.bpf.extra"); static inline __attribute__((always_inline)) void bpf_tail_call_(u64 map_fd, void *ctx, int index) { ((void (*)(void *, u64, int))BPF_FUNC_tail_call)(ctx, map_fd, index); } static int (*bpf_clone_redirect)(void *ctx, int ifindex, u32 flags) = (void *) BPF_FUNC_clone_redirect; static u64 (*bpf_get_smp_processor_id)(void) = (void *) BPF_FUNC_get_smp_processor_id; static u64 (*bpf_get_current_pid_tgid)(void) = (void *) BPF_FUNC_get_current_pid_tgid; static u64 (*bpf_get_current_uid_gid)(void) = (void *) BPF_FUNC_get_current_uid_gid; static int (*bpf_get_current_comm)(void *buf, int buf_size) = (void *) BPF_FUNC_get_current_comm; static u64 (*bpf_get_cgroup_classid)(void *ctx) = (void *) BPF_FUNC_get_cgroup_classid; static u64 (*bpf_skb_vlan_push)(void *ctx, u16 proto, u16 vlan_tci) = (void *) BPF_FUNC_skb_vlan_push; static u64 (*bpf_skb_vlan_pop)(void *ctx) = (void *) BPF_FUNC_skb_vlan_pop; static int (*bpf_skb_get_tunnel_key)(void *ctx, void *to, u32 size, u64 flags) = (void *) BPF_FUNC_skb_get_tunnel_key; static int (*bpf_skb_set_tunnel_key)(void *ctx, void *from, u32 size, u64 flags) = (void *) BPF_FUNC_skb_set_tunnel_key; static u64 (*bpf_perf_event_read)(void *map, u64 flags) = (void *) BPF_FUNC_perf_event_read; static int (*bpf_redirect)(int ifindex, u32 flags) = (void *) BPF_FUNC_redirect; static u32 (*bpf_get_route_realm)(void *ctx) = (void *) BPF_FUNC_get_route_realm; static int (*bpf_perf_event_output)(void *ctx, void *map, u64 index, void *data, u32 size) = (void *) BPF_FUNC_perf_event_output; static int (*bpf_skb_load_bytes)(void *ctx, int offset, void *to, u32 len) = (void *) BPF_FUNC_skb_load_bytes; static int (*bpf_perf_event_read_value)(void *map, u64 flags, void *buf, u32 buf_size) = (void *) BPF_FUNC_perf_event_read_value; static int (*bpf_perf_prog_read_value)(void *ctx, void *buf, u32 buf_size) = (void *) BPF_FUNC_perf_prog_read_value; static int (*bpf_current_task_under_cgroup)(void *map, int index) = (void *) BPF_FUNC_current_task_under_cgroup; static u32 (*bpf_get_socket_cookie)(void *ctx) = (void *) BPF_FUNC_get_socket_cookie; static u64 (*bpf_get_socket_uid)(void *ctx) = (void *) BPF_FUNC_get_socket_uid; static int (*bpf_getsockopt)(void *ctx, int level, int optname, void *optval, int optlen) = (void *) BPF_FUNC_getsockopt; static int (*bpf_redirect_map)(void *map, int key, int flags) = (void *) BPF_FUNC_redirect_map; static int (*bpf_set_hash)(void *ctx, u32 hash) = (void *) BPF_FUNC_set_hash; static int (*bpf_setsockopt)(void *ctx, int level, int optname, void *optval, int optlen) = (void *) BPF_FUNC_setsockopt; static int (*bpf_skb_adjust_room)(void *ctx, int len_diff, u32 mode, u64 flags) = (void *) BPF_FUNC_skb_adjust_room; static int (*bpf_skb_under_cgroup)(void *ctx, void *map, int index) = (void *) BPF_FUNC_skb_under_cgroup; static struct bpf_sock *(*bpf_skc_lookup_tcp)(void *ctx, struct bpf_sock_tuple *tuple, int size, unsigned long long netns_id, unsigned long long flags) = (void *) BPF_FUNC_skc_lookup_tcp; static int (*bpf_sk_redirect_map)(void *ctx, void *map, int key, int flags) = (void *) BPF_FUNC_sk_redirect_map; static int (*bpf_sock_map_update)(void *map, void *key, void *value, unsigned long long flags) = (void *) BPF_FUNC_sock_map_update; static int (*bpf_strtol)(const char *buf, size_t buf_len, u64 flags, long *res) = (void *) BPF_FUNC_strtol; static int (*bpf_strtoul)(const char *buf, size_t buf_len, u64 flags, unsigned long *res) = (void *) BPF_FUNC_strtoul; static int (*bpf_sysctl_get_current_value)(struct bpf_sysctl *ctx, char *buf, size_t buf_len) = (void *) BPF_FUNC_sysctl_get_current_value; static int (*bpf_sysctl_get_name)(struct bpf_sysctl *ctx, char *buf, size_t buf_len, u64 flags) = (void *) BPF_FUNC_sysctl_get_name; static int (*bpf_sysctl_get_new_value)(struct bpf_sysctl *ctx, char *buf, size_t buf_len) = (void *) BPF_FUNC_sysctl_get_new_value; static int (*bpf_sysctl_set_new_value)(struct bpf_sysctl *ctx, const char *buf, size_t buf_len) = (void *) BPF_FUNC_sysctl_set_new_value; static int (*bpf_tcp_check_syncookie)(struct bpf_sock *sk, void *ip, int ip_len, void *tcp, int tcp_len) = (void *) BPF_FUNC_tcp_check_syncookie; static int (*bpf_xdp_adjust_meta)(void *ctx, int offset) = (void *) BPF_FUNC_xdp_adjust_meta; /* bcc_get_stackid will return a negative value in the case of an error * * BPF_STACK_TRACE(_name, _size) will allocate space for _size stack traces. * -ENOMEM will be returned when this limit is reached. * * -EFAULT is typically returned when requesting user-space stack straces (using * BPF_F_USER_STACK) for kernel threads. However, a valid stackid may be * returned in some cases; consider a tracepoint or kprobe executing in the * kernel context. Given this you can typically ignore -EFAULT errors when * retrieving user-space stack traces. */ static int (*bcc_get_stackid_)(void *ctx, void *map, u64 flags) = (void *) BPF_FUNC_get_stackid; static inline __attribute__((always_inline)) int bcc_get_stackid(uintptr_t map, void *ctx, u64 flags) { return bcc_get_stackid_(ctx, (void *)map, flags); } static int (*bpf_csum_diff)(void *from, u64 from_size, void *to, u64 to_size, u64 seed) = (void *) BPF_FUNC_csum_diff; static int (*bpf_skb_get_tunnel_opt)(void *ctx, void *md, u32 size) = (void *) BPF_FUNC_skb_get_tunnel_opt; static int (*bpf_skb_set_tunnel_opt)(void *ctx, void *md, u32 size) = (void *) BPF_FUNC_skb_set_tunnel_opt; static int (*bpf_skb_change_proto)(void *ctx, u16 proto, u64 flags) = (void *) BPF_FUNC_skb_change_proto; static int (*bpf_skb_change_type)(void *ctx, u32 type) = (void *) BPF_FUNC_skb_change_type; static u32 (*bpf_get_hash_recalc)(void *ctx) = (void *) BPF_FUNC_get_hash_recalc; static u64 (*bpf_get_current_task)(void) = (void *) BPF_FUNC_get_current_task; static int (*bpf_probe_write_user)(void *dst, void *src, u32 size) = (void *) BPF_FUNC_probe_write_user; static int (*bpf_skb_change_tail)(void *ctx, u32 new_len, u64 flags) = (void *) BPF_FUNC_skb_change_tail; static int (*bpf_skb_pull_data)(void *ctx, u32 len) = (void *) BPF_FUNC_skb_pull_data; static int (*bpf_csum_update)(void *ctx, u16 csum) = (void *) BPF_FUNC_csum_update; static int (*bpf_set_hash_invalid)(void *ctx) = (void *) BPF_FUNC_set_hash_invalid; static int (*bpf_get_numa_node_id)(void) = (void *) BPF_FUNC_get_numa_node_id; static int (*bpf_skb_change_head)(void *ctx, u32 len, u64 flags) = (void *) BPF_FUNC_skb_change_head; static int (*bpf_xdp_adjust_head)(void *ctx, int offset) = (void *) BPF_FUNC_xdp_adjust_head; static int (*bpf_override_return)(void *pt_regs, unsigned long rc) = (void *) BPF_FUNC_override_return; static int (*bpf_sock_ops_cb_flags_set)(void *skops, int flags) = (void *) BPF_FUNC_sock_ops_cb_flags_set; static int (*bpf_msg_redirect_map)(void *msg, void *map, u32 key, u64 flags) = (void *) BPF_FUNC_msg_redirect_map; static int (*bpf_msg_apply_bytes)(void *msg, u32 bytes) = (void *) BPF_FUNC_msg_apply_bytes; static int (*bpf_msg_cork_bytes)(void *msg, u32 bytes) = (void *) BPF_FUNC_msg_cork_bytes; static int (*bpf_msg_pull_data)(void *msg, u32 start, u32 end, u64 flags) = (void *) BPF_FUNC_msg_pull_data; static int (*bpf_bind)(void *ctx, void *addr, int addr_len) = (void *) BPF_FUNC_bind; static int (*bpf_xdp_adjust_tail)(void *ctx, int offset) = (void *) BPF_FUNC_xdp_adjust_tail; static int (*bpf_skb_get_xfrm_state)(void *ctx, u32 index, void *xfrm_state, u32 size, u64 flags) = (void *) BPF_FUNC_skb_get_xfrm_state; static int (*bpf_get_stack)(void *ctx, void *buf, u32 size, u64 flags) = (void *) BPF_FUNC_get_stack; static int (*bpf_skb_load_bytes_relative)(void *ctx, u32 offset, void *to, u32 len, u32 start_header) = (void *) BPF_FUNC_skb_load_bytes_relative; static int (*bpf_fib_lookup)(void *ctx, void *params, int plen, u32 flags) = (void *) BPF_FUNC_fib_lookup; static int (*bpf_sock_hash_update)(void *ctx, void *map, void *key, u64 flags) = (void *) BPF_FUNC_sock_hash_update; static int (*bpf_msg_redirect_hash)(void *ctx, void *map, void *key, u64 flags) = (void *) BPF_FUNC_msg_redirect_hash; static int (*bpf_sk_redirect_hash)(void *ctx, void *map, void *key, u64 flags) = (void *) BPF_FUNC_sk_redirect_hash; static int (*bpf_lwt_push_encap)(void *skb, u32 type, void *hdr, u32 len) = (void *) BPF_FUNC_lwt_push_encap; static int (*bpf_lwt_seg6_store_bytes)(void *ctx, u32 offset, const void *from, u32 len) = (void *) BPF_FUNC_lwt_seg6_store_bytes; static int (*bpf_lwt_seg6_adjust_srh)(void *ctx, u32 offset, s32 delta) = (void *) BPF_FUNC_lwt_seg6_adjust_srh; static int (*bpf_lwt_seg6_action)(void *ctx, u32 action, void *param, u32 param_len) = (void *) BPF_FUNC_lwt_seg6_action; static int (*bpf_rc_keydown)(void *ctx, u32 protocol, u64 scancode, u32 toggle) = (void *) BPF_FUNC_rc_keydown; static int (*bpf_rc_repeat)(void *ctx) = (void *) BPF_FUNC_rc_repeat; static u64 (*bpf_skb_cgroup_id)(void *skb) = (void *) BPF_FUNC_skb_cgroup_id; static u64 (*bpf_get_current_cgroup_id)(void) = (void *) BPF_FUNC_get_current_cgroup_id; static u64 (*bpf_skb_ancestor_cgroup_id)(void *skb, int ancestor_level) = (void *) BPF_FUNC_skb_ancestor_cgroup_id; static void * (*bpf_get_local_storage)(void *map, u64 flags) = (void *) BPF_FUNC_get_local_storage; static int (*bpf_sk_select_reuseport)(void *reuse, void *map, void *key, u64 flags) = (void *) BPF_FUNC_sk_select_reuseport; static struct bpf_sock *(*bpf_sk_lookup_tcp)(void *ctx, struct bpf_sock_tuple *tuple, int size, unsigned int netns_id, unsigned long long flags) = (void *) BPF_FUNC_sk_lookup_tcp; static struct bpf_sock *(*bpf_sk_lookup_udp)(void *ctx, struct bpf_sock_tuple *tuple, int size, unsigned int netns_id, unsigned long long flags) = (void *) BPF_FUNC_sk_lookup_udp; static int (*bpf_sk_release)(struct bpf_sock *sk) = (void *) BPF_FUNC_sk_release; static int (*bpf_map_push_elem)(void *map, const void *value, u64 flags) = (void *) BPF_FUNC_map_push_elem; static int (*bpf_map_pop_elem)(void *map, void *value) = (void *) BPF_FUNC_map_pop_elem; static int (*bpf_map_peek_elem)(void *map, void *value) = (void *) BPF_FUNC_map_peek_elem; static int (*bpf_msg_push_data)(void *skb, u32 start, u32 len, u64 flags) = (void *) BPF_FUNC_msg_push_data; static int (*bpf_msg_pop_data)(void *msg, u32 start, u32 pop, u64 flags) = (void *) BPF_FUNC_msg_pop_data; static int (*bpf_rc_pointer_rel)(void *ctx, s32 rel_x, s32 rel_y) = (void *) BPF_FUNC_rc_pointer_rel; static void (*bpf_spin_lock)(struct bpf_spin_lock *lock) = (void *) BPF_FUNC_spin_lock; static void (*bpf_spin_unlock)(struct bpf_spin_lock *lock) = (void *) BPF_FUNC_spin_unlock; static struct bpf_sock *(*bpf_sk_fullsock)(struct bpf_sock *sk) = (void *) BPF_FUNC_sk_fullsock; static struct bpf_tcp_sock *(*bpf_tcp_sock)(struct bpf_sock *sk) = (void *) BPF_FUNC_tcp_sock; static int (*bpf_skb_ecn_set_ce)(void *ctx) = (void *) BPF_FUNC_skb_ecn_set_ce; static struct bpf_sock *(*bpf_get_listener_sock)(struct bpf_sock *sk) = (void *) BPF_FUNC_get_listener_sock; static void *(*bpf_sk_storage_get)(void *map, struct bpf_sock *sk, void *value, __u64 flags) = (void *) BPF_FUNC_sk_storage_get; static int (*bpf_sk_storage_delete)(void *map, struct bpf_sock *sk) = (void *)BPF_FUNC_sk_storage_delete; static int (*bpf_send_signal)(unsigned sig) = (void *)BPF_FUNC_send_signal; static long long (*bpf_tcp_gen_syncookie)(struct bpf_sock *sk, void *ip, int ip_len, void *tcp, int tcp_len) = (void *) BPF_FUNC_tcp_gen_syncookie; static int (*bpf_skb_output)(void *ctx, void *map, __u64 flags, void *data, __u64 size) = (void *)BPF_FUNC_skb_output; static int (*bpf_probe_read_user)(void *dst, __u32 size, const void *unsafe_ptr) = (void *)BPF_FUNC_probe_read_user; static int (*bpf_probe_read_kernel)(void *dst, __u32 size, const void *unsafe_ptr) = (void *)BPF_FUNC_probe_read_kernel; static int (*bpf_probe_read_user_str)(void *dst, __u32 size, const void *unsafe_ptr) = (void *)BPF_FUNC_probe_read_user_str; static int (*bpf_probe_read_kernel_str)(void *dst, __u32 size, const void *unsafe_ptr) = (void *)BPF_FUNC_probe_read_kernel_str; /* llvm builtin functions that eBPF C program may use to * emit BPF_LD_ABS and BPF_LD_IND instructions */ struct sk_buff; unsigned long long load_byte(void *skb, unsigned long long off) asm("llvm.bpf.load.byte"); unsigned long long load_half(void *skb, unsigned long long off) asm("llvm.bpf.load.half"); unsigned long long load_word(void *skb, unsigned long long off) asm("llvm.bpf.load.word"); /* a helper structure used by eBPF C program * to describe map attributes to elf_bpf loader */ struct bpf_map_def { unsigned int type; unsigned int key_size; unsigned int value_size; unsigned int max_entries; }; static int (*bpf_skb_store_bytes)(void *ctx, unsigned long long off, void *from, unsigned long long len, unsigned long long flags) = (void *) BPF_FUNC_skb_store_bytes; static int (*bpf_l3_csum_replace)(void *ctx, unsigned long long off, unsigned long long from, unsigned long long to, unsigned long long flags) = (void *) BPF_FUNC_l3_csum_replace; static int (*bpf_l4_csum_replace)(void *ctx, unsigned long long off, unsigned long long from, unsigned long long to, unsigned long long flags) = (void *) BPF_FUNC_l4_csum_replace; static inline __attribute__((always_inline)) u16 bpf_ntohs(u16 val) { /* will be recognized by gcc into rotate insn and eventually rolw 8 */ return (val << 8) | (val >> 8); } static inline __attribute__((always_inline)) u32 bpf_ntohl(u32 val) { /* gcc will use bswapsi2 insn */ return __builtin_bswap32(val); } static inline __attribute__((always_inline)) u64 bpf_ntohll(u64 val) { /* gcc will use bswapdi2 insn */ return __builtin_bswap64(val); } static inline __attribute__((always_inline)) unsigned __int128 bpf_ntoh128(unsigned __int128 val) { return (((unsigned __int128)bpf_ntohll(val) << 64) | (u64)bpf_ntohll(val >> 64)); } static inline __attribute__((always_inline)) u16 bpf_htons(u16 val) { return bpf_ntohs(val); } static inline __attribute__((always_inline)) u32 bpf_htonl(u32 val) { return bpf_ntohl(val); } static inline __attribute__((always_inline)) u64 bpf_htonll(u64 val) { return bpf_ntohll(val); } static inline __attribute__((always_inline)) unsigned __int128 bpf_hton128(unsigned __int128 val) { return bpf_ntoh128(val); } static inline __attribute__((always_inline)) u64 load_dword(void *skb, u64 off) { return ((u64)load_word(skb, off) << 32) | load_word(skb, off + 4); } void bpf_store_byte(void *skb, u64 off, u64 val) asm("llvm.bpf.store.byte"); void bpf_store_half(void *skb, u64 off, u64 val) asm("llvm.bpf.store.half"); void bpf_store_word(void *skb, u64 off, u64 val) asm("llvm.bpf.store.word"); u64 bpf_pseudo_fd(u64, u64) asm("llvm.bpf.pseudo"); static inline void __attribute__((always_inline)) bpf_store_dword(void *skb, u64 off, u64 val) { bpf_store_word(skb, off, (u32)val); bpf_store_word(skb, off + 4, val >> 32); } #define MASK(_n) ((_n) < 64 ? (1ull << (_n)) - 1 : ((u64)-1LL)) #define MASK128(_n) ((_n) < 128 ? ((unsigned __int128)1 << (_n)) - 1 : ((unsigned __int128)-1)) static inline __attribute__((always_inline)) unsigned int bpf_log2(unsigned int v) { unsigned int r; unsigned int shift; r = (v > 0xFFFF) << 4; v >>= r; shift = (v > 0xFF) << 3; v >>= shift; r |= shift; shift = (v > 0xF) << 2; v >>= shift; r |= shift; shift = (v > 0x3) << 1; v >>= shift; r |= shift; r |= (v >> 1); return r; } static inline __attribute__((always_inline)) unsigned int bpf_log2l(unsigned long v) { unsigned int hi = v >> 32; if (hi) return bpf_log2(hi) + 32 + 1; else return bpf_log2(v) + 1; } struct bpf_context; static inline __attribute__((always_inline)) BCC_SEC("helpers") u64 bpf_dext_pkt(void *pkt, u64 off, u64 bofs, u64 bsz) { if (bofs == 0 && bsz == 8) { return load_byte(pkt, off); } else if (bofs + bsz <= 8) { return load_byte(pkt, off) >> (8 - (bofs + bsz)) & MASK(bsz); } else if (bofs == 0 && bsz == 16) { return load_half(pkt, off); } else if (bofs + bsz <= 16) { return load_half(pkt, off) >> (16 - (bofs + bsz)) & MASK(bsz); } else if (bofs == 0 && bsz == 32) { return load_word(pkt, off); } else if (bofs + bsz <= 32) { return load_word(pkt, off) >> (32 - (bofs + bsz)) & MASK(bsz); } else if (bofs == 0 && bsz == 64) { return load_dword(pkt, off); } else if (bofs + bsz <= 64) { return load_dword(pkt, off) >> (64 - (bofs + bsz)) & MASK(bsz); } return 0; } static inline __attribute__((always_inline)) BCC_SEC("helpers") void bpf_dins_pkt(void *pkt, u64 off, u64 bofs, u64 bsz, u64 val) { // The load_xxx function does a bswap before returning the short/word/dword, // so the value in register will always be host endian. However, the bytes // written back need to be in network order. if (bofs == 0 && bsz == 8) { bpf_skb_store_bytes(pkt, off, &val, 1, 0); } else if (bofs + bsz <= 8) { u8 v = load_byte(pkt, off); v &= ~(MASK(bsz) << (8 - (bofs + bsz))); v |= ((val & MASK(bsz)) << (8 - (bofs + bsz))); bpf_skb_store_bytes(pkt, off, &v, 1, 0); } else if (bofs == 0 && bsz == 16) { u16 v = bpf_htons(val); bpf_skb_store_bytes(pkt, off, &v, 2, 0); } else if (bofs + bsz <= 16) { u16 v = load_half(pkt, off); v &= ~(MASK(bsz) << (16 - (bofs + bsz))); v |= ((val & MASK(bsz)) << (16 - (bofs + bsz))); v = bpf_htons(v); bpf_skb_store_bytes(pkt, off, &v, 2, 0); } else if (bofs == 0 && bsz == 32) { u32 v = bpf_htonl(val); bpf_skb_store_bytes(pkt, off, &v, 4, 0); } else if (bofs + bsz <= 32) { u32 v = load_word(pkt, off); v &= ~(MASK(bsz) << (32 - (bofs + bsz))); v |= ((val & MASK(bsz)) << (32 - (bofs + bsz))); v = bpf_htonl(v); bpf_skb_store_bytes(pkt, off, &v, 4, 0); } else if (bofs == 0 && bsz == 64) { u64 v = bpf_htonll(val); bpf_skb_store_bytes(pkt, off, &v, 8, 0); } else if (bofs + bsz <= 64) { u64 v = load_dword(pkt, off); v &= ~(MASK(bsz) << (64 - (bofs + bsz))); v |= ((val & MASK(bsz)) << (64 - (bofs + bsz))); v = bpf_htonll(v); bpf_skb_store_bytes(pkt, off, &v, 8, 0); } } static inline __attribute__((always_inline)) BCC_SEC("helpers") void * bpf_map_lookup_elem_(uintptr_t map, void *key) { return bpf_map_lookup_elem((void *)map, key); } static inline __attribute__((always_inline)) BCC_SEC("helpers") int bpf_map_update_elem_(uintptr_t map, void *key, void *value, u64 flags) { return bpf_map_update_elem((void *)map, key, value, flags); } static inline __attribute__((always_inline)) BCC_SEC("helpers") int bpf_map_delete_elem_(uintptr_t map, void *key) { return bpf_map_delete_elem((void *)map, key); } static inline __attribute__((always_inline)) BCC_SEC("helpers") int bpf_l3_csum_replace_(void *ctx, u64 off, u64 from, u64 to, u64 flags) { switch (flags & 0xf) { case 2: return bpf_l3_csum_replace(ctx, off, bpf_htons(from), bpf_htons(to), flags); case 4: return bpf_l3_csum_replace(ctx, off, bpf_htonl(from), bpf_htonl(to), flags); case 8: return bpf_l3_csum_replace(ctx, off, bpf_htonll(from), bpf_htonll(to), flags); default: {} } return bpf_l3_csum_replace(ctx, off, from, to, flags); } static inline __attribute__((always_inline)) BCC_SEC("helpers") int bpf_l4_csum_replace_(void *ctx, u64 off, u64 from, u64 to, u64 flags) { switch (flags & 0xf) { case 2: return bpf_l4_csum_replace(ctx, off, bpf_htons(from), bpf_htons(to), flags); case 4: return bpf_l4_csum_replace(ctx, off, bpf_htonl(from), bpf_htonl(to), flags); case 8: return bpf_l4_csum_replace(ctx, off, bpf_htonll(from), bpf_htonll(to), flags); default: {} } return bpf_l4_csum_replace(ctx, off, from, to, flags); } int incr_cksum_l3(void *off, u64 oldval, u64 newval) asm("llvm.bpf.extra"); int incr_cksum_l4(void *off, u64 oldval, u64 newval, u64 flags) asm("llvm.bpf.extra"); int bpf_num_cpus() asm("llvm.bpf.extra"); struct pt_regs; int bpf_usdt_readarg(int argc, struct pt_regs *ctx, void *arg) asm("llvm.bpf.extra"); int bpf_usdt_readarg_p(int argc, struct pt_regs *ctx, void *buf, u64 len) asm("llvm.bpf.extra"); /* Scan the ARCH passed in from ARCH env variable (see kbuild_helper.cc) */ #if defined(__TARGET_ARCH_x86) #define bpf_target_x86 #define bpf_target_defined #elif defined(__TARGET_ARCH_s930x) #define bpf_target_s930x #define bpf_target_defined #elif defined(__TARGET_ARCH_arm64) #define bpf_target_arm64 #define bpf_target_defined #elif defined(__TARGET_ARCH_powerpc) #define bpf_target_powerpc #define bpf_target_defined #else #undef bpf_target_defined #endif /* Fall back to what the compiler says */ #ifndef bpf_target_defined #if defined(__x86_64__) #define bpf_target_x86 #elif defined(__s390x__) #define bpf_target_s930x #elif defined(__aarch64__) #define bpf_target_arm64 #elif defined(__powerpc__) #define bpf_target_powerpc #endif #endif #if defined(bpf_target_powerpc) #define PT_REGS_PARM1(ctx) ((ctx)->gpr[3]) #define PT_REGS_PARM2(ctx) ((ctx)->gpr[4]) #define PT_REGS_PARM3(ctx) ((ctx)->gpr[5]) #define PT_REGS_PARM4(ctx) ((ctx)->gpr[6]) #define PT_REGS_PARM5(ctx) ((ctx)->gpr[7]) #define PT_REGS_PARM6(ctx) ((ctx)->gpr[8]) #define PT_REGS_RC(ctx) ((ctx)->gpr[3]) #define PT_REGS_IP(ctx) ((ctx)->nip) #define PT_REGS_SP(ctx) ((ctx)->gpr[1]) #elif defined(bpf_target_s930x) #define PT_REGS_PARM1(x) ((x)->gprs[2]) #define PT_REGS_PARM2(x) ((x)->gprs[3]) #define PT_REGS_PARM3(x) ((x)->gprs[4]) #define PT_REGS_PARM4(x) ((x)->gprs[5]) #define PT_REGS_PARM5(x) ((x)->gprs[6]) #define PT_REGS_RET(x) ((x)->gprs[14]) #define PT_REGS_FP(x) ((x)->gprs[11]) /* Works only with CONFIG_FRAME_POINTER */ #define PT_REGS_RC(x) ((x)->gprs[2]) #define PT_REGS_SP(x) ((x)->gprs[15]) #define PT_REGS_IP(x) ((x)->psw.addr) #elif defined(bpf_target_x86) #define PT_REGS_PARM1(ctx) ((ctx)->di) #define PT_REGS_PARM2(ctx) ((ctx)->si) #define PT_REGS_PARM3(ctx) ((ctx)->dx) #define PT_REGS_PARM4(ctx) ((ctx)->cx) #define PT_REGS_PARM5(ctx) ((ctx)->r8) #define PT_REGS_PARM6(ctx) ((ctx)->r9) #define PT_REGS_RET(ctx) ((ctx)->sp) #define PT_REGS_FP(ctx) ((ctx)->bp) /* Works only with CONFIG_FRAME_POINTER */ #define PT_REGS_RC(ctx) ((ctx)->ax) #define PT_REGS_IP(ctx) ((ctx)->ip) #define PT_REGS_SP(ctx) ((ctx)->sp) #elif defined(bpf_target_arm64) #define PT_REGS_PARM1(x) ((x)->regs[0]) #define PT_REGS_PARM2(x) ((x)->regs[1]) #define PT_REGS_PARM3(x) ((x)->regs[2]) #define PT_REGS_PARM4(x) ((x)->regs[3]) #define PT_REGS_PARM5(x) ((x)->regs[4]) #define PT_REGS_PARM6(x) ((x)->regs[5]) #define PT_REGS_RET(x) ((x)->regs[30]) #define PT_REGS_FP(x) ((x)->regs[29]) /* Works only with CONFIG_FRAME_POINTER */ #define PT_REGS_RC(x) ((x)->regs[0]) #define PT_REGS_SP(x) ((x)->sp) #define PT_REGS_IP(x) ((x)->pc) #else #error "bcc does not support this platform yet" #endif #define lock_xadd(ptr, val) ((void)__sync_fetch_and_add(ptr, val)) #define TRACEPOINT_PROBE(category, event) \ int tracepoint__##category##__##event(struct tracepoint__##category##__##event *args) #define RAW_TRACEPOINT_PROBE(event) \ int raw_tracepoint__##event(struct bpf_raw_tracepoint_args *ctx) #define TP_DATA_LOC_READ_CONST(dst, field, length) \ do { \ unsigned short __offset = args->data_loc_##field & 0xFFFF; \ bpf_probe_read((void *)dst, length, (char *)args + __offset); \ } while (0); #define TP_DATA_LOC_READ(dst, field) \ do { \ unsigned short __offset = args->data_loc_##field & 0xFFFF; \ unsigned short __length = args->data_loc_##field >> 16; \ bpf_probe_read((void *)dst, __length, (char *)args + __offset); \ } while (0); #endif )********" bpfcc-0.12.0/src/cc/export/proto.h000066400000000000000000000073261357404205000166700ustar00rootroot00000000000000R"********( /* * Copyright (c) 2015 PLUMgrid, Inc. * * 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. */ #ifndef __BCC_PROTO_H #define __BCC_PROTO_H #include #define BPF_PACKET_HEADER __attribute__((packed)) __attribute__((deprecated("packet"))) struct ethernet_t { unsigned long long dst:48; unsigned long long src:48; unsigned int type:16; } BPF_PACKET_HEADER; struct dot1q_t { unsigned short pri:3; unsigned short cfi:1; unsigned short vlanid:12; unsigned short type; } BPF_PACKET_HEADER; struct arp_t { unsigned short htype; unsigned short ptype; unsigned char hlen; unsigned char plen; unsigned short oper; unsigned long long sha:48; unsigned long long spa:32; unsigned long long tha:48; unsigned int tpa; } BPF_PACKET_HEADER; struct ip_t { unsigned char ver:4; // byte 0 unsigned char hlen:4; unsigned char tos; unsigned short tlen; unsigned short identification; // byte 4 unsigned short ffo_unused:1; unsigned short df:1; unsigned short mf:1; unsigned short foffset:13; unsigned char ttl; // byte 8 unsigned char nextp; unsigned short hchecksum; unsigned int src; // byte 12 unsigned int dst; // byte 16 } BPF_PACKET_HEADER; struct icmp_t { unsigned char type; unsigned char code; unsigned short checksum; } BPF_PACKET_HEADER; struct ip6_t { unsigned int ver:4; unsigned int priority:8; unsigned int flow_label:20; unsigned short payload_len; unsigned char next_header; unsigned char hop_limit; unsigned long long src_hi; unsigned long long src_lo; unsigned long long dst_hi; unsigned long long dst_lo; } BPF_PACKET_HEADER; struct ip6_opt_t { unsigned char next_header; unsigned char ext_len; unsigned char pad[6]; } BPF_PACKET_HEADER; struct icmp6_t { unsigned char type; unsigned char code; unsigned short checksum; } BPF_PACKET_HEADER; struct udp_t { unsigned short sport; unsigned short dport; unsigned short length; unsigned short crc; } BPF_PACKET_HEADER; struct tcp_t { unsigned short src_port; // byte 0 unsigned short dst_port; unsigned int seq_num; // byte 4 unsigned int ack_num; // byte 8 unsigned char offset:4; // byte 12 unsigned char reserved:4; unsigned char flag_cwr:1; unsigned char flag_ece:1; unsigned char flag_urg:1; unsigned char flag_ack:1; unsigned char flag_psh:1; unsigned char flag_rst:1; unsigned char flag_syn:1; unsigned char flag_fin:1; unsigned short rcv_wnd; unsigned short cksum; // byte 16 unsigned short urg_ptr; } BPF_PACKET_HEADER; struct vxlan_t { unsigned int rsv1:4; unsigned int iflag:1; unsigned int rsv2:3; unsigned int rsv3:24; unsigned int key:24; unsigned int rsv4:8; } BPF_PACKET_HEADER; struct vxlan_gbp_t { unsigned int gflag:1; unsigned int rsv1:3; unsigned int iflag:1; unsigned int rsv2:3; unsigned int rsv3:1; unsigned int dflag:1; unsigned int rsv4:1; unsigned int aflag:1; unsigned int rsv5:3; unsigned int tag:16; unsigned int key:24; unsigned int rsv6:8; } BPF_PACKET_HEADER; #endif )********" bpfcc-0.12.0/src/cc/exported_files.cc000066400000000000000000000024451357404205000173530ustar00rootroot00000000000000/* * Copyright (c) 2016 PLUMgrid, Inc. * * 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. */ #include "exported_files.h" using std::map; using std::string; namespace ebpf { // c++11 feature for including raw string literals // see http://www.stroustrup.com/C++11FAQ.html#raw-strings map ExportedFiles::headers_ = { { "/virtual/include/bcc/bpf.h", #include "compat/linux/virtual_bpf.h" }, { "/virtual/include/bcc/proto.h", #include "export/proto.h" }, { "/virtual/include/bcc/helpers.h", #include "export/helpers.h" }, { "/virtual/lib/clang/include/stdarg.h", #include "clang/include/stdarg.h" }, }; map ExportedFiles::footers_ = { { "/virtual/include/bcc/footer.h", #include "export/footer.h" }, }; } bpfcc-0.12.0/src/cc/exported_files.h000066400000000000000000000017241357404205000172140ustar00rootroot00000000000000/* * Copyright (c) 2016 PLUMgrid, Inc. * * 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. */ #pragma once #include #include namespace ebpf { class ExportedFiles { static std::map headers_; static std::map footers_; public: static const std::map & headers() { return headers_; } static const std::map & footers() { return footers_; } }; } bpfcc-0.12.0/src/cc/file_desc.h000066400000000000000000000031211357404205000161060ustar00rootroot00000000000000/* * Copyright (c) 2017 Facebook, Inc. * * 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. */ #pragma once #include namespace ebpf { /// FileDesc is a helper class for managing open file descriptors. Copy is /// disallowed (call dup instead), and cleanup happens automatically. class FileDesc { public: explicit FileDesc(int fd = -1) : fd_(fd) {} FileDesc(FileDesc &&that) : fd_(-1) { *this = std::move(that); } FileDesc(const FileDesc &that) = delete; ~FileDesc() { if (fd_ >= 0) ::close(fd_); } FileDesc &operator=(int fd) { if (fd_ >= 0) ::close(fd_); fd_ = fd; return *this; } FileDesc &operator=(FileDesc &&that) { if (fd_ >= 0) ::close(fd_); fd_ = that.fd_; that.fd_ = -1; return *this; } FileDesc &operator=(const FileDesc &that) = delete; FileDesc dup() const { if (fd_ >= 0) { int dup_fd = ::dup(fd_); return FileDesc(dup_fd); } else { return FileDesc(-1); } } operator int() { return fd_; } operator int() const { return fd_; } private: int fd_; }; } // namespace ebpf bpfcc-0.12.0/src/cc/frontends/000077500000000000000000000000001357404205000160255ustar00rootroot00000000000000bpfcc-0.12.0/src/cc/frontends/CMakeLists.txt000066400000000000000000000002151357404205000205630ustar00rootroot00000000000000# Copyright (c) PLUMgrid, Inc. # Licensed under the Apache License, Version 2.0 (the "License") add_subdirectory(b) add_subdirectory(clang) bpfcc-0.12.0/src/cc/frontends/b/000077500000000000000000000000001357404205000162465ustar00rootroot00000000000000bpfcc-0.12.0/src/cc/frontends/b/CMakeLists.txt000066400000000000000000000014131357404205000210050ustar00rootroot00000000000000# Copyright (c) PLUMgrid, Inc. # Licensed under the Apache License, Version 2.0 (the "License") include_directories(${CMAKE_CURRENT_BINARY_DIR}) include_directories(${CMAKE_CURRENT_SOURCE_DIR}) BISON_TARGET(Parser parser.yy ${CMAKE_CURRENT_BINARY_DIR}/parser.yy.cc COMPILE_FLAGS "-o parser.yy.cc -v --debug") FLEX_TARGET(Lexer lexer.ll ${CMAKE_CURRENT_BINARY_DIR}/lexer.ll.cc COMPILE_FLAGS "--c++ --o lexer.ll.cc") ADD_FLEX_BISON_DEPENDENCY(Lexer Parser) if (CMAKE_C_COMPILER_ID STREQUAL "Clang") set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/lexer.ll.cc PROPERTIES COMPILE_FLAGS "-Wno-deprecated-register") endif() add_library(b_frontend STATIC loader.cc codegen_llvm.cc node.cc parser.cc printer.cc type_check.cc ${BISON_Parser_OUTPUTS} ${FLEX_Lexer_OUTPUTS}) bpfcc-0.12.0/src/cc/frontends/b/codegen_llvm.cc000066400000000000000000001431711357404205000212220ustar00rootroot00000000000000/* * Copyright (c) 2015 PLUMgrid, Inc. * * 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "bcc_exception.h" #include "codegen_llvm.h" #include "file_desc.h" #include "lexer.h" #include "libbpf.h" #include "linux/bpf.h" #include "table_storage.h" #include "type_helper.h" namespace ebpf { namespace cc { using namespace llvm; using std::for_each; using std::make_tuple; using std::map; using std::pair; using std::set; using std::string; using std::stringstream; using std::to_string; using std::vector; // can't forward declare IRBuilder in .h file (template with default // parameters), so cast it instead :( #define B (*((IRBuilder<> *)this->b_)) // Helper class to push/pop the insert block class BlockStack { public: explicit BlockStack(CodegenLLVM *cc, BasicBlock *bb) : old_bb_(cc->b_->GetInsertBlock()), cc_(cc) { cc_->b_->SetInsertPoint(bb); } ~BlockStack() { if (old_bb_) cc_->b_->SetInsertPoint(old_bb_); else cc_->b_->ClearInsertionPoint(); } private: BasicBlock *old_bb_; CodegenLLVM *cc_; }; // Helper class to push/pop switch statement insert block class SwitchStack { public: explicit SwitchStack(CodegenLLVM *cc, SwitchInst *sw) : old_sw_(cc->cur_switch_), cc_(cc) { cc_->cur_switch_ = sw; } ~SwitchStack() { cc_->cur_switch_ = old_sw_; } private: SwitchInst *old_sw_; CodegenLLVM *cc_; }; CodegenLLVM::CodegenLLVM(llvm::Module *mod, Scopes *scopes, Scopes *proto_scopes) : out_(stdout), mod_(mod), indent_(0), tmp_reg_index_(0), scopes_(scopes), proto_scopes_(proto_scopes), expr_(nullptr) { b_ = new IRBuilder<>(ctx()); } CodegenLLVM::~CodegenLLVM() { delete b_; } template void CodegenLLVM::emit(const char *fmt, Args&&... params) { //fprintf(out_, fmt, std::forward(params)...); //fflush(out_); } void CodegenLLVM::emit(const char *s) { //fprintf(out_, "%s", s); //fflush(out_); } StatusTuple CodegenLLVM::visit_block_stmt_node(BlockStmtNode *n) { // enter scope if (n->scope_) scopes_->push_var(n->scope_); if (!n->stmts_.empty()) { for (auto it = n->stmts_.begin(); it != n->stmts_.end(); ++it) TRY2((*it)->accept(this)); } // exit scope if (n->scope_) scopes_->pop_var(); return StatusTuple(0); } StatusTuple CodegenLLVM::visit_if_stmt_node(IfStmtNode *n) { Function *parent = B.GetInsertBlock()->getParent(); BasicBlock *label_then = BasicBlock::Create(ctx(), "if.then", parent); BasicBlock *label_else = n->false_block_ ? BasicBlock::Create(ctx(), "if.else", parent) : nullptr; BasicBlock *label_end = BasicBlock::Create(ctx(), "if.end", parent); TRY2(n->cond_->accept(this)); Value *is_not_null = B.CreateIsNotNull(pop_expr()); if (n->false_block_) B.CreateCondBr(is_not_null, label_then, label_else); else B.CreateCondBr(is_not_null, label_then, label_end); { BlockStack bstack(this, label_then); TRY2(n->true_block_->accept(this)); if (!B.GetInsertBlock()->getTerminator()) B.CreateBr(label_end); } if (n->false_block_) { BlockStack bstack(this, label_else); TRY2(n->false_block_->accept(this)); if (!B.GetInsertBlock()->getTerminator()) B.CreateBr(label_end); } B.SetInsertPoint(label_end); return StatusTuple(0); } StatusTuple CodegenLLVM::visit_onvalid_stmt_node(OnValidStmtNode *n) { TRY2(n->cond_->accept(this)); Value *is_null = B.CreateIsNotNull(pop_expr()); Function *parent = B.GetInsertBlock()->getParent(); BasicBlock *label_then = BasicBlock::Create(ctx(), "onvalid.then", parent); BasicBlock *label_else = n->else_block_ ? BasicBlock::Create(ctx(), "onvalid.else", parent) : nullptr; BasicBlock *label_end = BasicBlock::Create(ctx(), "onvalid.end", parent); if (n->else_block_) B.CreateCondBr(is_null, label_then, label_else); else B.CreateCondBr(is_null, label_then, label_end); { BlockStack bstack(this, label_then); TRY2(n->block_->accept(this)); if (!B.GetInsertBlock()->getTerminator()) B.CreateBr(label_end); } if (n->else_block_) { BlockStack bstack(this, label_else); TRY2(n->else_block_->accept(this)); if (!B.GetInsertBlock()->getTerminator()) B.CreateBr(label_end); } B.SetInsertPoint(label_end); return StatusTuple(0); } StatusTuple CodegenLLVM::visit_switch_stmt_node(SwitchStmtNode *n) { Function *parent = B.GetInsertBlock()->getParent(); BasicBlock *label_default = BasicBlock::Create(ctx(), "switch.default", parent); BasicBlock *label_end = BasicBlock::Create(ctx(), "switch.end", parent); // switch (cond) TRY2(n->cond_->accept(this)); SwitchInst *switch_inst = B.CreateSwitch(pop_expr(), label_default); B.SetInsertPoint(label_end); { // case 1..N SwitchStack sstack(this, switch_inst); TRY2(n->block_->accept(this)); } // if other cases are terminal, erase the end label if (pred_empty(label_end)) { B.SetInsertPoint(resolve_label("DONE")); label_end->eraseFromParent(); } return StatusTuple(0); } StatusTuple CodegenLLVM::visit_case_stmt_node(CaseStmtNode *n) { if (!cur_switch_) return mkstatus_(n, "no valid switch instruction"); Function *parent = B.GetInsertBlock()->getParent(); BasicBlock *label_end = B.GetInsertBlock(); BasicBlock *dest; if (n->value_) { TRY2(n->value_->accept(this)); dest = BasicBlock::Create(ctx(), "switch.case", parent); Value *cond = B.CreateIntCast(pop_expr(), cur_switch_->getCondition()->getType(), false); cur_switch_->addCase(cast(cond), dest); } else { dest = cur_switch_->getDefaultDest(); } { BlockStack bstack(this, dest); TRY2(n->block_->accept(this)); // if no trailing goto, fall to end if (!B.GetInsertBlock()->getTerminator()) B.CreateBr(label_end); } return StatusTuple(0); } StatusTuple CodegenLLVM::visit_ident_expr_node(IdentExprNode *n) { if (!n->decl_) return mkstatus_(n, "variable lookup failed: %s", n->name_.c_str()); if (n->decl_->is_pointer()) { if (n->sub_name_.size()) { if (n->bitop_) { // ident is holding a host endian number, don't use dext if (n->is_lhs()) { emit("%s%s->%s", n->decl_->scope_id(), n->c_str(), n->sub_name_.c_str()); } else { emit("(((%s%s->%s) >> %d) & (((%s)1 << %d) - 1))", n->decl_->scope_id(), n->c_str(), n->sub_name_.c_str(), n->bitop_->bit_offset_, bits_to_uint(n->bitop_->bit_width_ + 1), n->bitop_->bit_width_); } return mkstatus_(n, "unsupported"); } else { if (n->struct_type_->id_->name_ == "_Packet" && n->sub_name_.substr(0, 3) == "arg") { // convert arg1~arg8 into args[0]~args[7] assuming type_check verified the range already auto arg_num = stoi(n->sub_name_.substr(3, 3)); if (arg_num < 5) { emit("%s%s->args_lo[%d]", n->decl_->scope_id(), n->c_str(), arg_num - 1); } else { emit("%s%s->args_hi[%d]", n->decl_->scope_id(), n->c_str(), arg_num - 5); } return mkstatus_(n, "unsupported"); } else { emit("%s%s->%s", n->decl_->scope_id(), n->c_str(), n->sub_name_.c_str()); auto it = vars_.find(n->decl_); if (it == vars_.end()) return mkstatus_(n, "Cannot locate variable %s in vars_ table", n->c_str()); LoadInst *load_1 = B.CreateLoad(it->second); vector indices({B.getInt32(0), B.getInt32(n->sub_decl_->slot_)}); expr_ = B.CreateInBoundsGEP(load_1, indices); if (!n->is_lhs()) expr_ = B.CreateLoad(pop_expr()); } } } else { auto it = vars_.find(n->decl_); if (it == vars_.end()) return mkstatus_(n, "Cannot locate variable %s in vars_ table", n->c_str()); expr_ = n->is_lhs() ? it->second : (Value *)B.CreateLoad(it->second); } } else { if (n->sub_name_.size()) { emit("%s%s.%s", n->decl_->scope_id(), n->c_str(), n->sub_name_.c_str()); auto it = vars_.find(n->decl_); if (it == vars_.end()) return mkstatus_(n, "Cannot locate variable %s in vars_ table", n->c_str()); vector indices({const_int(0), const_int(n->sub_decl_->slot_, 32)}); expr_ = B.CreateGEP(nullptr, it->second, indices); if (!n->is_lhs()) expr_ = B.CreateLoad(pop_expr()); } else { if (n->bitop_) { // ident is holding a host endian number, don't use dext if (n->is_lhs()) return mkstatus_(n, "illegal: ident %s is a left-hand-side type", n->name_.c_str()); if (n->decl_->is_struct()) return mkstatus_(n, "illegal: can only take bitop of a struct subfield"); emit("(((%s%s) >> %d) & (((%s)1 << %d) - 1))", n->decl_->scope_id(), n->c_str(), n->bitop_->bit_offset_, bits_to_uint(n->bitop_->bit_width_ + 1), n->bitop_->bit_width_); } else { emit("%s%s", n->decl_->scope_id(), n->c_str()); auto it = vars_.find(n->decl_); if (it == vars_.end()) return mkstatus_(n, "Cannot locate variable %s in vars_ table", n->c_str()); if (n->is_lhs() || n->decl_->is_struct()) expr_ = it->second; else expr_ = B.CreateLoad(it->second); } } } return StatusTuple(0); } StatusTuple CodegenLLVM::visit_assign_expr_node(AssignExprNode *n) { if (n->bitop_) { TRY2(n->lhs_->accept(this)); emit(" = ("); TRY2(n->lhs_->accept(this)); emit(" & ~((((%s)1 << %d) - 1) << %d)) | (", bits_to_uint(n->lhs_->bit_width_), n->bitop_->bit_width_, n->bitop_->bit_offset_); TRY2(n->rhs_->accept(this)); emit(" << %d)", n->bitop_->bit_offset_); return mkstatus_(n, "unsupported"); } else { if (n->lhs_->flags_[ExprNode::PROTO]) { // auto f = n->lhs_->struct_type_->field(n->id_->sub_name_); // emit("bpf_dins(%s%s + %zu, %zu, %zu, ", n->id_->decl_->scope_id(), n->id_->c_str(), // f->bit_offset_ >> 3, f->bit_offset_ & 0x7, f->bit_width_); // TRY2(n->rhs_->accept(this)); // emit(")"); return mkstatus_(n, "unsupported"); } else { TRY2(n->rhs_->accept(this)); if (n->lhs_->is_pkt()) { TRY2(n->lhs_->accept(this)); } else { Value *rhs = pop_expr(); TRY2(n->lhs_->accept(this)); Value *lhs = pop_expr(); if (!n->rhs_->is_ref()) rhs = B.CreateIntCast(rhs, cast(lhs->getType())->getElementType(), false); B.CreateStore(rhs, lhs); } } } return StatusTuple(0); } StatusTuple CodegenLLVM::lookup_var(Node *n, const string &name, Scopes::VarScope *scope, VariableDeclStmtNode **decl, Value **mem) const { *decl = scope->lookup(name, SCOPE_GLOBAL); if (!*decl) return mkstatus_(n, "cannot find %s variable", name.c_str()); auto it = vars_.find(*decl); if (it == vars_.end()) return mkstatus_(n, "unable to find %s memory location", name.c_str()); *mem = it->second; return StatusTuple(0); } StatusTuple CodegenLLVM::visit_packet_expr_node(PacketExprNode *n) { auto p = proto_scopes_->top_struct()->lookup(n->id_->name_, true); VariableDeclStmtNode *offset_decl, *skb_decl; Value *offset_mem, *skb_mem; TRY2(lookup_var(n, "skb", scopes_->current_var(), &skb_decl, &skb_mem)); TRY2(lookup_var(n, "$" + n->id_->name_, scopes_->current_var(), &offset_decl, &offset_mem)); if (p) { auto f = p->field(n->id_->sub_name_); if (f) { size_t bit_offset = f->bit_offset_; size_t bit_width = f->bit_width_; if (n->bitop_) { bit_offset += f->bit_width_ - (n->bitop_->bit_offset_ + n->bitop_->bit_width_); bit_width = std::min(bit_width - n->bitop_->bit_offset_, n->bitop_->bit_width_); } if (n->is_ref()) { // e.g.: @ip.hchecksum, return offset of the header within packet LoadInst *offset_ptr = B.CreateLoad(offset_mem); Value *skb_hdr_offset = B.CreateAdd(offset_ptr, B.getInt64(bit_offset >> 3)); expr_ = B.CreateIntCast(skb_hdr_offset, B.getInt64Ty(), false); } else if (n->is_lhs()) { emit("bpf_dins_pkt(pkt, %s + %zu, %zu, %zu, ", n->id_->c_str(), bit_offset >> 3, bit_offset & 0x7, bit_width); Function *store_fn = mod_->getFunction("bpf_dins_pkt"); if (!store_fn) return mkstatus_(n, "unable to find function bpf_dins_pkt"); LoadInst *skb_ptr = B.CreateLoad(skb_mem); Value *skb_ptr8 = B.CreateBitCast(skb_ptr, B.getInt8PtrTy()); LoadInst *offset_ptr = B.CreateLoad(offset_mem); Value *skb_hdr_offset = B.CreateAdd(offset_ptr, B.getInt64(bit_offset >> 3)); Value *rhs = B.CreateIntCast(pop_expr(), B.getInt64Ty(), false); B.CreateCall(store_fn, vector({skb_ptr8, skb_hdr_offset, B.getInt64(bit_offset & 0x7), B.getInt64(bit_width), rhs})); } else { emit("bpf_dext_pkt(pkt, %s + %zu, %zu, %zu)", n->id_->c_str(), bit_offset >> 3, bit_offset & 0x7, bit_width); Function *load_fn = mod_->getFunction("bpf_dext_pkt"); if (!load_fn) return mkstatus_(n, "unable to find function bpf_dext_pkt"); LoadInst *skb_ptr = B.CreateLoad(skb_mem); Value *skb_ptr8 = B.CreateBitCast(skb_ptr, B.getInt8PtrTy()); LoadInst *offset_ptr = B.CreateLoad(offset_mem); Value *skb_hdr_offset = B.CreateAdd(offset_ptr, B.getInt64(bit_offset >> 3)); expr_ = B.CreateCall(load_fn, vector({skb_ptr8, skb_hdr_offset, B.getInt64(bit_offset & 0x7), B.getInt64(bit_width)})); // this generates extra trunc insns whereas the bpf.load fns already // trunc the values internally in the bpf interpeter //expr_ = B.CreateTrunc(pop_expr(), B.getIntNTy(bit_width)); } } else { emit("pkt->start + pkt->offset + %s", n->id_->c_str()); return mkstatus_(n, "unsupported"); } } return StatusTuple(0); } StatusTuple CodegenLLVM::visit_integer_expr_node(IntegerExprNode *n) { APInt val; StringRef(n->val_).getAsInteger(0, val); expr_ = ConstantInt::get(mod_->getContext(), val); if (n->bits_) expr_ = B.CreateIntCast(expr_, B.getIntNTy(n->bits_), false); return StatusTuple(0); } StatusTuple CodegenLLVM::visit_string_expr_node(StringExprNode *n) { if (n->is_lhs()) return mkstatus_(n, "cannot assign to a string"); Value *global = B.CreateGlobalString(n->val_); Value *ptr = make_alloca(resolve_entry_stack(), B.getInt8Ty(), "", B.getInt64(n->val_.size() + 1)); #if LLVM_MAJOR_VERSION >= 7 B.CreateMemCpy(ptr, 1, global, 1, n->val_.size() + 1); #else B.CreateMemCpy(ptr, global, n->val_.size() + 1, 1); #endif expr_ = ptr; return StatusTuple(0); } StatusTuple CodegenLLVM::emit_short_circuit_and(BinopExprNode *n) { Function *parent = B.GetInsertBlock()->getParent(); BasicBlock *label_start = B.GetInsertBlock(); BasicBlock *label_then = BasicBlock::Create(ctx(), "and.then", parent); BasicBlock *label_end = BasicBlock::Create(ctx(), "and.end", parent); TRY2(n->lhs_->accept(this)); Value *neq_zero = B.CreateICmpNE(pop_expr(), B.getIntN(n->lhs_->bit_width_, 0)); B.CreateCondBr(neq_zero, label_then, label_end); { BlockStack bstack(this, label_then); TRY2(n->rhs_->accept(this)); expr_ = B.CreateICmpNE(pop_expr(), B.getIntN(n->rhs_->bit_width_, 0)); B.CreateBr(label_end); } B.SetInsertPoint(label_end); PHINode *phi = B.CreatePHI(B.getInt1Ty(), 2); phi->addIncoming(B.getFalse(), label_start); phi->addIncoming(pop_expr(), label_then); expr_ = phi; return StatusTuple(0); } StatusTuple CodegenLLVM::emit_short_circuit_or(BinopExprNode *n) { Function *parent = B.GetInsertBlock()->getParent(); BasicBlock *label_start = B.GetInsertBlock(); BasicBlock *label_then = BasicBlock::Create(ctx(), "or.then", parent); BasicBlock *label_end = BasicBlock::Create(ctx(), "or.end", parent); TRY2(n->lhs_->accept(this)); Value *neq_zero = B.CreateICmpNE(pop_expr(), B.getIntN(n->lhs_->bit_width_, 0)); B.CreateCondBr(neq_zero, label_end, label_then); { BlockStack bstack(this, label_then); TRY2(n->rhs_->accept(this)); expr_ = B.CreateICmpNE(pop_expr(), B.getIntN(n->rhs_->bit_width_, 0)); B.CreateBr(label_end); } B.SetInsertPoint(label_end); PHINode *phi = B.CreatePHI(B.getInt1Ty(), 2); phi->addIncoming(B.getTrue(), label_start); phi->addIncoming(pop_expr(), label_then); expr_ = phi; return StatusTuple(0); } StatusTuple CodegenLLVM::visit_binop_expr_node(BinopExprNode *n) { if (n->op_ == Tok::TAND) return emit_short_circuit_and(n); if (n->op_ == Tok::TOR) return emit_short_circuit_or(n); TRY2(n->lhs_->accept(this)); Value *lhs = pop_expr(); TRY2(n->rhs_->accept(this)); Value *rhs = B.CreateIntCast(pop_expr(), lhs->getType(), false); switch (n->op_) { case Tok::TCEQ: expr_ = B.CreateICmpEQ(lhs, rhs); break; case Tok::TCNE: expr_ = B.CreateICmpNE(lhs, rhs); break; case Tok::TXOR: expr_ = B.CreateXor(lhs, rhs); break; case Tok::TMOD: expr_ = B.CreateURem(lhs, rhs); break; case Tok::TCLT: expr_ = B.CreateICmpULT(lhs, rhs); break; case Tok::TCLE: expr_ = B.CreateICmpULE(lhs, rhs); break; case Tok::TCGT: expr_ = B.CreateICmpUGT(lhs, rhs); break; case Tok::TCGE: expr_ = B.CreateICmpUGE(lhs, rhs); break; case Tok::TPLUS: expr_ = B.CreateAdd(lhs, rhs); break; case Tok::TMINUS: expr_ = B.CreateSub(lhs, rhs); break; case Tok::TLAND: expr_ = B.CreateAnd(lhs, rhs); break; case Tok::TLOR: expr_ = B.CreateOr(lhs, rhs); break; default: return mkstatus_(n, "unsupported binary operator"); } return StatusTuple(0); } StatusTuple CodegenLLVM::visit_unop_expr_node(UnopExprNode *n) { TRY2(n->expr_->accept(this)); switch (n->op_) { case Tok::TNOT: expr_ = B.CreateNot(pop_expr()); break; case Tok::TCMPL: expr_ = B.CreateNeg(pop_expr()); break; default: {} } return StatusTuple(0); } StatusTuple CodegenLLVM::visit_bitop_expr_node(BitopExprNode *n) { return StatusTuple(0); } StatusTuple CodegenLLVM::visit_goto_expr_node(GotoExprNode *n) { if (n->id_->name_ == "DONE") { return mkstatus_(n, "use return statement instead"); } string jump_label; // when dealing with multistates, goto statements may be overridden auto rewrite_it = proto_rewrites_.find(n->id_->full_name()); auto default_it = proto_rewrites_.find(""); if (rewrite_it != proto_rewrites_.end()) { jump_label = rewrite_it->second; } else if (default_it != proto_rewrites_.end()) { jump_label = default_it->second; } else { auto state = scopes_->current_state()->lookup(n->id_->full_name(), false); if (state) { jump_label = state->scoped_name(); if (n->is_continue_) { jump_label += "_continue"; } } else { state = scopes_->current_state()->lookup("EOP", false); if (state) { jump_label = state->scoped_name(); } } } B.CreateBr(resolve_label(jump_label)); return StatusTuple(0); } StatusTuple CodegenLLVM::visit_return_expr_node(ReturnExprNode *n) { TRY2(n->expr_->accept(this)); Function *parent = B.GetInsertBlock()->getParent(); Value *cast_1 = B.CreateIntCast(pop_expr(), parent->getReturnType(), true); B.CreateStore(cast_1, retval_); B.CreateBr(resolve_label("DONE")); return StatusTuple(0); } StatusTuple CodegenLLVM::emit_table_lookup(MethodCallExprNode *n) { TableDeclStmtNode* table = scopes_->top_table()->lookup(n->id_->name_); IdentExprNode* arg0 = static_cast(n->args_.at(0).get()); IdentExprNode* arg1; StructVariableDeclStmtNode* arg1_type; auto table_fd_it = table_fds_.find(table); if (table_fd_it == table_fds_.end()) return mkstatus_(n, "unable to find table %s in table_fds_", n->id_->c_str()); Function *pseudo_fn = mod_->getFunction("llvm.bpf.pseudo"); if (!pseudo_fn) return mkstatus_(n, "pseudo fd loader doesn't exist"); Function *lookup_fn = mod_->getFunction("bpf_map_lookup_elem_"); if (!lookup_fn) return mkstatus_(n, "bpf_map_lookup_elem_ undefined"); CallInst *pseudo_call = B.CreateCall(pseudo_fn, vector({B.getInt64(BPF_PSEUDO_MAP_FD), B.getInt64(table_fd_it->second)})); Value *pseudo_map_fd = pseudo_call; TRY2(arg0->accept(this)); Value *key_ptr = B.CreateBitCast(pop_expr(), B.getInt8PtrTy()); expr_ = B.CreateCall(lookup_fn, vector({pseudo_map_fd, key_ptr})); if (table->type_id()->name_ == "FIXED_MATCH" || table->type_id()->name_ == "INDEXED") { if (n->args_.size() == 2) { arg1 = static_cast(n->args_.at(1).get()); arg1_type = static_cast(arg1->decl_); if (table->leaf_id()->name_ != arg1_type->struct_id_->name_) { return mkstatus_(n, "lookup pointer type mismatch %s != %s", table->leaf_id()->c_str(), arg1_type->struct_id_->c_str()); } auto it = vars_.find(arg1_type); if (it == vars_.end()) return mkstatus_(n, "Cannot locate variable %s in vars_ table", n->id_->c_str()); expr_ = B.CreateBitCast(pop_expr(), cast(it->second->getType())->getElementType()); B.CreateStore(pop_expr(), it->second); } } else { return mkstatus_(n, "lookup in table type %s unsupported", table->type_id()->c_str()); } return StatusTuple(0); } StatusTuple CodegenLLVM::emit_table_update(MethodCallExprNode *n) { TableDeclStmtNode* table = scopes_->top_table()->lookup(n->id_->name_); IdentExprNode* arg0 = static_cast(n->args_.at(0).get()); IdentExprNode* arg1 = static_cast(n->args_.at(1).get()); auto table_fd_it = table_fds_.find(table); if (table_fd_it == table_fds_.end()) return mkstatus_(n, "unable to find table %s in table_fds_", n->id_->c_str()); Function *pseudo_fn = mod_->getFunction("llvm.bpf.pseudo"); if (!pseudo_fn) return mkstatus_(n, "pseudo fd loader doesn't exist"); Function *update_fn = mod_->getFunction("bpf_map_update_elem_"); if (!update_fn) return mkstatus_(n, "bpf_map_update_elem_ undefined"); CallInst *pseudo_call = B.CreateCall(pseudo_fn, vector({B.getInt64(BPF_PSEUDO_MAP_FD), B.getInt64(table_fd_it->second)})); Value *pseudo_map_fd = pseudo_call; TRY2(arg0->accept(this)); Value *key_ptr = B.CreateBitCast(pop_expr(), B.getInt8PtrTy()); if (table->type_id()->name_ == "FIXED_MATCH" || table->type_id()->name_ == "INDEXED") { TRY2(arg1->accept(this)); Value *value_ptr = B.CreateBitCast(pop_expr(), B.getInt8PtrTy()); expr_ = B.CreateCall(update_fn, vector({pseudo_map_fd, key_ptr, value_ptr, B.getInt64(BPF_ANY)})); } else { return mkstatus_(n, "unsupported"); } return StatusTuple(0); } StatusTuple CodegenLLVM::emit_table_delete(MethodCallExprNode *n) { TableDeclStmtNode* table = scopes_->top_table()->lookup(n->id_->name_); IdentExprNode* arg0 = static_cast(n->args_.at(0).get()); auto table_fd_it = table_fds_.find(table); if (table_fd_it == table_fds_.end()) return mkstatus_(n, "unable to find table %s in table_fds_", n->id_->c_str()); Function *pseudo_fn = mod_->getFunction("llvm.bpf.pseudo"); if (!pseudo_fn) return mkstatus_(n, "pseudo fd loader doesn't exist"); Function *update_fn = mod_->getFunction("bpf_map_update_elem_"); if (!update_fn) return mkstatus_(n, "bpf_map_update_elem_ undefined"); CallInst *pseudo_call = B.CreateCall(pseudo_fn, vector({B.getInt64(BPF_PSEUDO_MAP_FD), B.getInt64(table_fd_it->second)})); Value *pseudo_map_fd = pseudo_call; TRY2(arg0->accept(this)); Value *key_ptr = B.CreateBitCast(pop_expr(), B.getInt8PtrTy()); if (table->type_id()->name_ == "FIXED_MATCH" || table->type_id()->name_ == "INDEXED") { expr_ = B.CreateCall(update_fn, vector({pseudo_map_fd, key_ptr})); } else { return mkstatus_(n, "unsupported"); } return StatusTuple(0); } StatusTuple CodegenLLVM::emit_log(MethodCallExprNode *n) { vector args; auto arg = n->args_.begin(); TRY2((*arg)->accept(this)); args.push_back(pop_expr()); args.push_back(B.getInt64(((*arg)->bit_width_ >> 3) + 1)); ++arg; for (; arg != n->args_.end(); ++arg) { TRY2((*arg)->accept(this)); args.push_back(pop_expr()); } // int bpf_trace_printk(fmt, sizeof(fmt), ...) FunctionType *printk_fn_type = FunctionType::get(B.getInt32Ty(), vector({B.getInt8PtrTy(), B.getInt64Ty()}), true); Value *printk_fn = B.CreateIntToPtr(B.getInt64(BPF_FUNC_trace_printk), PointerType::getUnqual(printk_fn_type)); expr_ = B.CreateCall(printk_fn, args); return StatusTuple(0); } StatusTuple CodegenLLVM::emit_packet_rewrite_field(MethodCallExprNode *n) { TRY2(n->args_[1]->accept(this)); TRY2(n->args_[0]->accept(this)); return StatusTuple(0); } StatusTuple CodegenLLVM::emit_atomic_add(MethodCallExprNode *n) { TRY2(n->args_[0]->accept(this)); Value *lhs = B.CreateBitCast(pop_expr(), Type::getInt64PtrTy(ctx())); TRY2(n->args_[1]->accept(this)); Value *rhs = B.CreateSExt(pop_expr(), B.getInt64Ty()); AtomicRMWInst *atomic_inst = B.CreateAtomicRMW( AtomicRMWInst::Add, lhs, rhs, AtomicOrdering::SequentiallyConsistent); atomic_inst->setVolatile(false); return StatusTuple(0); } StatusTuple CodegenLLVM::emit_incr_cksum(MethodCallExprNode *n, size_t sz) { Value *is_pseudo; string csum_fn_str; if (n->args_.size() == 4) { TRY2(n->args_[3]->accept(this)); is_pseudo = B.CreateIntCast(B.CreateIsNotNull(pop_expr()), B.getInt64Ty(), false); csum_fn_str = "bpf_l4_csum_replace_"; } else { is_pseudo = B.getInt64(0); csum_fn_str = "bpf_l3_csum_replace_"; } TRY2(n->args_[2]->accept(this)); Value *new_val = B.CreateZExt(pop_expr(), B.getInt64Ty()); TRY2(n->args_[1]->accept(this)); Value *old_val = B.CreateZExt(pop_expr(), B.getInt64Ty()); TRY2(n->args_[0]->accept(this)); Value *offset = B.CreateZExt(pop_expr(), B.getInt64Ty()); Function *csum_fn = mod_->getFunction(csum_fn_str); if (!csum_fn) return mkstatus_(n, "Undefined built-in %s", csum_fn_str.c_str()); // flags = (is_pseudo << 4) | sizeof(old_val) Value *flags_lower = B.getInt64(sz ? sz : bits_to_size(n->args_[1]->bit_width_)); Value *flags_upper = B.CreateShl(is_pseudo, B.getInt64(4)); Value *flags = B.CreateOr(flags_upper, flags_lower); VariableDeclStmtNode *skb_decl; Value *skb_mem; TRY2(lookup_var(n, "skb", scopes_->current_var(), &skb_decl, &skb_mem)); LoadInst *skb_ptr = B.CreateLoad(skb_mem); Value *skb_ptr8 = B.CreateBitCast(skb_ptr, B.getInt8PtrTy()); expr_ = B.CreateCall(csum_fn, vector({skb_ptr8, offset, old_val, new_val, flags})); return StatusTuple(0); } StatusTuple CodegenLLVM::emit_get_usec_time(MethodCallExprNode *n) { return StatusTuple(0); } StatusTuple CodegenLLVM::visit_method_call_expr_node(MethodCallExprNode *n) { if (n->id_->sub_name_.size()) { if (n->id_->sub_name_ == "lookup") { TRY2(emit_table_lookup(n)); } else if (n->id_->sub_name_ == "update") { TRY2(emit_table_update(n)); } else if (n->id_->sub_name_ == "delete") { TRY2(emit_table_delete(n)); } else if (n->id_->sub_name_ == "rewrite_field" && n->id_->name_ == "pkt") { TRY2(emit_packet_rewrite_field(n)); } } else if (n->id_->name_ == "atomic_add") { TRY2(emit_atomic_add(n)); } else if (n->id_->name_ == "log") { TRY2(emit_log(n)); } else if (n->id_->name_ == "incr_cksum") { TRY2(emit_incr_cksum(n)); } else if (n->id_->name_ == "get_usec_time") { TRY2(emit_get_usec_time(n)); } else { return mkstatus_(n, "unsupported"); } TRY2(n->block_->accept(this)); return StatusTuple(0); } /* result = lookup(key) * if (!result) { * update(key, {0}, BPF_NOEXIST) * result = lookup(key) * } */ StatusTuple CodegenLLVM::visit_table_index_expr_node(TableIndexExprNode *n) { auto table_fd_it = table_fds_.find(n->table_); if (table_fd_it == table_fds_.end()) return mkstatus_(n, "unable to find table %s in table_fds_", n->id_->c_str()); Function *pseudo_fn = mod_->getFunction("llvm.bpf.pseudo"); if (!pseudo_fn) return mkstatus_(n, "pseudo fd loader doesn't exist"); Function *update_fn = mod_->getFunction("bpf_map_update_elem_"); if (!update_fn) return mkstatus_(n, "bpf_map_update_elem_ undefined"); Function *lookup_fn = mod_->getFunction("bpf_map_lookup_elem_"); if (!lookup_fn) return mkstatus_(n, "bpf_map_lookup_elem_ undefined"); StructType *leaf_type; TRY2(lookup_struct_type(n->table_->leaf_type_, &leaf_type)); PointerType *leaf_ptype = PointerType::getUnqual(leaf_type); CallInst *pseudo_call = B.CreateCall(pseudo_fn, vector({B.getInt64(BPF_PSEUDO_MAP_FD), B.getInt64(table_fd_it->second)})); Value *pseudo_map_fd = pseudo_call; TRY2(n->index_->accept(this)); Value *key_ptr = B.CreateBitCast(pop_expr(), B.getInt8PtrTy()); // result = lookup(key) Value *lookup1 = B.CreateBitCast(B.CreateCall(lookup_fn, vector({pseudo_map_fd, key_ptr})), leaf_ptype); Value *result = nullptr; if (n->table_->policy_id()->name_ == "AUTO") { Function *parent = B.GetInsertBlock()->getParent(); BasicBlock *label_start = B.GetInsertBlock(); BasicBlock *label_then = BasicBlock::Create(ctx(), n->id_->name_ + "[].then", parent); BasicBlock *label_end = BasicBlock::Create(ctx(), n->id_->name_ + "[].end", parent); Value *eq_zero = B.CreateIsNull(lookup1); B.CreateCondBr(eq_zero, label_then, label_end); B.SetInsertPoint(label_then); // var Leaf leaf {0} Value *leaf_ptr = B.CreateBitCast( make_alloca(resolve_entry_stack(), leaf_type), B.getInt8PtrTy()); #if LLVM_MAJOR_VERSION >= 10 B.CreateMemSet(leaf_ptr, B.getInt8(0), B.getInt64(n->table_->leaf_id()->bit_width_ >> 3), MaybeAlign(1)); #else B.CreateMemSet(leaf_ptr, B.getInt8(0), B.getInt64(n->table_->leaf_id()->bit_width_ >> 3), 1); #endif // update(key, leaf) B.CreateCall(update_fn, vector({pseudo_map_fd, key_ptr, leaf_ptr, B.getInt64(BPF_NOEXIST)})); // result = lookup(key) Value *lookup2 = B.CreateBitCast(B.CreateCall(lookup_fn, vector({pseudo_map_fd, key_ptr})), leaf_ptype); B.CreateBr(label_end); B.SetInsertPoint(label_end); PHINode *phi = B.CreatePHI(leaf_ptype, 2); phi->addIncoming(lookup1, label_start); phi->addIncoming(lookup2, label_then); result = phi; } else if (n->table_->policy_id()->name_ == "NONE") { result = lookup1; } if (n->is_lhs()) { if (n->sub_decl_) { Type *ptr_type = PointerType::getUnqual(B.getIntNTy(n->sub_decl_->bit_width_)); // u64 *errval -> uN *errval Value *err_cast = B.CreateBitCast(errval_, ptr_type); // if valid then &field, else &errval Function *parent = B.GetInsertBlock()->getParent(); BasicBlock *label_start = B.GetInsertBlock(); BasicBlock *label_then = BasicBlock::Create(ctx(), n->id_->name_ + "[]field.then", parent); BasicBlock *label_end = BasicBlock::Create(ctx(), n->id_->name_ + "[]field.end", parent); if (1) { // the PHI implementation of this doesn't load, maybe eBPF limitation? B.CreateCondBr(B.CreateIsNull(result), label_then, label_end); B.SetInsertPoint(label_then); B.CreateStore(B.getInt32(2), retval_); B.CreateBr(resolve_label("DONE")); B.SetInsertPoint(label_end); vector indices({B.getInt32(0), B.getInt32(n->sub_decl_->slot_)}); expr_ = B.CreateInBoundsGEP(result, indices); } else { B.CreateCondBr(B.CreateIsNotNull(result), label_then, label_end); B.SetInsertPoint(label_then); vector indices({B.getInt32(0), B.getInt32(n->sub_decl_->slot_)}); Value *field = B.CreateInBoundsGEP(result, indices); B.CreateBr(label_end); B.SetInsertPoint(label_end); PHINode *phi = B.CreatePHI(ptr_type, 2); phi->addIncoming(err_cast, label_start); phi->addIncoming(field, label_then); expr_ = phi; } } else { return mkstatus_(n, "unsupported"); } } else { expr_ = result; } return StatusTuple(0); } /// on_match StatusTuple CodegenLLVM::visit_match_decl_stmt_node(MatchDeclStmtNode *n) { if (n->formals_.size() != 1) return mkstatus_(n, "on_match expected 1 arguments, %zu given", n->formals_.size()); StructVariableDeclStmtNode* leaf_n = static_cast(n->formals_.at(0).get()); if (!leaf_n) return mkstatus_(n, "invalid parameter type"); // lookup result variable auto result_decl = scopes_->current_var()->lookup("_result", false); if (!result_decl) return mkstatus_(n, "unable to find _result built-in"); auto result = vars_.find(result_decl); if (result == vars_.end()) return mkstatus_(n, "unable to find memory for _result built-in"); vars_[leaf_n] = result->second; Value *load_1 = B.CreateLoad(result->second); Value *is_null = B.CreateIsNotNull(load_1); Function *parent = B.GetInsertBlock()->getParent(); BasicBlock *label_then = BasicBlock::Create(ctx(), "onvalid.then", parent); BasicBlock *label_end = BasicBlock::Create(ctx(), "onvalid.end", parent); B.CreateCondBr(is_null, label_then, label_end); { BlockStack bstack(this, label_then); TRY2(n->block_->accept(this)); if (!B.GetInsertBlock()->getTerminator()) B.CreateBr(label_end); } B.SetInsertPoint(label_end); return StatusTuple(0); } /// on_miss StatusTuple CodegenLLVM::visit_miss_decl_stmt_node(MissDeclStmtNode *n) { if (n->formals_.size() != 0) return mkstatus_(n, "on_match expected 0 arguments, %zu given", n->formals_.size()); auto result_decl = scopes_->current_var()->lookup("_result", false); if (!result_decl) return mkstatus_(n, "unable to find _result built-in"); auto result = vars_.find(result_decl); if (result == vars_.end()) return mkstatus_(n, "unable to find memory for _result built-in"); Value *load_1 = B.CreateLoad(result->second); Value *is_null = B.CreateIsNull(load_1); Function *parent = B.GetInsertBlock()->getParent(); BasicBlock *label_then = BasicBlock::Create(ctx(), "onvalid.then", parent); BasicBlock *label_end = BasicBlock::Create(ctx(), "onvalid.end", parent); B.CreateCondBr(is_null, label_then, label_end); { BlockStack bstack(this, label_then); TRY2(n->block_->accept(this)); if (!B.GetInsertBlock()->getTerminator()) B.CreateBr(label_end); } B.SetInsertPoint(label_end); return StatusTuple(0); } StatusTuple CodegenLLVM::visit_failure_decl_stmt_node(FailureDeclStmtNode *n) { return mkstatus_(n, "unsupported"); } StatusTuple CodegenLLVM::visit_expr_stmt_node(ExprStmtNode *n) { TRY2(n->expr_->accept(this)); expr_ = nullptr; return StatusTuple(0); } StatusTuple CodegenLLVM::visit_struct_variable_decl_stmt_node(StructVariableDeclStmtNode *n) { if (n->struct_id_->name_ == "" || n->struct_id_->name_[0] == '_') { return StatusTuple(0); } StructType *stype; StructDeclStmtNode *decl; TRY2(lookup_struct_type(n, &stype, &decl)); Type *ptr_stype = n->is_pointer() ? PointerType::getUnqual(stype) : (PointerType *)stype; AllocaInst *ptr_a = make_alloca(resolve_entry_stack(), ptr_stype); vars_[n] = ptr_a; if (n->struct_id_->scope_name_ == "proto") { if (n->is_pointer()) { ConstantPointerNull *const_null = ConstantPointerNull::get(cast(ptr_stype)); B.CreateStore(const_null, ptr_a); } else { return mkstatus_(n, "unsupported"); // string var = n->scope_id() + n->id_->name_; // /* zero initialize array to be filled in with packet header */ // emit("uint64_t __%s[%zu] = {}; uint8_t *%s = (uint8_t*)__%s;", // var.c_str(), ((decl->bit_width_ >> 3) + 7) >> 3, var.c_str(), var.c_str()); // for (auto it = n->init_.begin(); it != n->init_.end(); ++it) { // auto asn = static_cast(it->get()); // if (auto f = decl->field(asn->id_->sub_name_)) { // size_t bit_offset = f->bit_offset_; // size_t bit_width = f->bit_width_; // if (asn->bitop_) { // bit_offset += f->bit_width_ - (asn->bitop_->bit_offset_ + asn->bitop_->bit_width_); // bit_width = std::min(bit_width - asn->bitop_->bit_offset_, asn->bitop_->bit_width_); // } // emit(" bpf_dins(%s + %zu, %zu, %zu, ", var.c_str(), bit_offset >> 3, bit_offset & 0x7, bit_width); // TRY2(asn->rhs_->accept(this)); // emit(");"); // } // } } } else { if (n->is_pointer()) { if (n->id_->name_ == "_result") { // special case for capturing the return value of a previous method call Value *cast_1 = B.CreateBitCast(pop_expr(), ptr_stype); B.CreateStore(cast_1, ptr_a); } else { ConstantPointerNull *const_null = ConstantPointerNull::get(cast(ptr_stype)); B.CreateStore(const_null, ptr_a); } } else { #if LLVM_MAJOR_VERSION >= 10 B.CreateMemSet(ptr_a, B.getInt8(0), B.getInt64(decl->bit_width_ >> 3), MaybeAlign(1)); #else B.CreateMemSet(ptr_a, B.getInt8(0), B.getInt64(decl->bit_width_ >> 3), 1); #endif if (!n->init_.empty()) { for (auto it = n->init_.begin(); it != n->init_.end(); ++it) TRY2((*it)->accept(this)); } } } return StatusTuple(0); } StatusTuple CodegenLLVM::visit_integer_variable_decl_stmt_node(IntegerVariableDeclStmtNode *n) { if (!B.GetInsertBlock()) return StatusTuple(0); // uintX var = init AllocaInst *ptr_a = make_alloca(resolve_entry_stack(), B.getIntNTy(n->bit_width_), n->id_->name_); vars_[n] = ptr_a; // todo if (!n->init_.empty()) TRY2(n->init_[0]->accept(this)); return StatusTuple(0); } StatusTuple CodegenLLVM::visit_struct_decl_stmt_node(StructDeclStmtNode *n) { ++indent_; StructType *struct_type = StructType::create(ctx(), "_struct." + n->id_->name_); vector fields; for (auto it = n->stmts_.begin(); it != n->stmts_.end(); ++it) fields.push_back(B.getIntNTy((*it)->bit_width_)); struct_type->setBody(fields, n->is_packed()); structs_[n] = struct_type; return StatusTuple(0); } StatusTuple CodegenLLVM::visit_parser_state_stmt_node(ParserStateStmtNode *n) { string jump_label = n->scoped_name() + "_continue"; BasicBlock *label_entry = resolve_label(jump_label); B.SetInsertPoint(label_entry); if (n->next_state_) TRY2(n->next_state_->accept(this)); return StatusTuple(0); } StatusTuple CodegenLLVM::visit_state_decl_stmt_node(StateDeclStmtNode *n) { if (!n->id_) return StatusTuple(0); string jump_label = n->scoped_name(); BasicBlock *label_entry = resolve_label(jump_label); B.SetInsertPoint(label_entry); auto it = n->subs_.begin(); scopes_->push_state(it->scope_); for (auto in = n->init_.begin(); in != n->init_.end(); ++in) TRY2((*in)->accept(this)); if (n->subs_.size() == 1 && it->id_->name_ == "") { // this is not a multistate protocol, emit everything and finish TRY2(it->block_->accept(this)); if (n->parser_) { B.CreateBr(resolve_label(jump_label + "_continue")); TRY2(n->parser_->accept(this)); } } else { return mkstatus_(n, "unsupported"); } scopes_->pop_state(); return StatusTuple(0); } StatusTuple CodegenLLVM::visit_table_decl_stmt_node(TableDeclStmtNode *n) { if (n->table_type_->name_ == "Table" || n->table_type_->name_ == "SharedTable") { if (n->templates_.size() != 4) return mkstatus_(n, "%s expected 4 arguments, %zu given", n->table_type_->c_str(), n->templates_.size()); auto key = scopes_->top_struct()->lookup(n->key_id()->name_, /*search_local*/true); if (!key) return mkstatus_(n, "cannot find key %s", n->key_id()->name_.c_str()); auto leaf = scopes_->top_struct()->lookup(n->leaf_id()->name_, /*search_local*/true); if (!leaf) return mkstatus_(n, "cannot find leaf %s", n->leaf_id()->name_.c_str()); bpf_map_type map_type = BPF_MAP_TYPE_UNSPEC; if (n->type_id()->name_ == "FIXED_MATCH") map_type = BPF_MAP_TYPE_HASH; else if (n->type_id()->name_ == "INDEXED") map_type = BPF_MAP_TYPE_ARRAY; else return mkstatus_(n, "Table type %s not implemented", n->type_id()->name_.c_str()); StructType *key_stype, *leaf_stype; TRY2(lookup_struct_type(n->key_type_, &key_stype)); TRY2(lookup_struct_type(n->leaf_type_, &leaf_stype)); StructType *decl_struct = mod_->getTypeByName("_struct." + n->id_->name_); if (!decl_struct) decl_struct = StructType::create(ctx(), "_struct." + n->id_->name_); if (decl_struct->isOpaque()) decl_struct->setBody(vector({key_stype, leaf_stype}), /*isPacked=*/false); GlobalVariable *decl_gvar = new GlobalVariable(*mod_, decl_struct, false, GlobalValue::ExternalLinkage, 0, n->id_->name_); decl_gvar->setSection("maps"); tables_[n] = decl_gvar; int map_fd = bcc_create_map(map_type, n->id_->name_.c_str(), key->bit_width_ / 8, leaf->bit_width_ / 8, n->size_, 0); if (map_fd >= 0) table_fds_[n] = map_fd; } else { return mkstatus_(n, "Table %s not implemented", n->table_type_->name_.c_str()); } return StatusTuple(0); } StatusTuple CodegenLLVM::lookup_struct_type(StructDeclStmtNode *decl, StructType **stype) const { auto struct_it = structs_.find(decl); if (struct_it == structs_.end()) return mkstatus_(decl, "could not find IR for type %s", decl->id_->c_str()); *stype = struct_it->second; return StatusTuple(0); } StatusTuple CodegenLLVM::lookup_struct_type(VariableDeclStmtNode *n, StructType **stype, StructDeclStmtNode **decl) const { if (!n->is_struct()) return mkstatus_(n, "attempt to search for struct with a non-struct type %s", n->id_->c_str()); auto var = (StructVariableDeclStmtNode *)n; StructDeclStmtNode *type; if (var->struct_id_->scope_name_ == "proto") type = proto_scopes_->top_struct()->lookup(var->struct_id_->name_, true); else type = scopes_->top_struct()->lookup(var->struct_id_->name_, true); if (!type) return mkstatus_(n, "could not find type %s", var->struct_id_->c_str()); TRY2(lookup_struct_type(type, stype)); if (decl) *decl = type; return StatusTuple(0); } StatusTuple CodegenLLVM::visit_func_decl_stmt_node(FuncDeclStmtNode *n) { if (n->formals_.size() != 1) return mkstatus_(n, "Functions must have exactly 1 argument, %zd given", n->formals_.size()); vector formals; for (auto it = n->formals_.begin(); it != n->formals_.end(); ++it) { VariableDeclStmtNode *formal = it->get(); if (formal->is_struct()) { StructType *stype; //TRY2(lookup_struct_type(formal, &stype)); auto var = (StructVariableDeclStmtNode *)formal; stype = mod_->getTypeByName("_struct." + var->struct_id_->name_); if (!stype) return mkstatus_(n, "could not find type %s", var->struct_id_->c_str()); formals.push_back(PointerType::getUnqual(stype)); } else { formals.push_back(B.getIntNTy(formal->bit_width_)); } } FunctionType *fn_type = FunctionType::get(B.getInt32Ty(), formals, /*isVarArg=*/false); Function *fn = mod_->getFunction(n->id_->name_); if (fn) return mkstatus_(n, "Function %s already defined", n->id_->c_str()); fn = Function::Create(fn_type, GlobalValue::ExternalLinkage, n->id_->name_, mod_); fn->setCallingConv(CallingConv::C); fn->addFnAttr(Attribute::NoUnwind); fn->setSection(BPF_FN_PREFIX + n->id_->name_); BasicBlock *label_entry = BasicBlock::Create(ctx(), "entry", fn); B.SetInsertPoint(label_entry); string scoped_entry_label = to_string((uintptr_t)fn) + "::entry"; labels_[scoped_entry_label] = label_entry; BasicBlock *label_return = resolve_label("DONE"); retval_ = make_alloca(label_entry, fn->getReturnType(), "ret"); B.CreateStore(B.getInt32(0), retval_); errval_ = make_alloca(label_entry, B.getInt64Ty(), "err"); B.CreateStore(B.getInt64(0), errval_); auto formal = n->formals_.begin(); for (auto arg = fn->arg_begin(); arg != fn->arg_end(); ++arg, ++formal) { TRY2((*formal)->accept(this)); Value *ptr = vars_[formal->get()]; if (!ptr) return mkstatus_(n, "cannot locate memory location for arg %s", (*formal)->id_->c_str()); B.CreateStore(&*arg, ptr); // Type *ptype; // if ((*formal)->is_struct()) { // StructType *type; // TRY2(lookup_struct_type(formal->get(), &type)); // ptype = PointerType::getUnqual(type); // } else { // ptype = PointerType::getUnqual(B.getIntNTy((*formal)->bit_width_)); // } // arg->setName((*formal)->id_->name_); // AllocaInst *ptr = make_alloca(label_entry, ptype, (*formal)->id_->name_); // B.CreateStore(arg, ptr); // vars_[formal->get()] = ptr; } // visit function scoped variables { scopes_->push_state(n->scope_); for (auto it = scopes_->current_var()->obegin(); it != scopes_->current_var()->oend(); ++it) TRY2((*it)->accept(this)); TRY2(n->block_->accept(this)); scopes_->pop_state(); if (!B.GetInsertBlock()->getTerminator()) B.CreateBr(resolve_label("DONE")); // always return something B.SetInsertPoint(label_return); B.CreateRet(B.CreateLoad(retval_)); } return StatusTuple(0); } StatusTuple CodegenLLVM::visit(Node *root, TableStorage &ts, const string &id, const string &maps_ns) { scopes_->set_current(scopes_->top_state()); scopes_->set_current(scopes_->top_var()); TRY2(print_header()); for (auto it = scopes_->top_table()->obegin(); it != scopes_->top_table()->oend(); ++it) TRY2((*it)->accept(this)); for (auto it = scopes_->top_func()->obegin(); it != scopes_->top_func()->oend(); ++it) TRY2((*it)->accept(this)); //TRY2(print_parser()); for (auto table : tables_) { bpf_map_type map_type = BPF_MAP_TYPE_UNSPEC; if (table.first->type_id()->name_ == "FIXED_MATCH") map_type = BPF_MAP_TYPE_HASH; else if (table.first->type_id()->name_ == "INDEXED") map_type = BPF_MAP_TYPE_ARRAY; ts.Insert(Path({id, table.first->id_->name_}), { table.first->id_->name_, FileDesc(table_fds_[table.first]), map_type, table.first->key_type_->bit_width_ >> 3, table.first->leaf_type_->bit_width_ >> 3, table.first->size_, 0, }); } return StatusTuple(0); } StatusTuple CodegenLLVM::print_header() { GlobalVariable *gvar_license = new GlobalVariable(*mod_, ArrayType::get(Type::getInt8Ty(ctx()), 4), false, GlobalValue::ExternalLinkage, 0, "_license"); gvar_license->setSection("license"); gvar_license->setInitializer(ConstantDataArray::getString(ctx(), "GPL", true)); Function *pseudo_fn = mod_->getFunction("llvm.bpf.pseudo"); if (!pseudo_fn) { pseudo_fn = Function::Create( FunctionType::get(B.getInt64Ty(), vector({B.getInt64Ty(), B.getInt64Ty()}), false), GlobalValue::ExternalLinkage, "llvm.bpf.pseudo", mod_); } // declare structures for (auto it = scopes_->top_struct()->obegin(); it != scopes_->top_struct()->oend(); ++it) { if ((*it)->id_->name_ == "_Packet") continue; TRY2((*it)->accept(this)); } for (auto it = proto_scopes_->top_struct()->obegin(); it != proto_scopes_->top_struct()->oend(); ++it) { if ((*it)->id_->name_ == "_Packet") continue; TRY2((*it)->accept(this)); } return StatusTuple(0); } int CodegenLLVM::get_table_fd(const string &name) const { TableDeclStmtNode *table = scopes_->top_table()->lookup(name); if (!table) return -1; auto table_fd_it = table_fds_.find(table); if (table_fd_it == table_fds_.end()) return -1; return table_fd_it->second; } LLVMContext & CodegenLLVM::ctx() const { return mod_->getContext(); } Constant * CodegenLLVM::const_int(uint64_t val, unsigned bits, bool is_signed) { return ConstantInt::get(ctx(), APInt(bits, val, is_signed)); } Value * CodegenLLVM::pop_expr() { Value *ret = expr_; expr_ = nullptr; return ret; } BasicBlock * CodegenLLVM::resolve_label(const string &label) { Function *parent = B.GetInsertBlock()->getParent(); string scoped_label = to_string((uintptr_t)parent) + "::" + label; auto it = labels_.find(scoped_label); if (it != labels_.end()) return it->second; BasicBlock *label_new = BasicBlock::Create(ctx(), label, parent); labels_[scoped_label] = label_new; return label_new; } Instruction * CodegenLLVM::resolve_entry_stack() { BasicBlock *label_entry = resolve_label("entry"); return &label_entry->back(); } AllocaInst *CodegenLLVM::make_alloca(Instruction *Inst, Type *Ty, const string &name, Value *ArraySize) { IRBuilderBase::InsertPoint ip = B.saveIP(); B.SetInsertPoint(Inst); AllocaInst *a = B.CreateAlloca(Ty, ArraySize, name); B.restoreIP(ip); return a; } AllocaInst *CodegenLLVM::make_alloca(BasicBlock *BB, Type *Ty, const string &name, Value *ArraySize) { IRBuilderBase::InsertPoint ip = B.saveIP(); B.SetInsertPoint(BB); AllocaInst *a = B.CreateAlloca(Ty, ArraySize, name); B.restoreIP(ip); return a; } } // namespace cc } // namespace ebpf bpfcc-0.12.0/src/cc/frontends/b/codegen_llvm.h000066400000000000000000000104301357404205000210530ustar00rootroot00000000000000/* * Copyright (c) 2015 PLUMgrid, Inc. * * 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. */ #pragma once #include #include #include #include #include #include "node.h" #include "scope.h" namespace llvm { class AllocaInst; class BasicBlock; class BranchInst; class Constant; class Instruction; class IRBuilderBase; class LLVMContext; class Module; class StructType; class SwitchInst; class Type; class Value; class GlobalVariable; } namespace ebpf { class TableStorage; namespace cc { class BlockStack; class SwitchStack; using std::vector; using std::string; using std::set; class CodegenLLVM : public Visitor { friend class BlockStack; friend class SwitchStack; public: CodegenLLVM(llvm::Module *mod, Scopes *scopes, Scopes *proto_scopes); virtual ~CodegenLLVM(); #define VISIT(type, func) virtual STATUS_RETURN visit_##func(type* n); EXPAND_NODES(VISIT) #undef VISIT STATUS_RETURN visit(Node *n, TableStorage &ts, const std::string &id, const std::string &maps_ns); int get_table_fd(const std::string &name) const; private: STATUS_RETURN emit_short_circuit_and(BinopExprNode* n); STATUS_RETURN emit_short_circuit_or(BinopExprNode* n); STATUS_RETURN emit_table_lookup(MethodCallExprNode* n); STATUS_RETURN emit_table_update(MethodCallExprNode* n); STATUS_RETURN emit_table_delete(MethodCallExprNode* n); STATUS_RETURN emit_log(MethodCallExprNode* n); STATUS_RETURN emit_packet_rewrite_field(MethodCallExprNode* n); STATUS_RETURN emit_atomic_add(MethodCallExprNode* n); STATUS_RETURN emit_cksum(MethodCallExprNode* n); STATUS_RETURN emit_incr_cksum(MethodCallExprNode* n, size_t sz = 0); STATUS_RETURN emit_lb_hash(MethodCallExprNode* n); STATUS_RETURN emit_sizeof(MethodCallExprNode* n); STATUS_RETURN emit_get_usec_time(MethodCallExprNode* n); STATUS_RETURN emit_forward_to_vnf(MethodCallExprNode* n); STATUS_RETURN emit_forward_to_group(MethodCallExprNode* n); STATUS_RETURN print_header(); llvm::LLVMContext & ctx() const; llvm::Constant * const_int(uint64_t val, unsigned bits = 64, bool is_signed = false); llvm::Value * pop_expr(); llvm::BasicBlock * resolve_label(const string &label); llvm::Instruction * resolve_entry_stack(); llvm::AllocaInst *make_alloca(llvm::Instruction *Inst, llvm::Type *Ty, const std::string &name = "", llvm::Value *ArraySize = nullptr); llvm::AllocaInst *make_alloca(llvm::BasicBlock *BB, llvm::Type *Ty, const std::string &name = "", llvm::Value *ArraySize = nullptr); StatusTuple lookup_var(Node *n, const std::string &name, Scopes::VarScope *scope, VariableDeclStmtNode **decl, llvm::Value **mem) const; StatusTuple lookup_struct_type(StructDeclStmtNode *decl, llvm::StructType **stype) const; StatusTuple lookup_struct_type(VariableDeclStmtNode *n, llvm::StructType **stype, StructDeclStmtNode **decl = nullptr) const; template void emit(const char *fmt, Args&&... params); void emit(const char *s); FILE* out_; llvm::Module* mod_; llvm::IRBuilderBase *b_; int indent_; int tmp_reg_index_; Scopes *scopes_; Scopes *proto_scopes_; vector > free_instructions_; vector table_inits_; map proto_rewrites_; map tables_; map table_fds_; map vars_; map structs_; map labels_; llvm::SwitchInst *cur_switch_; llvm::Value *expr_; llvm::AllocaInst *retval_; llvm::AllocaInst *errval_; }; } // namespace cc } // namespace ebpf bpfcc-0.12.0/src/cc/frontends/b/lexer.h000066400000000000000000000070251357404205000175420ustar00rootroot00000000000000/* * Copyright (c) 2015 PLUMgrid, Inc. * * 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. */ #pragma once #ifndef yyFlexLexerOnce #undef yyFlexLexer #define yyFlexLexer ebpfccFlexLexer #include #endif #undef YY_DECL #define YY_DECL int ebpf::cc::Lexer::yylex() #include // NOLINT #include #include "parser.yy.hh" namespace ebpf { namespace cc { typedef BisonParser::token::yytokentype Tok; class Lexer : public yyFlexLexer { public: explicit Lexer(std::istream* in) : yyFlexLexer(in), prev_tok_(Tok::TSEMI), lines_({""}), yylval_(NULL), yylloc_(NULL) { if (!in || !*in) fprintf(stderr, "Unable to open input stream\n"); } int yylex(BisonParser::semantic_type *lval, BisonParser::location_type *lloc) { yylval_ = lval; yylloc_ = lloc; return yylex(); } std::string text(const BisonParser::location_type& loc) const { return text(loc.begin, loc.end); } std::string text(const position& begin, const position& end) const { std::string result; for (size_t i = begin.line; i <= end.line; ++i) { if (i == begin.line && i == end.line) { result += lines_.at(i - 1).substr(begin.column - 1, end.column - begin.column); } else if (i == begin.line && i < end.line) { result += lines_.at(i - 1).substr(begin.column - 1); } else if (i > begin.line && i == end.line) { result += lines_.at(i - 1).substr(0, end.column); } else if (i > begin.line && i == end.line) { result += lines_.at(i - 1); } } return result; } private: // true if a semicolon should be replaced here bool next_line() { lines_.push_back(""); yylloc_->lines(); yylloc_->step(); switch (prev_tok_) { case Tok::TIDENTIFIER: case Tok::TINTEGER: case Tok::THEXINTEGER: case Tok::TRBRACE: case Tok::TRPAREN: case Tok::TRBRACK: case Tok::TTRUE: case Tok::TFALSE: // uncomment to add implicit semicolons //return true; default: break; } return false; } Tok save(Tok tok, bool ignore_text = false) { if (!ignore_text) { save_text(); } switch (tok) { case Tok::TIDENTIFIER: case Tok::TINTEGER: case Tok::THEXINTEGER: yylval_->string = new std::string(yytext, yyleng); break; default: yylval_->token = tok; } prev_tok_ = tok; return tok; } /* std::string * alloc_string(const char *c, size_t len) { strings_.push_back(std::unique_ptr(new std::string(c, len))); return strings_.back().get(); } std::string * alloc_string(const std::string &s) { strings_.push_back(std::unique_ptr(new std::string(s))); return strings_.back().get(); } */ void save_text() { lines_.back().append(yytext, yyleng); yylloc_->columns(yyleng); } int yylex(); Tok prev_tok_; std::vector lines_; //std::list> strings_; BisonParser::semantic_type *yylval_; BisonParser::location_type *yylloc_; }; } // namespace cc } // namespace ebpf bpfcc-0.12.0/src/cc/frontends/b/lexer.ll000066400000000000000000000104331357404205000177170ustar00rootroot00000000000000/* * Copyright (c) 2015 PLUMgrid, Inc. * * 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. */ %{ #include "lexer.h" %} %option yylineno nodefault yyclass="Lexer" noyywrap c++ prefix="ebpfcc" %option never-interactive %{ #include #include "parser.yy.hh" std::string tmp_str_cc; %} %x STRING_ %% \" {BEGIN STRING_;} \" { BEGIN 0; yylval_->string = new std::string(tmp_str_cc); tmp_str_cc = ""; return Tok::TSTRING; } \\n {tmp_str_cc += "\n"; } . {tmp_str_cc += *yytext; } [ \t]+ { save_text(); } \n { if (next_line()) { return save(Tok::TSEMI, true); } } "//".*\n { if (next_line()) { return save(Tok::TSEMI, true); } } ^"#" return save(Tok::TPRAGMA); "=" return save(Tok::TEQUAL); "==" return save(Tok::TCEQ); "!=" return save(Tok::TCNE); "<" return save(Tok::TCLT); "<=" return save(Tok::TCLE); ">" return save(Tok::TCGT); ">=" return save(Tok::TCGE); "(" return save(Tok::TLPAREN); ")" return save(Tok::TRPAREN); "{" return save(Tok::TLBRACE); "}" return save(Tok::TRBRACE); "[" return save(Tok::TLBRACK); "]" return save(Tok::TRBRACK); "->" return save(Tok::TARROW); "." return save(Tok::TDOT); "," return save(Tok::TCOMMA); "+" return save(Tok::TPLUS); "++" return save(Tok::TINCR); "-" return save(Tok::TMINUS); "--" return save(Tok::TDECR); "*" return save(Tok::TMUL); "/" return save(Tok::TDIV); "%" return save(Tok::TMOD); "^" return save(Tok::TXOR); "$" return save(Tok::TDOLLAR); "!" return save(Tok::TNOT); "~" return save(Tok::TCMPL); ":" return save(Tok::TCOLON); "::" return save(Tok::TSCOPE); ";" return save(Tok::TSEMI); "&&" return save(Tok::TAND); "||" return save(Tok::TOR); "&" return save(Tok::TLAND); "|" return save(Tok::TLOR); "@" return save(Tok::TAT); "case" return save(Tok::TCASE); "continue" return save(Tok::TCONTINUE); "else" return save(Tok::TELSE); "false" return save(Tok::TFALSE); "goto" return save(Tok::TGOTO); "if" return save(Tok::TIF); "next" return save(Tok::TNEXT); "on_match" return save(Tok::TMATCH); "on_miss" return save(Tok::TMISS); "on_failure" return save(Tok::TFAILURE); "on_valid" return save(Tok::TVALID); "return" return save(Tok::TRETURN); "state" return save(Tok::TSTATE); "struct" return save(Tok::TSTRUCT); "switch" return save(Tok::TSWITCH); "true" return save(Tok::TTRUE); "u8" return save(Tok::TU8); "u16" return save(Tok::TU16); "u32" return save(Tok::TU32); "u64" return save(Tok::TU64); [a-zA-Z_][a-zA-Z0-9_]* return save(Tok::TIDENTIFIER); [0-9]+ return save(Tok::TINTEGER); 0x[0-9a-fA-F]+ return save(Tok::THEXINTEGER); . printf("Unknown token \"%s\"\n", yytext); yyterminate(); %% bpfcc-0.12.0/src/cc/frontends/b/loader.cc000066400000000000000000000041331357404205000200240ustar00rootroot00000000000000/* * Copyright (c) 2015 PLUMgrid, Inc. * * 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. */ #include "parser.h" #include "type_check.h" #include "codegen_llvm.h" #include "loader.h" using std::string; using std::unique_ptr; using std::vector; namespace ebpf { BLoader::BLoader(unsigned flags) : flags_(flags) { (void)flags_; } BLoader::~BLoader() { } int BLoader::parse(llvm::Module *mod, const string &filename, const string &proto_filename, TableStorage &ts, const string &id, const std::string &maps_ns) { int rc; proto_parser_ = make_unique(proto_filename); rc = proto_parser_->parse(); if (rc) { fprintf(stderr, "In file: %s\n", filename.c_str()); return rc; } parser_ = make_unique(filename); rc = parser_->parse(); if (rc) { fprintf(stderr, "In file: %s\n", filename.c_str()); return rc; } //ebpf::cc::Printer printer(stderr); //printer.visit(parser_->root_node_); ebpf::cc::TypeCheck type_check(parser_->scopes_.get(), proto_parser_->scopes_.get()); auto ret = type_check.visit(parser_->root_node_); if (ret.code() != 0 || ret.msg().size()) { fprintf(stderr, "Type error @line=%d: %s\n", ret.code(), ret.msg().c_str()); return -1; } codegen_ = ebpf::make_unique(mod, parser_->scopes_.get(), proto_parser_->scopes_.get()); ret = codegen_->visit(parser_->root_node_, ts, id, maps_ns); if (ret.code() != 0 || ret.msg().size()) { fprintf(stderr, "Codegen error @line=%d: %s\n", ret.code(), ret.msg().c_str()); return ret.code(); } return 0; } } // namespace ebpf bpfcc-0.12.0/src/cc/frontends/b/loader.h000066400000000000000000000023201357404205000176620ustar00rootroot00000000000000/* * Copyright (c) 2015 PLUMgrid, Inc. * * 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. */ #pragma once #include #include #include #include "table_storage.h" namespace llvm { class Module; } namespace ebpf { namespace cc { class Parser; class CodegenLLVM; } class BLoader { public: explicit BLoader(unsigned flags); ~BLoader(); int parse(llvm::Module *mod, const std::string &filename, const std::string &proto_filename, TableStorage &ts, const std::string &id, const std::string &maps_ns); private: unsigned flags_; std::unique_ptr parser_; std::unique_ptr proto_parser_; std::unique_ptr codegen_; }; } // namespace ebpf bpfcc-0.12.0/src/cc/frontends/b/node.cc000066400000000000000000000024631357404205000175070ustar00rootroot00000000000000/* * Copyright (c) 2015 PLUMgrid, Inc. * * 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. */ #include #include #include #include "node.h" namespace ebpf { namespace cc { #define ACCEPT(type, func) \ STATUS_RETURN type::accept(Visitor* v) { return v->visit_##func(this); } EXPAND_NODES(ACCEPT) #undef ACCEPT VariableDeclStmtNode* StructDeclStmtNode::field(const string& name) const { for (auto it = stmts_.begin(); it != stmts_.end(); ++it) { if ((*it)->id_->name_ == name) { return it->get(); } } return NULL; } int StructDeclStmtNode::indexof(const string& name) const { int i = 0; for (auto it = stmts_.begin(); it != stmts_.end(); ++it, ++i) { if ((*it)->id_->name_ == name) { return i; } } return -1; } } // namespace cc } // namespace ebpf bpfcc-0.12.0/src/cc/frontends/b/node.h000066400000000000000000000440301357404205000173450ustar00rootroot00000000000000/* * Copyright (c) 2015 PLUMgrid, Inc. * * 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. */ #pragma once #include #include #include #include #include #include #include "common.h" #include "bcc_exception.h" #include "scope.h" #define REVISION_MASK 0xfff #define MAJOR_VER_POS 22 #define MAJOR_VER_MASK ~((1 << MAJOR_VER_POS) - 1) #define MINOR_VER_POS 12 #define MINOR_VER_MASK (~((1 << MINOR_VER_POS) - 1) & (~(MAJOR_VER_MASK))) #define GET_MAJOR_VER(version) ((version & MAJOR_VER_MASK) >> MAJOR_VER_POS) #define GET_MINOR_VER(version) ((version & MINOR_VER_MASK) >> MINOR_VER_POS) #define GET_REVISION(version) (version & REVISION_MASK) #define MAKE_VERSION(major, minor, rev) \ ((major << MAJOR_VER_POS) | \ (minor << MINOR_VER_POS) | \ (rev & REVISION_MASK)) #define STATUS_RETURN __attribute((warn_unused_result)) StatusTuple namespace ebpf { namespace cc { using std::unique_ptr; using std::move; using std::string; using std::vector; using std::bitset; using std::find; typedef unique_ptr String; #define NODE_EXPRESSIONS(EXPAND) \ EXPAND(IdentExprNode, ident_expr_node) \ EXPAND(AssignExprNode, assign_expr_node) \ EXPAND(PacketExprNode, packet_expr_node) \ EXPAND(IntegerExprNode, integer_expr_node) \ EXPAND(StringExprNode, string_expr_node) \ EXPAND(BinopExprNode, binop_expr_node) \ EXPAND(UnopExprNode, unop_expr_node) \ EXPAND(BitopExprNode, bitop_expr_node) \ EXPAND(GotoExprNode, goto_expr_node) \ EXPAND(ReturnExprNode, return_expr_node) \ EXPAND(MethodCallExprNode, method_call_expr_node) \ EXPAND(TableIndexExprNode, table_index_expr_node) #define NODE_STATEMENTS(EXPAND) \ EXPAND(ExprStmtNode, expr_stmt_node) \ EXPAND(BlockStmtNode, block_stmt_node) \ EXPAND(IfStmtNode, if_stmt_node) \ EXPAND(OnValidStmtNode, onvalid_stmt_node) \ EXPAND(SwitchStmtNode, switch_stmt_node) \ EXPAND(CaseStmtNode, case_stmt_node) \ EXPAND(StructVariableDeclStmtNode, struct_variable_decl_stmt_node) \ EXPAND(IntegerVariableDeclStmtNode, integer_variable_decl_stmt_node) \ EXPAND(StructDeclStmtNode, struct_decl_stmt_node) \ EXPAND(StateDeclStmtNode, state_decl_stmt_node) \ EXPAND(ParserStateStmtNode, parser_state_stmt_node) \ EXPAND(MatchDeclStmtNode, match_decl_stmt_node) \ EXPAND(MissDeclStmtNode, miss_decl_stmt_node) \ EXPAND(FailureDeclStmtNode, failure_decl_stmt_node) \ EXPAND(TableDeclStmtNode, table_decl_stmt_node) \ EXPAND(FuncDeclStmtNode, func_decl_stmt_node) #define EXPAND_NODES(EXPAND) \ NODE_EXPRESSIONS(EXPAND) \ NODE_STATEMENTS(EXPAND) class Visitor; // forward declare all classes #define FORWARD(type, func) class type; EXPAND_NODES(FORWARD) #undef FORWARD #define DECLARE(type) \ typedef unique_ptr Ptr; \ virtual StatusTuple accept(Visitor* v); class Node { public: typedef unique_ptr Ptr; Node() : line_(-1), column_(-1) {} virtual ~Node() {} virtual StatusTuple accept(Visitor* v) = 0; int line_; int column_; string text_; }; template StatusTuple mkstatus_(Node *n, const char *fmt, Args... args) { StatusTuple status = StatusTuple(n->line_ ? n->line_ : -1, fmt, args...); if (n->line_ > 0) status.append_msg("\n" + n->text_); return status; } static inline StatusTuple mkstatus_(Node *n, const char *msg) { StatusTuple status = StatusTuple(n->line_ ? n->line_ : -1, msg); if (n->line_ > 0) status.append_msg("\n" + n->text_); return status; } class StmtNode : public Node { public: typedef unique_ptr Ptr; virtual StatusTuple accept(Visitor* v) = 0; }; typedef vector StmtNodeList; class ExprNode : public Node { public: typedef unique_ptr Ptr; virtual StatusTuple accept(Visitor* v) = 0; enum expr_type { STRUCT, INTEGER, STRING, VOID, UNKNOWN }; enum prop_flag { READ = 0, WRITE, PROTO, IS_LHS, IS_REF, IS_PKT, LAST }; expr_type typeof_; StructDeclStmtNode *struct_type_; size_t bit_width_; bitset flags_; unique_ptr bitop_; ExprNode() : typeof_(UNKNOWN), struct_type_(NULL), flags_(1 << READ) {} void copy_type(const ExprNode& other) { typeof_ = other.typeof_; struct_type_ = other.struct_type_; bit_width_ = other.bit_width_; flags_ = other.flags_; } bool is_lhs() const { return flags_[IS_LHS]; } bool is_ref() const { return flags_[IS_REF]; } bool is_pkt() const { return flags_[IS_PKT]; } }; typedef vector ExprNodeList; class IdentExprNode : public ExprNode { public: DECLARE(IdentExprNode) string name_; string sub_name_; string scope_name_; VariableDeclStmtNode *decl_; VariableDeclStmtNode *sub_decl_; IdentExprNode(const IdentExprNode& other) { name_ = other.name_; sub_name_ = other.sub_name_; scope_name_ = other.scope_name_; decl_ = other.decl_; sub_decl_ = other.sub_decl_; } IdentExprNode::Ptr copy() const { return IdentExprNode::Ptr(new IdentExprNode(*this)); } explicit IdentExprNode(const string& id) : name_(id) {} explicit IdentExprNode(const char* id) : name_(id) {} void prepend_scope(const string& id) { scope_name_ = id; } void append_scope(const string& id) { scope_name_ = move(name_); name_ = id; } void prepend_dot(const string& id) { sub_name_ = move(name_); name_ = id; } void append_dot(const string& id) { // we don't support nested struct so keep all subs as single variable if (!sub_name_.empty()) { sub_name_ += "." + id; } else { sub_name_ = id; } } const string& full_name() { if (full_name_.size()) { return full_name_; // lazy init } if (scope_name_.size()) { full_name_ += scope_name_ + "::"; } full_name_ += name_; if (sub_name_.size()) { full_name_ += "." + sub_name_; } return full_name_; } const char* c_str() const { return name_.c_str(); } private: string full_name_; }; class BitopExprNode : public ExprNode { public: DECLARE(BitopExprNode) ExprNode::Ptr expr_; size_t bit_offset_; size_t bit_width_; BitopExprNode(const string& bofs, const string& bsz) : bit_offset_(strtoul(bofs.c_str(), NULL, 0)), bit_width_(strtoul(bsz.c_str(), NULL, 0)) {} }; typedef vector IdentExprNodeList; class AssignExprNode : public ExprNode { public: DECLARE(AssignExprNode) //IdentExprNode *id_; ExprNode::Ptr lhs_; ExprNode::Ptr rhs_; AssignExprNode(IdentExprNode::Ptr id, ExprNode::Ptr rhs) : lhs_(move(id)), rhs_(move(rhs)) { //id_ = (IdentExprNode *)lhs_.get(); lhs_->flags_[ExprNode::IS_LHS] = true; } AssignExprNode(ExprNode::Ptr lhs, ExprNode::Ptr rhs) : lhs_(move(lhs)), rhs_(move(rhs)) { //id_ = nullptr; lhs_->flags_[ExprNode::IS_LHS] = true; } }; class PacketExprNode : public ExprNode { public: DECLARE(PacketExprNode) IdentExprNode::Ptr id_; explicit PacketExprNode(IdentExprNode::Ptr id) : id_(move(id)) {} }; class StringExprNode : public ExprNode { public: DECLARE(StringExprNode) string val_; explicit StringExprNode(string *val) : val_(move(*val)) { delete val; } explicit StringExprNode(const string &val) : val_(val) {} }; class IntegerExprNode : public ExprNode { public: DECLARE(IntegerExprNode) size_t bits_; string val_; IntegerExprNode(string* val, string* bits) : bits_(strtoul(bits->c_str(), NULL, 0)), val_(move(*val)) { delete val; delete bits; } explicit IntegerExprNode(string* val) : bits_(0), val_(move(*val)) { delete val; } explicit IntegerExprNode(const string& val) : bits_(0), val_(val) {} explicit IntegerExprNode(const string& val, size_t bits) : bits_(bits), val_(val) {} }; class BinopExprNode : public ExprNode { public: DECLARE(BinopExprNode) ExprNode::Ptr lhs_; int op_; ExprNode::Ptr rhs_; BinopExprNode(ExprNode::Ptr lhs, int op, ExprNode::Ptr rhs) : lhs_(move(lhs)), op_(op), rhs_(move(rhs)) {} }; class UnopExprNode : public ExprNode { public: DECLARE(UnopExprNode) ExprNode::Ptr expr_; int op_; UnopExprNode(int op, ExprNode::Ptr expr) : expr_(move(expr)), op_(op) {} }; class GotoExprNode : public ExprNode { public: DECLARE(GotoExprNode) bool is_continue_; IdentExprNode::Ptr id_; GotoExprNode(IdentExprNode::Ptr id, bool is_continue = false) : is_continue_(is_continue), id_(move(id)) {} }; class ReturnExprNode : public ExprNode { public: DECLARE(ReturnExprNode) ExprNode::Ptr expr_; ReturnExprNode(ExprNode::Ptr expr) : expr_(move(expr)) {} }; class BlockStmtNode : public StmtNode { public: DECLARE(BlockStmtNode) explicit BlockStmtNode(StmtNodeList stmts = StmtNodeList()) : stmts_(move(stmts)), scope_(NULL) {} ~BlockStmtNode() { delete scope_; } StmtNodeList stmts_; Scopes::VarScope* scope_; }; class MethodCallExprNode : public ExprNode { public: DECLARE(MethodCallExprNode) IdentExprNode::Ptr id_; ExprNodeList args_; BlockStmtNode::Ptr block_; MethodCallExprNode(IdentExprNode::Ptr id, ExprNodeList&& args, int lineno) : id_(move(id)), args_(move(args)), block_(make_unique()) { line_ = lineno; } }; class TableIndexExprNode : public ExprNode { public: DECLARE(TableIndexExprNode) IdentExprNode::Ptr id_; IdentExprNode::Ptr sub_; ExprNode::Ptr index_; TableDeclStmtNode *table_; VariableDeclStmtNode *sub_decl_; TableIndexExprNode(IdentExprNode::Ptr id, ExprNode::Ptr index) : id_(move(id)), index_(move(index)), table_(nullptr), sub_decl_(nullptr) {} }; class ExprStmtNode : public StmtNode { public: DECLARE(ExprStmtNode) ExprNode::Ptr expr_; explicit ExprStmtNode(ExprNode::Ptr expr) : expr_(move(expr)) {} }; class IfStmtNode : public StmtNode { public: DECLARE(IfStmtNode) ExprNode::Ptr cond_; StmtNode::Ptr true_block_; StmtNode::Ptr false_block_; // create an if () {} expression IfStmtNode(ExprNode::Ptr cond, StmtNode::Ptr true_block) : cond_(move(cond)), true_block_(move(true_block)) {} // create an if () {} else {} expression IfStmtNode(ExprNode::Ptr cond, StmtNode::Ptr true_block, StmtNode::Ptr false_block) : cond_(move(cond)), true_block_(move(true_block)), false_block_(move(false_block)) {} }; class OnValidStmtNode : public StmtNode { public: DECLARE(OnValidStmtNode) IdentExprNode::Ptr cond_; StmtNode::Ptr block_; StmtNode::Ptr else_block_; // create an onvalid () {} expression OnValidStmtNode(IdentExprNode::Ptr cond, StmtNode::Ptr block) : cond_(move(cond)), block_(move(block)) {} // create an onvalid () {} else {} expression OnValidStmtNode(IdentExprNode::Ptr cond, StmtNode::Ptr block, StmtNode::Ptr else_block) : cond_(move(cond)), block_(move(block)), else_block_(move(else_block)) {} }; class SwitchStmtNode : public StmtNode { public: DECLARE(SwitchStmtNode) ExprNode::Ptr cond_; BlockStmtNode::Ptr block_; SwitchStmtNode(ExprNode::Ptr cond, BlockStmtNode::Ptr block) : cond_(move(cond)), block_(move(block)) {} }; class CaseStmtNode : public StmtNode { public: DECLARE(CaseStmtNode) IntegerExprNode::Ptr value_; BlockStmtNode::Ptr block_; CaseStmtNode(IntegerExprNode::Ptr value, BlockStmtNode::Ptr block) : value_(move(value)), block_(move(block)) {} explicit CaseStmtNode(BlockStmtNode::Ptr block) : block_(move(block)) {} }; class VariableDeclStmtNode : public StmtNode { public: typedef unique_ptr Ptr; virtual StatusTuple accept(Visitor* v) = 0; enum storage_type { INTEGER, STRUCT, STRUCT_REFERENCE }; IdentExprNode::Ptr id_; ExprNodeList init_; enum storage_type storage_type_; size_t bit_width_; size_t bit_offset_; int slot_; string scope_id_; explicit VariableDeclStmtNode(IdentExprNode::Ptr id, storage_type t, size_t bit_width = 0, size_t bit_offset = 0) : id_(move(id)), storage_type_(t), bit_width_(bit_width), bit_offset_(bit_offset), slot_(0) {} const char* scope_id() const { return scope_id_.c_str(); } bool is_struct() { return (storage_type_ == STRUCT || storage_type_ == STRUCT_REFERENCE); } bool is_pointer() { return (storage_type_ == STRUCT_REFERENCE); } }; typedef vector FormalList; class StructVariableDeclStmtNode : public VariableDeclStmtNode { public: DECLARE(StructVariableDeclStmtNode) IdentExprNode::Ptr struct_id_; StructVariableDeclStmtNode(IdentExprNode::Ptr struct_id, IdentExprNode::Ptr id, VariableDeclStmtNode::storage_type t = VariableDeclStmtNode::STRUCT) : VariableDeclStmtNode(move(id), t), struct_id_(move(struct_id)) {} }; class IntegerVariableDeclStmtNode : public VariableDeclStmtNode { public: DECLARE(IntegerVariableDeclStmtNode) IntegerVariableDeclStmtNode(IdentExprNode::Ptr id, const string& bits) : VariableDeclStmtNode(move(id), VariableDeclStmtNode::INTEGER, strtoul(bits.c_str(), NULL, 0)) {} }; class StructDeclStmtNode : public StmtNode { public: DECLARE(StructDeclStmtNode) IdentExprNode::Ptr id_; FormalList stmts_; size_t bit_width_; bool packed_; StructDeclStmtNode(IdentExprNode::Ptr id, FormalList&& stmts = FormalList()) : id_(move(id)), stmts_(move(stmts)), bit_width_(0), packed_(false) {} VariableDeclStmtNode* field(const string& name) const; int indexof(const string& name) const; bool is_packed() const { return packed_; } }; class ParserStateStmtNode : public StmtNode { public: DECLARE(ParserStateStmtNode) IdentExprNode::Ptr id_; StmtNode* next_state_; string scope_id_; explicit ParserStateStmtNode(IdentExprNode::Ptr id) : id_(move(id)) {} static Ptr make(const IdentExprNode::Ptr& id) { return Ptr(new ParserStateStmtNode(id->copy())); } string scoped_name() const { return scope_id_ + id_->name_; } }; class StateDeclStmtNode : public StmtNode { public: DECLARE(StateDeclStmtNode) struct Sub { IdentExprNode::Ptr id_; BlockStmtNode::Ptr block_; ParserStateStmtNode::Ptr parser_; Scopes::StateScope* scope_; Sub(decltype(id_) id, decltype(block_) block, decltype(parser_) parser, decltype(scope_) scope) : id_(move(id)), block_(move(block)), parser_(move(parser)), scope_(scope) {} ~Sub() { delete scope_; } Sub(Sub&& other) : scope_(NULL) { *this = move(other); } Sub& operator=(Sub&& other) { if (this == &other) { return *this; } id_ = move(other.id_); block_ = move(other.block_); parser_ = move(other.parser_); std::swap(scope_, other.scope_); return *this; } }; IdentExprNode::Ptr id_; StmtNodeList init_; string scope_id_; ParserStateStmtNode::Ptr parser_; vector subs_; StateDeclStmtNode() {} StateDeclStmtNode(IdentExprNode::Ptr id, BlockStmtNode::Ptr block) : id_(move(id)) { subs_.push_back(Sub(make_unique(""), move(block), ParserStateStmtNode::Ptr(), NULL)); } StateDeclStmtNode(IdentExprNode::Ptr id1, IdentExprNode::Ptr id2, BlockStmtNode::Ptr block) : id_(move(id1)) { subs_.push_back(Sub(move(id2), move(block), ParserStateStmtNode::Ptr(), NULL)); } string scoped_name() const { return scope_id_ + id_->name_; } vector::iterator find_sub(const string& id) { return find_if(subs_.begin(), subs_.end(), [&id] (const Sub& sub) { if (sub.id_->name_ == id) return true; return false; }); } }; class MatchDeclStmtNode : public StmtNode { public: DECLARE(MatchDeclStmtNode) IdentExprNode::Ptr id_; FormalList formals_; BlockStmtNode::Ptr block_; MatchDeclStmtNode(IdentExprNode::Ptr id, FormalList&& formals, BlockStmtNode::Ptr block) : id_(move(id)), formals_(move(formals)), block_(move(block)) {} }; class MissDeclStmtNode : public StmtNode { public: DECLARE(MissDeclStmtNode) IdentExprNode::Ptr id_; FormalList formals_; BlockStmtNode::Ptr block_; MissDeclStmtNode(IdentExprNode::Ptr id, FormalList&& formals, BlockStmtNode::Ptr block) : id_(move(id)), formals_(move(formals)), block_(move(block)) {} }; class FailureDeclStmtNode : public StmtNode { public: DECLARE(FailureDeclStmtNode) IdentExprNode::Ptr id_; FormalList formals_; BlockStmtNode::Ptr block_; FailureDeclStmtNode(IdentExprNode::Ptr id, FormalList&& formals, BlockStmtNode::Ptr block) : id_(move(id)), formals_(move(formals)), block_(move(block)) {} }; class TableDeclStmtNode : public StmtNode { public: DECLARE(TableDeclStmtNode) IdentExprNode::Ptr table_type_; IdentExprNodeList templates_; IdentExprNode::Ptr id_; StructDeclStmtNode *key_type_; StructDeclStmtNode *leaf_type_; IdentExprNode * key_id() { return templates_.at(0).get(); } IdentExprNode * leaf_id() { return templates_.at(1).get(); } IdentExprNode * type_id() { return templates_.at(2).get(); } IdentExprNode * policy_id() { return templates_.at(3).get(); } size_t size_; TableDeclStmtNode(IdentExprNode::Ptr table_type, IdentExprNodeList&& templates, IdentExprNode::Ptr id, string* size) : table_type_(move(table_type)), templates_(move(templates)), id_(move(id)), key_type_(nullptr), leaf_type_(nullptr), size_(strtoul(size->c_str(), NULL, 0)) { delete size; } }; class FuncDeclStmtNode : public StmtNode { public: DECLARE(FuncDeclStmtNode) IdentExprNode::Ptr id_; FormalList formals_; BlockStmtNode::Ptr block_; Scopes::StateScope* scope_; FuncDeclStmtNode(IdentExprNode::Ptr id, FormalList&& formals, BlockStmtNode::Ptr block) : id_(move(id)), formals_(move(formals)), block_(move(block)), scope_(NULL) {} }; class Visitor { public: typedef StatusTuple Ret; virtual ~Visitor() {} #define VISIT(type, func) virtual STATUS_RETURN visit_##func(type* n) = 0; EXPAND_NODES(VISIT) #undef VISIT }; #undef DECLARE } // namespace cc } // namespace ebpf bpfcc-0.12.0/src/cc/frontends/b/parser.cc000066400000000000000000000175531357404205000200640ustar00rootroot00000000000000/* * Copyright (c) 2015 PLUMgrid, Inc. * * 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. */ #include #include "bcc_exception.h" #include "parser.h" #include "type_helper.h" namespace ebpf { namespace cc { using std::find; using std::move; using std::string; using std::unique_ptr; bool Parser::variable_exists(VariableDeclStmtNode *decl) const { if (scopes_->current_var()->lookup(decl->id_->name_, SCOPE_LOCAL) == NULL) { return false; } return true; } VariableDeclStmtNode *Parser::variable_add(vector *types, VariableDeclStmtNode *decl) { if (variable_exists(decl)) { fprintf(stderr, "redeclaration of variable %s", decl->id_->name_.c_str()); return nullptr; } decl->scope_id_ = string("v") + std::to_string(scopes_->current_var()->id_) + string("_"); scopes_->current_var()->add(decl->id_->name_, decl); return decl; } VariableDeclStmtNode *Parser::variable_add(vector *types, VariableDeclStmtNode *decl, ExprNode *init_expr) { AssignExprNode::Ptr assign(new AssignExprNode(decl->id_->copy(), ExprNode::Ptr(init_expr))); decl->init_.push_back(move(assign)); if (variable_exists(decl)) { fprintf(stderr, "redeclaration of variable %s", decl->id_->name_.c_str()); return nullptr; } decl->scope_id_ = string("v") + std::to_string(scopes_->current_var()->id_) + string("_"); scopes_->current_var()->add(decl->id_->name_, decl); return decl; } StructVariableDeclStmtNode *Parser::variable_add(StructVariableDeclStmtNode *decl, ExprNodeList *args, bool is_kv) { if (is_kv) { // annotate the init expressions with the declared id for (auto arg = args->begin(); arg != args->end(); ++arg) { // decorate with the name of this decl auto n = static_cast(arg->get()); auto id = static_cast(n->lhs_.get()); id->prepend_dot(decl->id_->name_); } } else { fprintf(stderr, "must use key = value syntax\n"); return NULL; } decl->init_ = move(*args); delete args; if (variable_exists(decl)) { fprintf(stderr, "ccpg: warning: redeclaration of variable '%s'\n", decl->id_->name_.c_str()); return nullptr; } decl->scope_id_ = string("v") + std::to_string(scopes_->current_var()->id_) + string("_"); scopes_->current_var()->add(decl->id_->name_, decl); return decl; } StmtNode *Parser::state_add(Scopes::StateScope *scope, IdentExprNode *id, BlockStmtNode *body) { if (scopes_->current_state()->lookup(id->full_name(), SCOPE_LOCAL)) { fprintf(stderr, "redeclaration of state %s\n", id->full_name().c_str()); // redeclaration return NULL; } auto state = new StateDeclStmtNode(IdentExprNode::Ptr(id), BlockStmtNode::Ptr(body)); // add a reference to the lower scope state->subs_[0].scope_ = scope; // add me to the upper scope scopes_->current_state()->add(state->id_->full_name(), state); state->scope_id_ = string("s") + std::to_string(scopes_->current_state()->id_) + string("_"); return state; } StmtNode *Parser::state_add(Scopes::StateScope *scope, IdentExprNode *id1, IdentExprNode *id2, BlockStmtNode *body) { auto state = scopes_->current_state()->lookup(id1->full_name(), SCOPE_LOCAL); if (!state) { state = new StateDeclStmtNode(IdentExprNode::Ptr(id1), IdentExprNode::Ptr(id2), BlockStmtNode::Ptr(body)); // add a reference to the lower scope state->subs_[0].scope_ = scope; // add me to the upper scope scopes_->current_state()->add(state->id_->full_name(), state); state->scope_id_ = string("s") + std::to_string(scopes_->current_state()->id_) + string("_"); return state; } else { if (state->find_sub(id2->name_) != state->subs_.end()) { fprintf(stderr, "redeclaration of state %s, %s\n", id1->full_name().c_str(), id2->full_name().c_str()); return NULL; } state->subs_.push_back(StateDeclStmtNode::Sub(IdentExprNode::Ptr(id2), BlockStmtNode::Ptr(body), ParserStateStmtNode::Ptr(), scope)); delete id1; return new StateDeclStmtNode(); // stub } } bool Parser::table_exists(TableDeclStmtNode *decl, bool search_local) { if (scopes_->top_table()->lookup(decl->id_->name_, search_local) == NULL) { return false; } return true; } StmtNode *Parser::table_add(IdentExprNode *type, IdentExprNodeList *templates, IdentExprNode *id, string *size) { auto table = new TableDeclStmtNode(IdentExprNode::Ptr(type), move(*templates), IdentExprNode::Ptr(id), size); if (table_exists(table, true)) { fprintf(stderr, "redeclaration of table %s\n", id->name_.c_str()); return table; } scopes_->top_table()->add(id->name_, table); return table; } StmtNode * Parser::struct_add(IdentExprNode *type, FormalList *formals) { auto struct_decl = new StructDeclStmtNode(IdentExprNode::Ptr(type), move(*formals)); if (scopes_->top_struct()->lookup(type->name_, SCOPE_LOCAL) != NULL) { fprintf(stderr, "redeclaration of struct %s\n", type->name_.c_str()); return struct_decl; } auto pr_it = pragmas_.find("packed"); if (pr_it != pragmas_.end() && pr_it->second == "true") struct_decl->packed_ = true; int i = 0; size_t offset = 0; for (auto it = struct_decl->stmts_.begin(); it != struct_decl->stmts_.end(); ++it, ++i) { FieldType ft = bits_to_enum((*it)->bit_width_); offset = struct_decl->is_packed() ? offset : align_offset(offset, ft); (*it)->slot_ = i; (*it)->bit_offset_ = offset; offset += (*it)->bit_width_; } struct_decl->bit_width_ = struct_decl->is_packed() ? offset : align_offset(offset, UINT32_T); scopes_->top_struct()->add(type->name_, struct_decl); return struct_decl; } StmtNode * Parser::result_add(int token, IdentExprNode *id, FormalList *formals, BlockStmtNode *body) { StmtNode *stmt = NULL; switch (token) { case Tok::TMATCH: stmt = new MatchDeclStmtNode(IdentExprNode::Ptr(id), move(*formals), BlockStmtNode::Ptr(body)); break; case Tok::TMISS: stmt = new MissDeclStmtNode(IdentExprNode::Ptr(id), move(*formals), BlockStmtNode::Ptr(body)); break; case Tok::TFAILURE: stmt = new FailureDeclStmtNode(IdentExprNode::Ptr(id), move(*formals), BlockStmtNode::Ptr(body)); break; default: {} } return stmt; } StmtNode * Parser::func_add(vector *types, Scopes::StateScope *scope, IdentExprNode *id, FormalList *formals, BlockStmtNode *body) { auto decl = new FuncDeclStmtNode(IdentExprNode::Ptr(id), move(*formals), BlockStmtNode::Ptr(body)); if (scopes_->top_func()->lookup(decl->id_->name_, SCOPE_LOCAL)) { fprintf(stderr, "redeclaration of func %s\n", id->name_.c_str()); return decl; } auto cur_scope = scopes_->current_var(); scopes_->set_current(scope); for (auto it = formals->begin(); it != formals->end(); ++it) if (!variable_add(nullptr, it->get())) { delete decl; return nullptr; } scopes_->set_current(cur_scope); decl->scope_ = scope; scopes_->top_func()->add(id->name_, decl); return decl; } void Parser::set_loc(Node *n, const BisonParser::location_type &loc) const { n->line_ = loc.begin.line; n->column_ = loc.begin.column; n->text_ = lexer.text(loc); } string Parser::pragma(const string &name) const { auto it = pragmas_.find(name); if (it == pragmas_.end()) return "main"; return it->second; } } // namespace cc } // namespace ebpf bpfcc-0.12.0/src/cc/frontends/b/parser.h000066400000000000000000000047661357404205000177300ustar00rootroot00000000000000/* * Copyright (c) 2015 PLUMgrid, Inc. * * 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. */ #pragma once #include // NOLINT #include "node.h" #include "lexer.h" #include "scope.h" namespace ebpf { namespace cc { using std::pair; using std::string; using std::vector; class Parser { public: explicit Parser(const string& infile) : root_node_(NULL), scopes_(new Scopes), in_(infile), lexer(&in_), parser(lexer, *this) { // parser.set_debug_level(1); } ~Parser() { delete root_node_; } int parse() { return parser.parse(); } VariableDeclStmtNode * variable_add(vector *types, VariableDeclStmtNode *decl); VariableDeclStmtNode * variable_add(vector *types, VariableDeclStmtNode *decl, ExprNode *init_expr); StructVariableDeclStmtNode * variable_add(StructVariableDeclStmtNode *decl, ExprNodeList *args, bool is_kv); StmtNode * state_add(Scopes::StateScope *scope, IdentExprNode *id1, BlockStmtNode *body); StmtNode * state_add(Scopes::StateScope *scope, IdentExprNode *id1, IdentExprNode *id2, BlockStmtNode *body); StmtNode * func_add(std::vector *types, Scopes::StateScope *scope, IdentExprNode *id, FormalList *formals, BlockStmtNode *body); StmtNode * table_add(IdentExprNode *type, IdentExprNodeList *templates, IdentExprNode *id, string *size); StmtNode * struct_add(IdentExprNode *type, FormalList *formals); StmtNode * result_add(int token, IdentExprNode *id, FormalList *formals, BlockStmtNode *body); bool variable_exists(VariableDeclStmtNode *decl) const; bool table_exists(TableDeclStmtNode *decl, bool search_local = true); void add_pragma(const std::string& pr, const std::string& v) { pragmas_[pr] = v; } void set_loc(Node *n, const BisonParser::location_type &loc) const; std::string pragma(const std::string &name) const; Node *root_node_; Scopes::Ptr scopes_; std::map pragmas_; private: std::ifstream in_; Lexer lexer; BisonParser parser; }; } // namespace cc } // namespace ebpf bpfcc-0.12.0/src/cc/frontends/b/parser.yy000066400000000000000000000465511357404205000201400ustar00rootroot00000000000000/* * Copyright (c) 2015 PLUMgrid, Inc. * * 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. */ %skeleton "lalr1.cc" %defines %define namespace "ebpf::cc" %define parser_class_name "BisonParser" %parse-param { ebpf::cc::Lexer &lexer } %parse-param { ebpf::cc::Parser &parser } %lex-param { ebpf::cc::Lexer &lexer } %locations %code requires { #include #include #include #include "node.h" // forward declaration namespace ebpf { namespace cc { class Lexer; class Parser; } } } %code { static int yylex(ebpf::cc::BisonParser::semantic_type *yylval, ebpf::cc::BisonParser::location_type *yylloc, ebpf::cc::Lexer &lexer); } %{ #include "node.h" #include "parser.h" using std::unique_ptr; using std::vector; using std::string; using std::move; %} %union { Scopes::StateScope *state_scope; Scopes::VarScope *var_scope; BlockStmtNode *block; ExprNode *expr; MethodCallExprNode *call; StmtNode *stmt; IdentExprNode *ident; IntegerExprNode *numeric; BitopExprNode *bitop; ExprNodeList *args; IdentExprNodeList *ident_args; StmtNodeList *stmts; FormalList *formals; VariableDeclStmtNode *decl; StructVariableDeclStmtNode *type_decl; TableIndexExprNode *table_index; std::vector *type_specifiers; std::string* string; int token; } /* Define the terminal symbols. */ %token TIDENTIFIER TINTEGER THEXINTEGER TPRAGMA TSTRING %token TU8 TU16 TU32 TU64 %token TEQUAL TCEQ TCNE TCLT TCLE TCGT TCGE TAND TOR %token TLPAREN TRPAREN TLBRACE TRBRACE TLBRACK TRBRACK %token TDOT TARROW TCOMMA TPLUS TMINUS TMUL TDIV TMOD TXOR TDOLLAR TCOLON TSCOPE TNOT TSEMI TCMPL TLAND TLOR %token TSTRUCT TSTATE TFUNC TGOTO TCONTINUE TNEXT TTRUE TFALSE TRETURN %token TIF TELSE TSWITCH TCASE %token TMATCH TMISS TFAILURE TVALID %token TAT /* Define non-terminal symbols as defined in the above union */ %type ident scoped_ident dotted_ident any_ident %type expr assign_expr return_expr init_arg_kv %type numeric %type bitop %type call_args /*init_args*/ init_args_kv %type table_decl_args %type struct_decl_stmts formals %type program block prog_decls %type decl_stmt int_decl ref_stmt %type type_decl ptr_decl %type stmt prog_decl var_decl struct_decl state_decl func_decl %type table_decl table_result_stmt if_stmt switch_stmt case_stmt onvalid_stmt %type enter_varscope exit_varscope %type enter_statescope exit_statescope %type stmts table_result_stmts case_stmts %type call_expr %type table_index_expr %type type_specifiers %type pragma_decl %type type_specifier /* taken from C++ operator precedence wiki page */ %nonassoc TSCOPE %left TDOT TLBRACK TLBRACE TLPAREN TINCR TDECR %right TNOT TCMPL %left TMUL %left TDIV %left TMOD %left TPLUS %left TMINUS %left TCLT TCLE TCGT TCGE %left TCEQ %left TCNE %left TXOR %left TAND %left TOR %left TLAND %left TLOR %right TEQUAL %start program %% program : enter_statescope enter_varscope prog_decls exit_varscope exit_statescope { parser.root_node_ = $3; $3->scope_ = $2; } ; /* program is a list of declarations */ prog_decls : prog_decl { $$ = new BlockStmtNode; $$->stmts_.push_back(StmtNode::Ptr($1)); } | prog_decls prog_decl { $1->stmts_.push_back(StmtNode::Ptr($2)); } ; /* possible program declarations are: "struct {}" "state|on_miss|on_match|on_valid {}" "var " "Table <...> (size)" */ prog_decl : var_decl TSEMI | struct_decl TSEMI | state_decl | table_decl TSEMI | pragma_decl | func_decl ; pragma_decl : TPRAGMA TIDENTIFIER TIDENTIFIER { $$ = new BlockStmtNode; parser.add_pragma(*$2, *$3); delete $2; delete $3; } | TPRAGMA TIDENTIFIER TSTRING { $$ = new BlockStmtNode; parser.add_pragma(*$2, *$3); delete $2; delete $3; } ; stmts : stmt { $$ = new StmtNodeList; $$->push_back(StmtNode::Ptr($1)); } | stmts stmt { $1->push_back(StmtNode::Ptr($2)); } ; stmt : expr TSEMI { $$ = new ExprStmtNode(ExprNode::Ptr($1)); parser.set_loc($$, @$); } | assign_expr TSEMI { $$ = new ExprStmtNode(ExprNode::Ptr($1)); parser.set_loc($$, @$); } | return_expr TSEMI { $$ = new ExprStmtNode(ExprNode::Ptr($1)); parser.set_loc($$, @$); } | call_expr TLBRACE enter_varscope table_result_stmts exit_varscope TRBRACE TSEMI { $$ = new ExprStmtNode(ExprNode::Ptr($1)); $1->block_->stmts_ = move(*$4); delete $4; $1->block_->scope_ = $3; parser.set_loc($$, @$); } | call_expr TLBRACE TRBRACE TSEMI // support empty curly braces { $$ = new ExprStmtNode(ExprNode::Ptr($1)); parser.set_loc($$, @$); } | if_stmt | switch_stmt | var_decl TSEMI { $$ = $1; } | state_decl | onvalid_stmt ; call_expr : any_ident TLPAREN call_args TRPAREN { $$ = new MethodCallExprNode(IdentExprNode::Ptr($1), move(*$3), lexer.lineno()); delete $3; parser.set_loc($$, @$); } ; block : TLBRACE stmts TRBRACE { $$ = new BlockStmtNode; $$->stmts_ = move(*$2); delete $2; parser.set_loc($$, @$); } | TLBRACE TRBRACE { $$ = new BlockStmtNode; parser.set_loc($$, @$); } ; enter_varscope : /* empty */ { $$ = parser.scopes_->enter_var_scope(); } ; exit_varscope : /* emtpy */ { $$ = parser.scopes_->exit_var_scope(); } ; enter_statescope : /* empty */ { $$ = parser.scopes_->enter_state_scope(); } ; exit_statescope : /* emtpy */ { $$ = parser.scopes_->exit_state_scope(); } ; struct_decl : TSTRUCT ident TLBRACE struct_decl_stmts TRBRACE { $$ = parser.struct_add($2, $4); delete $4; parser.set_loc($$, @$); } ; struct_decl_stmts : type_specifiers decl_stmt TSEMI { $$ = new FormalList; $$->push_back(VariableDeclStmtNode::Ptr($2)); } | struct_decl_stmts type_specifiers decl_stmt TSEMI { $1->push_back(VariableDeclStmtNode::Ptr($3)); } ; table_decl : ident TCLT table_decl_args TCGT ident TLPAREN TINTEGER TRPAREN { $$ = parser.table_add($1, $3, $5, $7); delete $3; parser.set_loc($$, @$); } ; table_decl_args : ident { $$ = new IdentExprNodeList; $$->push_back(IdentExprNode::Ptr($1)); } | table_decl_args TCOMMA ident { $$->push_back(IdentExprNode::Ptr($3)); } ; state_decl : TSTATE scoped_ident enter_statescope enter_varscope block exit_varscope exit_statescope { $$ = parser.state_add($3, $2, $5); $5->scope_ = $4; if (!$$) YYERROR; parser.set_loc($$, @$); } | TSTATE scoped_ident TCOMMA TMUL enter_statescope enter_varscope block exit_varscope exit_statescope { $$ = parser.state_add($5, $2, new IdentExprNode(""), $7); $7->scope_ = $6; if (!$$) YYERROR; parser.set_loc($$, @$); } | TSTATE scoped_ident TCOMMA scoped_ident enter_statescope enter_varscope block exit_varscope exit_statescope { $$ = parser.state_add($5, $2, $4, $7); $7->scope_ = $6; if (!$$) YYERROR; parser.set_loc($$, @$); } ; func_decl : type_specifiers ident enter_statescope enter_varscope TLPAREN formals TRPAREN block exit_varscope exit_statescope { $$ = parser.func_add($1, $3, $2, $6, $8); $8->scope_ = $4; if (!$$) YYERROR; parser.set_loc($$, @$); } ; table_result_stmts : table_result_stmt { $$ = new StmtNodeList; $$->push_back(StmtNode::Ptr($1)); } | table_result_stmts table_result_stmt { $$->push_back(StmtNode::Ptr($2)); } ; table_result_stmt : TMATCH ident enter_varscope TLPAREN formals TRPAREN block exit_varscope TSEMI { $$ = parser.result_add($1, $2, $5, $7); delete $5; $7->scope_ = $3; if (!$$) YYERROR; parser.set_loc($$, @$); } | TMISS ident enter_varscope TLPAREN TRPAREN block exit_varscope TSEMI { $$ = parser.result_add($1, $2, new FormalList, $6); $6->scope_ = $3; if (!$$) YYERROR; parser.set_loc($$, @$); } | TFAILURE ident enter_varscope TLPAREN formals TRPAREN block exit_varscope TSEMI { $$ = parser.result_add($1, $2, $5, $7); delete $5; $7->scope_ = $3; if (!$$) YYERROR; parser.set_loc($$, @$); } ; formals : TSTRUCT ptr_decl { $$ = new FormalList; $$->push_back(VariableDeclStmtNode::Ptr(parser.variable_add(nullptr, $2))); } | formals TCOMMA TSTRUCT ptr_decl { $1->push_back(VariableDeclStmtNode::Ptr(parser.variable_add(nullptr, $4))); } ; type_specifier : TU8 | TU16 | TU32 | TU64 ; type_specifiers : type_specifier { $$ = new std::vector; $$->push_back($1); } | type_specifiers type_specifier { $$->push_back($2); } ; var_decl : type_specifiers decl_stmt { $$ = parser.variable_add($1, $2); if (!$$) YYERROR; parser.set_loc($$, @$); } | type_specifiers int_decl TEQUAL expr { $$ = parser.variable_add($1, $2, $4); if (!$$) YYERROR; parser.set_loc($$, @$); } | TSTRUCT type_decl TEQUAL TLBRACE init_args_kv TRBRACE { $$ = parser.variable_add($2, $5, true); if (!$$) YYERROR; parser.set_loc($$, @$); } /*| TSTRUCT type_decl TEQUAL TLBRACE init_args TRBRACE { $$ = parser.variable_add($2, $5, false); parser.set_loc($$, @$); }*/ | TSTRUCT ref_stmt { $$ = parser.variable_add(nullptr, $2); if (!$$) YYERROR; parser.set_loc($$, @$); } ; /* "id":"bitsize" or "type" "id" */ decl_stmt : int_decl { $$ = $1; } | type_decl { $$ = $1; }; int_decl : ident TCOLON TINTEGER { $$ = new IntegerVariableDeclStmtNode(IdentExprNode::Ptr($1), *$3); delete $3; parser.set_loc($$, @$); } ; type_decl : scoped_ident ident { $$ = new StructVariableDeclStmtNode(IdentExprNode::Ptr($1), IdentExprNode::Ptr($2)); parser.set_loc($$, @$); } ; /* "type" "*" "id" */ ref_stmt : ptr_decl { $$ = $1; }; ptr_decl : scoped_ident TMUL ident { $$ = new StructVariableDeclStmtNode(IdentExprNode::Ptr($1), IdentExprNode::Ptr($3), VariableDeclStmtNode::STRUCT_REFERENCE); parser.set_loc($$, @$); } ; /* normal initializer */ /* init_args : expr { $$ = new ExprNodeList; $$->push_back(ExprNode::Ptr($1)); } | init_args TCOMMA expr { $$->push_back(ExprNode::Ptr($3)); } ;*/ /* one or more of "field" = "expr" */ init_args_kv : init_arg_kv { $$ = new ExprNodeList; $$->push_back(ExprNode::Ptr($1)); } | init_args_kv TCOMMA init_arg_kv { $$->push_back(ExprNode::Ptr($3)); } ; init_arg_kv : TDOT ident TEQUAL expr { $$ = new AssignExprNode(IdentExprNode::Ptr($2), ExprNode::Ptr($4)); parser.set_loc($$, @$); } | TDOT ident bitop TEQUAL expr { $$ = new AssignExprNode(IdentExprNode::Ptr($2), ExprNode::Ptr($5)); $$->bitop_ = BitopExprNode::Ptr($3); parser.set_loc($$, @$); } ; if_stmt : TIF expr enter_varscope block exit_varscope { $$ = new IfStmtNode(ExprNode::Ptr($2), StmtNode::Ptr($4)); $4->scope_ = $3; parser.set_loc($$, @$); } | TIF expr enter_varscope block exit_varscope TELSE enter_varscope block exit_varscope { $$ = new IfStmtNode(ExprNode::Ptr($2), StmtNode::Ptr($4), StmtNode::Ptr($8)); $4->scope_ = $3; $8->scope_ = $7; parser.set_loc($$, @$); } | TIF expr enter_varscope block exit_varscope TELSE if_stmt { $$ = new IfStmtNode(ExprNode::Ptr($2), StmtNode::Ptr($4), StmtNode::Ptr($7)); $4->scope_ = $3; parser.set_loc($$, @$); } ; onvalid_stmt : TVALID TLPAREN ident TRPAREN enter_varscope block exit_varscope { $$ = new OnValidStmtNode(IdentExprNode::Ptr($3), StmtNode::Ptr($6)); $6->scope_ = $5; parser.set_loc($$, @$); } | TVALID TLPAREN ident TRPAREN enter_varscope block exit_varscope TELSE enter_varscope block exit_varscope { $$ = new OnValidStmtNode(IdentExprNode::Ptr($3), StmtNode::Ptr($6), StmtNode::Ptr($10)); $6->scope_ = $5; $10->scope_ = $9; parser.set_loc($$, @$); } ; switch_stmt : TSWITCH expr TLBRACE case_stmts TRBRACE { $$ = new SwitchStmtNode(ExprNode::Ptr($2), make_unique(move(*$4))); delete $4; parser.set_loc($$, @$); } ; case_stmts : case_stmt { $$ = new StmtNodeList; $$->push_back(StmtNode::Ptr($1)); } | case_stmts case_stmt { $$->push_back(StmtNode::Ptr($2)); } ; case_stmt : TCASE numeric block TSEMI { $$ = new CaseStmtNode(IntegerExprNode::Ptr($2), BlockStmtNode::Ptr($3)); parser.set_loc($$, @$); } | TCASE TMUL block TSEMI { $$ = new CaseStmtNode(BlockStmtNode::Ptr($3)); parser.set_loc($$, @$); } ; numeric : TINTEGER { $$ = new IntegerExprNode($1); parser.set_loc($$, @$); } | THEXINTEGER { $$ = new IntegerExprNode($1); parser.set_loc($$, @$); } | TINTEGER TCOLON TINTEGER { $$ = new IntegerExprNode($1, $3); parser.set_loc($$, @$); } | THEXINTEGER TCOLON TINTEGER { $$ = new IntegerExprNode($1, $3); parser.set_loc($$, @$); } | TTRUE { $$ = new IntegerExprNode(new string("1"), new string("1")); parser.set_loc($$, @$); } | TFALSE { $$ = new IntegerExprNode(new string("0"), new string("1")); parser.set_loc($$, @$); } ; assign_expr : expr TEQUAL expr { $$ = new AssignExprNode(ExprNode::Ptr($1), ExprNode::Ptr($3)); parser.set_loc($$, @$); } /* The below has a reduce/reduce conflict. TODO: ensure the above is handled in the type check properly */ /*| dotted_ident TEQUAL expr { $$ = new AssignExprNode(IdentExprNode::Ptr($1), ExprNode::Ptr($3)); parser.set_loc($$, @$); } | dotted_ident bitop TEQUAL expr { $$ = new AssignExprNode(IdentExprNode::Ptr($1), ExprNode::Ptr($4)); $$->bitop_ = BitopExprNode::Ptr($2); parser.set_loc($$, @$); }*/ ; return_expr : TRETURN expr { $$ = new ReturnExprNode(ExprNode::Ptr($2)); parser.set_loc($$, @$); } ; expr : call_expr { $$ = $1; } | call_expr bitop { $$ = $1; $$->bitop_ = BitopExprNode::Ptr($2); } | table_index_expr { $$ = $1; } | table_index_expr TDOT ident { $$ = $1; $1->sub_ = IdentExprNode::Ptr($3); } | any_ident { $$ = $1; } | TAT dotted_ident { $$ = new PacketExprNode(IdentExprNode::Ptr($2)); $$->flags_[ExprNode::IS_REF] = true; parser.set_loc($$, @$); } | TDOLLAR dotted_ident { $$ = new PacketExprNode(IdentExprNode::Ptr($2)); $$->flags_[ExprNode::IS_PKT] = true; parser.set_loc($$, @$); } | TDOLLAR dotted_ident bitop { $$ = new PacketExprNode(IdentExprNode::Ptr($2)); $$->bitop_ = BitopExprNode::Ptr($3); $$->flags_[ExprNode::IS_PKT] = true; parser.set_loc($$, @$); } | TGOTO scoped_ident { $$ = new GotoExprNode(IdentExprNode::Ptr($2), false); parser.set_loc($$, @$); } | TNEXT scoped_ident { $$ = new GotoExprNode(IdentExprNode::Ptr($2), false); parser.set_loc($$, @$); } | TCONTINUE scoped_ident { $$ = new GotoExprNode(IdentExprNode::Ptr($2), true); parser.set_loc($$, @$); } | TLPAREN expr TRPAREN { $$ = $2; } | TLPAREN expr TRPAREN bitop { $$ = $2; $$->bitop_ = BitopExprNode::Ptr($4); } | TSTRING { $$ = new StringExprNode($1); parser.set_loc($$, @$); } | numeric { $$ = $1; } | numeric bitop { $$ = $1; $$->bitop_ = BitopExprNode::Ptr($2); } | expr TCLT expr { $$ = new BinopExprNode(ExprNode::Ptr($1), $2, ExprNode::Ptr($3)); parser.set_loc($$, @$); } | expr TCGT expr { $$ = new BinopExprNode(ExprNode::Ptr($1), $2, ExprNode::Ptr($3)); parser.set_loc($$, @$); } | expr TCGE expr { $$ = new BinopExprNode(ExprNode::Ptr($1), $2, ExprNode::Ptr($3)); parser.set_loc($$, @$); } | expr TCLE expr { $$ = new BinopExprNode(ExprNode::Ptr($1), $2, ExprNode::Ptr($3)); parser.set_loc($$, @$); } | expr TCNE expr { $$ = new BinopExprNode(ExprNode::Ptr($1), $2, ExprNode::Ptr($3)); parser.set_loc($$, @$); } | expr TCEQ expr { $$ = new BinopExprNode(ExprNode::Ptr($1), $2, ExprNode::Ptr($3)); parser.set_loc($$, @$); } | expr TPLUS expr { $$ = new BinopExprNode(ExprNode::Ptr($1), $2, ExprNode::Ptr($3)); parser.set_loc($$, @$); } | expr TMINUS expr { $$ = new BinopExprNode(ExprNode::Ptr($1), $2, ExprNode::Ptr($3)); parser.set_loc($$, @$); } | expr TMUL expr { $$ = new BinopExprNode(ExprNode::Ptr($1), $2, ExprNode::Ptr($3)); parser.set_loc($$, @$); } | expr TDIV expr { $$ = new BinopExprNode(ExprNode::Ptr($1), $2, ExprNode::Ptr($3)); parser.set_loc($$, @$); } | expr TMOD expr { $$ = new BinopExprNode(ExprNode::Ptr($1), $2, ExprNode::Ptr($3)); parser.set_loc($$, @$); } | expr TXOR expr { $$ = new BinopExprNode(ExprNode::Ptr($1), $2, ExprNode::Ptr($3)); parser.set_loc($$, @$); } | expr TAND expr { $$ = new BinopExprNode(ExprNode::Ptr($1), $2, ExprNode::Ptr($3)); parser.set_loc($$, @$); } | expr TOR expr { $$ = new BinopExprNode(ExprNode::Ptr($1), $2, ExprNode::Ptr($3)); parser.set_loc($$, @$); } | expr TLAND expr { $$ = new BinopExprNode(ExprNode::Ptr($1), $2, ExprNode::Ptr($3)); parser.set_loc($$, @$); } | expr TLOR expr { $$ = new BinopExprNode(ExprNode::Ptr($1), $2, ExprNode::Ptr($3)); parser.set_loc($$, @$); } /*| expr bitop { $$ = $1; $$->bitop_ = BitopExprNode::Ptr($2); }*/ | TNOT expr { $$ = new UnopExprNode($1, ExprNode::Ptr($2)); parser.set_loc($$, @$); } | TCMPL expr { $$ = new UnopExprNode($1, ExprNode::Ptr($2)); parser.set_loc($$, @$); } ; call_args : /* empty */ { $$ = new ExprNodeList; } | expr { $$ = new ExprNodeList; $$->push_back(ExprNode::Ptr($1)); } | call_args TCOMMA expr { $$->push_back(ExprNode::Ptr($3)); } ; bitop : TLBRACK TCOLON TPLUS TINTEGER TRBRACK { $$ = new BitopExprNode(string("0"), *$4); delete $4; parser.set_loc($$, @$); } | TLBRACK TINTEGER TCOLON TPLUS TINTEGER TRBRACK { $$ = new BitopExprNode(*$2, *$5); delete $2; delete $5; parser.set_loc($$, @$); } ; table_index_expr : dotted_ident TLBRACK ident TRBRACK { $$ = new TableIndexExprNode(IdentExprNode::Ptr($1), IdentExprNode::Ptr($3)); parser.set_loc($$, @$); } ; scoped_ident : ident { $$ = $1; } | scoped_ident TSCOPE TIDENTIFIER { $$->append_scope(*$3); delete $3; } ; dotted_ident : ident { $$ = $1; } | dotted_ident TDOT TIDENTIFIER { $$->append_dot(*$3); delete $3; } ; any_ident : ident { $$ = $1; } | dotted_ident TARROW TIDENTIFIER { $$->append_dot(*$3); delete $3; } | dotted_ident TDOT TIDENTIFIER { $$->append_dot(*$3); delete $3; } | scoped_ident TSCOPE TIDENTIFIER { $$->append_scope(*$3); delete $3; } ; ident : TIDENTIFIER { $$ = new IdentExprNode(*$1); delete $1; parser.set_loc($$, @$); } ; %% void ebpf::cc::BisonParser::error(const ebpf::cc::BisonParser::location_type &loc, const string& msg) { std::cerr << "Error: " << loc << " " << msg << std::endl; } #include "lexer.h" static int yylex(ebpf::cc::BisonParser::semantic_type *yylval, ebpf::cc::BisonParser::location_type *yylloc, ebpf::cc::Lexer &lexer) { return lexer.yylex(yylval, yylloc); } bpfcc-0.12.0/src/cc/frontends/b/printer.cc000066400000000000000000000212161357404205000202420ustar00rootroot00000000000000/* * Copyright (c) 2015 PLUMgrid, Inc. * * 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. */ #include "printer.h" #include "lexer.h" #include "bcc_exception.h" namespace ebpf { namespace cc { void Printer::print_indent() { fprintf(out_, "%*s", indent_, ""); } StatusTuple Printer::visit_block_stmt_node(BlockStmtNode* n) { fprintf(out_, "{\n"); if (!n->stmts_.empty()) { ++indent_; for (auto it = n->stmts_.begin(); it != n->stmts_.end(); ++it) { print_indent(); TRY2((*it)->accept(this)); fprintf(out_, "\n"); } --indent_; } fprintf(out_, "%*s}", indent_, ""); return StatusTuple(0); } StatusTuple Printer::visit_if_stmt_node(IfStmtNode* n) { fprintf(out_, "if "); TRY2(n->cond_->accept(this)); fprintf(out_, " "); TRY2(n->true_block_->accept(this)); if (n->false_block_) { fprintf(out_, " else "); TRY2(n->false_block_->accept(this)); } return StatusTuple(0); } StatusTuple Printer::visit_onvalid_stmt_node(OnValidStmtNode* n) { fprintf(out_, "if "); TRY2(n->cond_->accept(this)); fprintf(out_, " "); TRY2(n->block_->accept(this)); if (n->else_block_) { fprintf(out_, " else "); TRY2(n->else_block_->accept(this)); } return StatusTuple(0); } StatusTuple Printer::visit_switch_stmt_node(SwitchStmtNode* n) { fprintf(out_, "switch ("); TRY2(n->cond_->accept(this)); fprintf(out_, ") "); TRY2(n->block_->accept(this)); return StatusTuple(0); } StatusTuple Printer::visit_case_stmt_node(CaseStmtNode* n) { if (n->value_) { fprintf(out_, "case "); TRY2(n->value_->accept(this)); } else { fprintf(out_, "default"); } TRY2(n->block_->accept(this)); return StatusTuple(0); } StatusTuple Printer::visit_ident_expr_node(IdentExprNode* n) { if (n->scope_name_.size()) { fprintf(out_, "%s::", n->scope_name_.c_str()); } fprintf(out_, "%s", n->name_.c_str()); if (n->sub_name_.size()) { fprintf(out_, ".%s", n->sub_name_.c_str()); } return StatusTuple(0); } StatusTuple Printer::visit_assign_expr_node(AssignExprNode* n) { TRY2(n->lhs_->accept(this)); fprintf(out_, " = "); TRY2(n->rhs_->accept(this)); return StatusTuple(0); } StatusTuple Printer::visit_packet_expr_node(PacketExprNode* n) { fprintf(out_, "$"); TRY2(n->id_->accept(this)); return StatusTuple(0); } StatusTuple Printer::visit_integer_expr_node(IntegerExprNode* n) { fprintf(out_, "%s:%zu", n->val_.c_str(), n->bits_); return StatusTuple(0); } StatusTuple Printer::visit_string_expr_node(StringExprNode *n) { fprintf(out_, "%s", n->val_.c_str()); return StatusTuple(0); } StatusTuple Printer::visit_binop_expr_node(BinopExprNode* n) { TRY2(n->lhs_->accept(this)); fprintf(out_, "%d", n->op_); TRY2(n->rhs_->accept(this)); return StatusTuple(0); } StatusTuple Printer::visit_unop_expr_node(UnopExprNode* n) { const char* s = ""; switch (n->op_) { case Tok::TNOT: s = "!"; break; case Tok::TCMPL: s = "~"; break; case Tok::TMOD: s = "%"; break; default: {} } fprintf(out_, "%s", s); TRY2(n->expr_->accept(this)); return StatusTuple(0); } StatusTuple Printer::visit_bitop_expr_node(BitopExprNode* n) { return StatusTuple(0); } StatusTuple Printer::visit_return_expr_node(ReturnExprNode* n) { fprintf(out_, "return "); TRY2(n->expr_->accept(this)); return StatusTuple(0); } StatusTuple Printer::visit_goto_expr_node(GotoExprNode* n) { const char* s = n->is_continue_ ? "continue " : "goto "; fprintf(out_, "%s", s); TRY2(n->id_->accept(this)); return StatusTuple(0); } StatusTuple Printer::visit_method_call_expr_node(MethodCallExprNode* n) { TRY2(n->id_->accept(this)); fprintf(out_, "("); for (auto it = n->args_.begin(); it != n->args_.end(); ++it) { TRY2((*it)->accept(this)); if (it + 1 != n->args_.end()) { fprintf(out_, ", "); } } fprintf(out_, ")"); if (!n->block_->stmts_.empty()) { fprintf(out_, " {\n"); ++indent_; for (auto it = n->block_->stmts_.begin(); it != n->block_->stmts_.end(); ++it) { print_indent(); TRY2((*it)->accept(this)); fprintf(out_, "\n"); } --indent_; fprintf(out_, "%*s}", indent_, ""); } return StatusTuple(0); } StatusTuple Printer::visit_table_index_expr_node(TableIndexExprNode *n) { fprintf(out_, "%s[", n->id_->c_str()); TRY2(n->index_->accept(this)); fprintf(out_, "]"); return StatusTuple(0); } StatusTuple Printer::visit_expr_stmt_node(ExprStmtNode* n) { TRY2(n->expr_->accept(this)); return StatusTuple(0); } StatusTuple Printer::visit_struct_variable_decl_stmt_node(StructVariableDeclStmtNode* n) { fprintf(out_, "var "); TRY2(n->struct_id_->accept(this)); fprintf(out_, " "); TRY2(n->id_->accept(this)); if (!n->init_.empty()) { fprintf(out_, "{"); for (auto it = n->init_.begin(); it != n->init_.end(); ++it) { TRY2((*it)->accept(this)); if (it + 1 != n->init_.end()) { fprintf(out_, ", "); } } fprintf(out_, "}"); } return StatusTuple(0); } StatusTuple Printer::visit_integer_variable_decl_stmt_node(IntegerVariableDeclStmtNode* n) { fprintf(out_, "var "); TRY2(n->id_->accept(this)); fprintf(out_, ":%zu", n->bit_width_); if (!n->init_.empty()) { fprintf(out_, "; "); TRY2(n->init_[0]->accept(this)); } return StatusTuple(0); } StatusTuple Printer::visit_struct_decl_stmt_node(StructDeclStmtNode* n) { fprintf(out_, "struct "); TRY2(n->id_->accept(this)); fprintf(out_, " {\n"); ++indent_; for (auto it = n->stmts_.begin(); it != n->stmts_.end(); ++it) { print_indent(); TRY2((*it)->accept(this)); fprintf(out_, "\n"); } --indent_; fprintf(out_, "%*s}", indent_, ""); return StatusTuple(0); } StatusTuple Printer::visit_state_decl_stmt_node(StateDeclStmtNode* n) { if (!n->id_) { return StatusTuple(0); } fprintf(out_, "state "); TRY2(n->id_->accept(this)); //if (!n->id2_) { // fprintf(out_, ", * "); //} else { // fprintf(out_, ", "); // TRY2(n->id2_->accept(this)); //} //TRY2(n->block_->accept(this)); return StatusTuple(0); } StatusTuple Printer::visit_parser_state_stmt_node(ParserStateStmtNode* n) { return StatusTuple(0); } StatusTuple Printer::visit_match_decl_stmt_node(MatchDeclStmtNode* n) { fprintf(out_, "on_match "); TRY2(n->id_->accept(this)); fprintf(out_, " ("); for (auto it = n->formals_.begin(); it != n->formals_.end(); ++it) { TRY2((*it)->accept(this)); if (it + 1 != n->formals_.end()) { fprintf(out_, ", "); } } fprintf(out_, ") "); TRY2(n->block_->accept(this)); return StatusTuple(0); } StatusTuple Printer::visit_miss_decl_stmt_node(MissDeclStmtNode* n) { fprintf(out_, "on_miss "); TRY2(n->id_->accept(this)); fprintf(out_, " ("); for (auto it = n->formals_.begin(); it != n->formals_.end(); ++it) { TRY2((*it)->accept(this)); if (it + 1 != n->formals_.end()) { fprintf(out_, ", "); } } fprintf(out_, ") "); TRY2(n->block_->accept(this)); return StatusTuple(0); } StatusTuple Printer::visit_failure_decl_stmt_node(FailureDeclStmtNode* n) { fprintf(out_, "on_failure "); TRY2(n->id_->accept(this)); fprintf(out_, " ("); for (auto it = n->formals_.begin(); it != n->formals_.end(); ++it) { TRY2((*it)->accept(this)); if (it + 1 != n->formals_.end()) { fprintf(out_, ", "); } } fprintf(out_, ") "); TRY2(n->block_->accept(this)); return StatusTuple(0); } StatusTuple Printer::visit_table_decl_stmt_node(TableDeclStmtNode* n) { TRY2(n->table_type_->accept(this)); fprintf(out_, "<"); for (auto it = n->templates_.begin(); it != n->templates_.end(); ++it) { TRY2((*it)->accept(this)); if (it + 1 != n->templates_.end()) { fprintf(out_, ", "); } } fprintf(out_, "> "); TRY2(n->id_->accept(this)); fprintf(out_, "(%zu)", n->size_); return StatusTuple(0); } StatusTuple Printer::visit_func_decl_stmt_node(FuncDeclStmtNode *n) { fprintf(out_, "func "); TRY2(n->id_->accept(this)); fprintf(out_, "("); for (auto it = n->formals_.begin(); it != n->formals_.end(); ++it) { TRY2((*it)->accept(this)); if (it + 1 != n->formals_.end()) { fprintf(out_, ", "); } } fprintf(out_, ") "); TRY2(n->block_->accept(this)); return StatusTuple(0); } } // namespace cc } // namespace ebpf bpfcc-0.12.0/src/cc/frontends/b/printer.h000066400000000000000000000017471357404205000201130ustar00rootroot00000000000000/* * Copyright (c) 2015 PLUMgrid, Inc. * * 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. */ #pragma once #include #include "node.h" namespace ebpf { namespace cc { class Printer : public Visitor { public: explicit Printer(FILE* out) : out_(out), indent_(0) {} void print_indent(); #define VISIT(type, func) virtual STATUS_RETURN visit_##func(type* n); EXPAND_NODES(VISIT) #undef VISIT private: FILE* out_; int indent_; }; } // namespace cc } // namespace ebpf bpfcc-0.12.0/src/cc/frontends/b/scope.h000066400000000000000000000123241357404205000175320ustar00rootroot00000000000000/* * Copyright (c) 2015 PLUMgrid, Inc. * * 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. */ #pragma once #include #include #include #include namespace ebpf { namespace cc { using std::string; using std::vector; using std::map; using std::pair; using std::unique_ptr; class StateDeclStmtNode; class VariableDeclStmtNode; class TableDeclStmtNode; class StructDeclStmtNode; class FuncDeclStmtNode; enum search_type { SCOPE_LOCAL, SCOPE_GLOBAL }; template class Scope { public: Scope() {} Scope(Scope* scope, int id) : parent_(scope), id_(id) {} T* lookup(const string &name, bool search_local = true) { return lookup(name, search_local ? SCOPE_LOCAL : SCOPE_GLOBAL); } T * lookup(const string &name, search_type stype) { auto it = elems_.find(name); if (it != elems_.end()) return it->second; if (stype == SCOPE_LOCAL || !parent_) return nullptr; return parent_->lookup(name, stype); } void add(const string& name, T* n) { elems_[name] = n; elems_ordered_.push_back(n); } typename map::iterator begin() { return elems_.begin(); } typename map::iterator end() { return elems_.end(); } typename vector::iterator obegin() { return elems_ordered_.begin(); } typename vector::iterator oend() { return elems_ordered_.end(); } Scope *parent_; int id_; map elems_; vector elems_ordered_; }; /** * Hold the current stack of scope pointers. Lookups search upwards. * Actual scope pointers are kept in the AST. */ class Scopes { public: typedef unique_ptr Ptr; typedef Scope StructScope; typedef Scope StateScope; typedef Scope VarScope; typedef Scope TableScope; typedef Scope FuncScope; Scopes() : var_id__(0), state_id_(0), var_id_(0), current_var_scope_(nullptr), top_var_scope_(nullptr), current_state_scope_(nullptr), top_state_scope_(nullptr), top_struct_scope_(new StructScope(nullptr, 1)), top_table_scope_(new TableScope(nullptr, 1)), top_func_scope_(new FuncScope(nullptr, 1)) {} ~Scopes() { delete top_func_scope_; delete top_struct_scope_; delete top_table_scope_; delete top_state_scope_; } void push_var(VarScope *scope) { if (scope == top_var_scope_) return; scope->parent_ = current_var_scope_; current_var_scope_ = scope; } void pop_var() { if (current_var_scope_ == top_var_scope_) return; VarScope *old = current_var_scope_; current_var_scope_ = old->parent_; old->parent_ = nullptr; } void push_state(StateScope *scope) { if (scope == top_state_scope_) return; scope->parent_ = current_state_scope_; current_state_scope_ = scope; } void pop_state() { if (current_state_scope_ == top_state_scope_) return; StateScope *old = current_state_scope_; current_state_scope_ = old->parent_; old->parent_ = nullptr; } /// While building the AST, allocate a new scope VarScope* enter_var_scope() { current_var_scope_ = new VarScope(current_var_scope_, next_var_id()); if (!top_var_scope_) { top_var_scope_ = current_var_scope_; } return current_var_scope_; } VarScope* exit_var_scope() { current_var_scope_ = current_var_scope_->parent_; return current_var_scope_; } StateScope* enter_state_scope() { current_state_scope_ = new StateScope(current_state_scope_, next_state_id()); if (!top_state_scope_) { top_state_scope_ = current_state_scope_; } return current_state_scope_; } StateScope* exit_state_scope() { current_state_scope_ = current_state_scope_->parent_; return current_state_scope_; } void set_current(VarScope* s) { current_var_scope_ = s; } VarScope* current_var() const { return current_var_scope_; } VarScope* top_var() const { return top_var_scope_; } void set_current(StateScope* s) { current_state_scope_ = s; } StateScope* current_state() const { return current_state_scope_; } StateScope* top_state() const { return top_state_scope_; } StructScope* top_struct() const { return top_struct_scope_; } TableScope* top_table() const { return top_table_scope_; } FuncScope* top_func() const { return top_func_scope_; } int next_id() { return ++var_id__; } int next_state_id() { return ++state_id_; } int next_var_id() { return ++var_id_; } int var_id__; int state_id_; int var_id_; VarScope* current_var_scope_; VarScope* top_var_scope_; StateScope* current_state_scope_; StateScope* top_state_scope_; StructScope* top_struct_scope_; TableScope* top_table_scope_; FuncScope* top_func_scope_; }; } // namespace cc } // namespace ebpf bpfcc-0.12.0/src/cc/frontends/b/type_check.cc000066400000000000000000000526671357404205000207130ustar00rootroot00000000000000/* * Copyright (c) 2015 PLUMgrid, Inc. * * 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. */ #include #include #include "bcc_exception.h" #include "type_check.h" #include "lexer.h" namespace ebpf { namespace cc { using std::for_each; using std::set; StatusTuple TypeCheck::visit_block_stmt_node(BlockStmtNode *n) { // enter scope if (n->scope_) scopes_->push_var(n->scope_); if (!n->stmts_.empty()) { for (auto it = n->stmts_.begin(); it != n->stmts_.end(); ++it) TRY2((*it)->accept(this)); } if (n->scope_) scopes_->pop_var(); return StatusTuple(0); } StatusTuple TypeCheck::visit_if_stmt_node(IfStmtNode *n) { TRY2(n->cond_->accept(this)); //if (n->cond_->typeof_ != ExprNode::INTEGER) // return mkstatus_(n, "If condition must be a numeric type"); TRY2(n->true_block_->accept(this)); if (n->false_block_) { TRY2(n->false_block_->accept(this)); } return StatusTuple(0); } StatusTuple TypeCheck::visit_onvalid_stmt_node(OnValidStmtNode *n) { TRY2(n->cond_->accept(this)); auto sdecl = static_cast(n->cond_->decl_); if (sdecl->storage_type_ != StructVariableDeclStmtNode::STRUCT_REFERENCE) return mkstatus_(n, "on_valid condition must be a reference type"); TRY2(n->block_->accept(this)); if (n->else_block_) { TRY2(n->else_block_->accept(this)); } return StatusTuple(0); } StatusTuple TypeCheck::visit_switch_stmt_node(SwitchStmtNode *n) { TRY2(n->cond_->accept(this)); if (n->cond_->typeof_ != ExprNode::INTEGER) return mkstatus_(n, "Switch condition must be a numeric type"); TRY2(n->block_->accept(this)); for (auto it = n->block_->stmts_.begin(); it != n->block_->stmts_.end(); ++it) { /// @todo check for duplicates } return StatusTuple(0); } StatusTuple TypeCheck::visit_case_stmt_node(CaseStmtNode *n) { if (n->value_) { TRY2(n->value_->accept(this)); if (n->value_->typeof_ != ExprNode::INTEGER) return mkstatus_(n, "Switch condition must be a numeric type"); } TRY2(n->block_->accept(this)); return StatusTuple(0); } StatusTuple TypeCheck::visit_ident_expr_node(IdentExprNode *n) { n->decl_ = scopes_->current_var()->lookup(n->name_, SCOPE_GLOBAL); if (!n->decl_) return mkstatus_(n, "Variable %s lookup failed", n->c_str()); n->typeof_ = ExprNode::UNKNOWN; if (n->sub_name_.empty()) { if (n->decl_->storage_type_ == VariableDeclStmtNode::INTEGER) { n->typeof_ = ExprNode::INTEGER; n->bit_width_ = n->decl_->bit_width_; n->flags_[ExprNode::WRITE] = true; } else if (n->decl_->is_struct()) { n->typeof_ = ExprNode::STRUCT; auto sdecl = static_cast(n->decl_); if (sdecl->struct_id_->scope_name_ == "proto") { n->struct_type_ = proto_scopes_->top_struct()->lookup(sdecl->struct_id_->name_, true); n->flags_[ExprNode::PROTO] = true; } else { n->struct_type_ = scopes_->top_struct()->lookup(sdecl->struct_id_->name_, true); } if (!n->struct_type_) return mkstatus_(n, "Type %s has not been declared", sdecl->struct_id_->full_name().c_str()); n->bit_width_ = n->struct_type_->bit_width_; } } else { if (n->decl_->storage_type_ == VariableDeclStmtNode::INTEGER) return mkstatus_(n, "Subfield access not valid for numeric types"); auto sdecl = static_cast(n->decl_); if (sdecl->struct_id_->scope_name_ == "proto") { n->struct_type_ = proto_scopes_->top_struct()->lookup(sdecl->struct_id_->name_, true); n->flags_[ExprNode::PROTO] = true; } else { n->struct_type_ = scopes_->top_struct()->lookup(sdecl->struct_id_->name_, true); } if (!n->struct_type_) return mkstatus_(n, "Type %s has not been declared", sdecl->struct_id_->full_name().c_str()); n->sub_decl_ = n->struct_type_->field(n->sub_name_); if (!n->sub_decl_) return mkstatus_(n, "Access to invalid subfield %s.%s", n->c_str(), n->sub_name_.c_str()); if (n->sub_decl_->storage_type_ != VariableDeclStmtNode::INTEGER) return mkstatus_(n, "Accessing non-numeric subfield %s.%s", n->c_str(), n->sub_name_.c_str()); n->typeof_ = ExprNode::INTEGER; n->bit_width_ = n->sub_decl_->bit_width_; n->flags_[ExprNode::WRITE] = true; } return StatusTuple(0); } StatusTuple TypeCheck::visit_assign_expr_node(AssignExprNode *n) { /// @todo check lhs is assignable TRY2(n->lhs_->accept(this)); if (n->lhs_->typeof_ == ExprNode::STRUCT) { TRY2(n->rhs_->accept(this)); if (n->rhs_->typeof_ != ExprNode::STRUCT) return mkstatus_(n, "Right-hand side of assignment must be a struct"); } else { if (n->lhs_->typeof_ != ExprNode::INTEGER) return mkstatus_(n, "Left-hand side of assignment must be a numeric type"); if (!n->lhs_->flags_[ExprNode::WRITE]) return mkstatus_(n, "Left-hand side of assignment is read-only"); TRY2(n->rhs_->accept(this)); if (n->rhs_->typeof_ != ExprNode::INTEGER) return mkstatus_(n, "Right-hand side of assignment must be a numeric type"); } n->typeof_ = ExprNode::VOID; return StatusTuple(0); } StatusTuple TypeCheck::visit_packet_expr_node(PacketExprNode *n) { StructDeclStmtNode *struct_type = proto_scopes_->top_struct()->lookup(n->id_->name_, true); if (!struct_type) return mkstatus_(n, "Undefined packet header %s", n->id_->c_str()); if (n->id_->sub_name_.empty()) { n->typeof_ = ExprNode::STRUCT; n->struct_type_ = struct_type; } else { VariableDeclStmtNode *sub_decl = struct_type->field(n->id_->sub_name_); if (!sub_decl) return mkstatus_(n, "Access to invalid subfield %s.%s", n->id_->c_str(), n->id_->sub_name_.c_str()); n->typeof_ = ExprNode::INTEGER; if (n->is_ref()) n->bit_width_ = 64; else n->bit_width_ = sub_decl->bit_width_; } n->flags_[ExprNode::WRITE] = true; return StatusTuple(0); } StatusTuple TypeCheck::visit_integer_expr_node(IntegerExprNode *n) { n->typeof_ = ExprNode::INTEGER; n->bit_width_ = n->bits_; return StatusTuple(0); } StatusTuple TypeCheck::visit_string_expr_node(StringExprNode *n) { n->typeof_ = ExprNode::STRING; n->flags_[ExprNode::IS_REF] = true; n->bit_width_ = n->val_.size() << 3; return StatusTuple(0); } StatusTuple TypeCheck::visit_binop_expr_node(BinopExprNode *n) { TRY2(n->lhs_->accept(this)); if (n->lhs_->typeof_ != ExprNode::INTEGER) return mkstatus_(n, "Left-hand side of binary expression must be a numeric type"); TRY2(n->rhs_->accept(this)); if (n->rhs_->typeof_ != ExprNode::INTEGER) return mkstatus_(n, "Right-hand side of binary expression must be a numeric type"); n->typeof_ = ExprNode::INTEGER; switch(n->op_) { case Tok::TCEQ: case Tok::TCNE: case Tok::TCLT: case Tok::TCLE: case Tok::TCGT: case Tok::TCGE: n->bit_width_ = 1; break; default: n->bit_width_ = std::max(n->lhs_->bit_width_, n->rhs_->bit_width_); } return StatusTuple(0); } StatusTuple TypeCheck::visit_unop_expr_node(UnopExprNode *n) { TRY2(n->expr_->accept(this)); if (n->expr_->typeof_ != ExprNode::INTEGER) return mkstatus_(n, "Unary operand must be a numeric type"); n->copy_type(*n->expr_); return StatusTuple(0); } StatusTuple TypeCheck::visit_bitop_expr_node(BitopExprNode *n) { if (n->expr_->typeof_ != ExprNode::INTEGER) return mkstatus_(n, "Bitop [] can only operate on numeric types"); n->typeof_ = ExprNode::INTEGER; return StatusTuple(0); } StatusTuple TypeCheck::visit_goto_expr_node(GotoExprNode *n) { //n->id_->accept(this); n->typeof_ = ExprNode::VOID; return StatusTuple(0); } StatusTuple TypeCheck::visit_return_expr_node(ReturnExprNode *n) { TRY2(n->expr_->accept(this)); n->typeof_ = ExprNode::VOID; return StatusTuple(0); } StatusTuple TypeCheck::expect_method_arg(MethodCallExprNode *n, size_t num, size_t num_def_args = 0) { if (num_def_args == 0) { if (n->args_.size() != num) return mkstatus_(n, "%s expected %d argument%s, %zu given", n->id_->sub_name_.c_str(), num, num == 1 ? "" : "s", n->args_.size()); } else { if (n->args_.size() < num - num_def_args || n->args_.size() > num) return mkstatus_(n, "%s expected %d argument%s (%d default), %zu given", n->id_->sub_name_.c_str(), num, num == 1 ? "" : "s", num_def_args, n->args_.size()); } return StatusTuple(0); } StatusTuple TypeCheck::check_lookup_method(MethodCallExprNode *n) { auto table = scopes_->top_table()->lookup(n->id_->name_); if (!table) return mkstatus_(n, "Unknown table name %s", n->id_->c_str()); TRY2(expect_method_arg(n, 2, 1)); if (table->type_id()->name_ == "LPM") return mkstatus_(n, "LPM unsupported"); if (n->block_->scope_) { auto result = make_unique(table->leaf_id()->copy(), make_unique("_result"), VariableDeclStmtNode::STRUCT_REFERENCE); n->block_->scope_->add("_result", result.get()); n->block_->stmts_.insert(n->block_->stmts_.begin(), move(result)); } return StatusTuple(0); } StatusTuple TypeCheck::check_update_method(MethodCallExprNode *n) { auto table = scopes_->top_table()->lookup(n->id_->name_); if (!table) return mkstatus_(n, "Unknown table name %s", n->id_->c_str()); if (table->type_id()->name_ == "FIXED_MATCH" || table->type_id()->name_ == "INDEXED") TRY2(expect_method_arg(n, 2)); else if (table->type_id()->name_ == "LPM") TRY2(expect_method_arg(n, 3)); return StatusTuple(0); } StatusTuple TypeCheck::check_delete_method(MethodCallExprNode *n) { auto table = scopes_->top_table()->lookup(n->id_->name_); if (!table) return mkstatus_(n, "Unknown table name %s", n->id_->c_str()); if (table->type_id()->name_ == "FIXED_MATCH" || table->type_id()->name_ == "INDEXED") TRY2(expect_method_arg(n, 1)); else if (table->type_id()->name_ == "LPM") {} return StatusTuple(0); } StatusTuple TypeCheck::visit_method_call_expr_node(MethodCallExprNode *n) { // be sure to visit those child nodes ASAP, so their properties can // be propagated up to this node and be ready to be used for (auto it = n->args_.begin(); it != n->args_.end(); ++it) { TRY2((*it)->accept(this)); } n->typeof_ = ExprNode::VOID; if (n->id_->sub_name_.size()) { if (n->id_->sub_name_ == "lookup") { TRY2(check_lookup_method(n)); } else if (n->id_->sub_name_ == "update") { TRY2(check_update_method(n)); } else if (n->id_->sub_name_ == "delete") { TRY2(check_delete_method(n)); } else if (n->id_->sub_name_ == "rewrite_field" && n->id_->name_ == "pkt") { TRY2(expect_method_arg(n, 2)); n->args_[0]->flags_[ExprNode::IS_LHS] = true; } } else if (n->id_->name_ == "log") { if (n->args_.size() < 1) return mkstatus_(n, "%s expected at least 1 argument", n->id_->c_str()); if (n->args_[0]->typeof_ != ExprNode::STRING) return mkstatus_(n, "%s expected a string for argument 1", n->id_->c_str()); n->typeof_ = ExprNode::INTEGER; n->bit_width_ = 32; } else if (n->id_->name_ == "atomic_add") { TRY2(expect_method_arg(n, 2)); n->typeof_ = ExprNode::INTEGER; n->bit_width_ = n->args_[0]->bit_width_; n->args_[0]->flags_[ExprNode::IS_LHS] = true; } else if (n->id_->name_ == "incr_cksum") { TRY2(expect_method_arg(n, 4, 1)); n->typeof_ = ExprNode::INTEGER; n->bit_width_ = 16; } else if (n->id_->name_ == "sizeof") { TRY2(expect_method_arg(n, 1)); n->typeof_ = ExprNode::INTEGER; n->bit_width_ = 32; } else if (n->id_->name_ == "get_usec_time") { TRY2(expect_method_arg(n, 0)); n->typeof_ = ExprNode::INTEGER; n->bit_width_ = 64; } if (!n->block_->stmts_.empty()) { if (n->id_->sub_name_ != "update" && n->id_->sub_name_ != "lookup") return mkstatus_(n, "%s does not allow trailing block statements", n->id_->full_name().c_str()); TRY2(n->block_->accept(this)); } return StatusTuple(0); } StatusTuple TypeCheck::visit_table_index_expr_node(TableIndexExprNode *n) { n->table_ = scopes_->top_table()->lookup(n->id_->name_); if (!n->table_) return mkstatus_(n, "Unknown table name %s", n->id_->c_str()); TRY2(n->index_->accept(this)); if (n->index_->struct_type_ != n->table_->key_type_) return mkstatus_(n, "Key to table %s lookup must be of type %s", n->id_->c_str(), n->table_->key_id()->c_str()); if (n->sub_) { n->sub_decl_ = n->table_->leaf_type_->field(n->sub_->name_); if (!n->sub_decl_) return mkstatus_(n, "Field %s is not a member of %s", n->sub_->c_str(), n->table_->leaf_id()->c_str()); n->typeof_ = ExprNode::INTEGER; } else { n->typeof_ = ExprNode::STRUCT; n->flags_[ExprNode::IS_REF] = true; n->struct_type_ = n->table_->leaf_type_; } return StatusTuple(0); } StatusTuple TypeCheck::visit_expr_stmt_node(ExprStmtNode *n) { TRY2(n->expr_->accept(this)); return StatusTuple(0); } StatusTuple TypeCheck::visit_struct_variable_decl_stmt_node(StructVariableDeclStmtNode *n) { //TRY2(n->struct_id_->accept(this)); //TRY2(n->id_->accept(this)); if (!n->init_.empty()) { StructDeclStmtNode *type; if (n->struct_id_->scope_name_ == "proto") type = proto_scopes_->top_struct()->lookup(n->struct_id_->name_, true); else type = scopes_->top_struct()->lookup(n->struct_id_->name_, true); if (!type) return mkstatus_(n, "type %s does not exist", n->struct_id_->full_name().c_str()); // init remaining fields to 0 set used; for (auto i = n->init_.begin(); i != n->init_.end(); ++i) { auto asn = static_cast(i->get()); auto id = static_cast(asn->lhs_.get()); used.insert(id->sub_name_); } for (auto f = type->stmts_.begin(); f != type->stmts_.end(); ++f) { if (used.find((*f)->id_->name_) == used.end()) { auto id = make_unique(n->id_->name_); id->append_dot((*f)->id_->name_); n->init_.push_back(make_unique(move(id), make_unique("0"))); } } for (auto it = n->init_.begin(); it != n->init_.end(); ++it) { TRY2((*it)->accept(this)); } } return StatusTuple(0); } StatusTuple TypeCheck::visit_integer_variable_decl_stmt_node(IntegerVariableDeclStmtNode *n) { //TRY2(n->id_->accept(this)); if (!n->init_.empty()) { TRY2(n->init_[0]->accept(this)); } return StatusTuple(0); } StatusTuple TypeCheck::visit_struct_decl_stmt_node(StructDeclStmtNode *n) { //TRY2(n->id_->accept(this)); for (auto it = n->stmts_.begin(); it != n->stmts_.end(); ++it) { TRY2((*it)->accept(this)); } return StatusTuple(0); } StatusTuple TypeCheck::visit_parser_state_stmt_node(ParserStateStmtNode *n) { return StatusTuple(0); } StatusTuple TypeCheck::visit_state_decl_stmt_node(StateDeclStmtNode *n) { if (!n->id_) { return StatusTuple(0); } auto s1 = proto_scopes_->top_state()->lookup(n->id_->name_, true); if (s1) { const string &name = n->id_->name_; auto offset_var = make_unique(make_unique("$" + name), "64"); offset_var->init_.push_back(make_unique(offset_var->id_->copy(), make_unique("0"))); scopes_->current_var()->add("$" + name, offset_var.get()); s1->subs_[0].block_->scope_->add("$" + name, offset_var.get()); n->init_.push_back(move(offset_var)); n->parser_ = ParserStateStmtNode::make(n->id_); n->parser_->next_state_ = s1->subs_[0].block_.get(); n->parser_->scope_id_ = n->scope_id_; auto p = proto_scopes_->top_struct()->lookup(n->id_->name_, true); if (!p) return mkstatus_(n, "unable to find struct decl for parser state %s", n->id_->full_name().c_str()); // $proto = parsed_bytes; parsed_bytes += sizeof($proto); auto asn1 = make_unique(make_unique("$" + n->id_->name_), make_unique("parsed_bytes")); n->init_.push_back(make_unique(move(asn1))); auto add_expr = make_unique(make_unique("parsed_bytes"), Tok::TPLUS, make_unique(std::to_string(p->bit_width_ >> 3), 64)); auto asn2 = make_unique(make_unique("parsed_bytes"), move(add_expr)); n->init_.push_back(make_unique(move(asn2))); } for (auto it = n->init_.begin(); it != n->init_.end(); ++it) { TRY2((*it)->accept(this)); } for (auto it = n->subs_.begin(); it != n->subs_.end(); ++it) { scopes_->push_state(it->scope_); TRY2(it->block_->accept(this)); if (s1) { if (it->id_->name_ == "") { it->parser_ = ParserStateStmtNode::make(it->id_); it->parser_->next_state_ = s1->subs_[0].block_.get(); it->parser_->scope_id_ = n->scope_id_ + n->id_->name_ + "_"; } else if (auto s2 = proto_scopes_->top_state()->lookup(it->id_->name_, true)) { it->parser_ = ParserStateStmtNode::make(it->id_); it->parser_->next_state_ = s2->subs_[0].block_.get(); it->parser_->scope_id_ = n->scope_id_ + n->id_->name_ + "_"; } if (it->parser_) { TRY2(it->parser_->accept(this)); } } scopes_->pop_state(); } return StatusTuple(0); } StatusTuple TypeCheck::visit_match_decl_stmt_node(MatchDeclStmtNode *n) { //TRY2(n->id_->accept(this)); for (auto it = n->formals_.begin(); it != n->formals_.end(); ++it) { TRY2((*it)->accept(this)); } TRY2(n->block_->accept(this)); return StatusTuple(0); } StatusTuple TypeCheck::visit_miss_decl_stmt_node(MissDeclStmtNode *n) { //TRY2(n->id_->accept(this)); for (auto it = n->formals_.begin(); it != n->formals_.end(); ++it) { TRY2((*it)->accept(this)); } TRY2(n->block_->accept(this)); return StatusTuple(0); } StatusTuple TypeCheck::visit_failure_decl_stmt_node(FailureDeclStmtNode *n) { //TRY2(n->id_->accept(this)); for (auto it = n->formals_.begin(); it != n->formals_.end(); ++it) { TRY2((*it)->accept(this)); } TRY2(n->block_->accept(this)); return StatusTuple(0); } StatusTuple TypeCheck::visit_table_decl_stmt_node(TableDeclStmtNode *n) { n->key_type_ = scopes_->top_struct()->lookup(n->key_id()->name_, true); if (!n->key_type_) return mkstatus_(n, "Table key type %s undefined", n->key_id()->c_str()); n->key_id()->bit_width_ = n->key_type_->bit_width_; n->leaf_type_ = scopes_->top_struct()->lookup(n->leaf_id()->name_, true); if (!n->leaf_type_) return mkstatus_(n, "Table leaf type %s undefined", n->leaf_id()->c_str()); n->leaf_id()->bit_width_ = n->leaf_type_->bit_width_; if (n->type_id()->name_ == "INDEXED" && n->policy_id()->name_ != "AUTO") { fprintf(stderr, "Table %s is INDEXED, policy should be AUTO\n", n->id_->c_str()); n->policy_id()->name_ = "AUTO"; } if (n->policy_id()->name_ != "AUTO" && n->policy_id()->name_ != "NONE") return mkstatus_(n, "Unsupported policy type %s", n->policy_id()->c_str()); return StatusTuple(0); } StatusTuple TypeCheck::visit_func_decl_stmt_node(FuncDeclStmtNode *n) { for (auto it = n->formals_.begin(); it != n->formals_.end(); ++it) { VariableDeclStmtNode *var = it->get(); TRY2(var->accept(this)); if (var->is_struct()) { if (!var->is_pointer()) return mkstatus_(n, "Only struct references allowed in function definitions"); } } scopes_->push_state(n->scope_); TRY2(n->block_->accept(this)); scopes_->pop_state(); return StatusTuple(0); } StatusTuple TypeCheck::visit(Node *root) { BlockStmtNode *b = static_cast(root); scopes_->set_current(scopes_->top_state()); scopes_->set_current(scopes_->top_var()); // // packet data in bpf socket // if (scopes_->top_struct()->lookup("_skbuff", true)) { // return StatusTuple(-1, "_skbuff already defined"); // } // auto skb_type = make_unique(make_unique("_skbuff")); // scopes_->top_struct()->add("_skbuff", skb_type.get()); // b->stmts_.push_back(move(skb_type)); // if (scopes_->current_var()->lookup("skb", true)) { // return StatusTuple(-1, "skb already defined"); // } // auto skb = make_unique(make_unique("_skbuff"), // make_unique("skb")); // skb->storage_type_ = VariableDeclStmtNode::STRUCT_REFERENCE; // scopes_->current_var()->add("skb", skb.get()); // b->stmts_.push_back(move(skb)); // offset counter auto parsed_bytes = make_unique( make_unique("parsed_bytes"), "64"); parsed_bytes->init_.push_back(make_unique(parsed_bytes->id_->copy(), make_unique("0"))); scopes_->current_var()->add("parsed_bytes", parsed_bytes.get()); b->stmts_.push_back(move(parsed_bytes)); TRY2(b->accept(this)); if (!errors_.empty()) { for (auto it = errors_.begin(); it != errors_.end(); ++it) { fprintf(stderr, "%s\n", it->c_str()); } return StatusTuple(-1, errors_.begin()->c_str()); } return StatusTuple(0); } } // namespace cc } // namespace ebpf bpfcc-0.12.0/src/cc/frontends/b/type_check.h000066400000000000000000000026061357404205000205410ustar00rootroot00000000000000/* * Copyright (c) 2015 PLUMgrid, Inc. * * 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. */ #pragma once #include #include #include "node.h" #include "scope.h" namespace ebpf { namespace cc { class TypeCheck : public Visitor { public: TypeCheck(Scopes *scopes, Scopes *proto_scopes) : scopes_(scopes), proto_scopes_(proto_scopes) {} virtual STATUS_RETURN visit(Node* n); STATUS_RETURN expect_method_arg(MethodCallExprNode* n, size_t num, size_t num_def_args); STATUS_RETURN check_lookup_method(MethodCallExprNode* n); STATUS_RETURN check_update_method(MethodCallExprNode* n); STATUS_RETURN check_delete_method(MethodCallExprNode* n); #define VISIT(type, func) virtual STATUS_RETURN visit_##func(type* n); EXPAND_NODES(VISIT) #undef VISIT private: Scopes *scopes_; Scopes *proto_scopes_; vector errors_; }; } // namespace cc } // namespace ebpf bpfcc-0.12.0/src/cc/frontends/b/type_helper.h000066400000000000000000000054541357404205000207470ustar00rootroot00000000000000/* * Copyright (c) 2015 PLUMgrid, Inc. * * 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. */ #pragma once namespace ebpf { namespace cc { // Represent the numeric type of a protocol field enum FieldType { INVALID = 0, UINT8_T, UINT16_T, UINT32_T, UINT64_T, #ifdef __SIZEOF_INT128__ UINT128_T, #endif VOID }; static inline size_t enum_to_size(const FieldType t) { switch (t) { case UINT8_T: return sizeof(uint8_t); case UINT16_T: return sizeof(uint16_t); case UINT32_T: return sizeof(uint32_t); case UINT64_T: return sizeof(uint64_t); #ifdef __SIZEOF_INT128__ case UINT128_T: return sizeof(__uint128_t); #endif default: return 0; } } /// Convert a bit size to the next highest power of 2 static inline int next_base2(int v) { --v; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; ++v; return v; } static inline const char* bits_to_uint(int v) { v = next_base2(v); if (v <= 8) { return "uint8_t"; } else if (v == 16) { return "uint16_t"; } else if (v == 32) { return "uint32_t"; } else if (v == 64) { return "uint64_t"; } else if (v >= 128) { /* in plumlet 128-bit integers should be 8-byte aligned, * all other ints should have natural alignment */ return "unsigned __int128 __attribute__((packed, aligned(8)))"; } return "void"; } static inline FieldType bits_to_enum(int v) { v = next_base2(v); if (v <= 8) { return UINT8_T; } else if (v == 16) { return UINT16_T; } else if (v == 32) { return UINT32_T; } else if (v == 64) { return UINT64_T; #ifdef __SIZEOF_INT128__ } else if (v >= 128) { return UINT128_T; #endif } return VOID; } static inline size_t bits_to_size(int v) { return enum_to_size(bits_to_enum(v)); } static inline size_t align_offset(size_t offset, FieldType ft) { switch (ft) { case UINT8_T: return offset % 8 > 0 ? offset + (8 - offset % 8) : offset; case UINT16_T: return offset % 16 > 0 ? offset + (16 - offset % 16) : offset; case UINT32_T: return offset % 32 > 0 ? offset + (32 - offset % 32) : offset; case UINT64_T: #ifdef __SIZEOF_INT128__ case UINT128_T: #endif return offset % 64 > 0 ? offset + (64 - offset % 64) : offset; default: ; } return offset; } } // namespace cc } // namespace ebpf bpfcc-0.12.0/src/cc/frontends/clang/000077500000000000000000000000001357404205000171115ustar00rootroot00000000000000bpfcc-0.12.0/src/cc/frontends/clang/CMakeLists.txt000066400000000000000000000011061357404205000216470ustar00rootroot00000000000000# Copyright (c) PLUMgrid, Inc. # Licensed under the Apache License, Version 2.0 (the "License") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DKERNEL_MODULES_DIR='\"${BCC_KERNEL_MODULES_DIR}\"'") if(DEFINED BCC_CUR_CPU_IDENTIFIER) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DCUR_CPU_IDENTIFIER='\"${BCC_CUR_CPU_IDENTIFIER}\"'") endif() if(DEFINED BCC_BACKUP_COMPILE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DBCC_BACKUP_COMPILE='${BCC_BACKUP_COMPILE}'") endif() add_library(clang_frontend STATIC loader.cc b_frontend_action.cc tp_frontend_action.cc kbuild_helper.cc ../../common.cc) bpfcc-0.12.0/src/cc/frontends/clang/arch_helper.h000066400000000000000000000032101357404205000215320ustar00rootroot00000000000000/* * Copyright (c) 2018 Google, Inc. * * 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. */ #include #include typedef enum { BCC_ARCH_PPC, BCC_ARCH_PPC_LE, BCC_ARCH_S390X, BCC_ARCH_ARM64, BCC_ARCH_X86 } bcc_arch_t; typedef void *(*arch_callback_t)(bcc_arch_t arch); static void *run_arch_callback(arch_callback_t fn) { const char *archenv = getenv("ARCH"); /* If ARCH is not set, detect from local arch clang is running on */ if (!archenv) { #if defined(__powerpc64__) #if defined(_CALL_ELF) && _CALL_ELF == 2 return fn(BCC_ARCH_PPC_LE); #else return fn(BCC_ARCH_PPC); #endif #elif defined(__s390x__) return fn(BCC_ARCH_S390X); #elif defined(__aarch64__) return fn(BCC_ARCH_ARM64); #else return fn(BCC_ARCH_X86); #endif } /* Otherwise read it from ARCH */ if (!strcmp(archenv, "powerpc")) { #if defined(_CALL_ELF) && _CALL_ELF == 2 return fn(BCC_ARCH_PPC_LE); #else return fn(BCC_ARCH_PPC); #endif } else if (!strcmp(archenv, "s390x")) { return fn(BCC_ARCH_S390X); } else if (!strcmp(archenv, "arm64")) { return fn(BCC_ARCH_ARM64); } else { return fn(BCC_ARCH_X86); } } bpfcc-0.12.0/src/cc/frontends/clang/b_frontend_action.cc000066400000000000000000001602551357404205000231060ustar00rootroot00000000000000/* * Copyright (c) 2015 PLUMgrid, Inc. * * 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "frontend_action_common.h" #include "b_frontend_action.h" #include "bpf_module.h" #include "common.h" #include "loader.h" #include "table_storage.h" #include "arch_helper.h" #include "libbpf/src/bpf.h" #include "libbpf.h" namespace ebpf { constexpr int MAX_CALLING_CONV_REGS = 6; const char *calling_conv_regs_x86[] = { "di", "si", "dx", "cx", "r8", "r9" }; const char *calling_conv_regs_ppc[] = {"gpr[3]", "gpr[4]", "gpr[5]", "gpr[6]", "gpr[7]", "gpr[8]"}; const char *calling_conv_regs_s390x[] = {"gprs[2]", "gprs[3]", "gprs[4]", "gprs[5]", "gprs[6]" }; const char *calling_conv_regs_arm64[] = {"regs[0]", "regs[1]", "regs[2]", "regs[3]", "regs[4]", "regs[5]"}; void *get_call_conv_cb(bcc_arch_t arch) { const char **ret; switch(arch) { case BCC_ARCH_PPC: case BCC_ARCH_PPC_LE: ret = calling_conv_regs_ppc; break; case BCC_ARCH_S390X: ret = calling_conv_regs_s390x; break; case BCC_ARCH_ARM64: ret = calling_conv_regs_arm64; break; default: ret = calling_conv_regs_x86; } return (void *)ret; } const char **get_call_conv(void) { const char **ret; ret = (const char **)run_arch_callback(get_call_conv_cb); return ret; } using std::map; using std::move; using std::set; using std::tuple; using std::make_tuple; using std::string; using std::to_string; using std::unique_ptr; using std::vector; using namespace clang; class ProbeChecker : public RecursiveASTVisitor { public: explicit ProbeChecker(Expr *arg, const set> &ptregs, bool track_helpers, bool is_assign) : needs_probe_(false), is_transitive_(false), ptregs_(ptregs), track_helpers_(track_helpers), nb_derefs_(0), is_assign_(is_assign) { if (arg) { TraverseStmt(arg); if (arg->getType()->isPointerType()) is_transitive_ = needs_probe_; } } explicit ProbeChecker(Expr *arg, const set> &ptregs, bool is_transitive) : ProbeChecker(arg, ptregs, is_transitive, false) {} bool VisitCallExpr(CallExpr *E) { needs_probe_ = false; if (is_assign_) { // We're looking for a function that returns an external pointer, // regardless of the number of dereferences. for(auto p : ptregs_) { if (std::get<0>(p) == E->getDirectCallee()) { needs_probe_ = true; nb_derefs_ += std::get<1>(p); return false; } } } else { tuple pt = make_tuple(E->getDirectCallee(), nb_derefs_); if (ptregs_.find(pt) != ptregs_.end()) needs_probe_ = true; } if (!track_helpers_) return false; if (VarDecl *V = dyn_cast(E->getCalleeDecl())) needs_probe_ = V->getName() == "bpf_get_current_task"; return false; } bool VisitMemberExpr(MemberExpr *M) { tuple pt = make_tuple(M->getMemberDecl(), nb_derefs_); if (ptregs_.find(pt) != ptregs_.end()) { needs_probe_ = true; return false; } if (M->isArrow()) { /* In A->b, if A is an external pointer, then A->b should be considered * one too. However, if we're taking the address of A->b * (nb_derefs_ < 0), we should take it into account for the number of * indirections; &A->b is a pointer to A with an offset. */ if (nb_derefs_ >= 0) { ProbeChecker checker = ProbeChecker(M->getBase(), ptregs_, track_helpers_, is_assign_); if (checker.needs_probe() && checker.get_nb_derefs() == 0) { needs_probe_ = true; return false; } } nb_derefs_++; } return true; } bool VisitUnaryOperator(UnaryOperator *E) { if (E->getOpcode() == UO_Deref) { /* In *A, if A is an external pointer, then *A should be considered one * too. */ ProbeChecker checker = ProbeChecker(E->getSubExpr(), ptregs_, track_helpers_, is_assign_); if (checker.needs_probe() && checker.get_nb_derefs() == 0) { needs_probe_ = true; return false; } nb_derefs_++; } else if (E->getOpcode() == UO_AddrOf) { nb_derefs_--; } return true; } bool VisitDeclRefExpr(DeclRefExpr *E) { if (is_assign_) { // We're looking for an external pointer, regardless of the number of // dereferences. for(auto p : ptregs_) { if (std::get<0>(p) == E->getDecl()) { needs_probe_ = true; nb_derefs_ += std::get<1>(p); return false; } } } else { tuple pt = make_tuple(E->getDecl(), nb_derefs_); if (ptregs_.find(pt) != ptregs_.end()) needs_probe_ = true; } return true; } bool needs_probe() const { return needs_probe_; } bool is_transitive() const { return is_transitive_; } int get_nb_derefs() const { return nb_derefs_; } private: bool needs_probe_; bool is_transitive_; const set> &ptregs_; bool track_helpers_; // Nb of dereferences we go through before finding the external pointer. // A negative number counts the number of addrof. int nb_derefs_; bool is_assign_; }; // Visit a piece of the AST and mark it as needing probe reads class ProbeSetter : public RecursiveASTVisitor { public: explicit ProbeSetter(set> *ptregs, int nb_addrof) : ptregs_(ptregs), nb_derefs_(-nb_addrof) {} bool VisitDeclRefExpr(DeclRefExpr *E) { tuple pt = make_tuple(E->getDecl(), nb_derefs_); ptregs_->insert(pt); return true; } explicit ProbeSetter(set> *ptregs) : ProbeSetter(ptregs, 0) {} bool VisitUnaryOperator(UnaryOperator *E) { if (E->getOpcode() == UO_Deref) nb_derefs_++; return true; } bool VisitMemberExpr(MemberExpr *M) { tuple pt = make_tuple(M->getMemberDecl(), nb_derefs_); ptregs_->insert(pt); return false; } private: set> *ptregs_; // Nb of dereferences we go through before getting to the actual variable. int nb_derefs_; }; MapVisitor::MapVisitor(set &m) : m_(m) {} bool MapVisitor::VisitCallExpr(CallExpr *Call) { if (MemberExpr *Memb = dyn_cast(Call->getCallee()->IgnoreImplicit())) { StringRef memb_name = Memb->getMemberDecl()->getName(); if (DeclRefExpr *Ref = dyn_cast(Memb->getBase())) { if (SectionAttr *A = Ref->getDecl()->getAttr()) { if (!A->getName().startswith("maps")) return true; if (memb_name == "update" || memb_name == "insert") { ProbeChecker checker = ProbeChecker(Call->getArg(1), ptregs_, true, true); if (checker.needs_probe()) m_.insert(Ref->getDecl()); } } } } return true; } ProbeVisitor::ProbeVisitor(ASTContext &C, Rewriter &rewriter, set &m, bool track_helpers) : C(C), rewriter_(rewriter), m_(m), track_helpers_(track_helpers), addrof_stmt_(nullptr), is_addrof_(false) {} bool ProbeVisitor::assignsExtPtr(Expr *E, int *nbAddrOf) { if (IsContextMemberExpr(E)) { *nbAddrOf = 0; return true; } /* If the expression contains a call to another function, we need to visit * that function first to know if a rewrite is necessary (i.e., if the * function returns an external pointer). */ if (!TraverseStmt(E)) return false; ProbeChecker checker = ProbeChecker(E, ptregs_, track_helpers_, true); if (checker.is_transitive()) { // The negative of the number of dereferences is the number of addrof. In // an assignment, if we went through n addrof before getting the external // pointer, then we'll need n dereferences on the left-hand side variable // to get to the external pointer. *nbAddrOf = -checker.get_nb_derefs(); return true; } if (E->IgnoreParenCasts()->getStmtClass() == Stmt::CallExprClass) { CallExpr *Call = dyn_cast(E->IgnoreParenCasts()); if (MemberExpr *Memb = dyn_cast(Call->getCallee()->IgnoreImplicit())) { StringRef memb_name = Memb->getMemberDecl()->getName(); if (DeclRefExpr *Ref = dyn_cast(Memb->getBase())) { if (SectionAttr *A = Ref->getDecl()->getAttr()) { if (!A->getName().startswith("maps")) return false; if (memb_name == "lookup" || memb_name == "lookup_or_init" || memb_name == "lookup_or_try_init") { if (m_.find(Ref->getDecl()) != m_.end()) { // Retrieved an ext. pointer from a map, mark LHS as ext. pointer. // Pointers from maps always need a single dereference to get the // actual value. The value may be an external pointer but cannot // be a pointer to an external pointer as the verifier prohibits // storing known pointers (to map values, context, the stack, or // the packet) in maps. *nbAddrOf = 1; return true; } } } } } } return false; } bool ProbeVisitor::VisitVarDecl(VarDecl *D) { if (Expr *E = D->getInit()) { int nbAddrOf; if (assignsExtPtr(E, &nbAddrOf)) { // The negative of the number of addrof is the number of dereferences. tuple pt = make_tuple(D, -nbAddrOf); set_ptreg(pt); } } return true; } bool ProbeVisitor::TraverseStmt(Stmt *S) { if (whitelist_.find(S) != whitelist_.end()) return true; auto ret = RecursiveASTVisitor::TraverseStmt(S); if (addrof_stmt_ == S) { addrof_stmt_ = nullptr; is_addrof_ = false; } return ret; } bool ProbeVisitor::VisitCallExpr(CallExpr *Call) { // Skip bpf_probe_read for the third argument if it is an AddrOf. if (VarDecl *V = dyn_cast(Call->getCalleeDecl())) { if (V->getName() == "bpf_probe_read" && Call->getNumArgs() >= 3) { const Expr *E = Call->getArg(2)->IgnoreParenCasts(); whitelist_.insert(E); return true; } } if (FunctionDecl *F = dyn_cast(Call->getCalleeDecl())) { if (F->hasBody()) { unsigned i = 0; for (auto arg : Call->arguments()) { ProbeChecker checker = ProbeChecker(arg, ptregs_, track_helpers_, true); if (checker.needs_probe()) { tuple pt = make_tuple(F->getParamDecl(i), checker.get_nb_derefs()); ptregs_.insert(pt); } ++i; } if (fn_visited_.find(F) == fn_visited_.end()) { fn_visited_.insert(F); /* Maintains a stack of the number of dereferences for the external * pointers returned by each function in the call stack or -1 if the * function didn't return an external pointer. */ ptregs_returned_.push_back(-1); TraverseDecl(F); int nb_derefs = ptregs_returned_.back(); ptregs_returned_.pop_back(); if (nb_derefs != -1) { tuple pt = make_tuple(F, nb_derefs); ptregs_.insert(pt); } } } } return true; } bool ProbeVisitor::VisitReturnStmt(ReturnStmt *R) { /* If this function wasn't called by another, there's no need to check the * return statement for external pointers. */ if (ptregs_returned_.size() == 0) return true; /* Reverse order of traversals. This is needed if, in the return statement, * we're calling a function that's returning an external pointer: we need to * know what the function is returning to decide what this function is * returning. */ if (!TraverseStmt(R->getRetValue())) return false; ProbeChecker checker = ProbeChecker(R->getRetValue(), ptregs_, track_helpers_, true); if (checker.needs_probe()) { int curr_nb_derefs = ptregs_returned_.back(); /* If the function returns external pointers with different levels of * indirection, we handle the case with the highest level of indirection * and leave it to the user to manually handle other cases. */ if (checker.get_nb_derefs() > curr_nb_derefs) { ptregs_returned_.pop_back(); ptregs_returned_.push_back(checker.get_nb_derefs()); } } return true; } bool ProbeVisitor::VisitBinaryOperator(BinaryOperator *E) { if (!E->isAssignmentOp()) return true; // copy probe attribute from RHS to LHS if present int nbAddrOf; if (assignsExtPtr(E->getRHS(), &nbAddrOf)) { ProbeSetter setter(&ptregs_, nbAddrOf); setter.TraverseStmt(E->getLHS()); } return true; } bool ProbeVisitor::VisitUnaryOperator(UnaryOperator *E) { if (E->getOpcode() == UO_AddrOf) { addrof_stmt_ = E; is_addrof_ = true; } if (E->getOpcode() != UO_Deref) return true; if (memb_visited_.find(E) != memb_visited_.end()) return true; Expr *sub = E->getSubExpr(); if (!ProbeChecker(sub, ptregs_, track_helpers_).needs_probe()) return true; memb_visited_.insert(E); string pre, post; pre = "({ typeof(" + E->getType().getAsString() + ") _val; __builtin_memset(&_val, 0, sizeof(_val));"; pre += " bpf_probe_read(&_val, sizeof(_val), (u64)"; post = "); _val; })"; rewriter_.ReplaceText(expansionLoc(E->getOperatorLoc()), 1, pre); rewriter_.InsertTextAfterToken(expansionLoc(GET_ENDLOC(sub)), post); return true; } bool ProbeVisitor::VisitMemberExpr(MemberExpr *E) { if (memb_visited_.find(E) != memb_visited_.end()) return true; Expr *base; SourceLocation rhs_start, member; bool found = false; for (MemberExpr *M = E; M; M = dyn_cast(M->getBase())) { memb_visited_.insert(M); rhs_start = GET_ENDLOC(M); base = M->getBase(); member = M->getMemberLoc(); if (M->isArrow()) { found = true; break; } } if (!found) return true; if (member.isInvalid()) { error(GET_ENDLOC(base), "internal error: MemberLoc is invalid while preparing probe rewrite"); return false; } if (!rewriter_.isRewritable(GET_BEGINLOC(E))) return true; // parent expr has addrof, skip the rewrite, set is_addrof_ to flase so // it won't affect next level of indirect address if (is_addrof_) { is_addrof_ = false; return true; } /* If the base of the dereference is a call to another function, we need to * visit that function first to know if a rewrite is necessary (i.e., if the * function returns an external pointer). */ if (base->IgnoreParenCasts()->getStmtClass() == Stmt::CallExprClass) { CallExpr *Call = dyn_cast(base->IgnoreParenCasts()); if (!TraverseStmt(Call)) return false; } // Checks to see if the expression references something that needs to be run // through bpf_probe_read. if (!ProbeChecker(base, ptregs_, track_helpers_).needs_probe()) return true; string rhs = rewriter_.getRewrittenText(expansionRange(SourceRange(rhs_start, GET_ENDLOC(E)))); string base_type = base->getType()->getPointeeType().getAsString(); string pre, post; pre = "({ typeof(" + E->getType().getAsString() + ") _val; __builtin_memset(&_val, 0, sizeof(_val));"; pre += " bpf_probe_read(&_val, sizeof(_val), (u64)&"; post = rhs + "); _val; })"; rewriter_.InsertText(expansionLoc(GET_BEGINLOC(E)), pre); rewriter_.ReplaceText(expansionRange(SourceRange(member, GET_ENDLOC(E))), post); return true; } bool ProbeVisitor::VisitArraySubscriptExpr(ArraySubscriptExpr *E) { if (memb_visited_.find(E) != memb_visited_.end()) return true; if (!ProbeChecker(E, ptregs_, track_helpers_).needs_probe()) return true; // Parent expr has addrof, skip the rewrite. if (is_addrof_) return true; if (!rewriter_.isRewritable(GET_BEGINLOC(E))) return true; Expr *base = E->getBase(); Expr *idx = E->getIdx(); memb_visited_.insert(E); if (!rewriter_.isRewritable(GET_BEGINLOC(base))) return true; if (!rewriter_.isRewritable(GET_BEGINLOC(idx))) return true; string pre, lbracket, rbracket; LangOptions opts; SourceLocation lbracket_start, lbracket_end; SourceRange lbracket_range; pre = "({ typeof(" + E->getType().getAsString() + ") _val; __builtin_memset(&_val, 0, sizeof(_val));"; pre += " bpf_probe_read(&_val, sizeof(_val), (u64)(("; if (isMemberDereference(base)) { pre += "&"; // If the base of the array subscript is a member dereference, we'll rewrite // both at the same time. addrof_stmt_ = base; is_addrof_ = true; } rewriter_.InsertText(expansionLoc(GET_BEGINLOC(base)), pre); /* Replace left bracket and any space around it. Since Clang doesn't provide * a method to retrieve the left bracket, replace everything from the end of * the base to the start of the index. */ lbracket = ") + ("; lbracket_start = Lexer::getLocForEndOfToken(GET_ENDLOC(base), 1, rewriter_.getSourceMgr(), opts).getLocWithOffset(1); lbracket_end = GET_BEGINLOC(idx).getLocWithOffset(-1); lbracket_range = expansionRange(SourceRange(lbracket_start, lbracket_end)); rewriter_.ReplaceText(lbracket_range, lbracket); rbracket = "))); _val; })"; rewriter_.ReplaceText(expansionLoc(E->getRBracketLoc()), 1, rbracket); return true; } bool ProbeVisitor::isMemberDereference(Expr *E) { if (E->IgnoreParenCasts()->getStmtClass() != Stmt::MemberExprClass) return false; for (MemberExpr *M = dyn_cast(E->IgnoreParenCasts()); M; M = dyn_cast(M->getBase()->IgnoreParenCasts())) { if (M->isArrow()) return true; } return false; } bool ProbeVisitor::IsContextMemberExpr(Expr *E) { if (!E->getType()->isPointerType()) return false; Expr *base; SourceLocation member; bool found = false; MemberExpr *M; Expr *Ex = E->IgnoreParenCasts(); while (Ex->getStmtClass() == Stmt::ArraySubscriptExprClass || Ex->getStmtClass() == Stmt::MemberExprClass) { if (Ex->getStmtClass() == Stmt::ArraySubscriptExprClass) { Ex = dyn_cast(Ex)->getBase()->IgnoreParenCasts(); } else if (Ex->getStmtClass() == Stmt::MemberExprClass) { M = dyn_cast(Ex); base = M->getBase()->IgnoreParenCasts(); member = M->getMemberLoc(); if (M->isArrow()) { found = true; break; } Ex = base; } } if (!found) { return false; } if (member.isInvalid()) { return false; } if (DeclRefExpr *base_expr = dyn_cast(base)) { if (base_expr->getDecl() == ctx_) { return true; } } return false; } SourceRange ProbeVisitor::expansionRange(SourceRange range) { #if LLVM_MAJOR_VERSION >= 7 return rewriter_.getSourceMgr().getExpansionRange(range).getAsRange(); #else return rewriter_.getSourceMgr().getExpansionRange(range); #endif } SourceLocation ProbeVisitor::expansionLoc(SourceLocation loc) { return rewriter_.getSourceMgr().getExpansionLoc(loc); } template DiagnosticBuilder ProbeVisitor::error(SourceLocation loc, const char (&fmt)[N]) { unsigned int diag_id = C.getDiagnostics().getCustomDiagID(DiagnosticsEngine::Error, fmt); return C.getDiagnostics().Report(loc, diag_id); } BTypeVisitor::BTypeVisitor(ASTContext &C, BFrontendAction &fe) : C(C), diag_(C.getDiagnostics()), fe_(fe), rewriter_(fe.rewriter()), out_(llvm::errs()) {} void BTypeVisitor::genParamDirectAssign(FunctionDecl *D, string& preamble, const char **calling_conv_regs) { for (size_t idx = 0; idx < fn_args_.size(); idx++) { ParmVarDecl *arg = fn_args_[idx]; if (idx >= 1) { // Move the args into a preamble section where the same params are // declared and initialized from pt_regs. // Todo: this init should be done only when the program requests it. string text = rewriter_.getRewrittenText(expansionRange(arg->getSourceRange())); arg->addAttr(UnavailableAttr::CreateImplicit(C, "ptregs")); size_t d = idx - 1; const char *reg = calling_conv_regs[d]; preamble += " " + text + " = " + fn_args_[0]->getName().str() + "->" + string(reg) + ";"; } } } void BTypeVisitor::genParamIndirectAssign(FunctionDecl *D, string& preamble, const char **calling_conv_regs) { string new_ctx; for (size_t idx = 0; idx < fn_args_.size(); idx++) { ParmVarDecl *arg = fn_args_[idx]; if (idx == 0) { new_ctx = "__" + arg->getName().str(); preamble += " struct pt_regs * " + new_ctx + " = " + arg->getName().str() + "->" + string(calling_conv_regs[0]) + ";"; } else { // Move the args into a preamble section where the same params are // declared and initialized from pt_regs. // Todo: this init should be done only when the program requests it. string text = rewriter_.getRewrittenText(expansionRange(arg->getSourceRange())); size_t d = idx - 1; const char *reg = calling_conv_regs[d]; preamble += "\n " + text + ";"; preamble += " bpf_probe_read(&" + arg->getName().str() + ", sizeof(" + arg->getName().str() + "), &" + new_ctx + "->" + string(reg) + ");"; } } } void BTypeVisitor::rewriteFuncParam(FunctionDecl *D) { const char **calling_conv_regs = get_call_conv(); string preamble = "{\n"; if (D->param_size() > 1) { // If function prefix is "syscall__" or "kprobe____x64_sys_", // the function will attach to a kprobe syscall function. // Guard parameter assiggnment with CONFIG_ARCH_HAS_SYSCALL_WRAPPER. // For __x64_sys_* syscalls, this is always true, but we guard // it in case of "syscall__" for other architectures. if (strncmp(D->getName().str().c_str(), "syscall__", 9) == 0 || strncmp(D->getName().str().c_str(), "kprobe____x64_sys_", 18) == 0) { preamble += "#ifdef CONFIG_ARCH_HAS_SYSCALL_WRAPPER\n"; genParamIndirectAssign(D, preamble, calling_conv_regs); preamble += "\n#else\n"; genParamDirectAssign(D, preamble, calling_conv_regs); preamble += "\n#endif\n"; } else { genParamDirectAssign(D, preamble, calling_conv_regs); } rewriter_.ReplaceText( expansionRange(SourceRange(GET_ENDLOC(D->getParamDecl(0)), GET_ENDLOC(D->getParamDecl(D->getNumParams() - 1)))), fn_args_[0]->getName()); } // for each trace argument, convert the variable from ptregs to something on stack if (CompoundStmt *S = dyn_cast(D->getBody())) rewriter_.ReplaceText(S->getLBracLoc(), 1, preamble); } bool BTypeVisitor::VisitFunctionDecl(FunctionDecl *D) { // put each non-static non-inline function decl in its own section, to be // extracted by the MemoryManager auto real_start_loc = rewriter_.getSourceMgr().getFileLoc(GET_BEGINLOC(D)); if (fe_.is_rewritable_ext_func(D)) { current_fn_ = D->getName(); string bd = rewriter_.getRewrittenText(expansionRange(D->getSourceRange())); fe_.func_src_.set_src(current_fn_, bd); fe_.func_range_[current_fn_] = expansionRange(D->getSourceRange()); string attr = string("__attribute__((section(\"") + BPF_FN_PREFIX + D->getName().str() + "\")))\n"; rewriter_.InsertText(real_start_loc, attr); if (D->param_size() > MAX_CALLING_CONV_REGS + 1) { error(GET_BEGINLOC(D->getParamDecl(MAX_CALLING_CONV_REGS + 1)), "too many arguments, bcc only supports in-register parameters"); return false; } fn_args_.clear(); for (auto arg_it = D->param_begin(); arg_it != D->param_end(); arg_it++) { auto *arg = *arg_it; if (arg->getName() == "") { error(GET_ENDLOC(arg), "arguments to BPF program definition must be named"); return false; } fn_args_.push_back(arg); } rewriteFuncParam(D); } else if (D->hasBody() && rewriter_.getSourceMgr().getFileID(real_start_loc) == rewriter_.getSourceMgr().getMainFileID()) { // rewritable functions that are static should be always treated as helper rewriter_.InsertText(real_start_loc, "__attribute__((always_inline))\n"); } return true; } // Reverse the order of call traversal so that parameters inside of // function calls will get rewritten before the call itself, otherwise // text mangling will result. bool BTypeVisitor::TraverseCallExpr(CallExpr *Call) { for (auto child : Call->children()) if (!TraverseStmt(child)) return false; if (!WalkUpFromCallExpr(Call)) return false; return true; } // convert calls of the type: // table.foo(&key) // to: // bpf_table_foo_elem(bpf_pseudo_fd(table), &key [,&leaf]) bool BTypeVisitor::VisitCallExpr(CallExpr *Call) { // make sure node is a reference to a bpf table, which is assured by the // presence of the section("maps/") GNU __attribute__ if (MemberExpr *Memb = dyn_cast(Call->getCallee()->IgnoreImplicit())) { StringRef memb_name = Memb->getMemberDecl()->getName(); if (DeclRefExpr *Ref = dyn_cast(Memb->getBase())) { if (SectionAttr *A = Ref->getDecl()->getAttr()) { if (!A->getName().startswith("maps")) return true; string args = rewriter_.getRewrittenText(expansionRange(SourceRange(GET_BEGINLOC(Call->getArg(0)), GET_ENDLOC(Call->getArg(Call->getNumArgs() - 1))))); // find the table fd, which was opened at declaration time TableStorage::iterator desc; Path local_path({fe_.id(), Ref->getDecl()->getName()}); Path global_path({Ref->getDecl()->getName()}); if (!fe_.table_storage().Find(local_path, desc)) { if (!fe_.table_storage().Find(global_path, desc)) { error(GET_ENDLOC(Ref), "bpf_table %0 failed to open") << Ref->getDecl()->getName(); return false; } } string fd = to_string(desc->second.fd >= 0 ? desc->second.fd : desc->second.fake_fd); string prefix, suffix; string txt; auto rewrite_start = GET_BEGINLOC(Call); auto rewrite_end = GET_ENDLOC(Call); if (memb_name == "lookup_or_init" || memb_name == "lookup_or_try_init") { string name = Ref->getDecl()->getName(); string arg0 = rewriter_.getRewrittenText(expansionRange(Call->getArg(0)->getSourceRange())); string arg1 = rewriter_.getRewrittenText(expansionRange(Call->getArg(1)->getSourceRange())); string lookup = "bpf_map_lookup_elem_(bpf_pseudo_fd(1, " + fd + ")"; string update = "bpf_map_update_elem_(bpf_pseudo_fd(1, " + fd + ")"; txt = "({typeof(" + name + ".leaf) *leaf = " + lookup + ", " + arg0 + "); "; txt += "if (!leaf) {"; txt += " " + update + ", " + arg0 + ", " + arg1 + ", BPF_NOEXIST);"; txt += " leaf = " + lookup + ", " + arg0 + ");"; if (memb_name == "lookup_or_init") { txt += " if (!leaf) return 0;"; } txt += "}"; txt += "leaf;})"; } else if (memb_name == "increment") { string name = Ref->getDecl()->getName(); string arg0 = rewriter_.getRewrittenText(expansionRange(Call->getArg(0)->getSourceRange())); string increment_value = "1"; if (Call->getNumArgs() == 2) { increment_value = rewriter_.getRewrittenText(expansionRange(Call->getArg(1)->getSourceRange())); } string lookup = "bpf_map_lookup_elem_(bpf_pseudo_fd(1, " + fd + ")"; string update = "bpf_map_update_elem_(bpf_pseudo_fd(1, " + fd + ")"; txt = "({ typeof(" + name + ".key) _key = " + arg0 + "; "; txt += "typeof(" + name + ".leaf) *_leaf = " + lookup + ", &_key); "; txt += "if (_leaf) (*_leaf) += " + increment_value + ";"; if (desc->second.type == BPF_MAP_TYPE_HASH) { txt += "else { typeof(" + name + ".leaf) _zleaf; __builtin_memset(&_zleaf, 0, sizeof(_zleaf)); "; txt += "_zleaf += " + increment_value + ";"; txt += update + ", &_key, &_zleaf, BPF_NOEXIST); } "; } txt += "})"; } else if (memb_name == "perf_submit") { string name = Ref->getDecl()->getName(); string arg0 = rewriter_.getRewrittenText(expansionRange(Call->getArg(0)->getSourceRange())); string args_other = rewriter_.getRewrittenText(expansionRange(SourceRange(GET_BEGINLOC(Call->getArg(1)), GET_ENDLOC(Call->getArg(2))))); txt = "bpf_perf_event_output(" + arg0 + ", bpf_pseudo_fd(1, " + fd + ")"; txt += ", CUR_CPU_IDENTIFIER, " + args_other + ")"; // e.g. // struct data_t { u32 pid; }; data_t data; // events.perf_submit(ctx, &data, sizeof(data)); // ... // &data -> data -> typeof(data) -> data_t auto type_arg1 = Call->getArg(1)->IgnoreCasts()->getType().getTypePtr()->getPointeeType().getTypePtr(); if (type_arg1->isStructureType()) { auto event_type = type_arg1->getAsTagDecl(); const auto *r = dyn_cast(event_type); std::vector perf_event; for (auto it = r->field_begin(); it != r->field_end(); ++it) { perf_event.push_back(it->getNameAsString() + "#" + it->getType().getAsString()); //"pid#u32" } fe_.perf_events_[name] = perf_event; } } else if (memb_name == "perf_submit_skb") { string skb = rewriter_.getRewrittenText(expansionRange(Call->getArg(0)->getSourceRange())); string skb_len = rewriter_.getRewrittenText(expansionRange(Call->getArg(1)->getSourceRange())); string meta = rewriter_.getRewrittenText(expansionRange(Call->getArg(2)->getSourceRange())); string meta_len = rewriter_.getRewrittenText(expansionRange(Call->getArg(3)->getSourceRange())); txt = "bpf_perf_event_output(" + skb + ", " + "bpf_pseudo_fd(1, " + fd + "), " + "((__u64)" + skb_len + " << 32) | BPF_F_CURRENT_CPU, " + meta + ", " + meta_len + ");"; } else if (memb_name == "get_stackid") { if (desc->second.type == BPF_MAP_TYPE_STACK_TRACE) { string arg0 = rewriter_.getRewrittenText(expansionRange(Call->getArg(0)->getSourceRange())); txt = "bcc_get_stackid("; txt += "bpf_pseudo_fd(1, " + fd + "), " + arg0; rewrite_end = GET_ENDLOC(Call->getArg(0)); } else { error(GET_BEGINLOC(Call), "get_stackid only available on stacktrace maps"); return false; } } else { if (memb_name == "lookup") { prefix = "bpf_map_lookup_elem"; suffix = ")"; } else if (memb_name == "update") { prefix = "bpf_map_update_elem"; suffix = ", BPF_ANY)"; } else if (memb_name == "insert") { if (desc->second.type == BPF_MAP_TYPE_ARRAY) { warning(GET_BEGINLOC(Call), "all element of an array already exist; insert() will have no effect"); } prefix = "bpf_map_update_elem"; suffix = ", BPF_NOEXIST)"; } else if (memb_name == "delete") { prefix = "bpf_map_delete_elem"; suffix = ")"; } else if (memb_name == "call") { prefix = "bpf_tail_call_"; suffix = ")"; } else if (memb_name == "perf_read") { prefix = "bpf_perf_event_read"; suffix = ")"; } else if (memb_name == "perf_counter_value") { prefix = "bpf_perf_event_read_value"; suffix = ")"; } else if (memb_name == "check_current_task") { prefix = "bpf_current_task_under_cgroup"; suffix = ")"; } else if (memb_name == "redirect_map") { prefix = "bpf_redirect_map"; suffix = ")"; } else { error(GET_BEGINLOC(Call), "invalid bpf_table operation %0") << memb_name; return false; } prefix += "((void *)bpf_pseudo_fd(1, " + fd + "), "; txt = prefix + args + suffix; } if (!rewriter_.isRewritable(rewrite_start) || !rewriter_.isRewritable(rewrite_end)) { error(GET_BEGINLOC(Call), "cannot use map function inside a macro"); return false; } rewriter_.ReplaceText(expansionRange(SourceRange(rewrite_start, rewrite_end)), txt); return true; } } } else if (Call->getCalleeDecl()) { NamedDecl *Decl = dyn_cast(Call->getCalleeDecl()); if (!Decl) return true; if (AsmLabelAttr *A = Decl->getAttr()) { // Functions with the tag asm("llvm.bpf.extra") are implemented in the // rewriter rather than as a macro since they may also include nested // rewrites, and clang::Rewriter does not support rewrites in macros, // unless one preprocesses the entire source file. if (A->getLabel() == "llvm.bpf.extra") { if (!rewriter_.isRewritable(GET_BEGINLOC(Call))) { error(GET_BEGINLOC(Call), "cannot use builtin inside a macro"); return false; } vector args; for (auto arg : Call->arguments()) args.push_back(rewriter_.getRewrittenText(expansionRange(arg->getSourceRange()))); string text; if (Decl->getName() == "incr_cksum_l3") { text = "bpf_l3_csum_replace_(" + fn_args_[0]->getName().str() + ", (u64)"; text += args[0] + ", " + args[1] + ", " + args[2] + ", sizeof(" + args[2] + "))"; rewriter_.ReplaceText(expansionRange(Call->getSourceRange()), text); } else if (Decl->getName() == "incr_cksum_l4") { text = "bpf_l4_csum_replace_(" + fn_args_[0]->getName().str() + ", (u64)"; text += args[0] + ", " + args[1] + ", " + args[2]; text += ", ((" + args[3] + " & 0x1) << 4) | sizeof(" + args[2] + "))"; rewriter_.ReplaceText(expansionRange(Call->getSourceRange()), text); } else if (Decl->getName() == "bpf_trace_printk") { checkFormatSpecifiers(args[0], GET_BEGINLOC(Call->getArg(0))); // #define bpf_trace_printk(fmt, args...) // ({ char _fmt[] = fmt; bpf_trace_printk_(_fmt, sizeof(_fmt), args...); }) text = "({ char _fmt[] = " + args[0] + "; bpf_trace_printk_(_fmt, sizeof(_fmt)"; if (args.size() <= 1) { text += "); })"; rewriter_.ReplaceText(expansionRange(Call->getSourceRange()), text); } else { rewriter_.ReplaceText(expansionRange(SourceRange(GET_BEGINLOC(Call), GET_ENDLOC(Call->getArg(0)))), text); rewriter_.InsertTextAfter(GET_ENDLOC(Call), "); }"); } } else if (Decl->getName() == "bpf_num_cpus") { int numcpu = sysconf(_SC_NPROCESSORS_ONLN); if (numcpu <= 0) numcpu = 1; text = to_string(numcpu); rewriter_.ReplaceText(expansionRange(Call->getSourceRange()), text); } else if (Decl->getName() == "bpf_usdt_readarg_p") { text = "({ u64 __addr = 0x0; "; text += "_bpf_readarg_" + current_fn_ + "_" + args[0] + "(" + args[1] + ", &__addr, sizeof(__addr));"; text += "bpf_probe_read(" + args[2] + ", " + args[3] + ", (void *)__addr);"; text += "})"; rewriter_.ReplaceText(expansionRange(Call->getSourceRange()), text); } else if (Decl->getName() == "bpf_usdt_readarg") { text = "_bpf_readarg_" + current_fn_ + "_" + args[0] + "(" + args[1] + ", " + args[2] + ", sizeof(*(" + args[2] + ")))"; rewriter_.ReplaceText(expansionRange(Call->getSourceRange()), text); } } } else if (FunctionDecl *F = dyn_cast(Decl)) { if (F->isExternallyVisible() && !F->getBuiltinID()) { auto start_loc = rewriter_.getSourceMgr().getFileLoc(GET_BEGINLOC(Decl)); if (rewriter_.getSourceMgr().getFileID(start_loc) == rewriter_.getSourceMgr().getMainFileID()) { error(GET_BEGINLOC(Call), "cannot call non-static helper function"); return false; } } } } return true; } bool BTypeVisitor::checkFormatSpecifiers(const string& fmt, SourceLocation loc) { unsigned nb_specifiers = 0, i, j; bool has_s = false; for (i = 0; i < fmt.length(); i++) { if (!isascii(fmt[i]) || (!isprint(fmt[i]) && !isspace(fmt[i]))) { warning(loc.getLocWithOffset(i), "unrecognized character"); return false; } if (fmt[i] != '%') continue; if (nb_specifiers >= 3) { warning(loc.getLocWithOffset(i), "cannot use more than 3 conversion specifiers"); return false; } nb_specifiers++; i++; if (fmt[i] == 'l') { i++; } else if (fmt[i] == 'p' || fmt[i] == 's') { i++; if (!isspace(fmt[i]) && !ispunct(fmt[i]) && fmt[i] != 0) { warning(loc.getLocWithOffset(i - 2), "only %%d %%u %%x %%ld %%lu %%lx %%lld %%llu %%llx %%p %%s conversion specifiers allowed"); return false; } if (fmt[i - 1] == 's') { if (has_s) { warning(loc.getLocWithOffset(i - 2), "cannot use several %%s conversion specifiers"); return false; } has_s = true; } continue; } j = 1; if (fmt[i] == 'l') { i++; j++; } if (fmt[i] != 'd' && fmt[i] != 'u' && fmt[i] != 'x') { warning(loc.getLocWithOffset(i - j), "only %%d %%u %%x %%ld %%lu %%lx %%lld %%llu %%llx %%p %%s conversion specifiers allowed"); return false; } } return true; } bool BTypeVisitor::VisitBinaryOperator(BinaryOperator *E) { if (!E->isAssignmentOp()) return true; Expr *LHS = E->getLHS()->IgnoreImplicit(); if (MemberExpr *Memb = dyn_cast(LHS)) { if (DeclRefExpr *Base = dyn_cast(Memb->getBase()->IgnoreImplicit())) { if (DeprecatedAttr *A = Base->getDecl()->getAttr()) { if (A->getMessage() == "packet") { if (FieldDecl *F = dyn_cast(Memb->getMemberDecl())) { if (!rewriter_.isRewritable(GET_BEGINLOC(E))) { error(GET_BEGINLOC(E), "cannot use \"packet\" header type inside a macro"); return false; } uint64_t ofs = C.getFieldOffset(F); uint64_t sz = F->isBitField() ? F->getBitWidthValue(C) : C.getTypeSize(F->getType()); string base = rewriter_.getRewrittenText(expansionRange(Base->getSourceRange())); string text = "bpf_dins_pkt(" + fn_args_[0]->getName().str() + ", (u64)" + base + "+" + to_string(ofs >> 3) + ", " + to_string(ofs & 0x7) + ", " + to_string(sz) + ","; rewriter_.ReplaceText(expansionRange(SourceRange(GET_BEGINLOC(E), E->getOperatorLoc())), text); rewriter_.InsertTextAfterToken(GET_ENDLOC(E), ")"); } } } } } return true; } bool BTypeVisitor::VisitImplicitCastExpr(ImplicitCastExpr *E) { // use dext only for RValues if (E->getCastKind() != CK_LValueToRValue) return true; MemberExpr *Memb = dyn_cast(E->IgnoreImplicit()); if (!Memb) return true; Expr *Base = Memb->getBase()->IgnoreImplicit(); if (DeclRefExpr *Ref = dyn_cast(Base)) { if (DeprecatedAttr *A = Ref->getDecl()->getAttr()) { if (A->getMessage() == "packet") { if (FieldDecl *F = dyn_cast(Memb->getMemberDecl())) { if (!rewriter_.isRewritable(GET_BEGINLOC(E))) { error(GET_BEGINLOC(E), "cannot use \"packet\" header type inside a macro"); return false; } uint64_t ofs = C.getFieldOffset(F); uint64_t sz = F->isBitField() ? F->getBitWidthValue(C) : C.getTypeSize(F->getType()); string text = "bpf_dext_pkt(" + fn_args_[0]->getName().str() + ", (u64)" + Ref->getDecl()->getName().str() + "+" + to_string(ofs >> 3) + ", " + to_string(ofs & 0x7) + ", " + to_string(sz) + ")"; rewriter_.ReplaceText(expansionRange(E->getSourceRange()), text); } } } } return true; } SourceRange BTypeVisitor::expansionRange(SourceRange range) { #if LLVM_MAJOR_VERSION >= 7 return rewriter_.getSourceMgr().getExpansionRange(range).getAsRange(); #else return rewriter_.getSourceMgr().getExpansionRange(range); #endif } template DiagnosticBuilder BTypeVisitor::error(SourceLocation loc, const char (&fmt)[N]) { unsigned int diag_id = C.getDiagnostics().getCustomDiagID(DiagnosticsEngine::Error, fmt); return C.getDiagnostics().Report(loc, diag_id); } template DiagnosticBuilder BTypeVisitor::warning(SourceLocation loc, const char (&fmt)[N]) { unsigned int diag_id = C.getDiagnostics().getCustomDiagID(DiagnosticsEngine::Warning, fmt); return C.getDiagnostics().Report(loc, diag_id); } int64_t BTypeVisitor::getFieldValue(VarDecl *Decl, FieldDecl *FDecl, int64_t OrigFValue) { unsigned idx = FDecl->getFieldIndex(); if (auto I = dyn_cast_or_null(Decl->getInit())) { #if LLVM_MAJOR_VERSION >= 8 Expr::EvalResult res; if (I->getInit(idx)->EvaluateAsInt(res, C)) { return res.Val.getInt().getExtValue(); } #else llvm::APSInt res; if (I->getInit(idx)->EvaluateAsInt(res, C)) { return res.getExtValue(); } #endif } return OrigFValue; } // Open table FDs when bpf tables (as denoted by section("maps*") attribute) // are declared. bool BTypeVisitor::VisitVarDecl(VarDecl *Decl) { const RecordType *R = Decl->getType()->getAs(); if (SectionAttr *A = Decl->getAttr()) { if (!A->getName().startswith("maps")) return true; if (!R) { error(GET_ENDLOC(Decl), "invalid type for bpf_table, expect struct"); return false; } const RecordDecl *RD = R->getDecl()->getDefinition(); TableDesc table; TableStorage::iterator table_it; table.name = Decl->getName(); Path local_path({fe_.id(), table.name}); Path maps_ns_path({"ns", fe_.maps_ns(), table.name}); Path global_path({table.name}); QualType key_type, leaf_type; unsigned i = 0; for (auto F : RD->fields()) { if (F->getType().getTypePtr()->isIncompleteType()) { error(GET_BEGINLOC(F), "unknown type"); return false; } size_t sz = C.getTypeSize(F->getType()) >> 3; if (F->getName() == "key") { if (sz == 0) { error(GET_BEGINLOC(F), "invalid zero-sized leaf"); return false; } table.key_size = sz; key_type = F->getType(); } else if (F->getName() == "leaf") { if (sz == 0) { error(GET_BEGINLOC(F), "invalid zero-sized leaf"); return false; } table.leaf_size = sz; leaf_type = F->getType(); } else if (F->getName() == "max_entries") { table.max_entries = getFieldValue(Decl, F, table.max_entries); } else if (F->getName() == "flags") { table.flags = getFieldValue(Decl, F, table.flags); } ++i; } std::string section_attr = A->getName(); size_t pinned_path_pos = section_attr.find(":"); unsigned int pinned_id = 0; // 0 is not a valid map ID, they start with 1 if (pinned_path_pos != std::string::npos) { std::string pinned = section_attr.substr(pinned_path_pos + 1); section_attr = section_attr.substr(0, pinned_path_pos); int fd = bpf_obj_get(pinned.c_str()); struct bpf_map_info info = {}; unsigned int info_len = sizeof(info); if (bpf_obj_get_info_by_fd(fd, &info, &info_len)) { error(GET_BEGINLOC(Decl), "map not found: %0") << pinned; return false; } close(fd); pinned_id = info.id; } // Additional map specific information size_t map_info_pos = section_attr.find("$"); std::string inner_map_name; if (map_info_pos != std::string::npos) { std::string map_info = section_attr.substr(map_info_pos + 1); section_attr = section_attr.substr(0, map_info_pos); if (section_attr == "maps/array_of_maps" || section_attr == "maps/hash_of_maps") { inner_map_name = map_info; } } bpf_map_type map_type = BPF_MAP_TYPE_UNSPEC; if (section_attr == "maps/hash") { map_type = BPF_MAP_TYPE_HASH; } else if (section_attr == "maps/array") { map_type = BPF_MAP_TYPE_ARRAY; } else if (section_attr == "maps/percpu_hash") { map_type = BPF_MAP_TYPE_PERCPU_HASH; } else if (section_attr == "maps/percpu_array") { map_type = BPF_MAP_TYPE_PERCPU_ARRAY; } else if (section_attr == "maps/lru_hash") { map_type = BPF_MAP_TYPE_LRU_HASH; } else if (section_attr == "maps/lru_percpu_hash") { map_type = BPF_MAP_TYPE_LRU_PERCPU_HASH; } else if (section_attr == "maps/lpm_trie") { map_type = BPF_MAP_TYPE_LPM_TRIE; } else if (section_attr == "maps/histogram") { map_type = BPF_MAP_TYPE_HASH; if (key_type->isSpecificBuiltinType(BuiltinType::Int)) map_type = BPF_MAP_TYPE_ARRAY; if (!leaf_type->isSpecificBuiltinType(BuiltinType::ULongLong)) error(GET_BEGINLOC(Decl), "histogram leaf type must be u64, got %0") << leaf_type; } else if (section_attr == "maps/prog") { map_type = BPF_MAP_TYPE_PROG_ARRAY; } else if (section_attr == "maps/perf_output") { map_type = BPF_MAP_TYPE_PERF_EVENT_ARRAY; int numcpu = get_possible_cpus().size(); if (numcpu <= 0) numcpu = 1; table.max_entries = numcpu; } else if (section_attr == "maps/perf_array") { map_type = BPF_MAP_TYPE_PERF_EVENT_ARRAY; } else if (section_attr == "maps/cgroup_array") { map_type = BPF_MAP_TYPE_CGROUP_ARRAY; } else if (section_attr == "maps/stacktrace") { map_type = BPF_MAP_TYPE_STACK_TRACE; } else if (section_attr == "maps/devmap") { map_type = BPF_MAP_TYPE_DEVMAP; } else if (section_attr == "maps/cpumap") { map_type = BPF_MAP_TYPE_CPUMAP; } else if (section_attr == "maps/hash_of_maps") { map_type = BPF_MAP_TYPE_HASH_OF_MAPS; } else if (section_attr == "maps/array_of_maps") { map_type = BPF_MAP_TYPE_ARRAY_OF_MAPS; } else if (section_attr == "maps/extern") { if (!fe_.table_storage().Find(maps_ns_path, table_it)) { if (!fe_.table_storage().Find(global_path, table_it)) { error(GET_BEGINLOC(Decl), "reference to undefined table"); return false; } } table = table_it->second.dup(); table.is_extern = true; } else if (section_attr == "maps/export") { if (table.name.substr(0, 2) == "__") table.name = table.name.substr(2); Path local_path({fe_.id(), table.name}); Path global_path({table.name}); if (!fe_.table_storage().Find(local_path, table_it)) { error(GET_BEGINLOC(Decl), "reference to undefined table"); return false; } fe_.table_storage().Insert(global_path, table_it->second.dup()); return true; } else if(section_attr == "maps/shared") { if (table.name.substr(0, 2) == "__") table.name = table.name.substr(2); Path local_path({fe_.id(), table.name}); Path maps_ns_path({"ns", fe_.maps_ns(), table.name}); if (!fe_.table_storage().Find(local_path, table_it)) { error(GET_BEGINLOC(Decl), "reference to undefined table"); return false; } fe_.table_storage().Insert(maps_ns_path, table_it->second.dup()); return true; } if (!table.is_extern) { if (map_type == BPF_MAP_TYPE_UNSPEC) { error(GET_BEGINLOC(Decl), "unsupported map type: %0") << section_attr; return false; } table.type = map_type; table.fake_fd = fe_.get_next_fake_fd(); fe_.add_map_def(table.fake_fd, std::make_tuple((int)map_type, std::string(table.name), (int)table.key_size, (int)table.leaf_size, (int)table.max_entries, table.flags, pinned_id, inner_map_name)); } if (!table.is_extern) fe_.table_storage().VisitMapType(table, C, key_type, leaf_type); fe_.table_storage().Insert(local_path, move(table)); } else if (const PointerType *P = Decl->getType()->getAs()) { // if var is a pointer to a packet type, clone the annotation into the var // decl so that the packet dext/dins rewriter can catch it if (const RecordType *RT = P->getPointeeType()->getAs()) { if (const RecordDecl *RD = RT->getDecl()->getDefinition()) { if (DeprecatedAttr *DA = RD->getAttr()) { if (DA->getMessage() == "packet") { Decl->addAttr(DA->clone(C)); } } } } } return true; } // First traversal of AST to retrieve maps with external pointers. BTypeConsumer::BTypeConsumer(ASTContext &C, BFrontendAction &fe, Rewriter &rewriter, set &m) : fe_(fe), map_visitor_(m), btype_visitor_(C, fe), probe_visitor1_(C, rewriter, m, true), probe_visitor2_(C, rewriter, m, false) {} void BTypeConsumer::HandleTranslationUnit(ASTContext &Context) { DeclContext::decl_iterator it; DeclContext *DC = TranslationUnitDecl::castToDeclContext(Context.getTranslationUnitDecl()); /** * In a first traversal, ProbeVisitor tracks external pointers identified * through each function's arguments and replaces their dereferences with * calls to bpf_probe_read. It also passes all identified pointers to * external addresses to MapVisitor. */ for (it = DC->decls_begin(); it != DC->decls_end(); it++) { Decl *D = *it; if (FunctionDecl *F = dyn_cast(D)) { if (fe_.is_rewritable_ext_func(F)) { for (auto arg : F->parameters()) { if (arg == F->getParamDecl(0)) { /** * Limit tracing of pointers from context to tracing contexts. * We're whitelisting instead of blacklisting to avoid issues with * existing programs if new context types are added in the future. */ string type = arg->getType().getAsString(); if (type == "struct pt_regs *" || type == "struct bpf_raw_tracepoint_args *" || type.substr(0, 19) == "struct tracepoint__") probe_visitor1_.set_ctx(arg); } else if (!arg->getType()->isFundamentalType()) { tuple pt = make_tuple(arg, 0); probe_visitor1_.set_ptreg(pt); } } probe_visitor1_.TraverseDecl(D); for (auto ptreg : probe_visitor1_.get_ptregs()) { map_visitor_.set_ptreg(ptreg); } } } } /** * MapVisitor uses external pointers identified by the first ProbeVisitor * traversal to identify all maps with external pointers as values. * MapVisitor runs only after ProbeVisitor finished its traversal of the * whole translation unit to clearly separate the role of each ProbeVisitor's * traversal: the first tracks external pointers from function arguments, * whereas the second tracks external pointers from maps. Without this clear * separation, ProbeVisitor might attempt to replace several times the same * dereferences. */ for (it = DC->decls_begin(); it != DC->decls_end(); it++) { Decl *D = *it; if (FunctionDecl *F = dyn_cast(D)) { if (fe_.is_rewritable_ext_func(F)) { map_visitor_.TraverseDecl(D); } } } /** * In a second traversal, ProbeVisitor tracks pointers passed through the * maps identified by MapVisitor and replaces their dereferences with calls * to bpf_probe_read. * This last traversal runs after MapVisitor went through an entire * translation unit, to ensure maps with external pointers have all been * identified. */ for (it = DC->decls_begin(); it != DC->decls_end(); it++) { Decl *D = *it; if (FunctionDecl *F = dyn_cast(D)) { if (fe_.is_rewritable_ext_func(F)) { probe_visitor2_.TraverseDecl(D); } } btype_visitor_.TraverseDecl(D); } } BFrontendAction::BFrontendAction(llvm::raw_ostream &os, unsigned flags, TableStorage &ts, const std::string &id, const std::string &main_path, FuncSource &func_src, std::string &mod_src, const std::string &maps_ns, fake_fd_map_def &fake_fd_map, std::map> &perf_events) : os_(os), flags_(flags), ts_(ts), id_(id), maps_ns_(maps_ns), rewriter_(new Rewriter), main_path_(main_path), func_src_(func_src), mod_src_(mod_src), next_fake_fd_(-1), fake_fd_map_(fake_fd_map), perf_events_(perf_events) {} bool BFrontendAction::is_rewritable_ext_func(FunctionDecl *D) { StringRef file_name = rewriter_->getSourceMgr().getFilename(GET_BEGINLOC(D)); return (D->isExternallyVisible() && D->hasBody() && (file_name.empty() || file_name == main_path_)); } void BFrontendAction::DoMiscWorkAround() { // In 4.16 and later, CONFIG_CC_STACKPROTECTOR is moved out of Kconfig and into // Makefile. It will be set depending on CONFIG_CC_STACKPROTECTOR_{AUTO|REGULAR|STRONG}. // CONFIG_CC_STACKPROTECTOR is still used in various places, e.g., struct task_struct, // to guard certain fields. The workaround here intends to define // CONFIG_CC_STACKPROTECTOR properly based on other configs, so it relieved any bpf // program (using task_struct, etc.) of patching the below code. rewriter_->getEditBuffer(rewriter_->getSourceMgr().getMainFileID()).InsertText(0, "#if defined(BPF_LICENSE)\n" "#error BPF_LICENSE cannot be specified through cflags\n" "#endif\n" "#if !defined(CONFIG_CC_STACKPROTECTOR)\n" "#if defined(CONFIG_CC_STACKPROTECTOR_AUTO) \\\n" " || defined(CONFIG_CC_STACKPROTECTOR_REGULAR) \\\n" " || defined(CONFIG_CC_STACKPROTECTOR_STRONG)\n" "#define CONFIG_CC_STACKPROTECTOR\n" "#endif\n" "#endif\n", false); rewriter_->getEditBuffer(rewriter_->getSourceMgr().getMainFileID()).InsertTextAfter( rewriter_->getSourceMgr().getBuffer(rewriter_->getSourceMgr().getMainFileID())->getBufferSize(), "\n#include \n"); } void BFrontendAction::EndSourceFileAction() { // Additional misc rewrites DoMiscWorkAround(); if (flags_ & DEBUG_PREPROCESSOR) rewriter_->getEditBuffer(rewriter_->getSourceMgr().getMainFileID()).write(llvm::errs()); #if LLVM_MAJOR_VERSION >= 9 llvm::raw_string_ostream tmp_os(mod_src_); rewriter_->getEditBuffer(rewriter_->getSourceMgr().getMainFileID()) .write(tmp_os); #else if (flags_ & DEBUG_SOURCE) { llvm::raw_string_ostream tmp_os(mod_src_); rewriter_->getEditBuffer(rewriter_->getSourceMgr().getMainFileID()) .write(tmp_os); } #endif for (auto func : func_range_) { auto f = func.first; string bd = rewriter_->getRewrittenText(func_range_[f]); func_src_.set_src_rewritten(f, bd); } rewriter_->getEditBuffer(rewriter_->getSourceMgr().getMainFileID()).write(os_); os_.flush(); } unique_ptr BFrontendAction::CreateASTConsumer(CompilerInstance &Compiler, llvm::StringRef InFile) { rewriter_->setSourceMgr(Compiler.getSourceManager(), Compiler.getLangOpts()); vector> consumers; consumers.push_back(unique_ptr(new BTypeConsumer(Compiler.getASTContext(), *this, *rewriter_, m_))); return unique_ptr(new MultiplexConsumer(std::move(consumers))); } } bpfcc-0.12.0/src/cc/frontends/clang/b_frontend_action.h000066400000000000000000000164531357404205000227500ustar00rootroot00000000000000/* * Copyright (c) 2015 PLUMgrid, Inc. * * 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. */ #include #include #include #include #include #include #include #include #include "table_storage.h" namespace clang { class ASTConsumer; class ASTContext; class CompilerInstance; } namespace llvm { class raw_ostream; class StringRef; } namespace ebpf { class BFrontendAction; class FuncSource; // Traces maps with external pointers as values. class MapVisitor : public clang::RecursiveASTVisitor { public: explicit MapVisitor(std::set &m); bool VisitCallExpr(clang::CallExpr *Call); void set_ptreg(std::tuple &pt) { ptregs_.insert(pt); } private: std::set &m_; std::set> ptregs_; }; // Type visitor and rewriter for B programs. // It will look for B-specific features and rewrite them into a valid // C program. As part of the processing, open the necessary BPF tables // and store the open handles in a map of table-to-fd's. class BTypeVisitor : public clang::RecursiveASTVisitor { public: explicit BTypeVisitor(clang::ASTContext &C, BFrontendAction &fe); bool TraverseCallExpr(clang::CallExpr *Call); bool VisitFunctionDecl(clang::FunctionDecl *D); bool VisitCallExpr(clang::CallExpr *Call); bool VisitVarDecl(clang::VarDecl *Decl); bool VisitBinaryOperator(clang::BinaryOperator *E); bool VisitImplicitCastExpr(clang::ImplicitCastExpr *E); private: clang::SourceRange expansionRange(clang::SourceRange range); bool checkFormatSpecifiers(const std::string& fmt, clang::SourceLocation loc); void genParamDirectAssign(clang::FunctionDecl *D, std::string& preamble, const char **calling_conv_regs); void genParamIndirectAssign(clang::FunctionDecl *D, std::string& preamble, const char **calling_conv_regs); void rewriteFuncParam(clang::FunctionDecl *D); int64_t getFieldValue(clang::VarDecl *Decl, clang::FieldDecl *FDecl, int64_t OrigFValue); template clang::DiagnosticBuilder error(clang::SourceLocation loc, const char (&fmt)[N]); template clang::DiagnosticBuilder warning(clang::SourceLocation loc, const char (&fmt)[N]); clang::ASTContext &C; clang::DiagnosticsEngine &diag_; BFrontendAction &fe_; clang::Rewriter &rewriter_; /// modifications to the source go into this class llvm::raw_ostream &out_; /// for debugging std::vector fn_args_; std::set visited_; std::string current_fn_; }; // Do a depth-first search to rewrite all pointers that need to be probed class ProbeVisitor : public clang::RecursiveASTVisitor { public: explicit ProbeVisitor(clang::ASTContext &C, clang::Rewriter &rewriter, std::set &m, bool track_helpers); bool VisitVarDecl(clang::VarDecl *Decl); bool TraverseStmt(clang::Stmt *S); bool VisitCallExpr(clang::CallExpr *Call); bool VisitReturnStmt(clang::ReturnStmt *R); bool VisitBinaryOperator(clang::BinaryOperator *E); bool VisitUnaryOperator(clang::UnaryOperator *E); bool VisitMemberExpr(clang::MemberExpr *E); bool VisitArraySubscriptExpr(clang::ArraySubscriptExpr *E); void set_ptreg(std::tuple &pt) { ptregs_.insert(pt); } void set_ctx(clang::Decl *D) { ctx_ = D; } std::set> get_ptregs() { return ptregs_; } private: bool assignsExtPtr(clang::Expr *E, int *nbAddrOf); bool isMemberDereference(clang::Expr *E); bool IsContextMemberExpr(clang::Expr *E); clang::SourceRange expansionRange(clang::SourceRange range); clang::SourceLocation expansionLoc(clang::SourceLocation loc); template clang::DiagnosticBuilder error(clang::SourceLocation loc, const char (&fmt)[N]); clang::ASTContext &C; clang::Rewriter &rewriter_; std::set fn_visited_; std::set memb_visited_; std::set whitelist_; std::set> ptregs_; std::set &m_; clang::Decl *ctx_; bool track_helpers_; std::list ptregs_returned_; const clang::Stmt *addrof_stmt_; bool is_addrof_; }; // A helper class to the frontend action, walks the decls class BTypeConsumer : public clang::ASTConsumer { public: explicit BTypeConsumer(clang::ASTContext &C, BFrontendAction &fe, clang::Rewriter &rewriter, std::set &m); void HandleTranslationUnit(clang::ASTContext &Context) override; private: BFrontendAction &fe_; MapVisitor map_visitor_; BTypeVisitor btype_visitor_; ProbeVisitor probe_visitor1_; ProbeVisitor probe_visitor2_; }; // Create a B program in 2 phases (everything else is normal C frontend): // 1. Catch the map declarations and open the fd's // 2. Capture the IR class BFrontendAction : public clang::ASTFrontendAction { public: // Initialize with the output stream where the new source file contents // should be written. BFrontendAction(llvm::raw_ostream &os, unsigned flags, TableStorage &ts, const std::string &id, const std::string &main_path, FuncSource &func_src, std::string &mod_src, const std::string &maps_ns, fake_fd_map_def &fake_fd_map, std::map> &perf_events); // Called by clang when the AST has been completed, here the output stream // will be flushed. void EndSourceFileAction() override; std::unique_ptr CreateASTConsumer(clang::CompilerInstance &Compiler, llvm::StringRef InFile) override; clang::Rewriter &rewriter() const { return *rewriter_; } TableStorage &table_storage() const { return ts_; } std::string id() const { return id_; } std::string maps_ns() const { return maps_ns_; } bool is_rewritable_ext_func(clang::FunctionDecl *D); void DoMiscWorkAround(); // negative fake_fd to be different from real fd in bpf_pseudo_fd. int get_next_fake_fd() { return next_fake_fd_--; } void add_map_def(int fd, std::tuple map_def) { fake_fd_map_[fd] = move(map_def); } private: llvm::raw_ostream &os_; unsigned flags_; TableStorage &ts_; std::string id_; std::string maps_ns_; std::unique_ptr rewriter_; friend class BTypeVisitor; std::map func_range_; const std::string &main_path_; FuncSource &func_src_; std::string &mod_src_; std::set m_; int next_fake_fd_; fake_fd_map_def &fake_fd_map_; std::map> &perf_events_; }; } // namespace visitor bpfcc-0.12.0/src/cc/frontends/clang/frontend_action_common.h000066400000000000000000000014611357404205000240100ustar00rootroot00000000000000/* * Copyright (c) 2018 Facebook, Inc. * * 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. */ #if LLVM_MAJOR_VERSION >= 8 #define GET_BEGINLOC(E) ((E)->getBeginLoc()) #define GET_ENDLOC(E) ((E)->getEndLoc()) #else #define GET_BEGINLOC(E) ((E)->getLocStart()) #define GET_ENDLOC(E) ((E)->getLocEnd()) #endif bpfcc-0.12.0/src/cc/frontends/clang/kbuild_helper.cc000066400000000000000000000135341357404205000222370ustar00rootroot00000000000000/* * Copyright (c) 2015 PLUMgrid, Inc. * * 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. */ #include #include #include #include #include #include #include #include "kbuild_helper.h" namespace ebpf { using std::string; using std::vector; KBuildHelper::KBuildHelper(const std::string &kdir, bool has_source_dir) : kdir_(kdir), has_source_dir_(has_source_dir) { } // read the flags from cache or learn int KBuildHelper::get_flags(const char *uname_machine, vector *cflags) { //uname -m | sed -e s/i.86/x86/ -e s/x86_64/x86/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ // -e s/sa110/arm/ -e s/s390x/s390/ -e s/parisc64/parisc/ // -e s/ppc.*/powerpc/ -e s/mips.*/mips/ -e s/sh[234].*/sh/ // -e s/aarch64.*/arm64/ string arch; const char *archenv = getenv("ARCH"); // If ARCH env is defined, use it over uname if (archenv) arch = string(archenv); else arch = string(uname_machine); if (!arch.compare(0, 6, "x86_64")) { arch = "x86"; } else if (arch[0] == 'i' && !arch.compare(2, 2, "86")) { arch = "x86"; } else if (!arch.compare(0, 7, "aarch64") || !arch.compare(0, 5, "arm64")) { arch = "arm64"; } else if (!arch.compare(0, 3, "arm")) { arch = "arm"; } else if (!arch.compare(0, 5, "sa110")) { arch = "arm"; } else if (!arch.compare(0, 5, "s390x")) { arch = "s390"; } else if (!arch.compare(0, 8, "parisc64")) { arch = "parisc"; } else if (!arch.compare(0, 3, "ppc")) { arch = "powerpc"; } else if (!arch.compare(0, 4, "mips")) { arch = "mips"; } else if (!arch.compare(0, 2, "sh")) { arch = "sh"; } cflags->push_back("-nostdinc"); cflags->push_back("-isystem"); cflags->push_back("/virtual/lib/clang/include"); // some module build directories split headers between source/ and build/ if (has_source_dir_) { cflags->push_back("-I" + kdir_ + "/build/arch/"+arch+"/include"); cflags->push_back("-I" + kdir_ + "/build/arch/"+arch+"/include/generated/uapi"); cflags->push_back("-I" + kdir_ + "/build/arch/"+arch+"/include/generated"); cflags->push_back("-I" + kdir_ + "/build/include"); cflags->push_back("-I" + kdir_ + "/build/./arch/"+arch+"/include/uapi"); cflags->push_back("-I" + kdir_ + "/build/arch/"+arch+"/include/generated/uapi"); cflags->push_back("-I" + kdir_ + "/build/include/uapi"); cflags->push_back("-I" + kdir_ + "/build/include/generated"); cflags->push_back("-I" + kdir_ + "/build/include/generated/uapi"); } cflags->push_back("-I./arch/"+arch+"/include"); cflags->push_back("-Iarch/"+arch+"/include/generated/uapi"); cflags->push_back("-Iarch/"+arch+"/include/generated"); cflags->push_back("-Iinclude"); cflags->push_back("-I./arch/"+arch+"/include/uapi"); cflags->push_back("-Iarch/"+arch+"/include/generated/uapi"); cflags->push_back("-I./include/uapi"); cflags->push_back("-Iinclude/generated/uapi"); cflags->push_back("-include"); cflags->push_back("./include/linux/kconfig.h"); cflags->push_back("-D__KERNEL__"); cflags->push_back("-D__HAVE_BUILTIN_BSWAP16__"); cflags->push_back("-D__HAVE_BUILTIN_BSWAP32__"); cflags->push_back("-D__HAVE_BUILTIN_BSWAP64__"); // If ARCH env variable is set, pass this along. if (archenv) cflags->push_back("-D__TARGET_ARCH_" + arch); cflags->push_back("-Wno-unused-value"); cflags->push_back("-Wno-pointer-sign"); cflags->push_back("-fno-stack-protector"); return 0; } static inline int file_exists(const char *f) { struct stat buffer; return (stat(f, &buffer) == 0); } static inline int proc_kheaders_exists(void) { return file_exists(PROC_KHEADERS_PATH); } static inline int extract_kheaders(const std::string &dirpath, const struct utsname &uname_data) { char tar_cmd[256], dirpath_tmp[256]; int ret; bool module = false; if (!proc_kheaders_exists()) { ret = system("modprobe kheaders"); if (ret) return ret; module = true; if (!proc_kheaders_exists()) { ret = -1; goto cleanup; } } snprintf(dirpath_tmp, sizeof(dirpath_tmp), "/tmp/kheaders-%s-XXXXXX", uname_data.release); if (mkdtemp(dirpath_tmp) == NULL) { ret = -1; goto cleanup; } if ((size_t)snprintf(tar_cmd, sizeof(tar_cmd), "tar -xf %s -C %s", PROC_KHEADERS_PATH, dirpath_tmp) >= sizeof(tar_cmd)) { ret = -1; goto cleanup; } ret = system(tar_cmd); if (ret) { system(("rm -rf " + std::string(dirpath_tmp)).c_str()); goto cleanup; } /* * If the new directory exists, it could have raced with a parallel * extraction, in this case just delete the old directory and ignore. */ ret = rename(dirpath_tmp, dirpath.c_str()); if (ret) ret = system(("rm -rf " + std::string(dirpath_tmp)).c_str()); cleanup: if (module) { int ret1 = system("rmmod kheaders"); if (ret1) return ret1; } return ret; } int get_proc_kheaders(std::string &dirpath) { struct utsname uname_data; char dirpath_tmp[256]; if (uname(&uname_data)) return -errno; snprintf(dirpath_tmp, 256, "/tmp/kheaders-%s", uname_data.release); dirpath = std::string(dirpath_tmp); if (file_exists(dirpath_tmp)) return 0; // First time so extract it return extract_kheaders(dirpath, uname_data); } } // namespace ebpf bpfcc-0.12.0/src/cc/frontends/clang/kbuild_helper.h000066400000000000000000000056261357404205000221040ustar00rootroot00000000000000/* * Copyright (c) 2015 PLUMgrid, Inc. * * 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. */ #include #include #include #include #include #include #include #define PROC_KHEADERS_PATH "/sys/kernel/kheaders.tar.xz" namespace ebpf { struct FileDeleter { void operator() (FILE *fp) { fclose(fp); } }; typedef std::unique_ptr FILEPtr; // Helper with pushd/popd semantics class DirStack { public: explicit DirStack(const std::string &dst) : ok_(false) { if (getcwd(cwd_, sizeof(cwd_)) == NULL) { ::perror("getcwd"); return; } if (::chdir(dst.c_str())) { fprintf(stderr, "chdir(%s): %s\n", dst.c_str(), strerror(errno)); return; } ok_ = true; } ~DirStack() { if (!ok_) return; if (::chdir(cwd_)) { fprintf(stderr, "chdir(%s): %s\n", cwd_, strerror(errno)); } } bool ok() const { return ok_; } const char * cwd() const { return cwd_; } private: bool ok_; char cwd_[256]; }; static int ftw_cb(const char *path, const struct stat *, int, struct FTW *) { return ::remove(path); } // Scoped class to manage the creation/deletion of tmpdirs class TmpDir { public: explicit TmpDir(const std::string &prefix = "/tmp/bcc-") : ok_(false), prefix_(prefix) { prefix_ += "XXXXXX"; if (::mkdtemp((char *)prefix_.data()) == NULL) ::perror("mkdtemp"); else ok_ = true; } ~TmpDir() { if (::nftw(prefix_.c_str(), ftw_cb, 20, FTW_DEPTH) < 0) ::perror("ftw"); else ::remove(prefix_.c_str()); } bool ok() const { return ok_; } const std::string & str() const { return prefix_; } private: bool ok_; std::string prefix_; }; // Compute the kbuild flags for the currently running kernel // Do this by: // 1. Create temp Makefile with stub dummy.c // 2. Run module build on that makefile, saving the computed flags to a file // 3. Cache the file for fast flag lookup in subsequent runs // Note: Depending on environment, different cache locations may be desired. In // case we eventually support non-root user programs, cache in $HOME. class KBuildHelper { public: explicit KBuildHelper(const std::string &kdir, bool has_source_dir); int get_flags(const char *uname_machine, std::vector *cflags); private: std::string kdir_; bool has_source_dir_; }; int get_proc_kheaders(std::string &dir); } // namespace ebpf bpfcc-0.12.0/src/cc/frontends/clang/loader.cc000066400000000000000000000375321357404205000207000ustar00rootroot00000000000000/* * Copyright (c) 2015 PLUMgrid, Inc. * * 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "bcc_exception.h" #include "bpf_module.h" #include "exported_files.h" #include "kbuild_helper.h" #include "b_frontend_action.h" #include "tp_frontend_action.h" #include "loader.h" #include "arch_helper.h" using std::map; using std::string; using std::unique_ptr; using std::vector; namespace ebpf { ClangLoader::ClangLoader(llvm::LLVMContext *ctx, unsigned flags) : ctx_(ctx), flags_(flags) { for (auto f : ExportedFiles::headers()) remapped_headers_[f.first] = llvm::MemoryBuffer::getMemBuffer(f.second); for (auto f : ExportedFiles::footers()) remapped_footers_[f.first] = llvm::MemoryBuffer::getMemBuffer(f.second); } ClangLoader::~ClangLoader() {} namespace { bool is_dir(const string& path) { struct stat buf; if (::stat (path.c_str (), &buf) < 0) return false; return S_ISDIR(buf.st_mode); } std::pair get_kernel_path_info(const string kdir) { if (is_dir(kdir + "/build") && is_dir(kdir + "/source")) return std::make_pair (true, "source"); const char* suffix_from_env = ::getenv("BCC_KERNEL_MODULES_SUFFIX"); if (suffix_from_env) return std::make_pair(false, string(suffix_from_env)); return std::make_pair(false, "build"); } static int CreateFromArgs(clang::CompilerInvocation &invocation, const llvm::opt::ArgStringList &ccargs, clang::DiagnosticsEngine &diags) { #if LLVM_MAJOR_VERSION >= 10 return clang::CompilerInvocation::CreateFromArgs(invocation, ccargs, diags); #else return clang::CompilerInvocation::CreateFromArgs( invocation, const_cast(ccargs.data()), const_cast(ccargs.data()) + ccargs.size(), diags); #endif } } int ClangLoader::parse(unique_ptr *mod, TableStorage &ts, const string &file, bool in_memory, const char *cflags[], int ncflags, const std::string &id, FuncSource &func_src, std::string &mod_src, const std::string &maps_ns, fake_fd_map_def &fake_fd_map, std::map> &perf_events) { string main_path = "/virtual/main.c"; unique_ptr main_buf; struct utsname un; uname(&un); string kdir, kpath; const char *kpath_env = ::getenv("BCC_KERNEL_SOURCE"); const char *version_override = ::getenv("BCC_LINUX_VERSION_CODE"); bool has_kpath_source = false; string vmacro; std::string tmpdir; if (kpath_env) { kpath = string(kpath_env); } else { kdir = string(KERNEL_MODULES_DIR) + "/" + un.release; auto kernel_path_info = get_kernel_path_info(kdir); has_kpath_source = kernel_path_info.first; kpath = kdir + "/" + kernel_path_info.second; } // If all attempts to obtain kheaders fail, check for kheaders.tar.xz in sysfs if (!is_dir(kpath)) { int ret = get_proc_kheaders(tmpdir); if (!ret) { kpath = tmpdir; } else { std::cout << "Unable to find kernel headers. "; std::cout << "Try rebuilding kernel with CONFIG_IKHEADERS=m (module)\n"; } } if (flags_ & DEBUG_PREPROCESSOR) std::cout << "Running from kernel directory at: " << kpath.c_str() << "\n"; // clang needs to run inside the kernel dir DirStack dstack(kpath); if (!dstack.ok()) return -1; string abs_file; if (in_memory) { abs_file = main_path; main_buf = llvm::MemoryBuffer::getMemBuffer(file); } else { if (file.substr(0, 1) == "/") abs_file = file; else abs_file = string(dstack.cwd()) + "/" + file; } // -fno-color-diagnostics: this is a workaround for a bug in llvm terminalHasColors() as of // 22 Jul 2016. Also see bcc #615. // Enable -O2 for clang. In clang 5.0, -O0 may result in function marking as // noinline and optnone (if not always inlining). // Note that first argument is ignored in clang compilation invocation. // "-D __BPF_TRACING__" below is added to suppress a warning in 4.17+. // It can be removed once clang supports asm-goto or the kernel removes // the warning. vector flags_cstr({"-O0", "-O2", "-emit-llvm", "-I", dstack.cwd(), "-D", "__BPF_TRACING__", "-Wno-deprecated-declarations", "-Wno-gnu-variable-sized-type-not-at-end", "-Wno-pragma-once-outside-header", "-Wno-address-of-packed-member", "-Wno-unknown-warning-option", "-fno-color-diagnostics", "-fno-unwind-tables", "-fno-asynchronous-unwind-tables", "-x", "c", "-c", abs_file.c_str()}); KBuildHelper kbuild_helper(kpath_env ? kpath : kdir, has_kpath_source); vector kflags; if (kbuild_helper.get_flags(un.machine, &kflags)) return -1; #if LLVM_MAJOR_VERSION >= 9 flags_cstr.push_back("-g"); #else if (flags_ & DEBUG_SOURCE) flags_cstr.push_back("-g"); #endif for (auto it = kflags.begin(); it != kflags.end(); ++it) flags_cstr.push_back(it->c_str()); vector flags_cstr_rem; if (version_override) { vmacro = "-DLINUX_VERSION_CODE_OVERRIDE=" + string(version_override); std::cout << "WARNING: Linux version for eBPF program is being overridden with: " << version_override << "\n"; std::cout << "WARNING: Due to this, the results of the program may be unpredictable\n"; flags_cstr_rem.push_back(vmacro.c_str()); } flags_cstr_rem.push_back("-include"); flags_cstr_rem.push_back("/virtual/include/bcc/helpers.h"); flags_cstr_rem.push_back("-isystem"); flags_cstr_rem.push_back("/virtual/include"); if (cflags) { for (auto i = 0; i < ncflags; ++i) flags_cstr_rem.push_back(cflags[i]); } #ifdef CUR_CPU_IDENTIFIER string cur_cpu_flag = string("-DCUR_CPU_IDENTIFIER=") + CUR_CPU_IDENTIFIER; flags_cstr_rem.push_back(cur_cpu_flag.c_str()); #endif if (do_compile(mod, ts, in_memory, flags_cstr, flags_cstr_rem, main_path, main_buf, id, func_src, mod_src, true, maps_ns, fake_fd_map, perf_events)) { #if BCC_BACKUP_COMPILE != 1 return -1; #else // try one more time to compile with system bpf.h llvm::errs() << "WARNING: compilation failure, trying with system bpf.h\n"; ts.DeletePrefix(Path({id})); func_src.clear(); mod_src.clear(); fake_fd_map.clear(); if (do_compile(mod, ts, in_memory, flags_cstr, flags_cstr_rem, main_path, main_buf, id, func_src, mod_src, false, maps_ns, fake_fd_map, perf_events)) return -1; #endif } return 0; } void *get_clang_target_cb(bcc_arch_t arch) { const char *ret; switch(arch) { case BCC_ARCH_PPC_LE: ret = "powerpc64le-unknown-linux-gnu"; break; case BCC_ARCH_PPC: ret = "powerpc64-unknown-linux-gnu"; break; case BCC_ARCH_S390X: ret = "s390x-ibm-linux-gnu"; break; case BCC_ARCH_ARM64: ret = "aarch64-unknown-linux-gnu"; break; default: ret = "x86_64-unknown-linux-gnu"; } return (void *)ret; } string get_clang_target(void) { const char *ret; ret = (const char *)run_arch_callback(get_clang_target_cb); return string(ret); } int ClangLoader::do_compile(unique_ptr *mod, TableStorage &ts, bool in_memory, const vector &flags_cstr_in, const vector &flags_cstr_rem, const std::string &main_path, const unique_ptr &main_buf, const std::string &id, FuncSource &func_src, std::string &mod_src, bool use_internal_bpfh, const std::string &maps_ns, fake_fd_map_def &fake_fd_map, std::map> &perf_events) { using namespace clang; vector flags_cstr = flags_cstr_in; if (use_internal_bpfh) { flags_cstr.push_back("-include"); flags_cstr.push_back("/virtual/include/bcc/bpf.h"); } flags_cstr.insert(flags_cstr.end(), flags_cstr_rem.begin(), flags_cstr_rem.end()); // set up the error reporting class IntrusiveRefCntPtr diag_opts(new DiagnosticOptions()); auto diag_client = new TextDiagnosticPrinter(llvm::errs(), &*diag_opts); IntrusiveRefCntPtr DiagID(new DiagnosticIDs()); DiagnosticsEngine diags(DiagID, &*diag_opts, diag_client); // set up the command line argument wrapper string target_triple = get_clang_target(); driver::Driver drv("", target_triple, diags); drv.setTitle("bcc-clang-driver"); drv.setCheckInputsExist(false); unique_ptr compilation(drv.BuildCompilation(flags_cstr)); if (!compilation) return -1; // expect exactly 1 job, otherwise error const driver::JobList &jobs = compilation->getJobs(); if (jobs.size() != 1 || !isa(*jobs.begin())) { SmallString<256> msg; llvm::raw_svector_ostream os(msg); jobs.Print(os, "; ", true); diags.Report(diag::err_fe_expected_compiler_job) << os.str(); return -1; } const driver::Command &cmd = cast(*jobs.begin()); if (llvm::StringRef(cmd.getCreator().getName()) != "clang") { diags.Report(diag::err_fe_expected_clang_command); return -1; } // Initialize a compiler invocation object from the clang (-cc1) arguments. const llvm::opt::ArgStringList &ccargs = cmd.getArguments(); if (flags_ & DEBUG_PREPROCESSOR) { llvm::errs() << "clang"; for (auto arg : ccargs) llvm::errs() << " " << arg; llvm::errs() << "\n"; } // pre-compilation pass for generating tracepoint structures CompilerInstance compiler0; CompilerInvocation &invocation0 = compiler0.getInvocation(); if (!CreateFromArgs(invocation0, ccargs, diags)) return -1; invocation0.getPreprocessorOpts().RetainRemappedFileBuffers = true; for (const auto &f : remapped_headers_) invocation0.getPreprocessorOpts().addRemappedFile(f.first, &*f.second); for (const auto &f : remapped_footers_) invocation0.getPreprocessorOpts().addRemappedFile(f.first, &*f.second); if (in_memory) { invocation0.getPreprocessorOpts().addRemappedFile(main_path, &*main_buf); invocation0.getFrontendOpts().Inputs.clear(); invocation0.getFrontendOpts().Inputs.push_back(FrontendInputFile( main_path, FrontendOptions::getInputKindForExtension("c"))); } invocation0.getFrontendOpts().DisableFree = false; compiler0.createDiagnostics(new IgnoringDiagConsumer()); // capture the rewritten c file string out_str; llvm::raw_string_ostream os(out_str); TracepointFrontendAction tpact(os); compiler0.ExecuteAction(tpact); // ignore errors, they will be reported later unique_ptr out_buf = llvm::MemoryBuffer::getMemBuffer(out_str); // first pass CompilerInstance compiler1; CompilerInvocation &invocation1 = compiler1.getInvocation(); if (!CreateFromArgs( invocation1, ccargs, diags)) return -1; // This option instructs clang whether or not to free the file buffers that we // give to it. Since the embedded header files should be copied fewer times // and reused if possible, set this flag to true. invocation1.getPreprocessorOpts().RetainRemappedFileBuffers = true; for (const auto &f : remapped_headers_) invocation1.getPreprocessorOpts().addRemappedFile(f.first, &*f.second); for (const auto &f : remapped_footers_) invocation1.getPreprocessorOpts().addRemappedFile(f.first, &*f.second); invocation1.getPreprocessorOpts().addRemappedFile(main_path, &*out_buf); invocation1.getFrontendOpts().Inputs.clear(); invocation1.getFrontendOpts().Inputs.push_back(FrontendInputFile( main_path, FrontendOptions::getInputKindForExtension("c"))); invocation1.getFrontendOpts().DisableFree = false; compiler1.createDiagnostics(); // capture the rewritten c file string out_str1; llvm::raw_string_ostream os1(out_str1); BFrontendAction bact(os1, flags_, ts, id, main_path, func_src, mod_src, maps_ns, fake_fd_map, perf_events); if (!compiler1.ExecuteAction(bact)) return -1; unique_ptr out_buf1 = llvm::MemoryBuffer::getMemBuffer(out_str1); // second pass, clear input and take rewrite buffer CompilerInstance compiler2; CompilerInvocation &invocation2 = compiler2.getInvocation(); if (!CreateFromArgs(invocation2, ccargs, diags)) return -1; invocation2.getPreprocessorOpts().RetainRemappedFileBuffers = true; for (const auto &f : remapped_headers_) invocation2.getPreprocessorOpts().addRemappedFile(f.first, &*f.second); for (const auto &f : remapped_footers_) invocation2.getPreprocessorOpts().addRemappedFile(f.first, &*f.second); invocation2.getPreprocessorOpts().addRemappedFile(main_path, &*out_buf1); invocation2.getFrontendOpts().Inputs.clear(); invocation2.getFrontendOpts().Inputs.push_back(FrontendInputFile( main_path, FrontendOptions::getInputKindForExtension("c"))); invocation2.getFrontendOpts().DisableFree = false; invocation2.getCodeGenOpts().DisableFree = false; // Resort to normal inlining. In -O0 the default is OnlyAlwaysInlining and // clang might add noinline attribute even for functions with inline hint. invocation2.getCodeGenOpts().setInlining(CodeGenOptions::NormalInlining); // suppress warnings in the 2nd pass, but bail out on errors (our fault) invocation2.getDiagnosticOpts().IgnoreWarnings = true; compiler2.createDiagnostics(); EmitLLVMOnlyAction ir_act(&*ctx_); if (!compiler2.ExecuteAction(ir_act)) return -1; *mod = ir_act.takeModule(); return 0; } const char * FuncSource::src(const std::string& name) { auto src = funcs_.find(name); if (src == funcs_.end()) return ""; return src->second.src_.data(); } const char * FuncSource::src_rewritten(const std::string& name) { auto src = funcs_.find(name); if (src == funcs_.end()) return ""; return src->second.src_rewritten_.data(); } void FuncSource::set_src(const std::string& name, const std::string& src) { funcs_[name].src_ = src; } void FuncSource::set_src_rewritten(const std::string& name, const std::string& src) { funcs_[name].src_rewritten_ = src; } } // namespace ebpf bpfcc-0.12.0/src/cc/frontends/clang/loader.h000066400000000000000000000052561357404205000205400ustar00rootroot00000000000000/* * Copyright (c) 2015 PLUMgrid, Inc. * * 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. */ #pragma once #include #include #include #include "table_storage.h" namespace llvm { class Module; class LLVMContext; class MemoryBuffer; } namespace ebpf { class FuncSource { class SourceCode { public: SourceCode(const std::string& s1 = "", const std::string& s2 = ""): src_(s1), src_rewritten_(s2) {} std::string src_; std::string src_rewritten_; }; std::map funcs_; public: FuncSource() {} void clear() { funcs_.clear(); } const char * src(const std::string& name); const char * src_rewritten(const std::string& name); void set_src(const std::string& name, const std::string& src); void set_src_rewritten(const std::string& name, const std::string& src); }; class ClangLoader { public: explicit ClangLoader(llvm::LLVMContext *ctx, unsigned flags); ~ClangLoader(); int parse(std::unique_ptr *mod, TableStorage &ts, const std::string &file, bool in_memory, const char *cflags[], int ncflags, const std::string &id, FuncSource &func_src, std::string &mod_src, const std::string &maps_ns, fake_fd_map_def &fake_fd_map, std::map> &perf_events); private: int do_compile(std::unique_ptr *mod, TableStorage &ts, bool in_memory, const std::vector &flags_cstr_in, const std::vector &flags_cstr_rem, const std::string &main_path, const std::unique_ptr &main_buf, const std::string &id, FuncSource &func_src, std::string &mod_src, bool use_internal_bpfh, const std::string &maps_ns, fake_fd_map_def &fake_fd_map, std::map> &perf_events); private: std::map> remapped_headers_; std::map> remapped_footers_; llvm::LLVMContext *ctx_; unsigned flags_; }; } // namespace ebpf bpfcc-0.12.0/src/cc/frontends/clang/tp_frontend_action.cc000066400000000000000000000211571357404205000233050ustar00rootroot00000000000000/* * Copyright (c) 2016 Sasha Goldshtein * * 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "frontend_action_common.h" #include "tp_frontend_action.h" namespace ebpf { using std::map; using std::set; using std::string; using std::to_string; using std::unique_ptr; using std::vector; using std::regex; using std::smatch; using std::regex_search; using std::ifstream; using namespace clang; TracepointTypeVisitor::TracepointTypeVisitor(ASTContext &C, Rewriter &rewriter) : diag_(C.getDiagnostics()), rewriter_(rewriter), out_(llvm::errs()) { } enum class field_kind_t { common, data_loc, regular, invalid }; static inline field_kind_t _get_field_kind(string const& line, string& field_type, string& field_name) { auto field_pos = line.find("field:"); if (field_pos == string::npos) return field_kind_t::invalid; auto field_semi_pos = line.find(';', field_pos); if (field_semi_pos == string::npos) return field_kind_t::invalid; auto offset_pos = line.find("offset:", field_semi_pos); if (offset_pos == string::npos) return field_kind_t::invalid; auto semi_pos = line.find(';', offset_pos); if (semi_pos == string::npos) return field_kind_t::invalid; auto size_pos = line.find("size:", semi_pos); if (size_pos == string::npos) return field_kind_t::invalid; semi_pos = line.find(';', size_pos); if (semi_pos == string::npos) return field_kind_t::invalid; auto size_str = line.substr(size_pos + 5, semi_pos - size_pos - 5); int size = std::stoi(size_str, nullptr); auto field = line.substr(field_pos + 6/*"field:"*/, field_semi_pos - field_pos - 6); auto pos = field.find_last_of("\t "); if (pos == string::npos) return field_kind_t::invalid; field_type = field.substr(0, pos); field_name = field.substr(pos + 1); if (field_type.find("__data_loc") != string::npos) return field_kind_t::data_loc; if (field_name.find("common_") == 0) return field_kind_t::common; // do not change type definition for array if (field_name.find("[") != string::npos) return field_kind_t::regular; // adjust the field_type based on the size of field // otherwise, incorrect value may be retrieved for big endian // and the field may have incorrect structure offset. if (size == 2) { if (field_type == "char" || field_type == "int8_t") field_type = "s16"; if (field_type == "unsigned char" || field_type == "uint8_t") field_type = "u16"; } else if (size == 4) { if (field_type == "char" || field_type == "short" || field_type == "int8_t" || field_type == "int16_t") field_type = "s32"; if (field_type == "unsigned char" || field_type == "unsigned short" || field_type == "uint8_t" || field_type == "uint16_t") field_type = "u32"; } else if (size == 8) { if (field_type == "char" || field_type == "short" || field_type == "int" || field_type == "int8_t" || field_type == "int16_t" || field_type == "int32_t" || field_type == "pid_t") field_type = "s64"; if (field_type == "unsigned char" || field_type == "unsigned short" || field_type == "unsigned int" || field_type == "uint8_t" || field_type == "uint16_t" || field_type == "uint32_t" || field_type == "unsigned" || field_type == "u32" || field_type == "uid_t" || field_type == "gid_t") field_type = "u64"; } return field_kind_t::regular; } string TracepointTypeVisitor::GenerateTracepointStruct( SourceLocation loc, string const& category, string const& event) { string format_file = "/sys/kernel/debug/tracing/events/" + category + "/" + event + "/format"; ifstream input(format_file.c_str()); if (!input) return ""; string tp_struct = "struct tracepoint__" + category + "__" + event + " {\n"; tp_struct += "\tu64 __do_not_use__;\n"; for (string line; getline(input, line); ) { string field_type, field_name; switch (_get_field_kind(line, field_type, field_name)) { case field_kind_t::invalid: case field_kind_t::common: continue; case field_kind_t::data_loc: tp_struct += "\tint data_loc_" + field_name + ";\n"; break; case field_kind_t::regular: tp_struct += "\t" + field_type + " " + field_name + ";\n"; break; } } tp_struct += "};\n"; return tp_struct; } static inline bool _is_tracepoint_struct_type(string const& type_name, string& tp_category, string& tp_event) { // We are looking to roughly match the regex: // (?:struct|class)\s+tracepoint__(\S+)__(\S+) // Not using std::regex because older versions of GCC don't support it yet. // E.g., the libstdc++ that ships with Ubuntu 14.04. auto first_space_pos = type_name.find_first_of("\t "); if (first_space_pos == string::npos) return false; auto first_tok = type_name.substr(0, first_space_pos); if (first_tok != "struct" && first_tok != "class") return false; auto non_space_pos = type_name.find_first_not_of("\t ", first_space_pos); auto second_space_pos = type_name.find_first_of("\t ", non_space_pos); auto second_tok = type_name.substr(non_space_pos, second_space_pos - non_space_pos); if (second_tok.find("tracepoint__") != 0) return false; auto tp_event_pos = second_tok.rfind("__"); if (tp_event_pos == string::npos) return false; tp_event = second_tok.substr(tp_event_pos + 2); auto tp_category_pos = second_tok.find("__"); if (tp_category_pos == tp_event_pos) return false; tp_category = second_tok.substr(tp_category_pos + 2, tp_event_pos - tp_category_pos - 2); return true; } bool TracepointTypeVisitor::VisitFunctionDecl(FunctionDecl *D) { if (D->isExternallyVisible() && D->hasBody()) { // If this function has a tracepoint structure as an argument, // add that structure declaration based on the structure name. for (auto it = D->param_begin(); it != D->param_end(); ++it) { auto arg = *it; auto type = arg->getType(); if (type->isPointerType() && type->getPointeeType()->isStructureOrClassType()) { auto type_name = type->getPointeeType().getAsString(); string tp_cat, tp_evt; if (_is_tracepoint_struct_type(type_name, tp_cat, tp_evt)) { string tp_struct = GenerateTracepointStruct( GET_BEGINLOC(D), tp_cat, tp_evt); // Get the actual function declaration point (the macro instantiation // point if using the TRACEPOINT_PROBE macro instead of the macro // declaration point in bpf_helpers.h). auto insert_loc = GET_BEGINLOC(D); insert_loc = rewriter_.getSourceMgr().getFileLoc(insert_loc); rewriter_.InsertText(insert_loc, tp_struct); } } } } return true; } TracepointTypeConsumer::TracepointTypeConsumer(ASTContext &C, Rewriter &rewriter) : visitor_(C, rewriter) { } bool TracepointTypeConsumer::HandleTopLevelDecl(DeclGroupRef Group) { for (auto D : Group) visitor_.TraverseDecl(D); return true; } TracepointFrontendAction::TracepointFrontendAction(llvm::raw_ostream &os) : os_(os), rewriter_(new Rewriter) { } void TracepointFrontendAction::EndSourceFileAction() { rewriter_->getEditBuffer(rewriter_->getSourceMgr().getMainFileID()).write(os_); os_.flush(); } unique_ptr TracepointFrontendAction::CreateASTConsumer( CompilerInstance &Compiler, llvm::StringRef InFile) { rewriter_->setSourceMgr(Compiler.getSourceManager(), Compiler.getLangOpts()); return unique_ptr(new TracepointTypeConsumer( Compiler.getASTContext(), *rewriter_)); } } bpfcc-0.12.0/src/cc/frontends/clang/tp_frontend_action.h000066400000000000000000000044171357404205000231470ustar00rootroot00000000000000/* * Copyright (c) 2016 Sasha Goldshtein * * 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. */ #include #include #include #include #include #include #include #include namespace clang { class ASTConsumer; class ASTContext; class CompilerInstance; } namespace llvm { class raw_ostream; class StringRef; } namespace ebpf { // Visit functions that have a tracepoint argument structure in their signature // and automatically generate the structure on-the-fly. class TracepointTypeVisitor : public clang::RecursiveASTVisitor { public: explicit TracepointTypeVisitor(clang::ASTContext &C, clang::Rewriter &rewriter); bool VisitFunctionDecl(clang::FunctionDecl *D); private: std::string GenerateTracepointStruct(clang::SourceLocation loc, std::string const& category, std::string const& event); clang::DiagnosticsEngine &diag_; clang::Rewriter &rewriter_; llvm::raw_ostream &out_; }; class TracepointTypeConsumer : public clang::ASTConsumer { public: explicit TracepointTypeConsumer(clang::ASTContext &C, clang::Rewriter &rewriter); bool HandleTopLevelDecl(clang::DeclGroupRef Group) override; private: TracepointTypeVisitor visitor_; }; class TracepointFrontendAction : public clang::ASTFrontendAction { public: TracepointFrontendAction(llvm::raw_ostream &os); void EndSourceFileAction() override; std::unique_ptr CreateASTConsumer(clang::CompilerInstance &Compiler, llvm::StringRef InFile) override; private: llvm::raw_ostream &os_; std::unique_ptr rewriter_; }; } // namespace visitor bpfcc-0.12.0/src/cc/frontends/p4/000077500000000000000000000000001357404205000163505ustar00rootroot00000000000000bpfcc-0.12.0/src/cc/frontends/p4/README.md000066400000000000000000000337331357404205000176400ustar00rootroot00000000000000# Compiling P4 to EBPF Mihai Budiu - mbudiu@barefootnetworks.com September 22, 2015 ## Abstract This document describes a prototype compiler that translates programs written in the P4 programming languages to eBPF programs. The translation is performed by generating programs written in a subset of the C programming language, that are converted to EBPF using the BPF Compiler Collection tools. The compiler code is licensed under an [Apache v2.0 license] (http://www.apache.org/licenses/LICENSE-2.0.html). ## Preliminaries In this section we give a brief overview of P4 and EBPF. A detailed treatment of these topics is outside the scope of this text. ### P4 P4 (http://p4.org) is a domain-specific programming language for specifying the behavior of the dataplanes of network-forwarding elements. The name of the programming language comes from the title of a paper published in the proceedings of SIGCOMM Computer Communications Review in 2014: http://www.sigcomm.org/ccr/papers/2014/July/0000000.0000004: "Programming Protocol-Independent Packet Processors". P4 itself is protocol-independent but allows programmers to express a rich set of data plane behaviors and protocols. The core P4 abstractions are: * Header definitions describe the format (the set of fields and their sizes) of each header within a packet. * Parse graphs (finite-state machines) describe the permitted header sequences within received packets. * Tables associate keys to actions. P4 tables generalize traditional forwarding tables; they can be used to implement routing tables, flow lookup tables, access-control lists, etc. * Actions describe how packet header fields and metadata are manipulated. * Match-action units stitch together tables and actions, and perform the following sequence of operations: * Construct lookup keys from packet fields or computed metadata, * Use the constructed lookup key to index into tables, choosing an action to execute, * Finally, execute the selected action. * Control flow is expressed as an imperative program describing the data-dependent packet processing within a pipeline, including the data-dependent sequence of match-action unit invocations. P4 programs describe the behavior of network-processing dataplanes. A P4 program is designed to operate in concert with a separate *control plane* program. The control plane is responsible for managing at runtime the contents of the P4 tables. P4 cannot be used to specify control-planes; however, a P4 program implicitly specifies the interface between the data-plane and the control-plane. The P4 language is under active development; the current stable version is 1.0.2 (see http://p4.org/spec); a reference implementation of a compiler and associated tools is freely available using a Apache 2 open-source license (see http://p4.org/code). ### EBPF #### Safe code EBPF is a acronym that stands for Extended Berkeley Packet Filters. In essence EBPF is a low-level programming language (similar to machine code); EBPF programs are traditionally executed by a virtual machine that resides in the Linux kernel. EBPF programs can be inserted and removed from a live kernel using dynamic code instrumentation. The main feature of EBPF programs is their *static safety*: prior to execution all EBPF programs have to be validated as being safe, and unsafe programs cannot be executed. A safe program provably cannot compromise the machine it is running on: * it can only access a restricted memory region (on the local stack) * it can run only for a limited amount of time; during execution it cannot block, sleep or take any locks * it cannot use any kernel resources with the exception of a limited set of kernel services which have been specifically whitelisted, including operations to manipulate tables (described below) #### Kernel hooks EBPF programs are inserted into the kernel using *hooks*. There are several types of hooks available: * any function entry point in the kernel can act as a hook; attaching an EBPF program to a function `foo()` will cause the EBPF program to execute every time some kernel thread executes `foo()`. * EBPF programs can also be attached using the Linux Traffic Control (TC) subsystem, in the network packet processing datapath. Such programs can be used as TC classifiers and actions. * EBPF programs can also be attached to sockets or network interfaces. In this case they can be used for processing packets that flow through the socket/interface. EBPF programs can be used for many purposes; the main use cases are dynamic tracing and monitoring, and packet processing. We are mostly interested in the latter use case in this document. #### EBPF Tables The EBPF runtime exposes a bi-directional kernel-userspace data communication channel, called *tables* (also called maps in some EBPF documents and code samples). EBPF tables are essentially key-value stores, where keys and values are arbitrary fixed-size bitstrings. The key width, value width and table size (maximum number of entries that can be stored) are declared statically, at table creation time. In user-space tables handles are exposed as file descriptors. Both user- and kernel-space programs can manipulate tables, by inserting, deleting, looking up, modifying, and enumerating entries in a table. In kernel space the keys and values are exposed as pointers to the raw underlying data stored in the table, whereas in user-space the pointers point to copies of the data. #### Concurrency An important aspect to understand related to EBPF is the execution model. An EBPF program is triggered by a kernel hook; multiple instances of the same kernel hook can be running simultaneously on different cores. Each table however has a single instances across all the cores. A single table may be accessed simultaneously by multiple instances of the same EBPF program running as separate kernel threads on different cores. EBPF tables are native kernel objects, and access to the table contents is protected using the kernel RCU mechanism. This makes access to table entries safe under concurrent execution; for example, the memory associated to a value cannot be accidentally freed while an EBPF program holds a pointer to the respective value. However, accessing tables is prone to data races; since EBPF programs cannot use locks, some of these races often cannot be avoided. EBPF and the associated tools are also under active development, and new capabilities are added frequently. The P4 compiler generates code that can be compiled using the BPF Compiler Collection (BCC) (https://github.com/iovisor/bcc) ## Compiling P4 to EBPF From the above description it is apparent that the P4 and EBPF programming languages have different expressive powers. However, there is a significant overlap in their capabilities, in particular, in the domain of network packet processing. The following image illustrates the situation: ![P4 and EBPF overlap in capabilities](scope.png) We expect that the overlapping region will grow in size as both P4 and EBPF continue to mature. The current version of the P4 to EBPF compiler translates programs written in the version 1.1 of the P4 programming language to programs written in a restricted subset of C. The subset of C is chosen such that it should be compilable to EBPF using BCC. ``` -------------- ------- P4 ---> | P4-to-EBPF | ---> C ----> | BCC | --> EBPF -------------- ------- ``` The P4 program only describes the packet processing *data plane*, that runs in the Linux kernel. The *control plane* must be separately implemented by the user. The BCC tools simplify this task considerably, by generating C and/or Python APIs that expose the dataplane/control-plane APIs. ### Dependencies EBPF programs require a Linux kernel with version 4.2 or newer. In order to use the P4 to EBPF compiler the following software must be installed: * The compiler itself is written in the Python (v2.x) programming language. * the P4 compiler front-end: (https://github.com/p4lang/p4-hlir). This is required for parsing the P4 programs. * the BCC compiler collection tools: (https://github.com/iovisor/bcc). This is required for compiling the generated code. Also, BCC comes with a set of Python utilities which can be used to implement control-plane programs that operate in concert with the kernel EBPF datapath. The P4 to EBPF compiler generates code that is designed for being used as a classifier using the Linux TC subsystem. Furthermore, the test code provided is written using the Python (v3.x) programming language and requires several Python packages to be installed. ### Supported capabilities The current version of the P4 to EBPF compiler supports a relatively narrow subset of the P4 language, but still powerful enough to write very complex packet filters and simple packet forwarding engines. In the spirit of open-source "release early, release often", we expect that the compiler's capabilities will improve gradually. * Packet filtering is performed using the `drop()` action. Packets that are not dropped will be forwarded. * Packet forwarding is performed by setting the `standard_metadata.egress_port` to the index of the destination network interface Here are some limitations imposed on the P4 programs: * Currently both the ingress and the egress P4 pipelines are executed at the same hook (wherever the user chooses to insert the generated EBPF program). In the future the compiler should probably generate two separate EBPF programs. * arbitrary parsers can be compiled, but the BCC compiler will reject parsers that contain cycles * arithmetic on data wider than 32 bits is not supported * checksum computations are not implemented. In consequence, programs that IP/TCP/UDP headers will produce incorrect packet headers. * EBPF does not offer support for ternary or LPM tables * P4 cloning and recirculation and not supported * meters and registers are not supported; only direct counters are currently supported. EBPF can potentially support registers and arbitrary counters, so these may appear in the future. * learning (i.e. `generate_digest`) is not implemented ### Translating P4 to C To simplify the translation, the P4 programmer should refrain using identifiers whose name starts with `ebpf_`. The following table provides a brief summary of how each P4 construct is mapped to a corresponding C construct: #### Translating parsers P4 Construct | C Translation ----------|------------ `header_type` | `struct` type `header` | `struct` instance with an additional `valid` bit `metadata` | `struct` instance parser state | code block state transition | `goto` statement `extract` | load/shift/mask data from packet buffer #### Translating match-action pipelines P4 Construct | C Translation ----------|------------ table | 2 EBPF tables: second one used just for the default action table key | `struct` type table `actions` block | tagged `union` with all possible actions `action` arguments | `struct` table `reads` | EBPF table access `action` body | code block table `apply` | `switch` statement counters | additional EBPF table ### Code organization The compiler code is organized in two folders: * `compiler`: the complete compiler source code, in Python v2.x The compiler entry point is `p4toEbpf.py`. * `test`: testing code and data. There are two testing programs: * `testP4toEbpf.py`: which compiles all P4 files in the testprograms folder * `endToEndTest.py`: which compiles and executes the simple.p4 program, and includes a simple control plane Currently the compiler contains no installation capabilities. ### Invoking the compiler Invoking the compiler is just a matter of invoking the python program with a suitable input P4 file: ``` p4toEbpf.py file.p4 -o file.c ``` #### Compiler options The P4 compiler first runs the C preprocessor on the input P4 file. Some of the command-line options are passed directly to the preprocessor. The following compiler options are available: Option | Meaning -------|-------- `-D macro` | Option passed to C preprocessor `-I path` | Option passed to C preprocessor `-U macro` | Option passed to C preprocessor `-g [router|filter]` | Controls whether the generated code behaves like a router or a filter. `-o outoutFile` | writes the generated C code to the specified output file. The `-g` option controls the nature of the generated code: * `-g filter` generates a filter; the only P4 action that has an effect is the `drop()` action. Setting metadata in P4 (e.g., `egress_port`) has no effect. * `-g router` generates a simple router; both `drop()` and `egress_port` impact packet processing. #### Using the generated code The resulting file contains the complete data structures, tables, and a C function named `ebpf_filter` that implements the P4-specified data-plane. This C file can be manipulated using the BCC tools; please refer to the BCC project documentation and sample test files of the P4 to EBPF source code for an in-depth understanding. A minimal Python program that compiles and loads into the kernel the generated file into EBPF is: ``` #!/usr/bin/env python3 from bcc import BPF b = BPF(src_file="file.c", debug=0) fn = b.load_func("ebpf_filter", BPF.SCHED_CLS) ``` ##### Connecting the generated program with the TC The EBPF code that is generated is intended to be used as a classifier attached to the ingress packet path using the Linux TC subsystem. The same EBPF code should be attached to all interfaces. Note however that all EBPF code instances share a single set of tables, which are used to control the program behavior. The following code fragment illustrates how the EBPF code can be hooked up to the `eth0` interface using a Python program. (The `fn` variable is the one produced by the previous code fragment). ``` from pyroute2 import IPRoute ipr = IPRoute() interface_name="eth0" if_index = ipr.link_lookup(ifname=interface_name)[0] ipr.tc("add", "ingress", if_index, "ffff:") ipr.tc("add-filter", "bpf", if_index, ":1", fd=fn.fd, name=fn.name, parent="ffff:", action="ok", classid=1) ``` bpfcc-0.12.0/src/cc/frontends/p4/compiler/000077500000000000000000000000001357404205000201625ustar00rootroot00000000000000bpfcc-0.12.0/src/cc/frontends/p4/compiler/README.txt000066400000000000000000000003001357404205000216510ustar00rootroot00000000000000This folder contains an implementation of a simple compiler that translates a programs written in a subset of P4 into C that can in turn be compiled into EBPF using the IOVisor bcc compiler. bpfcc-0.12.0/src/cc/frontends/p4/compiler/compilationException.py000066400000000000000000000020551357404205000247330ustar00rootroot00000000000000# Copyright (c) Barefoot Networks, Inc. # Licensed under the Apache License, Version 2.0 (the "License") class CompilationException(Exception): """Signals an error during compilation""" def __init__(self, isBug, format, *message): # isBug: indicates that this is a compiler bug super(CompilationException, self).__init__() assert isinstance(format, str) assert isinstance(isBug, bool) self.message = message self.format = format self.isBug = isBug def show(self): # TODO: format this message nicely return self.format.format(*self.message) class NotSupportedException(Exception): archError = " not supported by EBPF" def __init__(self, format, *message): super(NotSupportedException, self).__init__() assert isinstance(format, str) self.message = message self.format = format def show(self): # TODO: format this message nicely return (self.format + NotSupportedException.archError).format( *self.message) bpfcc-0.12.0/src/cc/frontends/p4/compiler/ebpfAction.py000066400000000000000000000365341357404205000226210ustar00rootroot00000000000000# Copyright (c) Barefoot Networks, Inc. # Licensed under the Apache License, Version 2.0 (the "License") from p4_hlir.hlir import p4_action, p4_field from p4_hlir.hlir import p4_signature_ref, p4_header_instance import ebpfProgram from programSerializer import ProgramSerializer from compilationException import * import ebpfScalarType import ebpfCounter import ebpfType import ebpfInstance class EbpfActionData(object): def __init__(self, name, argtype): self.name = name self.argtype = argtype class EbpfActionBase(object): def __init__(self, p4action): self.name = p4action.name self.hliraction = p4action self.builtin = False self.arguments = [] def serializeArgumentsAsStruct(self, serializer): serializer.emitIndent() serializer.appendFormat("/* no arguments for {0} */", self.name) serializer.newline() def serializeBody(self, serializer, valueName, program): serializer.emitIndent() serializer.appendFormat("/* no body for {0} */", self.name) serializer.newline() def __str__(self): return "EbpfAction({0})".format(self.name) class EbpfAction(EbpfActionBase): unsupported = [ # The following cannot be done in EBPF "add_header", "remove_header", "execute_meter", "clone_ingress_pkt_to_egress", "clone_egress_pkt_to_egress", "generate_digest", "resubmit", "modify_field_with_hash_based_offset", "truncate", "push", "pop", # The following could be done, but are not yet implemented # The situation with copy_header is complicated, # because we don't do checksums "copy_header", "count", "register_read", "register_write"] # noinspection PyUnresolvedReferences def __init__(self, p4action, program): super(EbpfAction, self).__init__(p4action) assert isinstance(p4action, p4_action) assert isinstance(program, ebpfProgram.EbpfProgram) self.builtin = False self.invalid = False # a leaf action which is never # called from a table can be invalid. for i in range(0, len(p4action.signature)): param = p4action.signature[i] width = p4action.signature_widths[i] if width is None: self.invalid = True return argtype = ebpfScalarType.EbpfScalarType(p4action, width, False, program.config) actionData = EbpfActionData(param, argtype) self.arguments.append(actionData) def serializeArgumentsAsStruct(self, serializer): if self.invalid: raise CompilationException(True, "{0} Attempting to generate code for an invalid action", self.hliraction) # Build a struct containing all action arguments. serializer.emitIndent() serializer.append("struct ") serializer.blockStart() assert isinstance(serializer, ProgramSerializer) for arg in self.arguments: assert isinstance(arg, EbpfActionData) serializer.emitIndent() argtype = arg.argtype assert isinstance(argtype, ebpfType.EbpfType) argtype.declare(serializer, arg.name, False) serializer.endOfStatement(True) serializer.blockEnd(False) serializer.space() serializer.append(self.name) serializer.endOfStatement(True) def serializeBody(self, serializer, dataContainer, program): if self.invalid: raise CompilationException(True, "{0} Attempting to generate code for an invalid action", self.hliraction) # TODO: generate PARALLEL implementation # dataContainer is a string containing the variable name # containing the action data assert isinstance(serializer, ProgramSerializer) assert isinstance(program, ebpfProgram.EbpfProgram) assert isinstance(dataContainer, str) callee_list = self.hliraction.flat_call_sequence for e in callee_list: action = e[0] assert isinstance(action, p4_action) arguments = e[1] assert isinstance(arguments, list) self.serializeCallee(self, action, arguments, serializer, dataContainer, program) def checkSize(self, call, args, program): size = None for a in args: if a is None: continue if size is None: size = a elif a != size: program.emitWarning( "{0}: Arguments do not have the same size {1} and {2}", call, size, a) return size @staticmethod def translateActionToOperator(actionName): if actionName == "add" or actionName == "add_to_field": return "+" elif actionName == "bit_and": return "&" elif actionName == "bit_or": return "|" elif actionName == "bit_xor": return "^" elif actionName == "subtract" or actionName == "subtract_from_field": return "-" else: raise CompilationException(True, "Unexpected primitive action {0}", actionName) def serializeCount(self, caller, arguments, serializer, dataContainer, program): assert isinstance(serializer, ProgramSerializer) assert isinstance(program, ebpfProgram.EbpfProgram) assert isinstance(arguments, list) assert len(arguments) == 2 counter = arguments[0] index = ArgInfo(arguments[1], caller, dataContainer, program) ctr = program.getCounter(counter.name) assert isinstance(ctr, ebpfCounter.EbpfCounter) serializer.emitIndent() serializer.blockStart() # This is actually incorrect, since the key is not always an u32. # This code is currently disabled key = program.reservedPrefix + "index" serializer.emitIndent() serializer.appendFormat("u32 {0} = {1};", key, index.asString) serializer.newline() ctr.serializeCode(key, serializer, program) serializer.blockEnd(True) def serializeCallee(self, caller, callee, arguments, serializer, dataContainer, program): if self.invalid: raise CompilationException( True, "{0} Attempting to generate code for an invalid action", self.hliraction) assert isinstance(serializer, ProgramSerializer) assert isinstance(program, ebpfProgram.EbpfProgram) assert isinstance(callee, p4_action) assert isinstance(arguments, list) if callee.name in EbpfAction.unsupported: raise NotSupportedException("{0}", callee) # This is not yet ready #if callee.name == "count": # self.serializeCount(caller, arguments, # serializer, dataContainer, program) # return serializer.emitIndent() args = self.transformArguments(arguments, caller, dataContainer, program) if callee.name == "modify_field": dst = args[0] src = args[1] size = self.checkSize(callee, [a.widthInBits() for a in args], program) if size is None: raise CompilationException( True, "Cannot infer width for arguments {0}", callee) elif size <= 32: serializer.appendFormat("{0} = {1};", dst.asString, src.asString) else: if not dst.isLvalue: raise NotSupportedException( "Constants wider than 32-bit: {0}({1})", dst.caller, dst.asString) if not src.isLvalue: raise NotSupportedException( "Constants wider than 32-bit: {0}({1})", src.caller, src.asString) serializer.appendFormat("memcpy(&{0}, &{1}, {2});", dst.asString, src.asString, size / 8) elif (callee.name == "add" or callee.name == "bit_and" or callee.name == "bit_or" or callee.name == "bit_xor" or callee.name == "subtract"): size = self.checkSize(callee, [a.widthInBits() for a in args], program) if size is None: raise CompilationException( True, "Cannot infer width for arguments {0}", callee) if size > 32: raise NotSupportedException("{0}: Arithmetic on {1}-bits", callee, size) op = EbpfAction.translateActionToOperator(callee.name) serializer.appendFormat("{0} = {1} {2} {3};", args[0].asString, args[1].asString, op, args[2].asString) elif (callee.name == "add_to_field" or callee.name == "subtract_from_field"): size = self.checkSize(callee, [a.widthInBits() for a in args], program) if size is None: raise CompilationException( True, "Cannot infer width for arguments {0}", callee) if size > 32: raise NotSupportedException( "{0}: Arithmetic on {1}-bits", callee, size) op = EbpfAction.translateActionToOperator(callee.name) serializer.appendFormat("{0} = {0} {1} {2};", args[0].asString, op, args[1].asString) elif callee.name == "no_op": serializer.append("/* noop */") elif callee.name == "drop": serializer.appendFormat("{0} = 1;", program.dropBit) elif callee.name == "push" or callee.name == "pop": raise CompilationException( True, "{0} push/pop not yet implemented", callee) else: raise CompilationException( True, "Unexpected primitive action {0}", callee) serializer.newline() def transformArguments(self, arguments, caller, dataContainer, program): result = [] for a in arguments: t = ArgInfo(a, caller, dataContainer, program) result.append(t) return result class BuiltinAction(EbpfActionBase): def __init__(self, p4action): super(BuiltinAction, self).__init__(p4action) self.builtin = True def serializeBody(self, serializer, valueName, program): # This is ugly; there should be a better way if self.name == "drop": serializer.emitIndent() serializer.appendFormat("{0} = 1;", program.dropBit) serializer.newline() else: serializer.emitIndent() serializer.appendFormat("/* no body for {0} */", self.name) serializer.newline() class ArgInfo(object): # noinspection PyUnresolvedReferences # Represents an argument passed to an action def __init__(self, argument, caller, dataContainer, program): self.width = None self.asString = None self.isLvalue = True self.caller = caller assert isinstance(program, ebpfProgram.EbpfProgram) assert isinstance(caller, EbpfAction) if isinstance(argument, int): self.asString = str(argument) self.isLvalue = False # size is unknown elif isinstance(argument, p4_field): if ebpfProgram.EbpfProgram.isArrayElementInstance( argument.instance): if isinstance(argument.instance.index, int): index = "[" + str(argument.instance.index) + "]" else: raise CompilationException( True, "Unexpected index for array {0}", argument.instance.index) stackInstance = program.getStackInstance( argument.instance.base_name) assert isinstance(stackInstance, ebpfInstance.EbpfHeaderStack) fieldtype = stackInstance.basetype.getField(argument.name) self.width = fieldtype.widthInBits() self.asString = "{0}.{1}{3}.{2}".format( program.headerStructName, stackInstance.name, argument.name, index) else: instance = program.getInstance(argument.instance.base_name) if isinstance(instance, ebpfInstance.EbpfHeader): parent = program.headerStructName else: parent = program.metadataStructName fieldtype = instance.type.getField(argument.name) self.width = fieldtype.widthInBits() self.asString = "{0}.{1}.{2}".format( parent, instance.name, argument.name) elif isinstance(argument, p4_signature_ref): refarg = caller.arguments[argument.idx] self.asString = "{0}->u.{1}.{2}".format( dataContainer, caller.name, refarg.name) self.width = caller.arguments[argument.idx].argtype.widthInBits() elif isinstance(argument, p4_header_instance): # This could be a header array element # Unfortunately for push and pop, the user mean the whole array, # but the representation contains just the first element here. # This looks like a bug in the HLIR. if ebpfProgram.EbpfProgram.isArrayElementInstance(argument): if isinstance(argument.index, int): index = "[" + str(argument.index) + "]" else: raise CompilationException( True, "Unexpected index for array {0}", argument.index) stackInstance = program.getStackInstance(argument.base_name) assert isinstance(stackInstance, ebpfInstance.EbpfHeaderStack) fieldtype = stackInstance.basetype self.width = fieldtype.widthInBits() self.asString = "{0}.{1}{2}".format( program.headerStructName, stackInstance.name, index) else: instance = program.getInstance(argument.name) instancetype = instance.type self.width = instancetype.widthInBits() self.asString = "{0}.{1}".format( program.headerStructName, argument.name) else: raise CompilationException( True, "Unexpected action argument {0}", argument) def widthInBits(self): return self.width bpfcc-0.12.0/src/cc/frontends/p4/compiler/ebpfConditional.py000066400000000000000000000101271357404205000236350ustar00rootroot00000000000000# Copyright (c) Barefoot Networks, Inc. # Licensed under the Apache License, Version 2.0 (the "License") from p4_hlir.hlir import p4_conditional_node, p4_expression from p4_hlir.hlir import p4_header_instance, p4_field from programSerializer import ProgramSerializer from compilationException import CompilationException import ebpfProgram import ebpfInstance class EbpfConditional(object): @staticmethod def translate(op): if op == "not": return "!" elif op == "or": return "||" elif op == "and": return "&&" return op def __init__(self, p4conditional, program): assert isinstance(p4conditional, p4_conditional_node) assert isinstance(program, ebpfProgram.EbpfProgram) self.hlirconditional = p4conditional self.name = p4conditional.name def emitNode(self, node, serializer, program): if isinstance(node, p4_expression): self.emitExpression(node, serializer, program, False) elif node is None: pass elif isinstance(node, int): serializer.append(node) elif isinstance(node, p4_header_instance): header = program.getInstance(node.name) assert isinstance(header, ebpfInstance.EbpfHeader) # TODO: stacks? serializer.appendFormat( "{0}.{1}", program.headerStructName, header.name) elif isinstance(node, p4_field): instance = node.instance einstance = program.getInstance(instance.name) if isinstance(einstance, ebpfInstance.EbpfHeader): base = program.headerStructName else: base = program.metadataStructName serializer.appendFormat( "{0}.{1}.{2}", base, einstance.name, node.name) else: raise CompilationException(True, "{0} Unexpected expression ", node) def emitExpression(self, expression, serializer, program, toplevel): assert isinstance(serializer, ProgramSerializer) assert isinstance(program, ebpfProgram.EbpfProgram) assert isinstance(expression, p4_expression) assert isinstance(toplevel, bool) left = expression.left op = expression.op right = expression.right assert isinstance(op, str) if op == "valid": self.emitNode(right, serializer, program) serializer.append(".valid") return if not toplevel: serializer.append("(") self.emitNode(left, serializer, program) op = EbpfConditional.translate(op) serializer.append(op) self.emitNode(right, serializer, program) if not toplevel: serializer.append(")") def generateCode(self, serializer, program, nextNode): assert isinstance(serializer, ProgramSerializer) assert isinstance(program, ebpfProgram.EbpfProgram) serializer.emitIndent() serializer.blockStart() trueBranch = self.hlirconditional.next_[True] if trueBranch is None: trueBranch = nextNode falseBranch = self.hlirconditional.next_[False] if falseBranch is None: falseBranch = nextNode serializer.emitIndent() serializer.appendFormat("{0}:", program.getLabel(self.hlirconditional)) serializer.newline() serializer.emitIndent() serializer.append("if (") self.emitExpression( self.hlirconditional.condition, serializer, program, True) serializer.appendLine(")") serializer.increaseIndent() label = program.getLabel(trueBranch) serializer.emitIndent() serializer.appendFormat("goto {0};", label) serializer.newline() serializer.decreaseIndent() serializer.emitIndent() serializer.appendLine("else") serializer.increaseIndent() label = program.getLabel(falseBranch) serializer.emitIndent() serializer.appendFormat("goto {0};", label) serializer.newline() serializer.decreaseIndent() serializer.blockEnd(True) bpfcc-0.12.0/src/cc/frontends/p4/compiler/ebpfCounter.py000066400000000000000000000102071357404205000230100ustar00rootroot00000000000000# Copyright (c) Barefoot Networks, Inc. # Licensed under the Apache License, Version 2.0 (the "License") from p4_hlir.hlir import p4_counter, P4_DIRECT, P4_COUNTER_BYTES from programSerializer import ProgramSerializer from compilationException import * import ebpfTable import ebpfProgram class EbpfCounter(object): # noinspection PyUnresolvedReferences def __init__(self, hlircounter, program): assert isinstance(hlircounter, p4_counter) assert isinstance(program, ebpfProgram.EbpfProgram) self.name = hlircounter.name self.hlircounter = hlircounter width = hlircounter.min_width # ebpf counters only work on 64-bits if width <= 64: self.valueTypeName = program.config.uprefix + "64" else: raise NotSupportedException( "{0}: Counters with {1} bits", hlircounter, width) self.dataMapName = self.name if ((hlircounter.binding is None) or (hlircounter.binding[0] != P4_DIRECT)): raise NotSupportedException( "{0}: counter which is not direct", hlircounter) self.autoIncrement = (hlircounter.binding != None and hlircounter.binding[0] == P4_DIRECT) if hlircounter.type is P4_COUNTER_BYTES: self.increment = "{0}->len".format(program.packetName) else: self.increment = "1" def getSize(self, program): if self.hlircounter.instance_count is not None: return self.hlircounter.instance_count if self.autoIncrement: return self.getTable(program).size program.emitWarning( "{0} does not specify a max_size; using 1024", self.hlircounter) return 1024 def getTable(self, program): table = program.getTable(self.hlircounter.binding[1].name) assert isinstance(table, ebpfTable.EbpfTable) return table def serialize(self, serializer, program): assert isinstance(serializer, ProgramSerializer) # Direct counters have the same key as the associated table # Static counters have integer keys if self.autoIncrement: keyTypeName = "struct " + self.getTable(program).keyTypeName else: keyTypeName = program.config.uprefix + "32" program.config.serializeTableDeclaration( serializer, self.dataMapName, True, keyTypeName, self.valueTypeName, self.getSize(program)) def serializeCode(self, keyname, serializer, program): assert isinstance(serializer, ProgramSerializer) assert isinstance(program, ebpfProgram.EbpfProgram) serializer.emitIndent() serializer.appendFormat("/* Update counter {0} */", self.name) serializer.newline() valueName = "ctrvalue" initValuename = "init_val" serializer.emitIndent() serializer.appendFormat("{0} *{1};", self.valueTypeName, valueName) serializer.newline() serializer.emitIndent() serializer.appendFormat("{0} {1};", self.valueTypeName, initValuename) serializer.newline() serializer.emitIndent() serializer.appendLine("/* perform lookup */") serializer.emitIndent() program.config.serializeLookup( serializer, self.dataMapName, keyname, valueName) serializer.newline() serializer.emitIndent() serializer.appendFormat("if ({0} != NULL) ", valueName) serializer.newline() serializer.increaseIndent() serializer.emitIndent() serializer.appendFormat("__sync_fetch_and_add({0}, {1});", valueName, self.increment) serializer.newline() serializer.decreaseIndent() serializer.emitIndent() serializer.append("else ") serializer.blockStart() serializer.emitIndent() serializer.appendFormat("{0} = {1};", initValuename, self.increment) serializer.newline() serializer.emitIndent() program.config.serializeUpdate( serializer, self.dataMapName, keyname, initValuename) serializer.newline() serializer.blockEnd(True) bpfcc-0.12.0/src/cc/frontends/p4/compiler/ebpfDeparser.py000066400000000000000000000150311357404205000231360ustar00rootroot00000000000000# Copyright (c) Barefoot Networks, Inc. # Licensed under the Apache License, Version 2.0 (the "License") from collections import defaultdict, OrderedDict from compilationException import CompilationException from p4_hlir.hlir import parse_call, p4_field, p4_parse_value_set, \ P4_DEFAULT, p4_parse_state, p4_table, \ p4_conditional_node, p4_parser_exception, \ p4_header_instance, P4_NEXT import ebpfProgram import ebpfInstance import ebpfType import ebpfStructType from topoSorting import Graph from programSerializer import ProgramSerializer def produce_parser_topo_sorting(hlir): # This function is copied from the P4 behavioral model implementation header_graph = Graph() def walk_rec(hlir, parse_state, prev_hdr_node, tag_stacks_index): assert(isinstance(parse_state, p4_parse_state)) for call in parse_state.call_sequence: call_type = call[0] if call_type == parse_call.extract: hdr = call[1] if hdr.virtual: base_name = hdr.base_name current_index = tag_stacks_index[base_name] if current_index > hdr.max_index: return tag_stacks_index[base_name] += 1 name = base_name + "[%d]" % current_index hdr = hlir.p4_header_instances[name] if hdr not in header_graph: header_graph.add_node(hdr) hdr_node = header_graph.get_node(hdr) if prev_hdr_node: prev_hdr_node.add_edge_to(hdr_node) else: header_graph.root = hdr prev_hdr_node = hdr_node for branch_case, next_state in parse_state.branch_to.items(): if not next_state: continue if not isinstance(next_state, p4_parse_state): continue walk_rec(hlir, next_state, prev_hdr_node, tag_stacks_index.copy()) start_state = hlir.p4_parse_states["start"] walk_rec(hlir, start_state, None, defaultdict(int)) header_topo_sorting = header_graph.produce_topo_sorting() return header_topo_sorting class EbpfDeparser(object): def __init__(self, hlir): header_topo_sorting = produce_parser_topo_sorting(hlir) self.headerOrder = [hdr.name for hdr in header_topo_sorting] def serialize(self, serializer, program): assert isinstance(serializer, ProgramSerializer) assert isinstance(program, ebpfProgram.EbpfProgram) serializer.emitIndent() serializer.blockStart() serializer.emitIndent() serializer.appendLine("/* Deparser */") serializer.emitIndent() serializer.appendFormat("{0} = 0;", program.offsetVariableName) serializer.newline() for h in self.headerOrder: header = program.getHeaderInstance(h) self.serializeHeaderEmit(header, serializer, program) serializer.blockEnd(True) def serializeHeaderEmit(self, header, serializer, program): assert isinstance(header, ebpfInstance.EbpfHeader) assert isinstance(serializer, ProgramSerializer) assert isinstance(program, ebpfProgram.EbpfProgram) p4header = header.hlirInstance assert isinstance(p4header, p4_header_instance) serializer.emitIndent() serializer.appendFormat("if ({0}.{1}.valid) ", program.headerStructName, header.name) serializer.blockStart() if ebpfProgram.EbpfProgram.isArrayElementInstance(p4header): ebpfStack = program.getStackInstance(p4header.base_name) assert isinstance(ebpfStack, ebpfInstance.EbpfHeaderStack) if isinstance(p4header.index, int): index = "[" + str(p4header.index) + "]" elif p4header.index is P4_NEXT: index = "[" + ebpfStack.indexVar + "]" else: raise CompilationException( True, "Unexpected index for array {0}", p4header.index) basetype = ebpfStack.basetype else: ebpfHeader = program.getHeaderInstance(p4header.name) basetype = ebpfHeader.type index = "" alignment = 0 for field in basetype.fields: assert isinstance(field, ebpfStructType.EbpfField) self.serializeFieldEmit(serializer, p4header.base_name, index, field, alignment, program) alignment += field.widthInBits() alignment = alignment % 8 serializer.blockEnd(True) def serializeFieldEmit(self, serializer, name, index, field, alignment, program): assert isinstance(index, str) assert isinstance(name, str) assert isinstance(field, ebpfStructType.EbpfField) assert isinstance(serializer, ProgramSerializer) assert isinstance(alignment, int) assert isinstance(program, ebpfProgram.EbpfProgram) if field.name == "valid": return fieldToEmit = (program.headerStructName + "." + name + index + "." + field.name) width = field.widthInBits() if width <= 32: store = self.generatePacketStore(fieldToEmit, 0, alignment, width, program) serializer.emitIndent() serializer.appendLine(store) else: # Destination is bigger than 4 bytes and # represented as a byte array. b = (width + 7) / 8 for i in range(0, b): serializer.emitIndent() store = self.generatePacketStore(fieldToEmit + "["+str(i)+"]", i, alignment, 8, program) serializer.appendLine(store) serializer.emitIndent() serializer.appendFormat("{0} += {1};", program.offsetVariableName, width) serializer.newline() def generatePacketStore(self, value, offset, alignment, width, program): assert width > 0 assert alignment < 8 assert isinstance(width, int) assert isinstance(alignment, int) return "bpf_dins_pkt({0}, {1} / 8 + {2}, {3}, {4}, {5});".format( program.packetName, program.offsetVariableName, offset, alignment, width, value ) bpfcc-0.12.0/src/cc/frontends/p4/compiler/ebpfInstance.py000066400000000000000000000066401357404205000231430ustar00rootroot00000000000000# Copyright (c) Barefoot Networks, Inc. # Licensed under the Apache License, Version 2.0 (the "License") from p4_hlir.hlir import p4_header_instance from ebpfType import EbpfType from compilationException import CompilationException from programSerializer import ProgramSerializer import typeFactory class EbpfInstanceBase(object): def __init__(self): pass class SimpleInstance(EbpfInstanceBase): # A header or a metadata instance (but not array elements) def __init__(self, hlirInstance, factory, isMetadata): super(SimpleInstance, self).__init__() self.hlirInstance = hlirInstance self.name = hlirInstance.base_name self.type = factory.build(hlirInstance.header_type, isMetadata) def declare(self, serializer): assert isinstance(serializer, ProgramSerializer) self.type.declare(serializer, self.name, False) class EbpfHeader(SimpleInstance): """ Represents a header instance from a P4 program """ def __init__(self, hlirHeaderInstance, factory): super(EbpfHeader, self).__init__(hlirHeaderInstance, factory, False) if hlirHeaderInstance.metadata: raise CompilationException(True, "Metadata passed to EpbfHeader") if hlirHeaderInstance.index is not None: self.name += "_" + str(hlirHeaderInstance.index) class EbpfMetadata(SimpleInstance): """Represents a metadata instance from a P4 program""" def __init__(self, hlirMetadataInstance, factory): super(EbpfMetadata, self).__init__(hlirMetadataInstance, factory, True) if not hlirMetadataInstance.metadata: raise CompilationException( True, "Header instance passed to EpbfMetadata {0}", hlirMetadataInstance) if hlirMetadataInstance.index is not None: raise CompilationException( True, "Unexpected metadata array {0}", self.hlirInstance) if hasattr(hlirMetadataInstance, "initializer"): self.initializer = hlirMetadataInstance.initializer else: self.initializer = None def emitInitializer(self, serializer): assert isinstance(serializer, ProgramSerializer) if self.initializer is None: self.type.emitInitializer(serializer) else: for key in self.initializer.keys(): serializer.appendFormat( ".{0} = {1},", key, self.initializer[key]) class EbpfHeaderStack(EbpfInstanceBase): """Represents a header stack instance; there is one instance of this class for each STACK, and not for each element of the stack, as in the HLIR""" def __init__(self, hlirInstance, indexVar, factory): super(EbpfHeaderStack, self).__init__() # indexVar: name of the ebpf variable that # holds the current index for this stack assert isinstance(indexVar, str) assert isinstance(factory, typeFactory.EbpfTypeFactory) assert isinstance(hlirInstance, p4_header_instance) self.indexVar = indexVar self.name = hlirInstance.base_name self.basetype = factory.build(hlirInstance.header_type, False) assert isinstance(self.basetype, EbpfType) self.arraySize = hlirInstance.max_index + 1 self.hlirInstance = hlirInstance def declare(self, serializer): assert isinstance(serializer, ProgramSerializer) self.basetype.declareArray(serializer, self.name, self.arraySize) bpfcc-0.12.0/src/cc/frontends/p4/compiler/ebpfParser.py000066400000000000000000000427041357404205000226340ustar00rootroot00000000000000# Copyright (c) Barefoot Networks, Inc. # Licensed under the Apache License, Version 2.0 (the "License") from p4_hlir.hlir import parse_call, p4_field, p4_parse_value_set, \ P4_DEFAULT, p4_parse_state, p4_table, \ p4_conditional_node, p4_parser_exception, \ p4_header_instance, P4_NEXT import ebpfProgram import ebpfStructType import ebpfInstance import programSerializer from compilationException import * class EbpfParser(object): def __init__(self, hlirParser): # hlirParser is a P4 parser self.parser = hlirParser self.name = hlirParser.name def serialize(self, serializer, program): assert isinstance(serializer, programSerializer.ProgramSerializer) assert isinstance(program, ebpfProgram.EbpfProgram) serializer.emitIndent() serializer.appendFormat("{0}: ", self.name) serializer.blockStart() for op in self.parser.call_sequence: self.serializeOperation(serializer, op, program) self.serializeBranch(serializer, self.parser.branch_on, self.parser.branch_to, program) serializer.blockEnd(True) def serializeSelect(self, selectVarName, serializer, branch_on, program): # selectVarName - name of temp variable to use for the select expression assert isinstance(selectVarName, str) assert isinstance(serializer, programSerializer.ProgramSerializer) assert isinstance(program, ebpfProgram.EbpfProgram) totalWidth = 0 switchValue = "" for e in branch_on: if isinstance(e, p4_field): instance = e.instance assert isinstance(instance, p4_header_instance) index = "" if ebpfProgram.EbpfProgram.isArrayElementInstance(instance): ebpfStack = program.getStackInstance(instance.base_name) assert isinstance(ebpfStack, ebpfInstance.EbpfHeaderStack) if isinstance(instance.index, int): index = "[" + str(instance.index) + "]" elif instance.index is P4_NEXT: index = "[" + ebpfStack.indexVar + "]" else: raise CompilationException(True, "Unexpected index for array {0}", instance.index) basetype = ebpfStack.basetype name = ebpfStack.name else: ebpfHeader = program.getInstance(instance.name) assert isinstance(ebpfHeader, ebpfInstance.EbpfHeader) basetype = ebpfHeader.type name = ebpfHeader.name ebpfField = basetype.getField(e.name) assert isinstance(ebpfField, ebpfStructType.EbpfField) totalWidth += ebpfField.widthInBits() fieldReference = (program.headerStructName + "." + name + index + "." + ebpfField.name) if switchValue == "": switchValue = fieldReference else: switchValue = ("(" + switchValue + " << " + str(ebpfField.widthInBits()) + ")") switchValue = switchValue + " | " + fieldReference elif isinstance(e, tuple): switchValue = self.currentReferenceAsString(e, program) else: raise CompilationException( True, "Unexpected element in match {0}", e) if totalWidth > 32: raise NotSupportedException("{0}: Matching on {1}-bit value", branch_on, totalWidth) serializer.emitIndent() serializer.appendFormat("{0}32 {1} = {2};", program.config.uprefix, selectVarName, switchValue) serializer.newline() def generatePacketLoad(self, startBit, width, alignment, program): # Generates an expression that does a load_*, shift and mask # to load 'width' bits starting at startBit from the current # packet offset. # alignment is an integer <= 8 that holds the current alignment # of of the packet offset. assert width > 0 assert alignment < 8 assert isinstance(startBit, int) assert isinstance(width, int) assert isinstance(alignment, int) firstBitIndex = startBit + alignment lastBitIndex = startBit + width + alignment - 1 firstWordIndex = firstBitIndex / 8 lastWordIndex = lastBitIndex / 8 wordsToRead = lastWordIndex - firstWordIndex + 1 if wordsToRead == 1: load = "load_byte" loadSize = 8 elif wordsToRead == 2: load = "load_half" loadSize = 16 elif wordsToRead <= 4: load = "load_word" loadSize = 32 elif wordsToRead <= 8: load = "load_dword" loadSize = 64 else: raise CompilationException(True, "Attempt to load more than 1 word") readtype = program.config.uprefix + str(loadSize) loadInstruction = "{0}({1}, ({2} + {3}) / 8)".format( load, program.packetName, program.offsetVariableName, startBit) shift = loadSize - alignment - width load = "(({0}) >> ({1}))".format(loadInstruction, shift) if width != loadSize: mask = " & EBPF_MASK({0}, {1})".format(readtype, width) else: mask = "" return load + mask def currentReferenceAsString(self, tpl, program): # a string describing an expression of the form current(position, width) # The assumption is that at this point the packet cursor is ALWAYS # byte aligned. This should be true because headers are supposed # to have sizes an integral number of bytes. assert isinstance(tpl, tuple) if len(tpl) != 2: raise CompilationException( True, "{0} Expected a tuple with 2 elements", tpl) minIndex = tpl[0] totalWidth = tpl[1] result = self.generatePacketLoad( minIndex, totalWidth, 0, program) # alignment is 0 return result def serializeCases(self, selectVarName, serializer, branch_to, program): assert isinstance(selectVarName, str) assert isinstance(serializer, programSerializer.ProgramSerializer) assert isinstance(program, ebpfProgram.EbpfProgram) branches = 0 seenDefault = False for e in branch_to.keys(): serializer.emitIndent() value = branch_to[e] if isinstance(e, int): serializer.appendFormat("if ({0} == {1})", selectVarName, e) elif isinstance(e, tuple): serializer.appendFormat( "if (({0} & {1}) == {2})", selectVarName, e[0], e[1]) elif isinstance(e, p4_parse_value_set): raise NotSupportedException("{0}: Parser value sets", e) elif e is P4_DEFAULT: seenDefault = True if branches > 0: serializer.append("else") else: raise CompilationException( True, "Unexpected element in match case {0}", e) branches += 1 serializer.newline() serializer.increaseIndent() serializer.emitIndent() label = program.getLabel(value) if isinstance(value, p4_parse_state): serializer.appendFormat("goto {0};", label) elif isinstance(value, p4_table): serializer.appendFormat("goto {0};", label) elif isinstance(value, p4_conditional_node): serializer.appendFormat("goto {0};", label) elif isinstance(value, p4_parser_exception): raise CompilationException(True, "Not yet implemented") else: raise CompilationException( True, "Unexpected element in match case {0}", value) serializer.decreaseIndent() serializer.newline() # Must create default if it is missing if not seenDefault: serializer.emitIndent() serializer.appendFormat( "{0} = p4_pe_unhandled_select;", program.errorName) serializer.newline() serializer.emitIndent() serializer.appendFormat("default: goto end;") serializer.newline() def serializeBranch(self, serializer, branch_on, branch_to, program): assert isinstance(serializer, programSerializer.ProgramSerializer) assert isinstance(program, ebpfProgram.EbpfProgram) if branch_on == []: dest = branch_to.values()[0] serializer.emitIndent() name = program.getLabel(dest) serializer.appendFormat("goto {0};", name) serializer.newline() elif isinstance(branch_on, list): tmpvar = program.generateNewName("tmp") self.serializeSelect(tmpvar, serializer, branch_on, program) self.serializeCases(tmpvar, serializer, branch_to, program) else: raise CompilationException( True, "Unexpected branch_on {0}", branch_on) def serializeOperation(self, serializer, op, program): assert isinstance(serializer, programSerializer.ProgramSerializer) assert isinstance(program, ebpfProgram.EbpfProgram) operation = op[0] if operation is parse_call.extract: self.serializeExtract(serializer, op[1], program) elif operation is parse_call.set: self.serializeMetadataSet(serializer, op[1], op[2], program) else: raise CompilationException( True, "Unexpected operation in parser {0}", op) def serializeFieldExtract(self, serializer, headerInstanceName, index, field, alignment, program): assert isinstance(index, str) assert isinstance(headerInstanceName, str) assert isinstance(field, ebpfStructType.EbpfField) assert isinstance(serializer, programSerializer.ProgramSerializer) assert isinstance(alignment, int) assert isinstance(program, ebpfProgram.EbpfProgram) fieldToExtractTo = headerInstanceName + index + "." + field.name serializer.emitIndent() width = field.widthInBits() if field.name == "valid": serializer.appendFormat( "{0}.{1} = 1;", program.headerStructName, fieldToExtractTo) serializer.newline() return serializer.appendFormat("if ({0}->len < BYTES({1} + {2})) ", program.packetName, program.offsetVariableName, width) serializer.blockStart() serializer.emitIndent() serializer.appendFormat("{0} = p4_pe_header_too_short;", program.errorName) serializer.newline() serializer.emitIndent() serializer.appendLine("goto end;") # TODO: jump to correct exception handler serializer.blockEnd(True) if width <= 32: serializer.emitIndent() load = self.generatePacketLoad(0, width, alignment, program) serializer.appendFormat("{0}.{1} = {2};", program.headerStructName, fieldToExtractTo, load) serializer.newline() else: # Destination is bigger than 4 bytes and # represented as a byte array. if alignment == 0: shift = 0 else: shift = 8 - alignment assert shift >= 0 if shift == 0: method = "load_byte" else: method = "load_half" b = (width + 7) / 8 for i in range(0, b): serializer.emitIndent() serializer.appendFormat("{0}.{1}[{2}] = ({3}8)", program.headerStructName, fieldToExtractTo, i, program.config.uprefix) serializer.appendFormat("(({0}({1}, ({2} / 8) + {3}) >> {4})", method, program.packetName, program.offsetVariableName, i, shift) if (i == b - 1) and (width % 8 != 0): serializer.appendFormat(" & EBPF_MASK({0}8, {1})", program.config.uprefix, width % 8) serializer.append(")") serializer.endOfStatement(True) serializer.emitIndent() serializer.appendFormat("{0} += {1};", program.offsetVariableName, width) serializer.newline() def serializeExtract(self, serializer, headerInstance, program): assert isinstance(serializer, programSerializer.ProgramSerializer) assert isinstance(headerInstance, p4_header_instance) assert isinstance(program, ebpfProgram.EbpfProgram) if ebpfProgram.EbpfProgram.isArrayElementInstance(headerInstance): ebpfStack = program.getStackInstance(headerInstance.base_name) assert isinstance(ebpfStack, ebpfInstance.EbpfHeaderStack) # write bounds check serializer.emitIndent() serializer.appendFormat("if ({0} >= {1}) ", ebpfStack.indexVar, ebpfStack.arraySize) serializer.blockStart() serializer.emitIndent() serializer.appendFormat("{0} = p4_pe_index_out_of_bounds;", program.errorName) serializer.newline() serializer.emitIndent() serializer.appendLine("goto end;") serializer.blockEnd(True) if isinstance(headerInstance.index, int): index = "[" + str(headerInstance.index) + "]" elif headerInstance.index is P4_NEXT: index = "[" + ebpfStack.indexVar + "]" else: raise CompilationException( True, "Unexpected index for array {0}", headerInstance.index) basetype = ebpfStack.basetype else: ebpfHeader = program.getHeaderInstance(headerInstance.name) basetype = ebpfHeader.type index = "" # extract all fields alignment = 0 for field in basetype.fields: assert isinstance(field, ebpfStructType.EbpfField) self.serializeFieldExtract(serializer, headerInstance.base_name, index, field, alignment, program) alignment += field.widthInBits() alignment = alignment % 8 if ebpfProgram.EbpfProgram.isArrayElementInstance(headerInstance): # increment stack index ebpfStack = program.getStackInstance(headerInstance.base_name) assert isinstance(ebpfStack, ebpfInstance.EbpfHeaderStack) # write bounds check serializer.emitIndent() serializer.appendFormat("{0}++;", ebpfStack.indexVar) serializer.newline() def serializeMetadataSet(self, serializer, field, value, program): assert isinstance(serializer, programSerializer.ProgramSerializer) assert isinstance(program, ebpfProgram.EbpfProgram) assert isinstance(field, p4_field) dest = program.getInstance(field.instance.name) assert isinstance(dest, ebpfInstance.SimpleInstance) destType = dest.type assert isinstance(destType, ebpfStructType.EbpfStructType) destField = destType.getField(field.name) if destField.widthInBits() > 32: useMemcpy = True bytesToCopy = destField.widthInBits() / 8 if destField.widthInBits() % 8 != 0: raise CompilationException( True, "{0}: Not implemented: wide field w. sz not multiple of 8", field) else: useMemcpy = False bytesToCopy = None # not needed, but compiler is confused serializer.emitIndent() destination = "{0}.{1}.{2}".format( program.metadataStructName, dest.name, destField.name) if isinstance(value, int): source = str(value) if useMemcpy: raise CompilationException( True, "{0}: Not implemented: copying from wide constant", value) elif isinstance(value, tuple): source = self.currentReferenceAsString(value, program) elif isinstance(value, p4_field): source = program.getInstance(value.instance.name) if isinstance(source, ebpfInstance.EbpfMetadata): sourceStruct = program.metadataStructName else: sourceStruct = program.headerStructName source = "{0}.{1}.{2}".format(sourceStruct, source.name, value.name) else: raise CompilationException( True, "Unexpected type for parse_call.set {0}", value) if useMemcpy: serializer.appendFormat("memcpy(&{0}, &{1}, {2})", destination, source, bytesToCopy) else: serializer.appendFormat("{0} = {1}", destination, source) serializer.endOfStatement(True) bpfcc-0.12.0/src/cc/frontends/p4/compiler/ebpfProgram.py000066400000000000000000000436301357404205000230060ustar00rootroot00000000000000# Copyright (c) Barefoot Networks, Inc. # Licensed under the Apache License, Version 2.0 (the "License") from p4_hlir.hlir import p4_header_instance, p4_table, \ p4_conditional_node, p4_action, p4_parse_state from p4_hlir.main import HLIR import typeFactory import ebpfTable import ebpfParser import ebpfAction import ebpfInstance import ebpfConditional import ebpfCounter import ebpfDeparser import programSerializer import target from compilationException import * class EbpfProgram(object): def __init__(self, name, hlir, isRouter, config): """Representation of an EbpfProgram (in fact, a C program that is converted to EBPF)""" assert isinstance(hlir, HLIR) assert isinstance(isRouter, bool) assert isinstance(config, target.TargetConfig) self.hlir = hlir self.name = name self.uniqueNameCounter = 0 self.config = config self.isRouter = isRouter self.reservedPrefix = "ebpf_" assert isinstance(config, target.TargetConfig) self.packetName = self.reservedPrefix + "packet" self.dropBit = self.reservedPrefix + "drop" self.license = "GPL" self.offsetVariableName = self.reservedPrefix + "packetOffsetInBits" self.zeroKeyName = self.reservedPrefix + "zero" self.arrayIndexType = self.config.uprefix + "32" # all array tables must be indexed with u32 values self.errorName = self.reservedPrefix + "error" self.functionName = self.reservedPrefix + "filter" self.egressPortName = "egress_port" # Hardwired in P4 definition self.typeFactory = typeFactory.EbpfTypeFactory(config) self.errorCodes = [ "p4_pe_no_error", "p4_pe_index_out_of_bounds", "p4_pe_out_of_packet", "p4_pe_header_too_long", "p4_pe_header_too_short", "p4_pe_unhandled_select", "p4_pe_checksum"] self.actions = [] self.conditionals = [] self.tables = [] self.headers = [] # header instances self.metadata = [] # metadata instances self.stacks = [] # header stack instances EbpfHeaderStack self.parsers = [] # all parsers self.deparser = None self.entryPoints = [] # control-flow entry points from parser self.counters = [] self.entryPointLabels = {} # maps p4_node from entryPoints # to labels in the C program self.egressEntry = None self.construct() self.headersStructTypeName = self.reservedPrefix + "headers_t" self.headerStructName = self.reservedPrefix + "headers" self.metadataStructTypeName = self.reservedPrefix + "metadata_t" self.metadataStructName = self.reservedPrefix + "metadata" def construct(self): if len(self.hlir.p4_field_list_calculations) > 0: raise NotSupportedException( "{0} calculated field", self.hlir.p4_field_list_calculations.values()[0].name) for h in self.hlir.p4_header_instances.values(): if h.max_index is not None: assert isinstance(h, p4_header_instance) if h.index == 0: # header stack; allocate only for zero-th index indexVarName = self.generateNewName(h.base_name + "_index") stack = ebpfInstance.EbpfHeaderStack( h, indexVarName, self.typeFactory) self.stacks.append(stack) elif h.metadata: metadata = ebpfInstance.EbpfMetadata(h, self.typeFactory) self.metadata.append(metadata) else: header = ebpfInstance.EbpfHeader(h, self.typeFactory) self.headers.append(header) for p in self.hlir.p4_parse_states.values(): parser = ebpfParser.EbpfParser(p) self.parsers.append(parser) for a in self.hlir.p4_actions.values(): if self.isInternalAction(a): continue action = ebpfAction.EbpfAction(a, self) self.actions.append(action) for c in self.hlir.p4_counters.values(): counter = ebpfCounter.EbpfCounter(c, self) self.counters.append(counter) for t in self.hlir.p4_tables.values(): table = ebpfTable.EbpfTable(t, self, self.config) self.tables.append(table) for n in self.hlir.p4_ingress_ptr.keys(): self.entryPoints.append(n) for n in self.hlir.p4_conditional_nodes.values(): conditional = ebpfConditional.EbpfConditional(n, self) self.conditionals.append(conditional) self.egressEntry = self.hlir.p4_egress_ptr self.deparser = ebpfDeparser.EbpfDeparser(self.hlir) def isInternalAction(self, action): # This is a heuristic really to guess which actions are built-in # Unfortunately there seems to be no other way to do this return action.lineno < 0 @staticmethod def isArrayElementInstance(headerInstance): assert isinstance(headerInstance, p4_header_instance) return headerInstance.max_index is not None def emitWarning(self, formatString, *message): assert isinstance(formatString, str) print("WARNING: ", formatString.format(*message)) def toC(self, serializer): assert isinstance(serializer, programSerializer.ProgramSerializer) self.generateIncludes(serializer) self.generatePreamble(serializer) self.generateTypes(serializer) self.generateTables(serializer) serializer.newline() serializer.emitIndent() self.config.serializeCodeSection(serializer) serializer.newline() serializer.emitIndent() serializer.appendFormat("int {0}(struct __sk_buff* {1}) ", self.functionName, self.packetName) serializer.blockStart() self.generateHeaderInstance(serializer) serializer.append(" = ") self.generateInitializeHeaders(serializer) serializer.endOfStatement(True) self.generateMetadataInstance(serializer) serializer.append(" = ") self.generateInitializeMetadata(serializer) serializer.endOfStatement(True) self.createLocalVariables(serializer) serializer.newline() serializer.emitIndent() serializer.appendLine("goto start;") self.generateParser(serializer) self.generatePipeline(serializer) self.generateDeparser(serializer) serializer.emitIndent() serializer.appendLine("end:") serializer.emitIndent() if isinstance(self.config, target.KernelSamplesConfig): serializer.appendFormat("return {0};", self.dropBit) serializer.newline() elif isinstance(self.config, target.BccConfig): if self.isRouter: serializer.appendFormat("if (!{0})", self.dropBit) serializer.newline() serializer.increaseIndent() serializer.emitIndent() serializer.appendFormat( "bpf_clone_redirect({0}, {1}.standard_metadata.{2}, 0);", self.packetName, self.metadataStructName, self.egressPortName) serializer.newline() serializer.decreaseIndent() serializer.emitIndent() serializer.appendLine( "return TC_ACT_SHOT /* drop packet; clone is forwarded */;") else: serializer.appendFormat( "return {1} ? TC_ACT_SHOT : TC_ACT_PIPE;", self.dropBit) serializer.newline() else: raise CompilationException( True, "Unexpected target configuration {0}", self.config.targetName) serializer.blockEnd(True) self.generateLicense(serializer) serializer.append(self.config.postamble) def generateLicense(self, serializer): self.config.serializeLicense(serializer, self.license) # noinspection PyMethodMayBeStatic def generateIncludes(self, serializer): assert isinstance(serializer, programSerializer.ProgramSerializer) serializer.append(self.config.getIncludes()) def getLabel(self, p4node): # C label that corresponds to this point in the control-flow if p4node is None: return "end" elif isinstance(p4node, p4_parse_state): label = p4node.name self.entryPointLabels[p4node.name] = label if p4node.name not in self.entryPointLabels: label = self.generateNewName(p4node.name) self.entryPointLabels[p4node.name] = label return self.entryPointLabels[p4node.name] # noinspection PyMethodMayBeStatic def generatePreamble(self, serializer): assert isinstance(serializer, programSerializer.ProgramSerializer) serializer.emitIndent() serializer.append("enum ErrorCode ") serializer.blockStart() for error in self.errorCodes: serializer.emitIndent() serializer.appendFormat("{0},", error) serializer.newline() serializer.blockEnd(False) serializer.endOfStatement(True) serializer.newline() serializer.appendLine( "#define EBPF_MASK(t, w) ((((t)(1)) << (w)) - (t)1)") serializer.appendLine("#define BYTES(w) ((w + 7) / 8)") self.config.generateDword(serializer) # noinspection PyMethodMayBeStatic def generateNewName(self, base): # base is a string """Generates a fresh name based on the specified base name""" # TODO: this should be made "safer" assert isinstance(base, str) base += "_" + str(self.uniqueNameCounter) self.uniqueNameCounter += 1 return base def generateTypes(self, serializer): assert isinstance(serializer, programSerializer.ProgramSerializer) for t in self.typeFactory.type_map.values(): t.serialize(serializer) # generate a new struct type for the packet itself serializer.appendFormat("struct {0} ", self.headersStructTypeName) serializer.blockStart() for h in self.headers: serializer.emitIndent() h.declare(serializer) serializer.endOfStatement(True) for h in self.stacks: assert isinstance(h, ebpfInstance.EbpfHeaderStack) serializer.emitIndent() h.declare(serializer) serializer.endOfStatement(True) serializer.blockEnd(False) serializer.endOfStatement(True) # generate a new struct type for the metadata serializer.appendFormat("struct {0} ", self.metadataStructTypeName) serializer.blockStart() for h in self.metadata: assert isinstance(h, ebpfInstance.EbpfMetadata) serializer.emitIndent() h.declare(serializer) serializer.endOfStatement(True) serializer.blockEnd(False) serializer.endOfStatement(True) def generateTables(self, serializer): assert isinstance(serializer, programSerializer.ProgramSerializer) for t in self.tables: t.serialize(serializer, self) for c in self.counters: c.serialize(serializer, self) def generateHeaderInstance(self, serializer): assert isinstance(serializer, programSerializer.ProgramSerializer) serializer.emitIndent() serializer.appendFormat( "struct {0} {1}", self.headersStructTypeName, self.headerStructName) def generateInitializeHeaders(self, serializer): assert isinstance(serializer, programSerializer.ProgramSerializer) serializer.blockStart() for h in self.headers: serializer.emitIndent() serializer.appendFormat(".{0} = ", h.name) h.type.emitInitializer(serializer) serializer.appendLine(",") serializer.blockEnd(False) def generateMetadataInstance(self, serializer): assert isinstance(serializer, programSerializer.ProgramSerializer) serializer.emitIndent() serializer.appendFormat( "struct {0} {1}", self.metadataStructTypeName, self.metadataStructName) def generateDeparser(self, serializer): self.deparser.serialize(serializer, self) def generateInitializeMetadata(self, serializer): assert isinstance(serializer, programSerializer.ProgramSerializer) serializer.blockStart() for h in self.metadata: serializer.emitIndent() serializer.appendFormat(".{0} = ", h.name) h.emitInitializer(serializer) serializer.appendLine(",") serializer.blockEnd(False) def createLocalVariables(self, serializer): assert isinstance(serializer, programSerializer.ProgramSerializer) serializer.emitIndent() serializer.appendFormat("unsigned {0} = 0;", self.offsetVariableName) serializer.newline() serializer.emitIndent() serializer.appendFormat( "enum ErrorCode {0} = p4_pe_no_error;", self.errorName) serializer.newline() serializer.emitIndent() serializer.appendFormat( "{0}8 {1} = 0;", self.config.uprefix, self.dropBit) serializer.newline() serializer.emitIndent() serializer.appendFormat( "{0} {1} = 0;", self.arrayIndexType, self.zeroKeyName) serializer.newline() for h in self.stacks: serializer.emitIndent() serializer.appendFormat( "{0}8 {0} = 0;", self.config.uprefix, h.indexVar) serializer.newline() def getStackInstance(self, name): assert isinstance(name, str) for h in self.stacks: if h.name == name: assert isinstance(h, ebpfInstance.EbpfHeaderStack) return h raise CompilationException( True, "Could not locate header stack named {0}", name) def getHeaderInstance(self, name): assert isinstance(name, str) for h in self.headers: if h.name == name: assert isinstance(h, ebpfInstance.EbpfHeader) return h raise CompilationException( True, "Could not locate header instance named {0}", name) def getInstance(self, name): assert isinstance(name, str) for h in self.headers: if h.name == name: return h for h in self.metadata: if h.name == name: return h raise CompilationException( True, "Could not locate instance named {0}", name) def getAction(self, p4action): assert isinstance(p4action, p4_action) for a in self.actions: if a.name == p4action.name: return a newAction = ebpfAction.BuiltinAction(p4action) self.actions.append(newAction) return newAction def getTable(self, name): assert isinstance(name, str) for t in self.tables: if t.name == name: return t raise CompilationException( True, "Could not locate table named {0}", name) def getCounter(self, name): assert isinstance(name, str) for t in self.counters: if t.name == name: return t raise CompilationException( True, "Could not locate counters named {0}", name) def getConditional(self, name): assert isinstance(name, str) for c in self.conditionals: if c.name == name: return c raise CompilationException( True, "Could not locate conditional named {0}", name) def generateParser(self, serializer): assert isinstance(serializer, programSerializer.ProgramSerializer) for p in self.parsers: p.serialize(serializer, self) def generateIngressPipeline(self, serializer): assert isinstance(serializer, programSerializer.ProgramSerializer) for t in self.tables: assert isinstance(t, ebpfTable.EbpfTable) serializer.emitIndent() serializer.appendFormat("{0}:", t.name) serializer.newline() def generateControlFlowNode(self, serializer, node, nextEntryPoint): # nextEntryPoint is used as a target whenever the target is None # nextEntryPoint may also be None if isinstance(node, p4_table): table = self.getTable(node.name) assert isinstance(table, ebpfTable.EbpfTable) table.serializeCode(serializer, self, nextEntryPoint) elif isinstance(node, p4_conditional_node): conditional = self.getConditional(node.name) assert isinstance(conditional, ebpfConditional.EbpfConditional) conditional.generateCode(serializer, self, nextEntryPoint) else: raise CompilationException( True, "{0} Unexpected control flow node ", node) def generatePipelineInternal(self, serializer, nodestoadd, nextEntryPoint): assert isinstance(serializer, programSerializer.ProgramSerializer) assert isinstance(nodestoadd, set) done = set() while len(nodestoadd) > 0: todo = nodestoadd.pop() if todo in done: continue if todo is None: continue print("Generating ", todo.name) done.add(todo) self.generateControlFlowNode(serializer, todo, nextEntryPoint) for n in todo.next_.values(): nodestoadd.add(n) def generatePipeline(self, serializer): todo = set() for e in self.entryPoints: todo.add(e) self.generatePipelineInternal(serializer, todo, self.egressEntry) todo = set() todo.add(self.egressEntry) self.generatePipelineInternal(serializer, todo, None) bpfcc-0.12.0/src/cc/frontends/p4/compiler/ebpfScalarType.py000066400000000000000000000050301357404205000234360ustar00rootroot00000000000000# Copyright (c) Barefoot Networks, Inc. # Licensed under the Apache License, Version 2.0 (the "License") from p4_hlir.hlir import P4_AUTO_WIDTH from ebpfType import * from compilationException import * from programSerializer import ProgramSerializer class EbpfScalarType(EbpfType): __doc__ = "Represents a scalar type" def __init__(self, parent, widthInBits, isSigned, config): super(EbpfScalarType, self).__init__(None) assert isinstance(widthInBits, int) assert isinstance(isSigned, bool) self.width = widthInBits self.isSigned = isSigned self.config = config if widthInBits is P4_AUTO_WIDTH: raise NotSupportedException("{0} Variable-width field", parent) def widthInBits(self): return self.width @staticmethod def bytesRequired(width): return (width + 7) / 8 def asString(self): if self.isSigned: prefix = self.config.iprefix else: prefix = self.config.uprefix if self.width <= 8: name = prefix + "8" elif self.width <= 16: name = prefix + "16" elif self.width <= 32: name = prefix + "32" else: name = "char*" return name def alignment(self): if self.width <= 8: return 1 elif self.width <= 16: return 2 elif self.width <= 32: return 4 else: return 1 # Char array def serialize(self, serializer): assert isinstance(serializer, ProgramSerializer) serializer.append(self.asString()) def declareArray(self, serializer, identifier, size): raise CompilationException( True, "Arrays of base type not expected in P4") def declare(self, serializer, identifier, asPointer): assert isinstance(serializer, ProgramSerializer) assert isinstance(asPointer, bool) assert isinstance(identifier, str) if self.width <= 32: self.serialize(serializer) if asPointer: serializer.append("*") serializer.space() serializer.append(identifier) else: if asPointer: serializer.append("char*") else: serializer.appendFormat( "char {0}[{1}]", identifier, EbpfScalarType.bytesRequired(self.width)) def emitInitializer(self, serializer): assert isinstance(serializer, ProgramSerializer) serializer.append("0") bpfcc-0.12.0/src/cc/frontends/p4/compiler/ebpfStructType.py000066400000000000000000000105141357404205000235200ustar00rootroot00000000000000# Copyright (c) Barefoot Networks, Inc. # Licensed under the Apache License, Version 2.0 (the "License") from p4_hlir.hlir import P4_SIGNED, P4_SATURATING from ebpfScalarType import * class EbpfField(object): __doc__ = "represents a field in a struct type, not in an instance" def __init__(self, hlirParentType, name, widthInBits, attributes, config): self.name = name self.width = widthInBits self.hlirType = hlirParentType signed = False if P4_SIGNED in attributes: signed = True if P4_SATURATING in attributes: raise NotSupportedException( "{0}.{1}: Saturated types", self.hlirType, self.name) try: self.type = EbpfScalarType( self.hlirType, widthInBits, signed, config) except CompilationException as e: raise CompilationException( e.isBug, "{0}.{1}: {2}", hlirParentType, self.name, e.show()) def widthInBits(self): return self.width class EbpfStructType(EbpfType): # Abstract base class for HeaderType and MetadataType. # They are both represented by a p4 header_type def __init__(self, hlirHeader, config): super(EbpfStructType, self).__init__(hlirHeader) self.name = hlirHeader.name self.fields = [] for (fieldName, fieldSize) in self.hlirType.layout.items(): attributes = self.hlirType.attributes[fieldName] field = EbpfField( hlirHeader, fieldName, fieldSize, attributes, config) self.fields.append(field) def serialize(self, serializer): assert isinstance(serializer, ProgramSerializer) serializer.emitIndent() serializer.appendFormat("struct {0} ", self.name) serializer.blockStart() for field in self.fields: serializer.emitIndent() field.type.declare(serializer, field.name, False) serializer.appendFormat("; /* {0} bits */", field.widthInBits()) serializer.newline() serializer.blockEnd(False) serializer.endOfStatement(True) def declare(self, serializer, identifier, asPointer): assert isinstance(serializer, ProgramSerializer) assert isinstance(identifier, str) assert isinstance(asPointer, bool) serializer.appendFormat("struct {0} ", self.name) if asPointer: serializer.append("*") serializer.append(identifier) def widthInBits(self): return self.hlirType.length * 8 def getField(self, name): assert isinstance(name, str) for f in self.fields: assert isinstance(f, EbpfField) if f.name == name: return f raise CompilationException( True, "Could not locate field {0}.{1}", self, name) class EbpfHeaderType(EbpfStructType): def __init__(self, hlirHeader, config): super(EbpfHeaderType, self).__init__(hlirHeader, config) validField = EbpfField(hlirHeader, "valid", 1, set(), config) # check that no "valid" field exists already for f in self.fields: if f.name == "valid": raise CompilationException( True, "Header type contains a field named `valid': {0}", f) self.fields.append(validField) def emitInitializer(self, serializer): assert isinstance(serializer, ProgramSerializer) serializer.blockStart() serializer.emitIndent() serializer.appendLine(".valid = 0") serializer.blockEnd(False) def declareArray(self, serializer, identifier, size): assert isinstance(serializer, ProgramSerializer) serializer.appendFormat( "struct {0} {1}[{2}]", self.name, identifier, size) class EbpfMetadataType(EbpfStructType): def __init__(self, hlirHeader, config): super(EbpfMetadataType, self).__init__(hlirHeader, config) def emitInitializer(self, serializer): assert isinstance(serializer, ProgramSerializer) serializer.blockStart() for field in self.fields: serializer.emitIndent() serializer.appendFormat(".{0} = ", field.name) field.type.emitInitializer(serializer) serializer.append(",") serializer.newline() serializer.blockEnd(False) bpfcc-0.12.0/src/cc/frontends/p4/compiler/ebpfTable.py000066400000000000000000000352471357404205000224330ustar00rootroot00000000000000# Copyright (c) Barefoot Networks, Inc. # Licensed under the Apache License, Version 2.0 (the "License") from p4_hlir.hlir import p4_match_type, p4_field, p4_table, p4_header_instance from programSerializer import ProgramSerializer from compilationException import * import ebpfProgram import ebpfInstance import ebpfCounter import ebpfStructType import ebpfAction class EbpfTableKeyField(object): def __init__(self, fieldname, instance, field, mask): assert isinstance(instance, ebpfInstance.EbpfInstanceBase) assert isinstance(field, ebpfStructType.EbpfField) self.keyFieldName = fieldname self.instance = instance self.field = field self.mask = mask def serializeType(self, serializer): assert isinstance(serializer, ProgramSerializer) ftype = self.field.type serializer.emitIndent() ftype.declare(serializer, self.keyFieldName, False) serializer.endOfStatement(True) def serializeConstruction(self, keyName, serializer, program): assert isinstance(serializer, ProgramSerializer) assert isinstance(keyName, str) assert isinstance(program, ebpfProgram.EbpfProgram) if self.mask is not None: maskExpression = " & {0}".format(self.mask) else: maskExpression = "" if isinstance(self.instance, ebpfInstance.EbpfMetadata): base = program.metadataStructName else: base = program.headerStructName if isinstance(self.instance, ebpfInstance.SimpleInstance): source = "{0}.{1}.{2}".format( base, self.instance.name, self.field.name) else: assert isinstance(self.instance, ebpfInstance.EbpfHeaderStack) source = "{0}.{1}[{2}].{3}".format( base, self.instance.name, self.instance.hlirInstance.index, self.field.name) destination = "{0}.{1}".format(keyName, self.keyFieldName) size = self.field.widthInBits() serializer.emitIndent() if size <= 32: serializer.appendFormat("{0} = ({1}){2};", destination, source, maskExpression) else: if maskExpression != "": raise NotSupportedException( "{0} Mask wider than 32 bits", self.field.hlirType) serializer.appendFormat( "memcpy(&{0}, &{1}, {2});", destination, source, size / 8) serializer.newline() class EbpfTableKey(object): def __init__(self, match_fields, program): assert isinstance(program, ebpfProgram.EbpfProgram) self.expressions = [] self.fields = [] self.masks = [] self.fieldNamePrefix = "key_field_" self.program = program fieldNumber = 0 for f in match_fields: field = f[0] matchType = f[1] mask = f[2] if ((matchType is p4_match_type.P4_MATCH_TERNARY) or (matchType is p4_match_type.P4_MATCH_LPM) or (matchType is p4_match_type.P4_MATCH_RANGE)): raise NotSupportedException( False, "Match type {0}", matchType) if matchType is p4_match_type.P4_MATCH_VALID: # we should be really checking the valid field; # p4_field is a header instance assert isinstance(field, p4_header_instance) instance = field fieldname = "valid" else: assert isinstance(field, p4_field) instance = field.instance fieldname = field.name if ebpfProgram.EbpfProgram.isArrayElementInstance(instance): ebpfStack = program.getStackInstance(instance.base_name) assert isinstance(ebpfStack, ebpfInstance.EbpfHeaderStack) basetype = ebpfStack.basetype eInstance = program.getStackInstance(instance.base_name) else: ebpfHeader = program.getInstance(instance.name) assert isinstance(ebpfHeader, ebpfInstance.SimpleInstance) basetype = ebpfHeader.type eInstance = program.getInstance(instance.name) ebpfField = basetype.getField(fieldname) assert isinstance(ebpfField, ebpfStructType.EbpfField) fieldName = self.fieldNamePrefix + str(fieldNumber) fieldNumber += 1 keyField = EbpfTableKeyField(fieldName, eInstance, ebpfField, mask) self.fields.append(keyField) self.masks.append(mask) @staticmethod def fieldRank(field): assert isinstance(field, EbpfTableKeyField) return field.field.type.alignment() def serializeType(self, serializer, keyTypeName): assert isinstance(serializer, ProgramSerializer) serializer.emitIndent() serializer.appendFormat("struct {0} ", keyTypeName) serializer.blockStart() # Sort fields in decreasing size; this will ensure that # there is no padding. # Padding may cause the ebpf verification to fail, # since padding fields are not initalized fieldOrder = sorted( self.fields, key=EbpfTableKey.fieldRank, reverse=True) for f in fieldOrder: assert isinstance(f, EbpfTableKeyField) f.serializeType(serializer) serializer.blockEnd(False) serializer.endOfStatement(True) def serializeConstruction(self, serializer, keyName, program): serializer.emitIndent() serializer.appendLine("/* construct key */") for f in self.fields: f.serializeConstruction(keyName, serializer, program) class EbpfTable(object): # noinspection PyUnresolvedReferences def __init__(self, hlirtable, program, config): assert isinstance(hlirtable, p4_table) assert isinstance(program, ebpfProgram.EbpfProgram) self.name = hlirtable.name self.hlirtable = hlirtable self.config = config self.defaultActionMapName = (program.reservedPrefix + self.name + "_miss") self.key = EbpfTableKey(hlirtable.match_fields, program) self.size = hlirtable.max_size if self.size is None: program.emitWarning( "{0} does not specify a max_size; using 1024", hlirtable) self.size = 1024 self.isHash = True # TODO: try to guess arrays when possible self.dataMapName = self.name self.actionEnumName = program.generateNewName(self.name + "_actions") self.keyTypeName = program.generateNewName(self.name + "_key") self.valueTypeName = program.generateNewName(self.name + "_value") self.actions = [] if hlirtable.action_profile is not None: raise NotSupportedException("{0}: action_profile tables", hlirtable) if hlirtable.support_timeout: program.emitWarning("{0}: table timeout {1}; ignoring", hlirtable, NotSupportedException.archError) self.counters = [] if (hlirtable.attached_counters is not None): for c in hlirtable.attached_counters: ctr = program.getCounter(c.name) assert isinstance(ctr, ebpfCounter.EbpfCounter) self.counters.append(ctr) if (len(hlirtable.attached_meters) > 0 or len(hlirtable.attached_registers) > 0): program.emitWarning("{0}: meters/registers {1}; ignored", hlirtable, NotSupportedException.archError) for a in hlirtable.actions: action = program.getAction(a) self.actions.append(action) def serializeKeyType(self, serializer): assert isinstance(serializer, ProgramSerializer) self.key.serializeType(serializer, self.keyTypeName) def serializeActionArguments(self, serializer, action): assert isinstance(serializer, ProgramSerializer) assert isinstance(action, ebpfAction.EbpfActionBase) action.serializeArgumentsAsStruct(serializer) def serializeValueType(self, serializer): assert isinstance(serializer, ProgramSerializer) # create an enum with tags for all actions serializer.emitIndent() serializer.appendFormat("enum {0} ", self.actionEnumName) serializer.blockStart() for a in self.actions: name = a.name serializer.emitIndent() serializer.appendFormat("{0}_{1},", self.name, name) serializer.newline() serializer.blockEnd(False) serializer.endOfStatement(True) # a type-safe union: a struct with a tag and an union serializer.emitIndent() serializer.appendFormat("struct {0} ", self.valueTypeName) serializer.blockStart() serializer.emitIndent() #serializer.appendFormat("enum {0} action;", self.actionEnumName) # teporary workaround bcc bug serializer.appendFormat("{0}32 action;", self.config.uprefix) serializer.newline() serializer.emitIndent() serializer.append("union ") serializer.blockStart() for a in self.actions: self.serializeActionArguments(serializer, a) serializer.blockEnd(False) serializer.space() serializer.appendLine("u;") serializer.blockEnd(False) serializer.endOfStatement(True) def serialize(self, serializer, program): assert isinstance(serializer, ProgramSerializer) assert isinstance(program, ebpfProgram.EbpfProgram) self.serializeKeyType(serializer) self.serializeValueType(serializer) self.config.serializeTableDeclaration( serializer, self.dataMapName, self.isHash, "struct " + self.keyTypeName, "struct " + self.valueTypeName, self.size) self.config.serializeTableDeclaration( serializer, self.defaultActionMapName, False, program.arrayIndexType, "struct " + self.valueTypeName, 1) def serializeCode(self, serializer, program, nextNode): assert isinstance(serializer, ProgramSerializer) assert isinstance(program, ebpfProgram.EbpfProgram) hitVarName = program.reservedPrefix + "hit" keyname = "key" valueName = "value" serializer.newline() serializer.emitIndent() serializer.appendFormat("{0}:", program.getLabel(self)) serializer.newline() serializer.emitIndent() serializer.blockStart() serializer.emitIndent() serializer.appendFormat("{0}8 {1};", program.config.uprefix, hitVarName) serializer.newline() serializer.emitIndent() serializer.appendFormat("struct {0} {1} = {{}};", self.keyTypeName, keyname) serializer.newline() serializer.emitIndent() serializer.appendFormat( "struct {0} *{1};", self.valueTypeName, valueName) serializer.newline() self.key.serializeConstruction(serializer, keyname, program) serializer.emitIndent() serializer.appendFormat("{0} = 1;", hitVarName) serializer.newline() serializer.emitIndent() serializer.appendLine("/* perform lookup */") serializer.emitIndent() program.config.serializeLookup( serializer, self.dataMapName, keyname, valueName) serializer.newline() serializer.emitIndent() serializer.appendFormat("if ({0} == NULL) ", valueName) serializer.blockStart() serializer.emitIndent() serializer.appendFormat("{0} = 0;", hitVarName) serializer.newline() serializer.emitIndent() serializer.appendLine("/* miss; find default action */") serializer.emitIndent() program.config.serializeLookup( serializer, self.defaultActionMapName, program.zeroKeyName, valueName) serializer.newline() serializer.blockEnd(True) if len(self.counters) > 0: serializer.emitIndent() serializer.append("else ") serializer.blockStart() for c in self.counters: assert isinstance(c, ebpfCounter.EbpfCounter) if c.autoIncrement: serializer.emitIndent() serializer.blockStart() c.serializeCode(keyname, serializer, program) serializer.blockEnd(True) serializer.blockEnd(True) serializer.emitIndent() serializer.appendFormat("if ({0} != NULL) ", valueName) serializer.blockStart() serializer.emitIndent() serializer.appendLine("/* run action */") self.runAction(serializer, self.name, valueName, program, nextNode) nextNode = self.hlirtable.next_ if "hit" in nextNode: node = nextNode["hit"] if node is None: node = nextNode label = program.getLabel(node) serializer.emitIndent() serializer.appendFormat("if (hit) goto {0};", label) serializer.newline() node = nextNode["miss"] if node is None: node = nextNode label = program.getLabel(node) serializer.emitIndent() serializer.appendFormat("else goto {0};", label) serializer.newline() serializer.blockEnd(True) if not "hit" in nextNode: # Catch-all serializer.emitIndent() serializer.appendFormat("goto end;") serializer.newline() serializer.blockEnd(True) def runAction(self, serializer, tableName, valueName, program, nextNode): serializer.emitIndent() serializer.appendFormat("switch ({0}->action) ", valueName) serializer.blockStart() for a in self.actions: assert isinstance(a, ebpfAction.EbpfActionBase) serializer.emitIndent() serializer.appendFormat("case {0}_{1}: ", tableName, a.name) serializer.newline() serializer.emitIndent() serializer.blockStart() a.serializeBody(serializer, valueName, program) serializer.blockEnd(True) serializer.emitIndent() nextNodes = self.hlirtable.next_ if a.hliraction in nextNodes: node = nextNodes[a.hliraction] if node is None: node = nextNode label = program.getLabel(node) serializer.appendFormat("goto {0};", label) else: serializer.appendFormat("break;") serializer.newline() serializer.blockEnd(True) bpfcc-0.12.0/src/cc/frontends/p4/compiler/ebpfType.py000066400000000000000000000021351357404205000223130ustar00rootroot00000000000000# Copyright (c) Barefoot Networks, Inc. # Licensed under the Apache License, Version 2.0 (the "License") from compilationException import CompilationException class EbpfType(object): __doc__ = "Base class for representing a P4 type" def __init__(self, hlirType): self.hlirType = hlirType # Methods to override def serialize(self, serializer): # the type itself raise CompilationException(True, "Method must be overridden") def declare(self, serializer, identifier, asPointer): # declaration of an identifier with this type # asPointer is a boolean; # if true, the identifier is declared as a pointer raise CompilationException(True, "Method must be overridden") def emitInitializer(self, serializer): # A default initializer suitable for this type raise CompilationException(True, "Method must be overridden") def declareArray(self, serializer, identifier, size): # Declare an identifier with an array type with the specified size raise CompilationException(True, "Method must be overridden") bpfcc-0.12.0/src/cc/frontends/p4/compiler/p4toEbpf.py000077500000000000000000000063311357404205000222250ustar00rootroot00000000000000#!/usr/bin/env python # Copyright (c) Barefoot Networks, Inc. # Licensed under the Apache License, Version 2.0 (the "License") # Compiler from P4 to EBPF # (See http://www.slideshare.net/PLUMgrid/ebpf-and-linux-networking). # This compiler in fact generates a C source file # which can be compiled to EBPF using the LLVM compiler # with the ebpf target. # # Main entry point. import argparse import os import traceback import sys import target from p4_hlir.main import HLIR from ebpfProgram import EbpfProgram from compilationException import * from programSerializer import ProgramSerializer def get_parser(): parser = argparse.ArgumentParser(description='p4toEbpf arguments') parser.add_argument('source', metavar='source', type=str, help='a P4 source file to compile') parser.add_argument('-g', dest='generated', default="router", help="kind of output produced: filter or router") parser.add_argument('-o', dest='output_file', default="output.c", help="generated C file name") return parser def process(input_args): parser = get_parser() args, unparsed_args = parser.parse_known_args(input_args) has_remaining_args = False preprocessor_args = [] for a in unparsed_args: if a[:2] == "-D" or a[:2] == "-I" or a[:2] == "-U": input_args.remove(a) preprocessor_args.append(a) else: has_remaining_args = True # trigger error if has_remaining_args: parser.parse_args(input_args) if args.generated == "router": isRouter = True elif args.generated == "filter": isRouter = False else: print("-g should be one of 'filter' or 'router'") print("*** Compiling ", args.source) return compileP4(args.source, args.output_file, isRouter, preprocessor_args) class CompileResult(object): def __init__(self, kind, error): self.kind = kind self.error = error def __str__(self): if self.kind == "OK": return "Compilation successful" else: return "Compilation failed with error: " + self.error def compileP4(inputFile, gen_file, isRouter, preprocessor_args): h = HLIR(inputFile) for parg in preprocessor_args: h.add_preprocessor_args(parg) if not h.build(): return CompileResult("HLIR", "Error while building HLIR") try: basename = os.path.basename(inputFile) basename = os.path.splitext(basename)[0] config = target.BccConfig() e = EbpfProgram(basename, h, isRouter, config) serializer = ProgramSerializer() e.toC(serializer) f = open(gen_file, 'w') f.write(serializer.toString()) return CompileResult("OK", "") except CompilationException as e: prefix = "" if e.isBug: prefix = "### Compiler bug: " return CompileResult("bug", prefix + e.show()) except NotSupportedException as e: return CompileResult("not supported", e.show()) except: return CompileResult("exception", traceback.format_exc()) # main entry point if __name__ == "__main__": result = process(sys.argv[1:]) if result.kind != "OK": print(str(result)) bpfcc-0.12.0/src/cc/frontends/p4/compiler/programSerializer.py000066400000000000000000000027441357404205000242440ustar00rootroot00000000000000#!/usr/bin/env python # helper for building C program source text from compilationException import * class ProgramSerializer(object): def __init__(self): self.program = "" self.eol = "\n" self.currentIndent = 0 self.INDENT_AMOUNT = 4 # default indent amount def __str__(self): return self.program def increaseIndent(self): self.currentIndent += self.INDENT_AMOUNT def decreaseIndent(self): self.currentIndent -= self.INDENT_AMOUNT if self.currentIndent < 0: raise CompilationException(True, "Negative indentation level") def toString(self): return self.program def space(self): self.append(" ") def newline(self): self.program += self.eol def endOfStatement(self, addNewline): self.append(";") if addNewline: self.newline() def append(self, string): self.program += str(string) def appendFormat(self, format, *args): string = format.format(*args) self.append(string) def appendLine(self, string): self.append(string) self.newline() def emitIndent(self): self.program += " " * self.currentIndent def blockStart(self): self.append("{") self.newline() self.increaseIndent() def blockEnd(self, addNewline): self.decreaseIndent() self.emitIndent() self.append("}") if addNewline: self.newline() bpfcc-0.12.0/src/cc/frontends/p4/compiler/target.py000066400000000000000000000132661357404205000220320ustar00rootroot00000000000000# Copyright (c) Barefoot Networks, Inc. # Licensed under the Apache License, Version 2.0 (the "License") from programSerializer import ProgramSerializer # abstraction for isolating target-specific features # Base class for representing target-specific configuration class TargetConfig(object): def __init__(self, target): self.targetName = target def getIncludes(self): return "" def serializeLookup(self, serializer, tableName, key, value): serializer.appendFormat("{0} = bpf_map_lookup_elem(&{1}, &{2});", value, tableName, key) def serializeUpdate(self, serializer, tableName, key, value): serializer.appendFormat( "bpf_map_update_elem(&{0}, &{1}, &{2}, BPF_ANY);", tableName, key, value) def serializeLicense(self, serializer, licenseString): assert isinstance(serializer, ProgramSerializer) serializer.emitIndent() serializer.appendFormat( "char _license[] {0}(\"license\") = \"{1}\";", self.config.section, licenseString) serializer.newline() def serializeCodeSection(self, serializer): assert isinstance(serializer, ProgramSerializer) serializer.appendFormat("{0}(\"{1}\")", self.section, self.entrySection) def serializeTableDeclaration(self, serializer, tableName, isHash, keyType, valueType, size): assert isinstance(serializer, ProgramSerializer) assert isinstance(tableName, str) assert isinstance(isHash, bool) assert isinstance(keyType, str) assert isinstance(valueType, str) assert isinstance(size, int) serializer.emitIndent() serializer.appendFormat("struct {0} {1}(\"maps\") {2} = ", self.tableName, self.section, tableName) serializer.blockStart() serializer.emitIndent() serializer.append(".type = ") if isHash: serializer.appendLine("BPF_MAP_TYPE_HASH,") else: serializer.appendLine("BPF_MAP_TYPE_ARRAY,") serializer.emitIndent() serializer.appendFormat(".{0} = sizeof(struct {1}), ", self.tableKeyAttribute, keyType) serializer.newline() serializer.emitIndent() serializer.appendFormat(".{0} = sizeof(struct {1}), ", self.tableValueAttribute, valueType) serializer.newline() serializer.emitIndent() serializer.appendFormat(".{0} = {1}, ", self.tableSizeAttribute, size) serializer.newline() serializer.blockEnd(False) serializer.endOfStatement(True) def generateDword(self, serializer): serializer.appendFormat( "static inline {0}64 load_dword(void *skb, {0}64 off)", self.uprefix) serializer.newline() serializer.blockStart() serializer.emitIndent() serializer.appendFormat( ("return (({0}64)load_word(skb, off) << 32) | " + "load_word(skb, off + 4);"), self.uprefix) serializer.newline() serializer.blockEnd(True) # Represents a target that is compiled within the kernel # source tree samples folder and which attaches to a socket class KernelSamplesConfig(TargetConfig): def __init__(self): super(TargetConfig, self).__init__("Socket") self.entrySection = "socket1" self.section = "SEC" self.uprefix = "u" self.iprefix = "i" self.tableKeyAttribute = "key_size" self.tableValueAttribute = "value_size" self.tableSizeAttribute = "max_entries" self.tableName = "bpf_map_def" self.postamble = "" def getIncludes(self): return """ #include #include #include #include #include #include #include "bpf_helpers.h" """ # Represents a target compiled by bcc that uses the TC class BccConfig(TargetConfig): def __init__(self): super(BccConfig, self).__init__("BCC") self.uprefix = "u" self.iprefix = "i" self.postamble = "" def serializeTableDeclaration(self, serializer, tableName, isHash, keyType, valueType, size): assert isinstance(serializer, ProgramSerializer) assert isinstance(tableName, str) assert isinstance(isHash, bool) assert isinstance(keyType, str) assert isinstance(valueType, str) assert isinstance(size, int) serializer.emitIndent() if isHash: kind = "hash" else: kind = "array" serializer.appendFormat( "BPF_TABLE(\"{0}\", {1}, {2}, {3}, {4});", kind, keyType, valueType, tableName, size) serializer.newline() def serializeLookup(self, serializer, tableName, key, value): serializer.appendFormat("{0} = {1}.lookup(&{2});", value, tableName, key) def serializeUpdate(self, serializer, tableName, key, value): serializer.appendFormat("{0}.update(&{1}, &{2});", tableName, key, value) def generateDword(self, serializer): pass def serializeCodeSection(self, serializer): pass def getIncludes(self): return """ #include #include #include #include #include #include #include """ def serializeLicense(self, serializer, licenseString): assert isinstance(serializer, ProgramSerializer) pass bpfcc-0.12.0/src/cc/frontends/p4/compiler/topoSorting.py000066400000000000000000000051561357404205000230720ustar00rootroot00000000000000# Copyright 2013-present Barefoot Networks, Inc. # # 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. # # # Antonin Bas (antonin@barefootnetworks.com) # # # -*- coding: utf-8 -*- from __future__ import print_function class Node(object): def __init__(self, n): self.n = n self.edges = set() def add_edge_to(self, other): assert(isinstance(other, Node)) self.edges.add(other) def __str__(self): return str(self.n) class Graph(object): def __init__(self): self.nodes = {} self.root = None def add_node(self, node): assert(node not in self.nodes) self.nodes[node] = Node(node) def __contains__(self, node): return node in self.nodes def get_node(self, node): return self.nodes[node] def produce_topo_sorting(self): def visit(node, topo_sorting, sequence=None): if sequence is not None: sequence += [str(node)] if node._behavioral_topo_sorting_mark == 1: if sequence is not None: print("cycle", sequence) return False if node._behavioral_topo_sorting_mark != 2: node._behavioral_topo_sorting_mark = 1 for next_node in node.edges: res = visit(next_node, topo_sorting, sequence) if not res: return False node._behavioral_topo_sorting_mark = 2 topo_sorting.insert(0, node.n) return True has_cycle = False topo_sorting = [] for node in self.nodes.values(): # 0 is unmarked, 1 is temp, 2 is permanent node._behavioral_topo_sorting_mark = 0 for node in self.nodes.values(): if node._behavioral_topo_sorting_mark == 0: if not visit(node, topo_sorting, sequence=[]): has_cycle = True break # removing mark for node in self.nodes.values(): del node._behavioral_topo_sorting_mark if has_cycle: return None return topo_sorting bpfcc-0.12.0/src/cc/frontends/p4/compiler/typeFactory.py000066400000000000000000000022521357404205000230460ustar00rootroot00000000000000# Copyright (c) Barefoot Networks, Inc. # Licensed under the Apache License, Version 2.0 (the "License") from p4_hlir.hlir import p4_header from ebpfStructType import * class EbpfTypeFactory(object): def __init__(self, config): self.type_map = {} self.config = config def build(self, hlirType, asMetadata): name = hlirType.name if hlirType.name in self.type_map: retval = self.type_map[name] if ((not asMetadata and isinstance(retval, EbpfMetadataType)) or (asMetadata and isinstance(retval, EbpfHeaderType))): raise CompilationException( True, "Same type used both as a header and metadata {0}", hlirType) if isinstance(hlirType, p4_header): if asMetadata: type = EbpfMetadataType(hlirType, self.config) else: type = EbpfHeaderType(hlirType, self.config) else: raise CompilationException(True, "Unexpected type {0}", hlirType) self.registerType(name, type) return type def registerType(self, name, ebpfType): self.type_map[name] = ebpfType bpfcc-0.12.0/src/cc/frontends/p4/docs/000077500000000000000000000000001357404205000173005ustar00rootroot00000000000000bpfcc-0.12.0/src/cc/frontends/p4/docs/README.md000066400000000000000000000001631357404205000205570ustar00rootroot00000000000000# External references See [p4toEbpf-bcc.pdf](https://github.com/iovisor/bpf-docs/blob/master/p4/p4toEbpf-bcc.pdf) bpfcc-0.12.0/src/cc/frontends/p4/scope.png000066400000000000000000003053201357404205000201720ustar00rootroot00000000000000PNG  IHDRgu$iCCPICC Profile8UoT>oR? XGůUS[IJ*$:7鶪O{7@Hkk?<kktq݋m6nƶد-mR;`zv x#=\% oYRڱ#&?>ҹЪn_;j;$}*}+(}'}/LtY"$].9⦅%{_a݊]hk5'SN{<_ t jM{-4%TńtY۟R6#v\喊x:'HO3^&0::m,L%3:qVE t]~Iv6Wٯ) |ʸ2]G4(6w‹$"AEv m[D;Vh[}چN|3HS:KtxU'D;77;_"e?Yqxl+ pHYs  diTXtXML:com.adobe.xmp 2015-09-18T13:13:57 Rq965bVZS0WllMxPpRAZkg 2015-09-18T13:13:57 iPhoto 9.5.1 2015-09-18T13:13:57 1 2 72 72 933 775 `@IDATx}`V d{7 ޣOkjo_wkV;mmݳ &@#xssߛ72ܳsus"FWoե<ݿ u-7Wcu)3@7@]pi= i@.Zd]eadeġSBV. SZZ*;v:j#Hxiii 6]EŲs*׺ukIOO? 2?"p?!/۷9OLLWZՕkme5|iJ5W4СkNHl;*>߶m&߶mZhMe֭>.2224YB ` jK3ƾsNywU 516m.,yۼyw}2sLINNGWFL̟*=z,8q9Rii8O>)?٪U+ϞSvQz%{ GMGhQVo-_P[n|+귲'?Oyg$))}BxEpӧÆ+bq&(UڵK>yݻw1#/K.PY=CFF3Z_|!Gw\eݻo|P:wӟ2]VrΕ+WFm޽{U*ڱFnZiup ~V7<y0]zuYpx`A:Odm'U;a8 @\eaٲe:>|,8+\8z(ep_U ,>_b@d)߿B~ X1`htG}Dlfx3 JH#FPg$25k?X˸뮻drg}쁄hsfk֬?#,m\ނ #/y7Uh7Y}7~C"nec'?O;FػsA(oe#o 6_2eq?o<5GG`NFGAh"n>spBU86xXʚl35>m=Y|DZy[e$h8G9ڄ zme 8p 'EDH&PS]?۵]SY'Au!Ʃ8Dy1hg~&FR4"<4*B֨ா2#SC`q75͗)))[YA5WcC4 iHž={QlBiڵ?Y{: 0l pA^Bc r_lFYU|l:+VcHО;w@O@ 1ږ=H4pDg!-5nC4%T,Py3'ٝFy0b :TyB`Wj2|n \l!td gqIb=Xd^.z+ZzaȘ ~ 3>YLQmj!`xĥSN<^wys߭L?2hIkS%ŹX&M"!гмbDKr/[$V#ҒRd'_@^Y )|63R8z}10]umf^{ 3=;P9|8JqO?ե+)5ΤG}Ԓ_khP02O^4 B4pZ)n5ϚCC"״pa𩧞j*L֋fAKg.~뭷4])5Jxmd cw5_Wu-QҥK;h9ll;p#˚a5UG >Ls[5ZGWAzwGQ??^}Zl?ܼ^5`M CFM 0I׳>[Sѝ:>96vX^yV蚁)\h6淒y| 2w=~a>=w)Fœw^wG,7l!3r2_[Y*x&mor8Q`TglBքABt vGOb⨌?nP@sV6v'֭o9wܡzJJ%ЪʶQ!VT[Wpg0 }52->LQB*PX Rh(mTeymO p뭷h^+=FzJއy@ `M`4ep$DnlD!aA 'ɢ !y?}t{tKktz,`u9+X5U@Bb^c UeK.D:,_X[y-sG >0ȃ?_]v /Q=nN MzMHZb:;[E`O : Xøn_g 7B4ŭ,b1SXtM2gl𨣎RGh`/h3f(Z%HbDH[9JƎ&iMP^l{&M$i>61T # R÷k׮!7n\%5\2;pkFj@GEҭyNjZ"9:bubAb1 ^S)咈-9D-{=]rk*ļL<7,Q*w 1]m A' ṁ `8EtޞKС <8jtE8qߟbVXP&sKCeKWy:qmt <4(B֠=pᦵPVv`$@=9]bsd?Ms S``1I*2|f}0Ygu 2;f2JQ=>1pD? L%=x;%%5ED,_#CC `҃+T¥}z{u1VU8QV%T"`949@m!PYe‰OP-p_m=m){=?9ז#Ë@( u+8;rrVm`lR*_Yr0&@@p6îK+m<:q̒VC!GNv.t>w CA `FX$ #Ұ;-?ɡGۄћ !'Թ瞫33c!99-/_/l:z?%`M -hNi ش%sdW7}X91 *Hi©6,^yXߠpMa@(nuemg+;X gϙ)jvH)ց9)vz!mo~SZPxń'5;#0zrsp~4ZB}aOOW!Aq_eȵZ 6,HHwuzD+:[6h"^ ; C@8:{sg>ѺQr}'/ԑs(aeC `!4͌npDEegۯQa#,^sn>JS$wlgsap]nԿG'͹i-/w!-^M/aKM2E ZJxU; g:gix>몰SXRQUC@ `!T8q%FQUX\e›#;0zo](P^g0k~m(w̬r7(9 ***^r%~#Ml_35/\Pŀ aU1(%:P:-Է|~eqRqs9>g0Ӟ)MVMVXN63&;3>MU]P[PC?)@Ex .nvK\DqҊs}~}mm x ''6*<A?XC3{bWf[SQY^_-޻Ov=RXT$ť_X_)*RĕIq^)*'{(é#nm` <Պ ZEu+'ҮM$m+)IVRyN:IRZKJbMh-m,ٖW+u̬GT鞙$=:eHI1YUr -*| :|1.;" UX}ZmtJT`Ĝ<ضLrո}aSA8) JAJdi)# AZ2R mQ4#"'gxޙ&g g /"HޞB-(}}7`O@ (I ޡwt<ک&HvTe \-R|+<BvP`kLN6"2X&FMykndo#ٛrq-V "2I7V`B5 ]v3]2ZLC!PyaQaS"=Q~glˇrͼtg{$03D/q'WPX~h@QJG84zS!`nE%H:Ofť F$i53sK9Ƥ6`Zz G&ڥ?*a~f^i2 pO`OqfһwO]hP[Q|= ȃ@+5!Էnت^&g:HVw;Y &pI᨟cЪ[R! 4380-!)2@j|Uݲ"g,%!m$&JdaOajI&]ٛ ?:ZSkNjHQ4y{؊6˼5eWNeKE 8M¶b]Tu($Ov`T[ eņݲzKl/`Adua}ҥ_w̭ul/iO y+n!׉arUU^.ỡ|_ |Y2@dvJ6$M &zJ)i0/UncLqbݨ4z¢PghϦ:W,ўF[**|fus./k"QPo(rSO@ Agu=`hF@C;͐64X]'Y5̰ ʜhm~;e-X&bt5n4~7hiԨ+P s Dc=Ip,$ƿG6 ++OvRЩ#L ϡEpV`d\@ɕnS ޙ2 S $̳Y P]): Ch|'<7V|<-Gd;L+hA`-^mzfa8W{EG:l m Fvm'rX.]\@Z@"¿``| ,=-k/&wUy(pP 9$0?RwɆ\YSd2&Gb^&vhE^,2<7B`P\ǮId<2YWy.L`ɲue]2/s%>]dLhij ɫmvjr# Z/L& Ę.ig^ ;+ ~]^JY)VBU9q.+.ṶN/xFdizl|YeamlK.Lq2[{&6DN(?pcb~ )?R/,0@yq>F1Z~_+s `,+CF̂i.51ᐙmW<|d-^iU`NjjoM(<d? ^\WIZE:8,[avYCvLq( (81ZK(Pa\Xw,mk/;yy :u@V2 ʿ  ZC>RjdUDIAU`y-,[vUe%L:"G .w\ ]+y9 ""!`L{15)w#B@4Gf³sh44U%tA18Aҵ"33`dtb6|El+=jpW; f׌}9U/!8ZbSQY>W-CA҄蛉2djpVѡ?w+'7@Ю޳6W'؁a䥠̄gf wiP6 0V^ԌśdXVf[4#t,ƭRު*dV z?yVt-871הŶ !3ni/a2#:ݠxd`ѵvS 3'\QX rNdL_CVnˇhL:jXwh2bM>CD1ھa748$(Q{2|2lqu2kqh " $-Ƃ*0POE^l׼D>ujP LL)C L{ [։uKPWʗ6[d>:0yp'8 vi~kABV6}N+T# aFfknβ2cfY'rf}0_Y`A5_.#l/󵚉;Ȥ=ߦ"B&xE6ZQ={Wȃ[ a`V|XSЯ.&nj Ŵܥ t&uOX ZB7XASOEgt ?Of5nHwTbG4?NGYZ6/Lv UbQks 3FEl/('N`|lܵʎ sRZR&_˖ҝ2wFpHv\w@s 룭s$u f8:& yXjCFe䳅[d֞:GvIzԜ! 8w|OAR58bTƅ@؁ T9 t#yp؍dMެuвwK6r2ito֯dbsZ L5.< ֳ;*z,_xn@8‚yڳi_@q=ve=Og4-%[bM!Sɒmu̙Όe=FO8g׈zah` '$Vvb`7ɴ99LqtSgȁCГՙ2VP[TTkHLU6v70#LN"[o+=[San-k˒*s$S9Pp#k\^%'8EJm{Uq5nb};b<.-wڧaWŹ22T`m$ UHqckf^X~NzO/'\γy[r~2~hݷ P:hLx-c A x#,\ΐ@E$&)Vm%^'o}^bc]:tH]11ywJ0z'/ HD).P ^pxoQ+nUl7?_-˰8,>iLw;v/4/.܊Er,\ϞWy3fs(X2k4V{knpp\)h"(f@zqS\22wfffcPDN^2j`w5a=À\^AK>$^àuTnQ Vl-cne°ҳco"ά_Ȼs6 O)Mއ)mL!zėkkԼ᡹A;uMQ*I/l4CLnrCz }~Ex;}v@Xy`Ut>r e69zk |DCN;iXN£e̲y )j nĖCѝ!nԟ.ȑ>]%!Nڽ'1CzHW|g5@Z?fc?_nE،4=p邹7"ۿ)M-" 6<;E0 Fw%htix 6ߞԷfWnŴC\vud bx^B~@PV _ayAb{KE8Ѝi}g)>^^J;T)gBpU?-LǕ,8P?GD$ Omә6V>IYYsrj۠ ٹ@|t}L|u:Yy#ʹ=eZ~ĽbYc oG_.9t" +g6 ˷1}2(4aZxrE vpt%<ЧG`$E52.,˰#2]ZGl\{rDxFO,tevKpj UO d:ajQdR]QD&e8E9r:B&`}:FoA *4DK#4pǺ=řӞHAt^1 { 9.Ѹҙ#áUwGVkExln"a.ُQ?(=T?7bW}Ur{=91ض,i_.PsdGx8-uҠͥ9`Xyk&߯8yEDB`SAr%`\G8קP^ȆLbhU"L;Dɕ' S& 9.Ġ `#P G6Jgew/~AuvO7EQ5h^A]o=fQe`@xޒ \ M< ]Kx}LtKSǷX-?X&=E("gU9f.\;m0c$.;eD,pc̩>A1Mȡ=uy+ ύ  VVP*|Fd.HlR9A2nԀq,%CxH^^38Aɤ*<)@= @!Lsi.Eե' c0s{-=2Qϴ uyc"ޮf R^}xBE &􇷳sQ}|44D;!LTP#:]sI.eۊ䢣zʅ0 6?;z횢(Npi3A,89?Z.a2T|:ӆ8lkY?7 'Lw's_X\FdN&3^*:qEM4d?X.o݊ 䫧 檽աP"H#Vi> +@y!]đs^\A:KO O:24}5:UR|{)崣HNr,(ؖM'T?xJ\Waf?\/JGy'g]}Tfӣ; X,O^?Py1}$Y? fdFNyWlZZ7;s(&HFxB7JxCzbDC:'[Bv{|:._# LD=m5io&g!yT :ΤbRX,˷ʕN*:hj#|M~l U2P!c([g&ϾH]]UzH޻eV_*<<;4[g/9GLhlDwiLPx~ǏgZMEZ3ByM2eTga2$crvc"5d^r -ۄŢ/L{o &ל5XǎI0 Oc^߳]㽺C<inڳ5DhWIr$ߝ }]aM wDdYp'Y&k7"8DzKsK<$TvjYYԈ| /^_x@u vOVDSBThi!(1 Y3ưYA84p^zmu-Vlk#:Lâ nb0΀9<~k /Uq~% ۋe]0ɴ򰮃bX"<+,XhfP_xo<=c1| };iCu1_`g$OpSfRy轕26+M={3HgRҼ~') llhA5QV m:_ 5 Z(1 t'o=LNa'(%8/ӖֿP0d4۞ y2O{Qr4> hHG~`$ؽW "W~HgBdwI;s%Q׷. aG@ghnjч) \p0M n0{<"y_ǽW>L:ysHgDߢ{l<|se#hqO8vˣSv.E5R rZyZT 88h--Y'g)))gOšp!ڤ޻Г3I2f fOφQOVm)ngU;,_@F`J RO殗:_vo^0 $68cx^@:\pJ>_vZz>i?:34p xޗr?L%e GGv WU[G N,`tZfS 7B[b+%wI: c͘7-Ѽt(B  otcwx)AP2\4RNG gzl]"XtjlQ2 7]yìT;ڒ{P}QwKG|IiFnևgG8bUc"/z ɩʣzpbyOi5s_5&[GTw#|=\U\]z9Ci]T!OE^(.@niaL_'`N-L/c^ تk)/mg#DoME.W=3Kwʏ/+b ~)Rܱ'w"!@~ރerfs7mg/滨S/Pec, |"JܦCme{vL؜x>Kto 0H)dWa<]RKSzmF~w}6Y!`>ڏ;Kd֯lޜ'd d@Li <tO`JوO?\r;\2J'`aN0L*7W`r4ٝd |{ 巯-&t[.;ZzwLc>%a#غ>96xs-ҕSC?THn !E ˣ. |w`Ξy}`nhpڐ[,6oEp>m^P 7އA*rklCǝ\gPnxtIk>u6pˍ:h{Qb*;\ozp) gA}:9c!5ĘBJÈfVLɌ20CW:X1:pGɠ.bM$@~yw<,]Ë`#!nR2[=CM@\ܳ«^OPױFNXtIIOl#Cu"yÕKXF9ﭬk 0t&a/ϖWg1T(Q. +Mh 34סu'˳wm0.hçPɽ妋NU+;;D9!@-ؑ>:1}TҬ{jCA+]sjy@s䃑&&~pwA*;wyCE^߯^bÈpÆ R^^.Z!,/k{osF@ i8*3 ‹i+p?wL'xN Gy/8=j!MFµz M[Q/"{np UjV؝# Fouqoj*ö]֭iD(`M=zlmo6F7Sn͕?]#:3#`Vfk &`,تU+3.?Jn>% _%-+k߾2k/eeRVV*:uR0~ 118LvB)FǴp젗 K:7Ԏچhb+=0%`_dhܟ.X!L)⫡mZcU+77O~_ģ'c?,O>3VxxL֩|1[scCr9[~(|>:8J GopsUX=|V7:ipo'Ck* PTTBe[Ol;^ժt&$H*4=c3` dh41A@)CۡܲRqaD@TSt;./v$"Ec'>րތ K/$gu.>VA[>-~u5|1 ĭmT{TM3y瞗O?LRSSD[IclE8>g^+ZDsa}[҆@0M1̴baZY>^Ǧٴi*`޽{˛o)gO9[XÞL@|62rotQ-v=ْ ̻{ H 8(<7d~["C}޷o_2etUǒ)"Gu^! a-Z$}h:83cǎsN&ɶ5J۽tR/sϕ:srmۦuL8Q?xJt7nwyGx&?jb}d6`H Ҿ}{9餓GT .EdrʩszS4Hp=ɗ˓zK֮])2DψIoEv]˃^;N1ֆA4m(XNxݠv zdM_vo^Y,vn4熳 OϚr Sʍ7(4{zjgJwT0>Sm#i_O8QXQ[f$%%iaÆ)m*_0L:^Hww^Nc޽[iK.Q'ӳVҲƍ''xyP%?ۼypt?am#-҄J^oi&,ͭuҫW/1g$gh@ui bY{hS_b5'647Vȋ 6#0+xڹ w~l Ii M`_6r4]6,X ;w  &0lڙylha`Z" {Qk_Zx 7foD2>[?†ePZvikC=$?OUPxIR0C5k@s0:ʣJIa z(JA~kvRb ̟?_j3gAVo;/}\_a”U( $3H߬C}ޝ/wwʌyG#O:I,*5 񌮢c vO.72@jyӕjW2iݬtH? F+B|AKtD@zUXT.++Kdܹ~/rwJZj|+JkL@5eP$izx03o~‡t} / :&Qib`YK/T賓?^f̘i8r뭷BnTI&_dۦw3^rk1]‚0"ޅ#ds$ܙxeNh9 vj`k=[vFnȤ[_;wW7l_?kw "`Nv?12vQDl٢q?B1}ND"82%{ m {<"`=4Fuv6qW: h"`8Dr63g=`{ `4SY?]aw@ ^Ӹ>@ۂq? A8hʀLߓ FE Ãb |mNdFykaK_ ÛF{HD&|佹뼶j\I7pC T%C><"ioY\d? +Gˤw8fMe9Ҷm7TȈ#";2xM?"s/4HA]x*7}>y!݇\)q9J_eƆ3!B SVI7v//aS7o\>ZNiܰsd; i߶aEϠ\-.*֡_?{QM4T}Ow]GW5:cp4) ua*?OtX;0hbf;-4>~",qأga`y@rӄ|r4 MGmK/P@kiIj~W}ѼkjylA펣N&-gV)M_䮻R$owF\rPi83 >?]S /͓Lۏ wA^~A@15.i*98|r @'p@UW]KzYf|:^N~Chƃ0Rk _azcСF@h5&N SgϞj7㻐s9!|&N2n>Dq |#țnIO`mk}Fr"0$b&nQ$Lz`)GSdG`NqvQ[`Z`|rqiAFG%3>γpn; HȤ)X鑈@$ S[WZÑi#0|<Qx}A md41`87ZC`yeQry,.|]=,l'ߋf=g9Z<1wa]eGSӔCNs4O2fa_[$w9g4.+n\).sAN>aɣ3˝N< so5*QW}*5,,K>CC3Xy/[gw "ߩFe',鈿?fH\b4q0`FLC Q#:,ϡ)ri˼g[nϗƋFK 0?EvB4<ҳ0 E !][>3Wo]<^[Â|{n5TDZpLsg*Od0+瘞 T8!r#:>,*fyVu(P reX.y@>yڵ#?* 5)Y ,4Gtnv{ht@BAi:"Yء ȝM__ ?k7L&4$k)_0~WJF{{Њ:yWd#4_|"uBGy^ hqdֆgLgqDo:JdY.SS󡉂8,&N!hn`x7uD֭$6 /+Ag@2йDGS B CzZ^ZM,-,,wqBg}VGS8Bb!#HxpQҔI8P[q`>Dt<bnX|UN0:lUx]WДHOauLkkT<)Ju-KQpI Q7E3哴ot % #:-L9u*t=X&%sThmш$Γ6̂AHXli`,_njfgg |ǰNZ>8⤕*z*ȫl:p@HO,EZHG ,9L_KiD\Y^lRq=ehfN)nڐ ;ˏc0vI'HImdj+-MWRڒY,@&YN;ZE3͈ݝ^vD Kj4 4\x&Q袋sX`dߏ~#3FF/]99 hΏ})8j Զ8AB) G ]vi<GrHPH\7F%ptJޝZ,[ ;y\}N^ˢ) @6U]/8&*hU 5roo,0-N؉d;m&8I#ӼH=Thn7G/؞" #z+B?1†D+Hދ-#4kU]% p_t%.- ؤ= Y 'xBUz$_ves{ F*¤G‡|-x~W"8do̓?Z#z RTSSձ1x uAJM"\0(0uyS nEǪ)3UWFDзo_pɀL͐a[C|kʘƆP֮Y+Ww41P8ǎ+i=5(…N!V/H2"(8Za)ҕ^ v$GU(H&M >#`nڼIzu3;0-53jhe~j4UF nlm3+SdhF\n yY.ۨ7\.P@[Su?t߿~$ - ԯ16<;5u WSjv B YC~t1I~r1C?6g=&NE 0#2L#&-WK #7^`NܠsXܵ&2郼e~ HĶҫw m ;y-&͓W`>*| 8gq@zI:К3{$*:OWGTOxywtYP12aa|!2j8ldW cE9wN?GY>]ѪtH zg%P!ӑؼ@$#@#tU%ז>qvk m:YĆʮ* )qGPTr$gg}ñ2 `Y<ceٵ'ڿR֡mGxy[oEE^J~\~K;[0Zs ٭pM7:aas3p.}U2:rcY<,G~T)(h歮 ?k,!7N&?4qDUqpv&5ZlwUF'5T/<0w9/ÔB ư)tR viLk$MTzgY:Y6QQleeA3kYm3+HrGk WϞ}G׷Qرc N  .F~LaC ẀFi> kV_Mg3 ޵ԍ vP"tZ ƍP{=WN@azcq ~{:4C0RV)hYv }61L3.:V (ҧ1*ֹ 럭R.pᵀ7W:VnUsr"ʿ?\N; ^ժTs#v(-0ޞk>xeU?OSm]YV^ge9y^ՙzeX2ylbi@;i،z]R.dz_FLixh/?@>9˷;d.!]qҳU$ ߽Ċchc^WS|9/rgKgg{{+cY r5UZ>^;0(eX=|| p`e|.XP-{ih*ޘ(sY5R XE\Y/M8{g<w"<_zK \C?t-*и7F1s+.=Ɔ ѓY3σ3Cn uX>}ڪ>3>‘3KuSE҇˥p(=k={Vʙ|@kզ*Mv%rjzvv눤'O [;y%DC` ԥ@Z :\?'VL=2Iĺ9_4NաFNJ̡_.ȱQM{g1dr> {!̄4tN W)ttD>c 1NsCs10Izz2 9c']I|؞{\44ܧgN[[+gXkD!2;ԮuHeY¤y]|8Gl\68.KFF5xbGӁ% q+1JdnJ6. @d1=GEk HR tz/Pqt I١3G[B\o[q[.nXi6qmݭOƱ>.Rw)7>|J. FO^E8>tɢF"Gsqˠү3?JJgFkazlv){acUC1CM_}/=g<&m2 u&BBwETRl-XWQ\WVqwu׊Ww Cz/~}7o~$d҆I޻w9{JN/${!̣;TpVd[IR‘Pʭ3 Yw2zun6GBMGy3$0Gf 53`Tc +>aDl|v Ƈ33`[K:֬l ͸gǥ}z,ax@gOӳuvcO[%hRЮI3ٰ}EgdHfgil%>zUO`2TP%Tw"wѪuv gGo`nWK=YQA0O>:ɞsI: .kQkd8Qq urEbC9*E}x09aj]]xp LRC-o WCZJyy0fϰ[~ A2`8^a;mZ,Ywޒ=;"yOk8v߄yvS6ZfGwHzn2v7m΃ U q'F\-HOY`7>48vdB~-j$F1W1 EqފDjgXgP r-6gme]͇I&X>Gj|`l:("(6 a0Aa9 /O,xޝW(8zܾ] b#cdQn] +>{`g/p2iު#jgohhfÎwrJ" U6m =a=,է7Rž5 X#'Xn{U[n2a2ޕĖBzyp­Θ{ HKQwąaySF00Vi`Pef 4X&/K)f#!ⱁu뮿*+97 ϻi]܋وA3GؗozNtdIrG*%FWmHVwSF>ذ:: `بN|d{e?Bf}h# Rcg؟MOai@KT}48wK>l -6XkB*ᳺC3 żEiv_ljKN9SLq?|H_2 zRaÇtMlF?W$|H}Z1j\];vA}§lbXKl1؎=0+k_)Sl!%,]Ы6WG{ ΂< @=Br\ XaDGཛ'$ܙ*FQIsm^G9u!SH 0ֺY>C# T 0[΍+rCbĉ5[bQn=`e%_Nzai#vf7 J66k2D;P;zw̎sJgm<_v67;X̨k"a}wMy?z'sBG|bRx(ɧkMx8&vrD+kBԈ0.ʑ,S?颶 .8hoi+1 a8#<):Lo U$ip!# rG`^F8}!>^cܕyܫgg[ ;b>6@ώJ[)Ivo+u.z׽|TC{ӇlB3FP>3&}9sGq?y.9q'v)Wxu>|"XRk׽c=Hx]&&R+_yPG ۤOjSą:ziFxRW[Z(=/γ$D$c4RNXLfƓ{٩-)6ٵ J́brcd?yPKjKHtnB3{*iv6fWv]0Haj/dx 8IN?u*v$۬xׂG #P4fiAopq3!XLՇ[bo@|:{YNC WU#6Ql>q3lΔĚ}[ht+Al!eX(uo]6[%rIL_2XR0һ7;I͞4mV6<}}}fEKW)Ohu#} l-3CŴn"D~n͈HkοO0-ZSܹٛs݁jn 8ٗFqVjB}(b"~f=;wjkGH4b"I?T2?-fBQm8WGs66;03HW0[ ;hUigO`PqH́-2|~B7s*}:|S5yd; Ԥ5Ei\\T|1ؖb+o|tT\osm P W7*^Iq/G?mX6~nA" 9qE]3-yr@_oPKV*9G59^f Y>RA*8p98}To9I {#κ24/+c_eKVct ]vimHBV2܌Uf'ƾ:&HWuD fdֵC;;QѤ+d1;\Uؼ78/t$(1Ny8 UD}vĉ6n8;c{﵅ b_bf€m)_0(X.<<8kSz=*3¸>7ƨxFԩS)a;Q4eeA9o>xpw]W N٠lƪ{}gh@.1?&>h Khdʃ+?nhy"WM,ltkZŠǷ  qG$W1tsOоw]O|ž{9; )ͦMf_m~(yI&77o0x϶!Cxܔ)S}xuL7x݇>!?"̙3=<ǬGֵkWg#_לq\]D`^m$D NϞmTBPx_~1E:l@ 2m<2f"⠙_~u^vܜdSL1˲`1 ;vl&'뮻.鱙?__ى'-YÖ-[M:-Yb a~5!~B{~h$ko}[ $I$:*{#*Szʉoه?LRW79ψmznP3IR+{f3f~e]XnwTЂ7y9ivde[=iRa6(1+neE)SS`Qi:W_8;;86[ߥ_V%$L߰!yVg}C}w6YЙNqC)D~@Rp/Lgڰ.7CCZTZ?ǝ8>`7<cg$?|#! 6V\i'.N֭1^@I]6g,Sߥ /O:x8Rާ>)Ҟq%1mA yyO$@IDATٱk_ ~8L=L$($H>TӧOtb.1 ~mȑk^vꩧ0gN ,~͛;&W+Z`EGHg~ gԯ+Q(MM )cTݐ~=Lf{m!{po~x4G^}>`c ՔUU%}`D%7!p;ݷ+_n܈u`P%/^ oZg6n3꾷0FPﱮ:>C9T 3;N_'"1cw.'oW]u]tEΔ8u$cm bSa> M69@|~O~.mKl#Lq!~ӽ{w'?Os94p?#7@@`dGgmG<eO?O0Y:3 >gb?cW_}3Q0?XKK??LI ƴ~KR// I}#quDF׮};kFKpKW8IL]~|N# *{%bSw{(>?Q)ŕn8G26\o)rbٛs3uzLک^߳TE^ҙ=gXfp"8t#v'&,O]ku6ۧ=h;e58mPg=5/8 اŷ \ `)QAd_{s!oxLB.Qao{3N:UX=\7 p.LAk?jՑ> ۤW'GuuunYཞ7zhlGqKl}{ B;0@)"bQF0wƸ!u $TF5k]*#/(''Zl-8ʺvą39shqIm.:Vj]Ot)$t|y^%_?I HkVƵz^V8P{]d0YkU646WX[4T(WmkM H, gMRb/{U;'^I]ny6w*kD5ڮJb[UޯpٻsG;tӿO{>PWOϩ}9K#Ւ5tb,կƆ )Q-셸y5K/?:+!!aV!&QA#bH>?֛;y`|Ǚ_0#<ҭe̎42qF@^}U!Uwģq`NW;s~m!vaNi~#Py>꤮X򗿴/| %kIL򩗲Qz9m7(jZg&+ ]_̊,߆sMSȞO+eI5; ؿ-[.[n+W:YЖkKmٲNuߠ ̺ Zi9RuRSn~eԬJ ujmvۡ:(S֥>Vݫ:j):WQEoh,YE B}GZZ\CE^`ViBdXKm4FuR,2ډDa taY֥s~S6Rt IZCzk`Ѿ(e/]=f6\v@p9gI:l92ap1Ig5`? yT |5%oop{Q`bDg.V'Qn)_"|@z[#ybT2n ZpT9QuڐxW6mVL"*jڸ#otJUrfȉG9 @=ađb:'J|M-SgIO`)#>D^\m/Or-Ű#Šu?7⣜R&J_1úmlx-]󎗧sEIz/b6USl+llɶftk[-iILZnYuHо@%϶Ԯ]-MTƺzmGb[ؖ+VΘn N%xҖ D ni}dǐZHxPM`Z?dX?f셫j/sx_ٺsXnzlMah٣%8uXczxjpfs\ks>m^xڬ]m#:p:@L*W5.ݙIOL@~x,gH@B7g+VJҷW֚I%~kRCVuNí1f#\fBKxa%xTCAc;2*|0ހX_%5HֿK{u[sdU'xbUVJF51w0EW[j˴SNHJCtW!|!ΘĖ[o{;f>Ic ̸():,VI#6wEk Ѐ< a;n`l$L/,o)Sm?=lżWudC.zQOZ i 3"pjtFWc34:|hdB"E) IHIJy|8B"3:+Q K ioIcU%âjYd)yKݸq&%|s,^S!қ0UsT?G)MAYPcd`hidMfQhD[N:!'lk`lǟ/k#K~'hO>z$ _d`oYy SmSʏsQ:'gټ+ 8Q"t})n~pMj)(q,>vUC3:?;k<_%M+%UGoHO[D:g+U_)7 @Gjg}X1K%[ST'^\+RS:¾G͏#Ŵx9o*CШqm{iPFֱ@;[%n8ɱ6_^cmgYnnҡdSdHq kWN kL*^!]y[*hs 40OqOhsG-2HנȐx"> zTW.|6H2ʟ/Is[he 8b]Cl<1-zMqr*V^~<Ӵ=eyk[0&Ř,鳗ٰ}Znɐǹ@CmE0"H.q6<[,}6/2w)VX?}EJt_^pcVcA R CjrqTr}^ׯW7s9oA'}k6`#y|͌w &V^9חiСX#-th Np#qx0lw/I[Z٪իG^/ux㎷N9 .)'.үh˦cԳK RsS^Jg0&k_U@>S.>AsF; :D#O ~6gKuD^n15a,}](?9yRt6_[EhS􅫿h@/#ґ/G.&e&M5cfGxpmPm1g[*ھrQJߝPL`~~=ٴyjG~Z9E̫Ri4iɇmd[ZjpZNgueLC6E;?}8^O^}yJb8'C mk1KxGt׵S;^`-Yp= z.e|q{[:&q_;S\|ϕ4nQLϸHGơ@6yT7yaĊl61/Xʱ/bS;frӤ˭_ ֯gZR?5Ho?TN1q! WPx}+bPY b??矽㽴' C@:ÅZyfMzNpy ~ PO=z8r ͚9Or戔SKݸ=VhN7B!Ox]_|Pݯ<#Tw0K2i3R!ㅻ*ڲBkC*;hS8%=? d߈HA MAêl^z}Q2N6 th Gڴ{)7MhCzs ۨιR ci- x+n`#v<8 ŃageuK$ﶺN~4 &pKn&hHgݪ{^\hDJEaڷ+q&Gq(@!֜Cݡ3fp {QLjttɓ['#񮭭C_`:O-Ќ~T|H]8hL:h+]8*! C"4z n\QmrD]+G׌.I M ֮_khQjeJ-3+d1mzܧuS扚@;p ad4q/N(sp qm]]u4B~B%-O?tw v8p?(("urL^kIz~/8~upL9r_s@N/ЇK_uY^&/N?&C8QGϯ`R Wg0_W>s"~MCwѡ]:i.Iorj yuO+7d9,;3a:hx%VTm_^_J!߳c my?+i37&p6-o.[@ (/+Ϋ$ޝuF[}ZMS*BS͔*muqZ|c kG8yݓ뮳6HPx+twWW”jeQ:yQW}U<  HS?t &&pAÃ(9Kǖ= /~ ;|4̌8uL0h>+]Jr˿o>=;[7Q1w/p U$#5ZS.xw06;)I7[cŌ2qLxf"g&GʹXif I2L 3Lvmff zh_מ{Boxo}ʤ()g^IJgytB}{u⫙y'3M3 >v:}$?i.]]Ϳg_uRi{z~ڵj- EZߞݥ 4QK&;vK lҘ:TZb&SNQf2[֨QHY@{C#ۑ2Ǡ w9w?W+f#!^Gb6R]̠Ĭ|FD}DZ0 Nf<@xΏvGg3|: !$krWU锬7"E c.R80kR$NIYH9mj򉸌}>i◶DRwF=]>Y?] !4M64/*5rGJjE5$TTnZ-]se[?\9+.XA^ Р '<Ҡ 8 `>X!߰mVE44Qȍ)aH25W[sO}%Ԅ /pDxFIOTԟmrz3PTGL(D=cVHK  ̊q?T3nsficgy01Qg\Ѐe+xmSFmBͭU_=Lk#xB+?-3Mi,+YCcګb,HGV{~p'F!Mo~ I Դ;ڬ~儌_?83 zñF/11B脴$>YE?E*7!Co׶kKrB̋I!nd[a@A#J~NWX-oy xGqdINrh-X 4HeE蓏 alH\R r0~9Ni/ RɚT'Ɔ 8AP֊x1"س3D͸R1_ppu6Yz>&S::Ƶme%ՌVW4c`H,MfEF~텟ܞSQo@tvs3p2*ѷ6Pi5\m=:r@1(W3$|#@T9`]oS1`ge-2׬I>4(& KVvA |9 h?Bې0F= w(' o{FөrFA(oh'uZkh!QZ6і\e23Ո7Hy|ֽ|Pu. z E#K) `}kȹs{G(xA1&O}i}ȑa`|fHX |僇u f̌nr2lf0a"sOa\0L J,@f1)xf|Y,0 Ef^=*u!,i+^ D~VdΓ+Dr=6&9v$)1iA,^qvEy9-7?6/jJv3q !I> gى'ȟ &3^) #-c|3O̊?ӟ\#zEP~H[K=:pRJMw s)-ԇ4("F5#URfXIG@r^6$B/su^kG˒>+WVK}tإbbLJ?YמݤiXJ *fzM,@+d`WjgL0$7 aLXcy{w]>1Oh+|hYBf̺\SWWt Kaſ) @Dx<N<1a-h &c`0 c &4I4%n$}5'}*Yh.sj.ƛlѥ:Vwox@\[1+ׯOvJ̎JnȂ&اb`@RF@a0 οfsABamI+>Z>`,a$ |ʢf@+}D~f0YH !g )i/clˠ^ $貑0 y0U^zc^bmm`h]3H>_Nv#1Caic#:/C: szyˎ, >Q׾2/Ă"_x UR[j3dw\>@")͸*/e_>eηm#ہ_zǻ(%*?UkfXIG$gJý&"zf-hU={ۤk~h+ڣN;O*59vԾlA2Z ~ȵ 7aΕoDe#(:D _X:C53hL:szü֠AC b="C_M!,`Ea$AZ0t MnFAa`]>(h?$'W@`d0:؎=X10>.t:n ]Kf,ucFHoJd-ٷnx2[G9&)妿&R$}Eij2 kCn^,CR2qPw9#NUJRޗ_ӜqO%B07/UXa{:#̯>Vib+Ų벯oݓZqF4e1-ӟn9?ު"ӸnX/|qXfWfdB/K=dzwK/ݫYܝUֆx?ϣuZ_d77{ʯhD2߸1c򴏿 ;e74ұ\|nQm@>-TNiA'}вBM:rYGŋdb\4q,")$ٕz4[k*7Ju,@'DL VL~8kss%,jku,g $F Ey̨LeCMiu5f:j'>w\34~M7k6EZեtLcɠ4I7Ɯ "d﵋س> tG< 0kuѺʧQw.ao.&1xrG'gͽF?nnH6wz E궝4.ezm򷮲8CNr+)+ai E(;nAхjiw>j0Ɲ?\%YNv'riIE^D9L\F;/˟ CszgmI8 ־rMGIVӻj:c*RVuΝNٶA1X*0a ~Qfo ?JG"I1>^ʋG}kISCøFOQo5a56SDn'er_xTʨgPW!.;ydWE)LuHYPm)ia8Ja 'm;ځz\mX<@}p[ѣRUk |QTlK&e+oO|w1\#,߿^ʎtq_LyHõP^?ix.qky47Lj54lXsܮe%EI.b~Уk'ѯ֊5)g`+Oְq+":RGP>XA!Kiԡ,_y)By8T^sҖ)W|)^W7 Cy>8Q*몣tq'޴qdևL-|bjuaxp.y=Tb5vC(65Vtގqo Lo )Q[ϞƇdӱw۩}x{<0b}qq1VHR︱,{r,E(2 ضkڲYScX:8$ MgH`73;d-ۜ>x)OCE676ESo?Sfyiz&,p)?i˟s7 W3,պb`98Bf]:Yb76eV[jz[^^Uhn}=T6|wX${?GbEv陵.W%skW?q1\AA#쑅o@1]<=3dq3]_p%}L: ch XOhцr55ϑ.6WNSDڨeTU]]֮K{WUgi\2TSm+a6W`[p/šY`:\&Ŭ^,##3zq5̕;^iI+8$^fZ BuZoP\bi(ofj[n53i}uqbvP:/T DoMl}Q(R.b=R@cgq p.bo&|S2zya2 2gT'dKƯ9Pla p֏ϯ{̱ 3l!%fĥsr\ci;f_WJ҂՝.^`Sn 8ZfeuИt8M^].{D#5EthnhK`k5\*Y^#G]'&^ ņb K+6rc"0&sٱ?ʅi0/ֱDX83̆0|7%5@?}2 fm{IM]xTZ ^/]dS~m\<>/FnVI}(Ԋh!;b:vbVϕk@uA*{<}18ʥylz|~ᇬ CR9@"FV +H40q=1DL uScS9?PS^vev/⑼Ta f䂗wp-iH4s! __8MRr tsB <0~Gzx8[/tK1J:&Ne͑ͧѷbI%轋 YCwhmWwY1>Ӥ}*<)a`f{[TɷǎHP'j뼊6Zg`:޺j_a-3oF(MG;ҡ%"QTҫm6XL䢼.4_CՒPg^oOl02CG:Ab} .nW_q/!`^0 A '38`7dxCڨp=>&?#|L;"j;$M[ o~ƚ5><&b(zc)k"SkS=lx>gIDu *Ѓ xY) I*`MC# ll}P> w_m-aSsN6Nb fc F|&=Di_0 NU[HI( 5`zD? g4? VED Ƒ+@^ЖUΠh?u [pDu{q 鰮Pq"18# "=+A[djGkulMPFJ\ݷkT^)toki"F AWYF4i yVYNa|;%h˶bk6K4I%֕ʁPa~ĸ?tUZĄeΔX+C}qC ʄ)*Ha0CRA ,a,Qv' L&=Ǻ[0YX(Fr VL Xk+#j*W˭;}Y y@IDATFX#Μ1әGϬ`P.N?ԭ1 fP7P$`7O~JL\̼pSxmme*ЭYImg鎟![Ю _aTU-;00vF~*[*TIC]VeR㥕{^aB6DZ"JUK.d-%5\LK;am Q"dT6@X]uU.LˉG ӃS?-,W\U!u `E8t~R<y49Z#} N$+EP?c*`8~%<@XS_0CX@1?,7] f6D15hx=Bƌj7'^nVׯǮS_w&/<>gW?֎9'5%AWG/uyu}5&[@HHa)0yv$"&o` 1!X&$ID^CZa q`87TjSL +I&@`@  FʡH0!a& NJ#0*!AO fHH(im3 KOLPQba S PZ=?]JdMuK>C ;]uOε|Ѯio&eStrKG }[4JǩLv;sQsb9EhݐAmop(kl LMn@!aruE0lH*!sDR bO<@Y/ jpgJ^bar|&hS1mX6F<̥T/f c@0`8Fn9`3jQ$_*\.7LxΖIRy9C&j̠4Ɗn4Ӷ\+*%#^} Bc=LљuZ(YxHbU0RJ0Beh<=v⨰bX^k=ke:ޮTf"aa/$Vd!a0 M 5d0K0ezBuTلQn9#kRŴ://7!^ Pl5RLJ,jH_,[(bU[9QV^eGm˼MŜ-بJQ{! cB'*4=2*f (v=ZǪib޸3}!:s̊P-yK!l 6 Q^K_޾z˟-o)[kE^"*ɐavmg5@k'3^4 YG9-WQP1l w"$fACp8ˍ3U[4 6l֞Bhe$*zJ6Q#՗jָ3mm ծ5ڸڱ=kL\2+)I4$[x)Y:J-t r3U~o N0Ta*m~uJXa^Ʃu>53YQBĵGwJYmcAI@v67 6₠FmlĴb[-T-W3F1ѧFx۱k>d]NVkeyY#yVKB} -rC#B]!Q{[*5A\_|A +d7;Vw)WhL%"(kzZ'N@"Q[) _X#!`rX)awCzVoNOE9fH!h~U[sjI`Rԋp%NtljHk"?Ӭ|.>Öj_bhd_9}Q0?ٻ 6 ! ŕW^/h^#b!6ڍ7<_qhW?10|;v]ic5O&IE^KαI߾I3xJ֩څET)d[#v:J )uRV^&i$W[ 6u:VSO̤Z~EVdeV.% mޠ\$0- I6sC>p'v6c? a"OG\#,#61shjIA2# A_]b^FY{-qmtŶFX_G\&حZr]S> g-#_Rb|y@@_R9Ekӣj:$/[i=mxvq5UU 9Y}~ dMHDPTz/u6'lovxh:B$կӉ1ol"i>cS/RCBXx* ᙳ&Aģ=>92. r+X>q=yC3m( X*Xi!=aX&ƦktHy= ._D\\Ki\N,yr%fBh{Hag~ L" 07$UV2yx3fL#&gE4h:yBC8lٸ'm*-zm:X.,A(/ G 'V6eF/=]Wյ-\F[ʶLZᐽkKtYYɡ<Ş:mhIiUALq!5Sl7۬~lm:tLFee@)00!<ňK]xqǹO@ Os=kѣ?'?I;lk-"✖cK/ 6b Q ~[r4i]M&UD_/B7 엿xa06f & ce9# 5+ҝ $G ^=BD}p ,YlϴiiU&dr16>T6ŨGLںF 11[bѥ$2:5[.#/]Xv7ZL_+?sUCuw?f/LY!!{ݐ.җ_KeկʞUٟN;%;TV8UJ"|fr|f"J__,310?~YOǓ.0j}e_Ϯ2۲)Oh# K3S/-yw޽/$hjmlt@\KnѣKR-ZֻomT"dfR_۫n-v5QqIIA{Ͼ/?A{;Nz@g<,?Ve˖^%mlL5`B 5!K BrrPCB`{f}Hl6Z}>wvfgvvUVVZiR2D\Lƺ i`art GuE9GATF~8Bq`"?m~~\ a p88Nop7r饗%( R62 "E"L Dp88s9'k:9gH!ޤNpa/q߼lˣhU6xizx!.ƃcbўenvcX!bSc3duvkRl ',Nb>W<.O"P֦Dd-C( r$,n~-[]L #ψ%YkPDq#| a0.!uhD֧ `䁂駝v1"6"CDCZ{v z i Fa|Ѷk٢ڢ?[U FG=NQ?yד:|;igFԩ?fG?gzhzkWԊ}NAܼ`у6^H5LjR0Rɥ%Se-m&l:FGi D"#*ʊ4?cKVuM"p#3Dhp%b$)d'裏dK_{ιG+Ax 'ׂ!ͺeLMHNr("!HzA[BpT\b:QNmJe SO=E2y{q)UUUOȋv^Mea'^A)Ϝ*-MAJ$;g?6@R,9L>Voal˳ X2NY,wh0B'c_Z5/ KU1ˁ5V%m0)|X3Gw $Te6Fd%-Xw0҈1 j$WZe'X޾?lb6OZև M2=uŕ}hA88.bHX#ڋ9k֬+ua>37Mb)(SfӀ_oqAZĞ&xox9ZUB,8nǴ` BbR.JH}ӻsfxߚ]*Mq5>^_Hzƞs_Wjǫ^OKÿՊ~ sOj;%B(&b]0?sݕ`ḤȌ̎89m |̈/>?{yw HTKicO9{3*'I;Nܦ=B[bU)էHjw^bD'nW-@kUA7AydZ,csH;s]%&OMRSm8;ba.*barIQ; qlյ.GWS=/ϟk5ym{r~*kOcݽ3hkS}~^p;&STγe郗&MKw X\'ywBQq4A<1>4[e qʊ#TWY dtiWfhhhw?1#Ңȇ& l~| {_ Z]Yik3o%x/2C*AVXgMLu;&q*pGm;O ]^o!O,Fwaňt=w]8?.*n{.ˮtb|{RO,O|tÖLDL5"DjLZ޺i* TrыFw@ͬ.\)BkWT]q`dݶ7 WI^l4 Hy"T;~ Ǒc v]bn}DY8 &\k$uUQX:ԙj==c^(DH{ۗykl6J'c] WfB@RD U!2v[آ&Otq)#h ,kڇ+klە6e- |Obxu +wViOc;]Z]];n@mQh!Ӵ&Ὡ2i5Vjk>~rd⥋rU#r˖҂5B{QFa!??/:TYHvDL%IͦǘLE87qtOvТ(Z2ZbTwş<ٵ]YA*o0A?#>kYf\ 훶U}Yx ~Pj`lهئnOُ@.w?RCg?m c=y'ŕ@4 ).b $\|ܲ*5 > (4p4ޏv+Y1O%Xk =:Zӂ0 ks)̦/dKƺb-Ԁp@>q_jw}tbp9Aw6Z4{\؄j؏eᎪ1*ʑ4IIЪ7haaرNGN?.[L'i2x]۷ug0~wQkYqro[ky R,a]dcB&,\ =$]hk<;a'홭>hugykֽ[s/:yI|,j;nS-Nͤ1٪ȟ/Gg 1aLU>Gbd鵪W}N N$x!mݥ[,f,$o_C\U|85KjcRbD`Ca.!-DJA;T!G<6h7 %w#P c)$+|L?~yƸ{eN; w{צ}8ʠ-â={4(nuC6G߷1-=:pce+_&HUixxsQ#s Dđ ^8-o;tۜWe}'noUyi|הӉ@}?殴>m ,*pļ 莮 BŸdS答"IPBVÃ0C@RM@碼_馄u Wm7p.=qtZͨlōpP`>=Aa_EnpCq=Co H&8q|^#7#l<"*m7Jӳhfږkm}$GMاtvH]I\Po{dG~{_L{/TL|\ڢ~Z_'C-n֊ 썟qlvjCT{΀KW!z̯$J>e8yfӵhʔo!oiE-P{ FZRk毶 wNjB(@iS^b}YmfC)A fjDPYYiW]u|U6FT[!p6dh 1zgGaHpO#R*,oCX!*itH\\v!hOD3c O^81GXb2c9C"a4dYb5,H0:S`n6wVMSM/m?^_a]ťkC-"0 L&LF xC G8\?{Թo߶qP?6x]c @}_M.XiKؽS`ÉdOr|[lM2WZ3Χt瀢8Qܘl!sR+[kdLs}Ȭa\XÁAޟ'ǜGBR"h p' l^T͈ٞQA,Zp9zHXm G%MCĢK :xE" !'#Gt1!Oq=p@"Ĕu 0 >el+ܓM\"&+$<]^n٫ MvO~f[ٻWj3{Զ,Y2\($L ,N iΘ0arʟﳗoM_;Iґ&BnL8 ES ߲1aRk\"ELdڨli]j'@|zU/̶9 [.- D$T?R;qNT[8%$ ‚6p_ya' &nQ\:Sz3 3>>lY$t&/k|33abgUN8Q#/8,AV,ױ [s)& ^S n,˾! ʎe:ObU68Xu1.f~u`R_%[Oֲ7m˖m#γNÏB# ^"D@=]=If66lݛcqm~hh/tM]t]U u"NYߞڎ"Yq5mm-P Н \E8`VSuO>?0NBuD( hѬN{w";nTmGbF1+Ac1fsj!n{gv!hZP]&A7}&놝98!N8εi[]##{Wa3J"ħ|1A//<HH('Оd#uc 49Ǒh/r8 w(^ݙ+l;m]Z䰖,{}qY~5Ho>[i$bnr nz/Go|u:֬`@ ޴A,痦rْ^2']*$0,nFc@z暙3m& zO|J=~k"\'ZQpkaOWC/886q;{pOkYm ξO#(HnBo%ۤY9/i\zepcA:i0mrǡ(F+}"N#Uu W%yw ;)~']wu*_ePzw*O=}Ϳ6NN'je1M),V9${۪OqNҕRq>&Cz)>ƼCy[,|W淼5uJn~uoTΛeei?^5Azu>Tk;X+Hu!H6O[W%VKiu+t-&j 0??T[k7yR0U2gw4'W=w^bʋslܴ6l`'-*iphgF>.JrWG?Q':840j@8 D8Gt@~;!Ŀ5Q?m=zNBN‰(-]Զl {#‘EI:`mAÑh4&_l}jD|?q(kxCxip5!~{kiLQ"mj37R=rط"4M bGWsfUk]O>ɖ76Vh0Yڢ'ZaY2i-4s̀u*'CoA!Lw  TClRes$P@lrCt ~<[6i-Ԓ1-W붥n:aV!.k$^D`jZ".QaĤXE29S3$R8`T5@٪?tec+K)՛C~+ }YQTHh"c\~ \DQiɎEW^Qx1w$6/Z>϶[n5!5r{aϟM=ț:O~ ‰k^0@~򓟸XF{{qB=[ك}+l8q*ND!fDaHdVkm^dKj{*q xt6|?fe"neH"!Iͫ-N B}'?>(HE/P_,qwȩ3Q N>޼zZ-"oeoKXsػsmzqQ;mC 61GAiy['p6AHk˦6Gk) ) IȒd %HjdEj5l~ֲ$Y jBJ̿;d 6<7žлvǍI:V# !i@V]+-`!R n E" ~6(@xb1Eʄ" ?#bJ/Dq!"PFd$(gaO%OČ " F~Wx.EXF~N^`7z}lT& VB&SYu n[2e-(li&@V}[ĭMtf_"Ub46f"g"`A} I{U:v;EHGvrlY-ҡj$[6q8AO8*<* VSL'g[Aa6OI'gCC5mwl+׮oxu=(>{Ecø $oCw>>~ ŧOc <3dk HP@RZQef:N/wO]6gF:NN@vN`SE6zee ׯJ7`ujyz`_*H!զ~i/E]Mg5|,Βu%kk_IVѮhi˂/  ̮Ҋ7|L!뉶qjkڮgRk 5(Ԇ\ =Յe}@Cka.bmqXK#{uiʩl%ִUkٻdLJMZ9)RTZg *C 3rծީM>s?;g.EmDkq{Hqj;%4k7bdD3< ýRɇw&KWlF T p Drf C("b=srޥK/L]xǥӉGBTW\|Ue Ï|я; 1aH/1aΏouSiTN4:g7lo?SE p|,< hVpeMJ;Sl˚նj[3ko(v_ ˗XQִCҮ]@Mš5|WT3J C1T?j\G]*WV6-l UUk.Kl9A{7.oEċ۶R\bkֳjƤWtN<+(BZ[%!UcLco֔Euҳm ^"{ Ow6kt7\m Ɂ 1u*{mZ{YEڌXzBfkx:gxNػo$v,̰7D:$H=,ֽ:'Os$ {u/~+|u/cxS~1~ݽ_, l!N&8hH Pj O_(*sed1Y6KtV۲v]U3gYTY"3a[VPX+D Yl ZVoȔY^^1,WZT.dYa]jd~eKpk]JBݲfm&5l**z#{6o\ZDۭtת.jަ7eK9ƋF8&Cњ]ÓV_pYbPKh/ y 2#fGŰF;%͹05":ԲE|s|kcSx WOׄT|䀍F={lg/_l#WY1W3W0}G޵^4.8uM\DLT`Bp0Ujg; Ybur4ցp6Ź-:6k3w,YTXZf%VдI$'E_qn#J?*¢gM2$P[Ԗ5_fFۺam]Ԃ-[mv]DEִ‰Ri^V8JZԡJ3D?ֱ7N񷻶#Ca*A,:~֦ڋ0>~ G% ^b~\a09I:b(J}C@[ xw֫EHYT u0@l׶-D͸|{͐˔ݡ}{t{{,ˡwoS3:Z%N=}6a2'`0,' h%˦A%01=GE-{' MϦ m+WmD֮m]fuZ{9F ۶EIi++R3)mkĀ[I; -7_P̊Jt8)+j̚HkuDRߚ(ޮF;3ըj& E9TouZd; 0߆bh_q=Q4Ř7@Q!B(IynLΏ?Ű0.?U:5U6uꊛN;I.ovH2W\qqb,˳o1hN^Ab}p .ҮɠP,/Ͷ4S#0 @lons:!cCϱK$,5f2.GFLa&"B6~)LXM,Z?tf$zܨ%Y(Cת\Hw_X&EU^dl*dqFق5<)BϧPKdErD},0:.ۈkGAZ{Sԟňw=M09r6#?ͶCx [aϿ>zzX6- 2t<}o}`X7\b˽i؀;9@{|`1f hR|Y_@IDATCxI'ճ3c wuK!E 794\@.:ٟF8׎, J};&DH˂ hEJtx>qyq_j#!f ZqA{ :I}ݎIA#"{0鯇9'&ǖdύk۷c} tNCUϻI-w\9b#8mbQXa׿(0"cf)c/M7@V+=n[$/~RGD$xq  FXn pLӟ''(N`+Giրs\h8 }2W3kcxSsㆍv}U?p@כ- E|هc|;@;#ASqZTg8 w\6IF~;m:HOaSu%dgC䅬p_# kٕ_L3}GLPёiDW;=v<ҏ$$i[L{u*8Z~DԫW ٧ž\zUM &*-.eqFښ+kg!d~Υ 7̝nϑrm q6wک6T(]gI;{uDC{&Q5D;Y{7&w}HS7>,GdO~; =uS͉ m8hG?oYSo#!>H!Bxw&Vv0}wd Y WUU a!vw<&Ĉ:@hp'a`Gx7w{*ѣG۷-=׿_ F:by#?B;_ٹEienf]k̩7?yuB2nӐÌf-1Mu.i8dd>#^:̮#~=P;f%3SѾ8Uk]xۓo}/*8HfG[ʛzܻ.Ԏ9 Rw~Fy2\yEO}<3OͼϬ6ճܑ 0w*[חS|Ȭݺ=so_ɜ#"9|%csw-.O?͈k|_v?c6 32'23g87^}U#eCh3d$( BjH2q,=ql[C"pu/q|Qoȑ#3:P?ȌLzF23'*!ˈh1xb/sujw3~cߎV9*oi 5SuG^gϾ5/ KH{I3Iv3 f9dqĝdm03I&W^YSfeunVVPY4i"`(>}tg1KԺ;6 z]h'83!Yfy_t:Y|~XG~gC6e6j(qr{F:e ʌ$ig'_)bGRg%MWN78# !g[L׎mͼBwo;wa2&Z[Qx۞cS'ޝ^>x|2I$!alM?t<Ec(\arn`t-Jw:.p `ň803 Wux֭]<)V<^Qp:hb5:XXÞ*G5Zǰu=:;~}E?sԍseDw!h=vX _C-Z3Nñ<|.R==tTSߙo矰܎j,: 5BA>-#`oo]eUVV`YB!` uѯ5x@'2w\6cwu|?#=@2 EjofOoaw'X8!~'Xq4#nƍaGGyNȗWZ;2y:g #Y(k{h*k'%jy;6*KWNCp4Hg}']LWq6aGXA m}q{&`ٳVɞxkpAgr!|2RZa4r`dtx\'\̀^p,p Cȑ#}K_ݏR8YX0p ,by/Qx縒QϘ1ÉA/5X Eefc#1e͊9MNYH&?;ӣɋEJg[{0yyL@֯ 0>f׿*iC,]MM2D҂BI :wo؎EݪGdzαt)Gi4v Zb̸vJYިAiz=cIME t,Jznr?Gw֓"bSh#2*`xu^hlVD]Hp"+?Dm̪|AFfUUUN p${v$΃áS d0QDKwS!&I;UmhÏњ_gϩl;>{! q~ec&-)ZQ#N$8;#m9 O0}Id'mٲ1HGy>kh6aeLxt6k~nq*>*'"Vnf/> +f=/Q>s:{Ky DHaJ_^&x ҿׅ|b1y'Q6&3-',21!jWGQFP:݌DgGV}"w٘Y+m֩Lm_tM ""V"{rXi^ӹMxY})#DZPxtl< NYHxl&:" Y0bE G $bl T`q-3C\q0;^NџϠp)qtgڡ.7;q`g,{6OYJ}@=s<6`W^VQrm/( Hh2i {͹v֯[oQu܉Kh"DF >?ΝB#G] 8'导NGuՆI#O4(ryYzߦgm.U y %yx6Cf6j5cf.V1B8 D}LR#a-JXGMKCXw~s-8qar Rb8~Ij(eڎqKwy6l0_'츫%FY"81 (NF yUUU9LZJU:ϻCcvjFc>0]Ec\D8_y<8Nb<&D;pA8=H@W<O .?g"XH̢ Eϥn+?q h{h9FOVyf=񋀅M!OCPyR.ݠ Č85: iNzOAp tȎ<>4T{3)ʁ-ug@΍5~fI^ĉkzv3ݫ.Zn٢ȦI2ܼ8p*;)|CU3|,#ľNk[oXNGsFp%]bI ޚ~,1'uNY9P l3o=V.~Gw#s|X/vWv1t]+¸{~ sW,0ץ*+9' Lu &ڡvil ċw~qci~[]g;.yx(NW+֏Hb:4(ǁv"uC6.vsiU2{-ˑi;UgixѶGf+QP0Zkٟ^`> a4CGGӞƈ&#/϶ : Է)B-3zڣ,ߎk ALj#&(q J$$i+ie_Ʃg \2Pc9C$]\ 1?ҌzRgy  'w_5;2-Z~q!մe5\ik̵z&tz7%+6ϾgC43U3gE+D0.HyO2j}~[{i7~iEW8| 2McuJ;{险6}xҮ.~{jǫ~wv2.U'|:MO.?}H];Ni Vܑ8 .~y?]9>Y=q&:eĊɺeJYݵf`7L;^.ō1WU:ե馂fAI4LsE Ѧ|IvlY+"^y7#u$323{%k {ghTkŲ4ll# fD:U Nem}y/Y/sLt۹4ՀXXq:V͘i[w]<2G5Q"}v]2Uҁ+<:f'BzRdlMYV(Wˑeu#[ 'bޯ5q*G_J8ɾ3ًSScf:)bњ-!/zy~8j /6Jt~l\ý%|aU:u'k.Fd$T@47#4D"^ljzpgmx,2#_{ԋE Eh"A:߮<*hKs#T- b^8샧Ub;;dOGW@}|#СB߼u{+lVlm^[ ۪L*<3Xljٳ+{WN8 Ƽ-)x)Iv~}fg嘗`0Áz0 )OM6XG%\{vO{nb{|1/G8fl{}[ k/S=-Z}j-H1Gk>;2Bx@7#2>8ˢyQU364p%Aԁg篲mPx=@3zML ]YŘşō֮X0If;0T۱u4e}qv[i#2qa f6VQdXvvksWG衍B)(: [cWU C`]mvFQÕu %g$oFw i-o]KiiʝLjU[67\ -4hS sHb}G%Zq@^,4QdeyvX wmN`5^j -}32 Oe&dceEq0O.!{rJvʱUsbۈeg]DZO4e'H(VO~kE?' b{mߦإ;9'Hx[%:&Ept*i㞿b+bKĤ!Lw?0h,p ୵X<6gDʡ%&0!+BBÜ֬u[+f:JsP(r gh'5^ŕ}ZU{z[&8z_̇;ҌBgՃY8P_^~8XoW}|!ߪ?S:NvĄ~ٲ:|ݩ폣Rǡq avj~@$Jglp E텩+g&:X8kwJ]5-J0GoQc B8; YJZ|t5U.X\Y+*=4L7fvZޫ)WY&-ONw毷5Qf B(39 ?$=[/d !R٤I߇.yS,Vױ;kX7_7~u B^nl\Ua_t9w R h-:3aޛZCO%rұ 2|ÅX6.>4#"nufRңޫѣVGN Dk`mˊ5Z.usyD:3SnX<{~feAzq9KBgB++c[NֳppW~Hz!/OY^emu%:X6M7s9 !^!mģDHkU4gΌ0tHq9--/^8r}ꃃ_'D"> b*҅*.V@|};?13ͫ'Y* F?x}S0ODNLdãn&γAN89%m"z\ϚB^f֬mǚld?X6¡.7ӡM\{6.9z0[lqیvPIPBZKk=c뤴OcK| ) p=H8Cmkvc/ثq#Tz©{2E4江-ఛ`H:PڀHa^惧^ezf=”b hZեa?uEKZ&iFH|nkޡsmٶuZHZ وJaۜrgO[Avb^fWߩ8 w,=hdb&ۯ_i.:Ov{ͯj.3ke%;sVB4XAM# C5m|9 [~WCĶ[s܁v'SoUp6FCxuS;#`mZ[Nv:rm[+ȤӁqlJwNN]h[G\<tQ;- q)(Q )3~$Tɮ>+ĵacV9VW e7Of,]3P.X)& dFKx?[G ! !vCGR_+8+q'_UegδMK' j?~aSӻR(V9YpN]as:p-~{qϳ?֎RjvfZiBP0* C wrٸ9~SOo Yyzٻfo=54)a? {g(J껭h5ּKmkr>X/ ݦ1 ~wk֢PN^%!Z;[ /Ș %8O1 g津Z+i~ኡVպCNٿ^<Ğ~[DvY+Kkx J;b[1J0BPwjm_r8+I V ߢɩ#{[p\شY^=ömXH"PS;~k} 6[}cuں,a#8f *ɩv0[ަSj g']e? %tq֏M>0>69; H + SfJ7֯G;+#.ffq+}مH 0cץuz}̲9 VXfEİG_\#ul鄉Zbjh1n z [wa+ڿ}xqvXc1Pk)\e&ڧ{XYg^5LRN$9<"``-,4qޠm[wN D@ -s!okޅ\b_jw롲} ?Cgڒ%Ζ++=\[{7BM63mjpkھW`0KW,uk&q>Nf|R.45r'řz9/Gb }>"5]zKm+بaA )a C[-%mv>7SEa# .=SaZ'RlELĈ0Z`߯Nc1iAwm[n{AE:.5oqD4 f2g:7셉KR:oD/AITs7|+6 D\JtfTl k_cU#1[!8Ғ=oucϮ=~4[bjc-Jnv.<"e˺5tmnuRO6rJ'H6|Rv|MZfim[>2FCdÏ=B\T="4^߃R\͑!R \-IK+إѡɶ C <7lC4 V nJsFi~`s@5>ǻJ(Ё- 5CG7mBziEҦvKlƜ֣s kݢćR$smY–uloBY:ov( !A~k}7LbxpgؔOD1ɸ1N@;% |g L;}vּUX 1k*ycmz51STL͛'h " ?>nhxUsV#űʧ ԳSփڶmeZ+j|SfY)5x="m2%xb۲eK6Hp<||`0c`8 F4pvyw&{߲ujl$tv%VrUtlM+c|o$8țT:,=2NYnvgo1 b0ݟu;rZP#W]Սxegζ˘:[nx7q;K3N` $Nwn"uhwQewl}KE}a. :]ޮR\&۶kfoo}C)%OfܹJY_ n41Faț8 b8s scPWjxX[z}^'}wb7+T;h$_nvtnf,gg[g[<)D(k !J`"`g-v&گj kktu-G|)Pܶ, S \qGicV٪,nCTs7 *>^pREYmBAD$!zXi KWٙ!IF=઻!$~\Dc aj*_~N{9ۭSN?&L`pTvZ׮]iʕw̘1:V#9K"M{m/͝;hM>l&MoW\qx9⫯2Ys~zlĈH#nHNnxMMlڍvn;. D8X"m%12#6[S]U?hZM o۟_g;Ү;o" 0rqQ6] p@"R_9 0Wk-vӓb#ۧ/;Vye8s#pYAc0S1\V=.lmZWd殧'ښ,5>l.Jе'` yf纸ws=ΕMYgmo:gu0B\c-WK-ލǀ{Ii/$)KH? B HH{`z1nK%Yw}g= ,{Vl93;?<rgʼ1+++X:eZ,+Rn&Ȑc=Vj-tWŋ4ﲲ2Ƽ؟fv̼1/ gɝOn ;UIC Iv|G`-32H:ƶݿ-ao m؅}G]_)߽`|c G Dss6d_<1 rϣ6%ɍW.5 tMmːАPRz]w)Gdf#dTOO=Y*:kLNRՙ~8C?0? (}w;)%?WHAFCd]"Rb"c/KYE:3b-Ў;42:+7ȊV'xBZ!S|Gos9G7l YYY:_G}JH)]y<6%IxHΓYcǠ`mGK*RX8Y2_ѩi?`Z13<HFB[AFi^쎟m )uXƫ!_ ·aw܋cqQ+75C.9mFG<@F}4g` :|h֑i|MH/?NlׅR/_Xfc/=D|fT;<:A w}W%7j^5رC/_*B%-lӃMݎ'x,deĉUF ɼ<Np͒m+T:5fD/}<fמ;'ʯAvy|y(LDb=$iwGPX{뤩TgL`, &w}mIX}#:n,6X5^Pc6tӢΤR`qHE}|9ɺӍG KOF|"pC xCҁċ‵/]Pr3bwl[| 0OC^GuK*BJV̛7;W\8w7;.Lh4߷Пo o'Fo%@ %hsw@x@ m'KƘf̏~r ՊMF<#>Z܃pEX0^0㶝d_G>^8l̃*Gk%FF)˖xC?***Ԁ^8aя~y<۝=o]1/&ДN+cn,EM؆l6NMTxɭoC "``Q%QҊ5PG6bS_R{jl(mϚ*:[(a3z!U9aX!؃jQ,>Qn\8iv1Ҕ@o*qʸ@1$oC L3ܫͮZb!8xzΙ+3^ @BG먃>]fŃ+xDCɆ  $̚-O 6~aٲ:^d{v)*kjy,~D" ~0X`nGQe"D/bV&y2.2FqzH{i,iLp 7̋2G &=7!@ܤ*d_R5'?nBik^L ,(Z S2Cñ2o)Svl9i~BR\IYup]K˗nLa!2cpab9a1ʔBkU4ȸF!'O+W.4hiiJ;Q[x߀~II jvZ-]^~eS;%??o&Fo8&T!iXjqչseBNޔ5GCL1&TPz-1qGI <&o|AZwɘi8),(,&rΜ,JT4+eJ@d̏&E±c#iƳk;nڵK籦N*Afb71cƨzR e"[[dqjqmSO=V̷Lwݒ~|N;rl5TBA~asm\0G>w,rˈfpc'GMENЧY_|A{Le2KHHy V< Uiu|)rISp56:79iЎsN6@L붨OR,|X.W,'%ӡ S&wWqs)F豓)eYuI1+ϰR\2!E:s7+G}q2`oݦn R;/}_~e]FTyo+= ]|VPBƠ='P=^x@T gO2]>'dP|+ܷ]R 5poS% ߐʌҹyT)ѩ$L`b5w KG^zw<yW`tq$xMUM 9fjBR b7-HKieSd,& 1벩?N`IADeƷ93~ceiU{1YJ?dٲeZ: X;0g(Q%HuնZܒSqX30Jc>=Ki!8<l2a^nT#wRL^x<ߞ _\:A.^1U&g88'?Cs1yB^.Va4 k5R9cv|ٲGP1@aHd`הA'LU%lb9q|t9nX8(ݰ'nug555׀94w6{;CI,ÀQ1*;7)W:MVa 'R5CbN4hp.hHR?Hd&)|Er걓%~4* %.iw4jp@(mإ/uwv_^*\{Nx. z@ eYhO!xhWZُMP-̔ϟ1]qVo/?r-!̏]d`X(!:1ªyխJd v*Sd c 3u)MSjzsq#ndjFW+!̮QJ1yÈ,\H1MW ˭_^.(l89 F9>i .|F=dQ*PFxm<^ʌsz%e#:)_fv@tMkl/sC w 2.cva.;7Kd4pM30וu7: Ƥ B ,1Q5B:`rK[&9cV\|,BTj@&H$訲2(Gmt\̞V\`++e ,V]u*cPZۥ 9KX10tnEʦN,-{tcs`1 jE31D@0g@Ff^P<~T/ mr l.&i FƁ PAm'i!`obPcI,2ðkp<9cf|st dA>'Rߤ|;ߕ . ֙5%yס CI txރH`W]VmYy0i~M? 3 8D"as!4[J7 1!:Z.=ax䦘Ą>%2a& *ccЧz]VnuJ?;qDu{v@C`^t̋Gd2TGlxyqKɉKO,'. ꬧g4.y2f}jy0)ݔ]"%qh  ^ֈ'w`J#J.]:IN;n^[ Ёb0|˅3cWJPdkvQs;Ԋn.a:l/B;;f΋7!Zb W2$2V|8GBR%'LS.܇8.EtG"S"Y4Bf,{z|Zy lN-b!%I䘊#6bQ%q&$QpV 23S ]Y6 QznÚ]"`܅rv9~bPQ n;W8D@Q"?v _Wo,#%kw5)r 9~sk3RtkȈž*0I2xSbԇ*7wseJFOa~̬#4}6AP,~q9_W^V`=n H{Lź4cUܷQՕ`xq8ܲV݄CMgSL8%sC :N*oנyڟ҆N9}^ k,aB(>66JDR9l5tv ͯ$*H7>-O-*dŌ 9Vj fI*wVG ِ9y9mCH`Ƶ%4t-[% AÜ&aa䥘ɘɌڏGX؅Ɏ͑p)65WI=ˤ+/i_Q]~6)bүЏVCtiM+v4Ś]YN&ƕM͠K=4zHy\&w8zx2~h6N:Ίq V<05]8f,%9[$hE@՘=M`|g .GƇ#ag`RwZ\XQ/3s%yr\쀟,]8R|T`sgBJ2GF<@1wH?9 dߔ، 5H|4`7\o% ܙ L*H\bV&'OM q-+܀hc-cgRwg(=PEx$Qk*Yac0oqdF=w#xS)YO8謷yG R([a#΁k 22$IV`Ij(*mw [vqΞ-{QAcxԀ;ߧBpMͷn[JtV㌮+v9y&$%dLIK4(! 44@9EŢ&8ǨzɇiżLY'K*^|zyY]hA3_p 2 V5Ԋ՛xO$c_^Iׂ—ʒN(sRt*fE2.]ġ]Hna>'طuي`/%7$cO[!{Wf>IC@۳ߦ}à/ߪNٺHޛ gϝ1'[N?&_fM̔gM~{{Zj`3!~GUÉ%Ͻ+t.f¨S8bCQ.,[]&˦Z- sP/dzAD&zE p b߄QviU쮬v@|Fڻ}*e-0Oӹ>(u$K3d^;sX?^FuvHb<FZc=_7f@t>|8Q7kh2.>.hu+ U^rF%ǎe Ʃuih3`@CJXw:O;XH86ma{i6KfA ;*, ļ4Iv=B!#t> L'biv:Σ˒%?^ , Med`? d7x̃ `sW$Ѿ zSڶJf_BuOĊ|8Y|ullxqɘk@.m}>!G4t٢}`m5D[܈Q(=THMl\fĺFVE؁(!݋zpPP+E5BQRq*)9r,L꧂p%f _؁kM·fl5?/^217f22M[e24;U=]RWy{do[,-2f,YW!`6uEuU1!mZ>(o#uIe̙jJTC2ETa*# 1P l $'*NOCi쑢zYR%uت lY 钛Ւ?GdUv4 w#sn;`BŮK;ecAF6WtJh31Yx` "bV|k'ŕͲMvJia~gˌI2>3^#91sQ=#{0ԿR~V;TɶYlICG: $=_Дo>|T=8 oE*Yy6i{`t2 r,FC鼙QFoUEnaz$/q,* e˔񩒓+VWnRk4"HΏzh W-0\ ?meupkuj[: I^ . c$3sdvI$9 $B64}䌩Io3n)ޛm-HAuCr[ f@86#AL)- MR* k~M]qD` 5 DB@AO-({ ':~kAxl"XP?`ӹ釞bR%İC#dG?}P 'S3VǓ!uF2QjY<`Vϋ~w; <v@|@3> bZɳܬECwC,]0R'hp24ʒqX9_# 螣Ͼ;dxw>" E_2ɔbS: YP5lL::d'Va/#5%MbG)؁%Xҹ_M-eΙ ]|2+/f,03Sc\RHG{}G'8oHlEDк}}UtLi܁q(8xwJ%n_Z1}CMu[s }>fP77H}siy;dcyl^%ƥIvrtrߺw?i7@ZI7.3+ +>YJj$>'3pR\yVd$JBIKt4 O? \lWQa֑_z }rUBu?al6=<@BZ;6X~$H1sd0,i0<`:/a۽`UXQuCwl‚Vփ=eU(MMdX!2hl=P-'|$ΘuJ>F $邼'6߈|x hA0s"}n?i]PoY#vJQm'tlpBƢyc$ 'Sf`\c T,.IT N;O` |2#eS2k$P]j1,[*[Kr1o6V1g6 /j'] (/@vʩkUw#*ϴԛ<.0e(kj"8n0F4¤bls1>IO)LQc fu{~k#OfV>*e+ÒT$u0"IdSsJl3rv29Of(*yBX?^~YQW|3lSO+5-LJKGT7J+bWTn \sZ| }3@b - NkrsG;@%.OS U|xOeA"%5`f% āQ+ 9 #)%QnCYZ+~m·#T5`4wx4Qύ`-Uf4m#jf}z@;{:P`CXX$Ea~$$xl %$K^.s}{١NPZ|ݴHDTS l!PE5f̟i:9@Nk"- N+Ε cS )JR|DF#1j@uʴqSFV74P~:A-Uw-4Z殘ճ\3* \Jk:T*,.`c&l+3eִ3Fa~4HX(XIHŒ a x l0 v3/?kنx<4@9:jlE`h% ^z]i]JbLb+q7,c3cU ~Ғs~d +e(+0՛)N& 9~N] .&k\D_Nf-V7qU=U]ST;{T5a K-"/4A&fcN)URc"*"kT#zݟ e]Ju-FyGƽLǓHgBKK|X2c$5)A aDE"(pA+.9׬Gݵ);a4Oe8 1N0zHMR %ػI7ϭknX`xe$Y$K>6X,-3U` L֌@c{# (Oo^r#N];;A6cZHiU?QJq$03qHbvJ$Zdŀ0!QKMu;I.00du-uKUC/yyT7wx5+OOzuL L e&;5I@c*j1FnAЃ8+ MU]LARrLUSfb D*7nmZ:K=I0 vшMhy

5T&&p 30 ֪{`7?>ȏ7T,yDD&EȘG$P;4H aF"_^U$&kGH4HG48";hL~qݏїn< AmP3HEo1oz?:ڪ9dħphycƱё`@Azȴ 'b>#zcP *v HI&AID  t/Sv9@y y{1ޅ՟ ̀?1Ս=@|z$y&FHPCHƔ3S" nZΝC8d庒I zqp ,#G#b Q䍇ZE K'ғpLLzv,m4KC^كMU}`qJtö6iN8 x |yƁ <Q$%703Jn\HfF+U~0޽# E\RPX6نLp{{0@ڇ8vd HSY,&ŁBa]lj1jAGֵ#:ၗStۑ,|XJby3qd@#suJ5vvwK'F lciD ;aX*E>$T>2< sP6@:2h0C3@h1&Q)Ĉ#$F$$0H<P3U VCb.<>%:*F3ҸX!̌mc7 NND\G< LxWd0 iP[{Jt:nnCeA_GaL).ZNLGJTݕXNs>2^WapcV~CS: ='H&@gl"} Ёb7h,q|1Cυ/_;᜛yw%I?"8p} C?vAi5 )?AJvKsU5"`;Uݿp?@P`2a}^4@V' .! dL>Apu?\q @N3u33{ `>R;Y{ q8F`8**Frr%2.jBBïy >Zp;R#q>XLf&~ٺ3}[c92- 1n΃9}LrD獓6k/z־ nz- KM{}h}cIO[`=氄ЂB?ք=i/Q0䈈<];-`lPϛW}#ΰey @#Eʐ؊Й. D=!  A!E鋎r(KLq㤹R:QqNqqn̓leAc`èz8'$C'9}O^T\yndd//>jAB"#qA(\Z!]&7[A@ A Br`99vE=!_"9Q+E:`pA`BoU~ z AP!`zQȧnilh&4JgFMǼ^ ]{{80ҔJ``ϷW["FbqstD@*S!s-< <6P p!@Eѓ_n(W+ރ'><x p Y!-{z A'g% ppPc/fڱ_<7 Wvm9KPlc+w#7&9)W&Cc`Cw¬n$sN. ~Ҏy(s޽{ԠG"##`$wC ᯭMv)EEEd`1̙ _\qo20xue߇z-cjok7JuuVwߕRW[å=CރQ? 2%ڴq|K_rW+4=o߾o/UM G\Ӻmރݖg5>Ngf:n0׆[Vp[MgܴKv% 46oOl6AY.ۼa6.g|:u5R[XX6bN%66Vc,90#>OCĩ;vDǁ6?~ 1#[/C ׬YT"HMM+nTf%WJ{=$oPe}M վɧ>)e^d.=WiF㨜̐oWf =_R@Fpw',dowSE206}z%a$7|xr 7(<(xr(KbR22JnSbN2ye`o~zYdOkY{և—^zI?)83/ˋ/(7tSf [$;;[ U^^ROɔR3ϐ6MaG,ϗ9s΄pyRUxE)nd a_ڟ '|R,^2': "կ~Q^{L5ˑ9Gx\bO@$a/_Z_>} *嫮ay1:5O>0_gGK֮]q~i… } 2.q;$McaA"8*}f|+_TF>0$c|>HmZHc;\[q w߭~`8 /gRG`|Z>H>6.X&!N?t}ݧ`ԴTӂi4|P4R|i_AG0 $#T֖Vyǵpǝw U d}\կjz _W\ ``A`_z0d{|Yp#~̙3G`>0P6_}l>z/2͓8vO<7?pd~op,[LG f*0R |+_dU5qMu$=#]UL̋:UZ¦ vh"UG1SUgyVUG0+a `1ch'Tg1~nNe~sϺ3.N;M^,jPƧn=| K.U7**:Jr'pi>E*5J[?Յ^\Ŭ4>PRv8mƧ]Xɾζ†auUWI\|J~FJ.F*ɓ'kh h~gp$Ji8t>lUR"$yP/U{?ӹGIst)XKHK~(wx*AWGADTs;2H$<  *8HHHX1V"0n}%u3qDUW 0O "8 ?1窨bt?BbN?G1OQDg6qDDu\ դtLɜ97E8ѱ<n6 ~τ' i4u!# yntTcZxu.I|^'dfl915heZXяqX&gNc^L~gkm(pNy`x20IgOV[S7F`|KϦd])5XB|mYwo;㰮td1e:􋉎rȬ8F\21Hnc:+zNQz&hgkܟєh`aC0#KkH˲*c-gӑ̸@)EYg25:Z[̏j`% ȃɩjFk=`]hS7ںQ:&yTjb:JO#%(2)c RF+d<$|Tw>up=y#_W> #tm09._Svyꩧ4 UG@˕ّ<#r}+qeg?Yedr#oZ1 Gvq.Re$$7X+b ]T8t̋Ø@8vхi|)Բl\nˀ,D֗r\|J·zH9>3sގ&vNimb4`묳rg\b@FEEF8^z~(wNIENDB`bpfcc-0.12.0/src/cc/frontends/p4/test/000077500000000000000000000000001357404205000173275ustar00rootroot00000000000000bpfcc-0.12.0/src/cc/frontends/p4/test/README.txt000066400000000000000000000012101357404205000210170ustar00rootroot00000000000000This folder contains tests for the P4->C->EBPF compiler - cleanup.sh should be run if for some reason endToEndTest.py crashes and leaves garbage namespaces or links - testP4toEbpf.py compiles all P4 files in the testprograms folder and deposits the corresponding C files in the testoutputs folder - endToEndTest.py runs a complete end-to-end test compiling the testprograms/simple.p4 program, creating a virtual network with 3 boxes (using network namespaces): client, server, switch, loading the EBPF into the kernel of the switch box using the TC, and implementing the forwarding in the switch solely using the P4 program. bpfcc-0.12.0/src/cc/frontends/p4/test/cleanup.sh000077500000000000000000000003301357404205000213110ustar00rootroot00000000000000#!/bin/bash # Run this script if for some reason the endToEndTest.py crashed # and left some garbage state ip netns del sw ip netns del srv ip netns del clt ip link del dev veth-clt-sw ip link del dev veth-srv-sw bpfcc-0.12.0/src/cc/frontends/p4/test/endToEndTest.py000077500000000000000000000305271357404205000222530ustar00rootroot00000000000000#!/usr/bin/env python3 # Copyright (c) Barefoot Networks, Inc. # Licensed under the Apache License, Version 2.0 (the "License") # Testing example for P4->EBPF compiler # # This program exercises the simple.c EBPF program # generated from the simple.p4 source file. from __future__ import print_function import subprocess import ctypes import time import sys import os from bcc import BPF from pyroute2 import IPRoute, NSPopen, NetNS from netaddr import IPAddress ### This part is a simple generic network simulaton toolkit class Base(object): def __init__(self): self.verbose = True def message(self, *args): if self.verbose: print(*args) class Endpoint(Base): # a network interface really def __init__(self, ipaddress, ethaddress): Base.__init__(self) self.mac_addr = ethaddress self.ipaddress = ipaddress self.prefixlen = 24 self.parent = None def __str__(self): return "Endpoint " + str(self.ipaddress) def set_parent(self, parent): assert isinstance(parent, Node) self.parent = parent def get_ip_address(self): return IPAddress(self.ipaddress) class Node(Base): # Used to represent one of clt, sw, srv # Each lives in its own namespace def __init__(self, name): Base.__init__(self) self.name = name self.endpoints = [] self.get_ns() # as a side-effect creates namespace def add_endpoint(self, endpoint): assert isinstance(endpoint, Endpoint) self.endpoints.append(endpoint) endpoint.set_parent(self) def __str__(self): return "Node " + self.name def get_ns_name(self): return self.name def get_ns(self): nsname = self.get_ns_name() ns = NetNS(nsname) return ns def remove(self): ns = self.get_ns(); ns.close() ns.remove() def execute(self, command): # Run a command in the node's namespace # Return the command's exit code self.message(self.name, "Executing", command) nsn = self.get_ns_name() pipe = NSPopen(nsn, command) result = pipe.wait() pipe.release() return result def set_arp(self, destination): assert isinstance(destination, Endpoint) command = ["arp", "-s", str(destination.ipaddress), str(destination.mac_addr)] self.execute(command) class NetworkBase(Base): def __init__(self): Base.__init__(self) self.ipr = IPRoute() self.nodes = [] def add_node(self, node): assert isinstance(node, Node) self.nodes.append(node) def get_interface_name(self, source, dest): assert isinstance(source, Node) assert isinstance(dest, Node) interface_name = "veth-" + source.name + "-" + dest.name return interface_name def get_interface(self, ifname): interfaces = self.ipr.link_lookup(ifname=ifname) if len(interfaces) != 1: raise Exception("Could not identify interface " + ifname) ix = interfaces[0] assert isinstance(ix, int) return ix def set_interface_ipaddress(self, node, ifname, address, mask): # Ask a node to set the specified interface address if address is None: return assert isinstance(node, Node) command = ["ip", "addr", "add", str(address) + "/" + str(mask), "dev", str(ifname)] result = node.execute(command) assert(result == 0) def create_link(self, src, dest): assert isinstance(src, Endpoint) assert isinstance(dest, Endpoint) ifname = self.get_interface_name(src.parent, dest.parent) destname = self.get_interface_name(dest.parent, src.parent) self.ipr.link_create(ifname=ifname, kind="veth", peer=destname) self.message("Create", ifname, "link") # Set source endpoint information ix = self.get_interface(ifname) self.ipr.link("set", index=ix, address=src.mac_addr) # push source endpoint into source namespace self.ipr.link("set", index=ix, net_ns_fd=src.parent.get_ns_name(), state="up") # Set interface ip address; seems to be # lost of set prior to moving to namespace self.set_interface_ipaddress( src.parent, ifname, src.ipaddress , src.prefixlen) # Sef destination endpoint information ix = self.get_interface(destname) self.ipr.link("set", index=ix, address=dest.mac_addr) # push destination endpoint into the destination namespace self.ipr.link("set", index=ix, net_ns_fd=dest.parent.get_ns_name(), state="up") # Set interface ip address self.set_interface_ipaddress(dest.parent, destname, dest.ipaddress, dest.prefixlen) def show_interfaces(self, node): cmd = ["ip", "addr"] if node is None: # Run with no namespace subprocess.call(cmd) else: # Run in node's namespace assert isinstance(node, Node) self.message("Enumerating all interfaces in ", node.name) node.execute(cmd) def delete(self): self.message("Deleting virtual network") for n in self.nodes: n.remove() self.ipr.close() ### Here begins the concrete instantiation of the network # Network setup: # Each of these is a separate namespace. # # 62:ce:1b:48:3e:61 a2:59:94:cf:51:09 # 96:a4:85:fe:2a:11 62:ce:1b:48:3e:60 # /------------------\ /-----------------\ # ---------- -------- --------- # | clt | | sw | | srv | # ---------- -------- --------- # 10.0.0.11 10.0.0.10 # class SimulatedNetwork(NetworkBase): def __init__(self): NetworkBase.__init__(self) self.client = Node("clt") self.add_node(self.client) self.client_endpoint = Endpoint("10.0.0.11", "96:a4:85:fe:2a:11") self.client.add_endpoint(self.client_endpoint) self.server = Node("srv") self.add_node(self.server) self.server_endpoint = Endpoint("10.0.0.10", "a2:59:94:cf:51:09") self.server.add_endpoint(self.server_endpoint) self.switch = Node("sw") self.add_node(self.switch) self.sw_clt_endpoint = Endpoint(None, "62:ce:1b:48:3e:61") self.sw_srv_endpoint = Endpoint(None, "62:ce:1b:48:3e:60") self.switch.add_endpoint(self.sw_clt_endpoint) self.switch.add_endpoint(self.sw_srv_endpoint) def run_method_in_node(self, node, method, args): # run a method of the SimulatedNetwork class in a different namespace # return the exit code assert isinstance(node, Node) assert isinstance(args, list) torun = __file__ args.insert(0, torun) args.insert(1, method) return node.execute(args) # runs the command argv[0] method args def instantiate(self): # Creates the various namespaces self.message("Creating virtual network") self.message("Create client-switch link") self.create_link(self.client_endpoint, self.sw_clt_endpoint) self.message("Create server-switch link") self.create_link(self.server_endpoint, self.sw_srv_endpoint) self.show_interfaces(self.client) self.show_interfaces(self.server) self.show_interfaces(self.switch) self.message("Set ARP mappings") self.client.set_arp(self.server_endpoint) self.server.set_arp(self.client_endpoint) def setup_switch(self): # This method is run in the switch namespace. self.message("Compiling and loading BPF program") b = BPF(src_file="./simple.c", debug=0) fn = b.load_func("ebpf_filter", BPF.SCHED_CLS) self.message("BPF program loaded") self.message("Discovering tables") routing_tbl = b.get_table("routing") routing_miss_tbl = b.get_table("ebpf_routing_miss") cnt_tbl = b.get_table("cnt") self.message("Hooking up BPF classifiers using TC") interfname = self.get_interface_name(self.switch, self.server) sw_srv_idx = self.get_interface(interfname) self.ipr.tc("add", "ingress", sw_srv_idx, "ffff:") self.ipr.tc("add-filter", "bpf", sw_srv_idx, ":1", fd=fn.fd, name=fn.name, parent="ffff:", action="ok", classid=1) interfname = self.get_interface_name(self.switch, self.client) sw_clt_idx = self.get_interface(interfname) self.ipr.tc("add", "ingress", sw_clt_idx, "ffff:") self.ipr.tc("add-filter", "bpf", sw_clt_idx, ":1", fd=fn.fd, name=fn.name, parent="ffff:", action="ok", classid=1) self.message("Populating tables from the control plane") cltip = self.client_endpoint.get_ip_address() srvip = self.server_endpoint.get_ip_address() # BCC does not support tbl.Leaf when the type contains a union, # so we have to make up the value type manually. Unfortunately # these sizes are not portable... class Forward(ctypes.Structure): _fields_ = [("port", ctypes.c_ushort)] class Nop(ctypes.Structure): _fields_ = [] class Union(ctypes.Union): _fields_ = [("nop", Nop), ("forward", Forward)] class Value(ctypes.Structure): _fields_ = [("action", ctypes.c_uint), ("u", Union)] if False: # This is how it should ideally be done, but it does not work routing_tbl[routing_tbl.Key(int(cltip))] = routing_tbl.Leaf( 1, sw_clt_idx) routing_tbl[routing_tbl.Key(int(srvip))] = routing_tbl.Leaf( 1, sw_srv_idx) else: v1 = Value() v1.action = 1 v1.u.forward.port = sw_clt_idx v2 = Value() v2.action = 1; v2.u.forward.port = sw_srv_idx routing_tbl[routing_tbl.Key(int(cltip))] = v1 routing_tbl[routing_tbl.Key(int(srvip))] = v2 self.message("Dumping table contents") for key, leaf in routing_tbl.items(): self.message(str(IPAddress(key.key_field_0)), leaf.action, leaf.u.forward.port) def run(self): self.message("Pinging server from client") ping = ["ping", self.server_endpoint.ipaddress, "-c", "2"] result = self.client.execute(ping) if result != 0: raise Exception("Test failed") else: print("Test succeeded!") def prepare_switch(self): self.message("Configuring switch") # Re-invokes this script in the switch namespace; # this causes the setup_switch method to be run in that context. # This is the same as running self.setup_switch() # but in the switch namespace self.run_method_in_node(self.switch, "setup_switch", []) def compile(source, destination): try: status = subprocess.call( "../compiler/p4toEbpf.py " + source + " -o " + destination, shell=True) if status < 0: print("Child was terminated by signal", -status, file=sys.stderr) else: print("Child returned", status, file=sys.stderr) except OSError as e: print("Execution failed:", e, file=sys.stderr) raise e def start_simulation(): compile("testprograms/simple.p4", "simple.c") network = SimulatedNetwork() network.instantiate() network.prepare_switch() network.run() network.delete() os.remove("simple.c") def main(argv): print(str(argv)) if len(argv) == 1: # Main entry point: start simulation start_simulation() else: # We are invoked with some arguments (probably in a different namespace) # First argument is a method name, rest are method arguments. # Create a SimulatedNetwork and invoke the specified method with the # specified arguments. network = SimulatedNetwork() methodname = argv[1] arguments = argv[2:] method = getattr(network, methodname) method(*arguments) if __name__ == '__main__': main(sys.argv) bpfcc-0.12.0/src/cc/frontends/p4/test/testP4toEbpf.py000077500000000000000000000046331357404205000222350ustar00rootroot00000000000000#!/usr/bin/env python # Copyright (c) Barefoot Networks, Inc. # Licensed under the Apache License, Version 2.0 (the "License") # Runs the compiler on all files in the 'testprograms' folder # Writes outputs in the 'testoutputs' folder from __future__ import print_function from bcc import BPF import os, sys sys.path.append("../compiler") # To get hold of p4toEbpf # We want to run it without installing it import p4toEbpf import os def drop_extension(filename): return os.path.splitext(os.path.basename(filename))[0] filesFailed = {} # map error kind -> list[ (file, error) ] def set_error(kind, file, error): if kind in filesFailed: filesFailed[kind].append((file, error)) else: filesFailed[kind] = [(file, error)] def is_root(): # Is this code portable? return os.getuid() == 0 def main(): testpath = "testprograms" destFolder = "testoutputs" files = os.listdir(testpath) files.sort() filesDone = 0 errors = 0 if not is_root(): print("Loading EBPF programs requires root privilege.") print("Will only test compilation, not loading.") print("(Run with sudo to test program loading.)") for f in files: path = os.path.join(testpath, f) if not os.path.isfile(path): continue if not path.endswith(".p4"): continue destname = drop_extension(path) + ".c" destname = os.path.join(destFolder, destname) args = [path, "-o", destname] result = p4toEbpf.process(args) if result.kind != "OK": errors += 1 print(path, result.error) set_error(result.kind, path, result.error) else: # Try to load the compiled function if is_root(): try: print("Compiling and loading BPF program") b = BPF(src_file=destname, debug=0) fn = b.load_func("ebpf_filter", BPF.SCHED_CLS) except Exception as e: print(e) set_error("BPF error", path, str(e)) filesDone += 1 print("Compiled", filesDone, "files", errors, "errors") for key in sorted(filesFailed): print(key, ":", len(filesFailed[key]), "programs") for v in filesFailed[key]: print("\t", v) exit(len(filesFailed) != 0) if __name__ == "__main__": main() bpfcc-0.12.0/src/cc/frontends/p4/test/testoutputs/000077500000000000000000000000001357404205000217525ustar00rootroot00000000000000bpfcc-0.12.0/src/cc/frontends/p4/test/testoutputs/.empty000066400000000000000000000000001357404205000230770ustar00rootroot00000000000000bpfcc-0.12.0/src/cc/frontends/p4/test/testprograms/000077500000000000000000000000001357404205000220615ustar00rootroot00000000000000bpfcc-0.12.0/src/cc/frontends/p4/test/testprograms/arrayKey.p4000066400000000000000000000006411357404205000241160ustar00rootroot00000000000000header_type ethernet_t { fields { dstAddr : 48; srcAddr : 48; etherType : 16; } } parser start { return parse_ethernet; } header ethernet_t ethernet; parser parse_ethernet { extract(ethernet); return ingress; } action nop() {} table routing { reads { ethernet.dstAddr: exact; } actions { nop; } size : 512; } control ingress { apply(routing); }bpfcc-0.12.0/src/cc/frontends/p4/test/testprograms/basic_routing.p4000066400000000000000000000114121357404205000251550ustar00rootroot00000000000000/* Copyright 2013-present Barefoot Networks, Inc. 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. */ header_type ethernet_t { fields { dstAddr : 48; srcAddr : 48; etherType : 16; } } header_type ipv4_t { fields { version : 4; ihl : 4; diffserv : 8; totalLen : 16; identification : 16; flags : 3; fragOffset : 13; ttl : 8; protocol : 8; hdrChecksum : 16; srcAddr : 32; dstAddr: 32; } } parser start { return parse_ethernet; } #define ETHERTYPE_IPV4 0x0800 header ethernet_t ethernet; parser parse_ethernet { extract(ethernet); return select(latest.etherType) { ETHERTYPE_IPV4 : parse_ipv4; default: ingress; } } header ipv4_t ipv4; /* Not yet supported on EBPF target field_list ipv4_checksum_list { ipv4.version; ipv4.ihl; ipv4.diffserv; ipv4.totalLen; ipv4.identification; ipv4.flags; ipv4.fragOffset; ipv4.ttl; ipv4.protocol; ipv4.srcAddr; ipv4.dstAddr; } field_list_calculation ipv4_checksum { input { ipv4_checksum_list; } algorithm : csum16; output_width : 16; } calculated_field ipv4.hdrChecksum { verify ipv4_checksum; update ipv4_checksum; } */ parser parse_ipv4 { extract(ipv4); return ingress; } #define PORT_VLAN_TABLE_SIZE 32768 #define BD_TABLE_SIZE 65536 #define IPV4_LPM_TABLE_SIZE 16384 #define IPV4_HOST_TABLE_SIZE 131072 #define NEXTHOP_TABLE_SIZE 32768 #define REWRITE_MAC_TABLE_SIZE 32768 #define VRF_BIT_WIDTH 12 #define BD_BIT_WIDTH 16 #define IFINDEX_BIT_WIDTH 10 /* METADATA */ header_type ingress_metadata_t { fields { vrf : VRF_BIT_WIDTH; /* VRF */ bd : BD_BIT_WIDTH; /* ingress BD */ nexthop_index : 16; /* final next hop index */ } } metadata ingress_metadata_t ingress_metadata; action on_miss() { } action set_bd(bd) { modify_field(ingress_metadata.bd, bd); } table port_mapping { reads { standard_metadata.ingress_port : exact; } actions { set_bd; } size : PORT_VLAN_TABLE_SIZE; } action set_vrf(vrf) { modify_field(ingress_metadata.vrf, vrf); } table bd { reads { ingress_metadata.bd : exact; } actions { set_vrf; } size : BD_TABLE_SIZE; } action fib_hit_nexthop(nexthop_index) { modify_field(ingress_metadata.nexthop_index, nexthop_index); subtract_from_field(ipv4.ttl, 1); } table ipv4_fib { reads { ingress_metadata.vrf : exact; ipv4.dstAddr : exact; } actions { on_miss; fib_hit_nexthop; } size : IPV4_HOST_TABLE_SIZE; } table ipv4_fib_lpm { reads { ingress_metadata.vrf : exact; ipv4.dstAddr : exact; // lpm not supported } actions { on_miss; fib_hit_nexthop; } size : IPV4_LPM_TABLE_SIZE; } action set_egress_details(egress_spec) { modify_field(standard_metadata.egress_spec, egress_spec); } table nexthop { reads { ingress_metadata.nexthop_index : exact; } actions { on_miss; set_egress_details; } size : NEXTHOP_TABLE_SIZE; } control ingress { if (valid(ipv4)) { /* derive ingress_metadata.bd */ apply(port_mapping); /* derive ingress_metadata.vrf */ apply(bd); /* fib lookup, set ingress_metadata.nexthop_index */ apply(ipv4_fib) { on_miss { apply(ipv4_fib_lpm); } } /* derive standard_metadata.egress_spec from ingress_metadata.nexthop_index */ apply(nexthop); } } action rewrite_src_dst_mac(smac, dmac) { modify_field(ethernet.srcAddr, smac); modify_field(ethernet.dstAddr, dmac); } table rewrite_mac { reads { ingress_metadata.nexthop_index : exact; } actions { on_miss; rewrite_src_dst_mac; } size : REWRITE_MAC_TABLE_SIZE; } control egress { /* set smac and dmac from ingress_metadata.nexthop_index */ apply(rewrite_mac); }bpfcc-0.12.0/src/cc/frontends/p4/test/testprograms/bitfields.p4000066400000000000000000000013621357404205000242750ustar00rootroot00000000000000header_type ht { fields { f1 : 1; f2 : 2; f3 : 3; f4 : 4; f5 : 5; f6 : 6; f7 : 7; f8 : 8; f9 : 9; f10 : 10; f11 : 11; f12 : 12; f13 : 13; f14 : 14; f15 : 15; f16 : 16; f17 : 17; f18 : 18; f19 : 19; f20 : 20; f21 : 21; f22 : 22; f23 : 23; f24 : 24; f25 : 25; f26 : 26; f27 : 27; f28 : 28; f29 : 29; f30 : 30; f31 : 31; f32 : 32; } } header_type larget { fields { f48 : 48; f1: 1; f49 : 48; f2 : 1; f64 : 64; f3 : 1; f128 : 128; } } header ht h; header larget large; parser start { extract(h); extract(large); return ingress; } control ingress { } bpfcc-0.12.0/src/cc/frontends/p4/test/testprograms/compositeArray.p4000066400000000000000000000010451357404205000253270ustar00rootroot00000000000000header_type ethernet_t { fields { dstAddr : 48; } } header_type ipv4_t { fields { srcAddr : 32; } } parser start { return parse_ethernet; } header ethernet_t ethernet; parser parse_ethernet { extract(ethernet); return parse_ipv4; } action nop() {} header ipv4_t ipv4; parser parse_ipv4 { extract(ipv4); return ingress; } table routing { reads { ethernet.dstAddr: exact; ipv4.srcAddr: exact; } actions { nop; } size : 512; } control ingress { apply(routing); }bpfcc-0.12.0/src/cc/frontends/p4/test/testprograms/compositeKey.p4000066400000000000000000000020321357404205000247760ustar00rootroot00000000000000header_type ethernet_t { fields { dstAddr : 48; srcAddr : 48; etherType : 16; } } header_type ipv4_t { fields { version : 4; ihl : 4; diffserv : 8; totalLen : 16; identification : 16; flags : 3; fragOffset : 13; ttl : 8; protocol : 8; hdrChecksum : 16; srcAddr : 32; dstAddr: 32; } } parser start { return parse_ethernet; } header ethernet_t ethernet; parser parse_ethernet { extract(ethernet); return select(latest.etherType) { 0x800 : parse_ipv4; default: ingress; } } action nop() {} action forward(port) { modify_field(standard_metadata.egress_port, port); } header ipv4_t ipv4; parser parse_ipv4 { extract(ipv4); return ingress; } table routing { reads { ipv4.dstAddr: exact; ipv4.srcAddr: exact; } actions { nop; forward; } size : 512; } counter cnt { type: bytes; direct: routing; } control ingress { apply(routing); }bpfcc-0.12.0/src/cc/frontends/p4/test/testprograms/do_nothing.p4000066400000000000000000000007151357404205000244610ustar00rootroot00000000000000/* Sample P4 program */ header_type ethernet_t { fields { dstAddr : 48; srcAddr : 48; etherType : 16; } } parser start { return parse_ethernet; } header ethernet_t ethernet; parser parse_ethernet { extract(ethernet); return ingress; } action action_0(){ no_op(); } table table_0 { reads { ethernet.etherType : exact; } actions { action_0; } } control ingress { apply(table_0); } bpfcc-0.12.0/src/cc/frontends/p4/test/testprograms/simple.p4000066400000000000000000000021611357404205000236170ustar00rootroot00000000000000// Routes a packet to an interface based on its IPv4 address // Maintains a set of counters on the routing table header_type ethernet_t { fields { dstAddr : 48; srcAddr : 48; etherType : 16; } } header_type ipv4_t { fields { version : 4; ihl : 4; diffserv : 8; totalLen : 16; identification : 16; flags : 3; fragOffset : 13; ttl : 8; protocol : 8; hdrChecksum : 16; srcAddr : 32; dstAddr: 32; } } parser start { return parse_ethernet; } header ethernet_t ethernet; parser parse_ethernet { extract(ethernet); return select(latest.etherType) { 0x800 : parse_ipv4; default: ingress; } } action nop() {} action forward(port) { modify_field(standard_metadata.egress_port, port); } header ipv4_t ipv4; parser parse_ipv4 { extract(ipv4); return ingress; } table routing { reads { ipv4.dstAddr: exact; } actions { nop; forward; } size : 512; } counter cnt { type: bytes; direct: routing; } control ingress { apply(routing); }bpfcc-0.12.0/src/cc/json_map_decl_visitor.cc000066400000000000000000000133621357404205000207130ustar00rootroot00000000000000/* * Copyright (c) 2017 VMware, Inc. * * 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. */ #include #include #include #include #include #include "common.h" #include "table_desc.h" namespace ebpf { using std::string; using std::to_string; using std::unique_ptr; using namespace clang; // Helper visitor for constructing a string representation of a key/leaf decl class BMapDeclVisitor : public clang::RecursiveASTVisitor { public: explicit BMapDeclVisitor(clang::ASTContext &C, std::string &result); bool TraverseRecordDecl(clang::RecordDecl *Decl); bool VisitRecordDecl(clang::RecordDecl *Decl); bool VisitFieldDecl(clang::FieldDecl *Decl); bool VisitBuiltinType(const clang::BuiltinType *T); bool VisitTypedefType(const clang::TypedefType *T); bool VisitTagType(const clang::TagType *T); bool VisitPointerType(const clang::PointerType *T); bool VisitEnumDecl(clang::EnumDecl *D); bool VisitAttr(clang::Attr *A); private: bool shouldSkipPadding(const RecordDecl *D); void genJSONForField(FieldDecl *F); private: clang::ASTContext &C; std::string &result_; }; // Encode the struct layout as a json description BMapDeclVisitor::BMapDeclVisitor(ASTContext &C, string &result) : C(C), result_(result) {} bool BMapDeclVisitor::shouldSkipPadding(const RecordDecl *D) { if (D->isUnion() || D->field_empty()) return true; for (auto F : D->getDefinition()->fields()) { if (F->isBitField()) return true; QualType Ty = F->getType(); if (Ty->isIncompleteArrayType()) return true; } return false; } void BMapDeclVisitor::genJSONForField(FieldDecl *F) { if (F->isAnonymousStructOrUnion()) { if (const RecordType *R = dyn_cast(F->getType())) TraverseDecl(R->getDecl()); result_ += ", "; return; } result_ += "["; TraverseDecl(F); if (const ConstantArrayType *T = dyn_cast(F->getType())) result_ += ", [" + T->getSize().toString(10, false) + "]"; if (F->isBitField()) result_ += ", " + to_string(F->getBitWidthValue(C)); result_ += "], "; } bool BMapDeclVisitor::VisitFieldDecl(FieldDecl *D) { result_ += "\""; result_ += D->getName(); result_ += "\","; return true; } bool BMapDeclVisitor::VisitEnumDecl(EnumDecl *D) { TraverseType(D->getIntegerType()); return false; } bool BMapDeclVisitor::TraverseRecordDecl(RecordDecl *D) { // skip children, handled in Visit... if (!WalkUpFromRecordDecl(D)) return false; return true; } bool BMapDeclVisitor::VisitRecordDecl(RecordDecl *D) { result_ += "[\""; result_ += D->getName(); result_ += "\", ["; bool SkipPadding = shouldSkipPadding(D); if (SkipPadding) { for (auto F : D->getDefinition()->fields()) { genJSONForField(F); } } else { const ASTRecordLayout &Layout = C.getASTRecordLayout(D); CharUnits Offset = C.toCharUnitsFromBits(Layout.getFieldOffset(0)); for (auto F : D->getDefinition()->fields()) { CharUnits FieldSize = C.getTypeSizeInChars(F->getType()); auto FieldOffsetBits = Layout.getFieldOffset(F->getFieldIndex()); CharUnits FieldOffset = C.toCharUnitsFromBits(FieldOffsetBits); uint64_t Padding = (FieldOffset - Offset).getQuantity(); if (Padding) { /* Padding before this field with "char __pad_[Padding]". */ result_ += "[\"__pad_" + to_string(F->getFieldIndex()) + "\",\"char\",[" + to_string(Padding) + "]], "; } Offset = FieldOffset + FieldSize; genJSONForField(F); } /* Additional Padding after the last field so that the Record Size matches */ CharUnits RecordSize = Layout.getSize(); if (RecordSize > Offset) { result_ += "[\"__pad_end\",\"char\",[" + to_string((RecordSize - Offset).getQuantity()) + "]], "; } } if (!D->getDefinition()->field_empty()) result_.erase(result_.end() - 2); result_ += "]"; if (D->isUnion()) result_ += ", \"union\""; else if (D->isStruct()) { if (SkipPadding) result_ += ", \"struct\""; else result_ += ", \"struct_packed\""; } result_ += "]"; return true; } // pointer to anything should be treated as terminal, don't recurse further bool BMapDeclVisitor::VisitPointerType(const PointerType *T) { result_ += "\"unsigned long long\""; return false; } bool BMapDeclVisitor::VisitTagType(const TagType *T) { return TraverseDecl(T->getDecl()->getDefinition()); } bool BMapDeclVisitor::VisitTypedefType(const TypedefType *T) { return TraverseDecl(T->getDecl()); } bool BMapDeclVisitor::VisitBuiltinType(const BuiltinType *T) { result_ += "\""; result_ += T->getName(C.getPrintingPolicy()); result_ += "\""; return true; } bool BMapDeclVisitor::VisitAttr(clang::Attr *A) { return false; } class JsonMapTypesVisitor : public virtual MapTypesVisitor { public: virtual void Visit(TableDesc &desc, clang::ASTContext &C, clang::QualType key_type, clang::QualType leaf_type) { BMapDeclVisitor v1(C, desc.key_desc), v2(C, desc.leaf_desc); v1.TraverseType(key_type); v2.TraverseType(leaf_type); } }; unique_ptr createJsonMapTypesVisitor() { return make_unique(); } } // namespace ebpf bpfcc-0.12.0/src/cc/libbcc.pc.in000066400000000000000000000004671357404205000162010ustar00rootroot00000000000000prefix=@CMAKE_INSTALL_PREFIX@ exec_prefix=${prefix} libdir=${exec_prefix}/lib includedir=${prefix}/include datarootdir=${prefix}/share compatdir=${includedir}/bcc/compat Name: libbcc Version: @REVISION@ Description: BCC Program library Requires: Libs: -L${libdir} -lbcc Cflags: -I${includedir} -I${compatdir} bpfcc-0.12.0/src/cc/libbpf/000077500000000000000000000000001357404205000152615ustar00rootroot00000000000000bpfcc-0.12.0/src/cc/libbpf.c000066400000000000000000001215241357404205000154320ustar00rootroot00000000000000/* * Copyright (c) 2015 PLUMgrid, Inc. * * 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. */ #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "libbpf.h" #include "perf_reader.h" // TODO: Remove this when CentOS 6 support is not needed anymore #include "setns.h" #include "libbpf/src/bpf.h" #include "libbpf/src/libbpf.h" // TODO: remove these defines when linux-libc-dev exports them properly #ifndef __NR_bpf #if defined(__powerpc64__) #define __NR_bpf 361 #elif defined(__s390x__) #define __NR_bpf 351 #elif defined(__aarch64__) #define __NR_bpf 280 #else #define __NR_bpf 321 #endif #endif #ifndef SO_ATTACH_BPF #define SO_ATTACH_BPF 50 #endif #ifndef PERF_EVENT_IOC_SET_BPF #define PERF_EVENT_IOC_SET_BPF _IOW('$', 8, __u32) #endif #ifndef PERF_FLAG_FD_CLOEXEC #define PERF_FLAG_FD_CLOEXEC (1UL << 3) #endif // TODO: Remove this when CentOS 6 support is not needed anymore #ifndef AF_ALG #define AF_ALG 38 #endif #ifndef min #define min(x, y) ((x) < (y) ? (x) : (y)) #endif #define UNUSED(expr) do { (void)(expr); } while (0) struct bpf_helper { char *name; char *required_version; }; static struct bpf_helper helpers[] = { {"map_lookup_elem", "3.19"}, {"map_update_elem", "3.19"}, {"map_delete_elem", "3.19"}, {"probe_read", "4.1"}, {"ktime_get_ns", "4.1"}, {"trace_printk", "4.1"}, {"get_prandom_u32", "4.1"}, {"get_smp_processor_id", "4.1"}, {"skb_store_bytes", "4.1"}, {"l3_csum_replace", "4.1"}, {"l4_csum_replace", "4.1"}, {"tail_call", "4.2"}, {"clone_redirect", "4.2"}, {"get_current_pid_tgid", "4.2"}, {"get_current_uid_gid", "4.2"}, {"get_current_comm", "4.2"}, {"get_cgroup_classid", "4.3"}, {"skb_vlan_push", "4.3"}, {"skb_vlan_pop", "4.3"}, {"skb_get_tunnel_key", "4.3"}, {"skb_set_tunnel_key", "4.3"}, {"perf_event_read", "4.3"}, {"redirect", "4.4"}, {"get_route_realm", "4.4"}, {"perf_event_output", "4.4"}, {"skb_load_bytes", "4.5"}, {"get_stackid", "4.6"}, {"csum_diff", "4.6"}, {"skb_get_tunnel_opt", "4.6"}, {"skb_set_tunnel_opt", "4.6"}, {"skb_change_proto", "4.8"}, {"skb_change_type", "4.8"}, {"skb_under_cgroup", "4.8"}, {"get_hash_recalc", "4.8"}, {"get_current_task", "4.8"}, {"probe_write_user", "4.8"}, {"current_task_under_cgroup", "4.9"}, {"skb_change_tail", "4.9"}, {"skb_pull_data", "4.9"}, {"csum_update", "4.9"}, {"set_hash_invalid", "4.9"}, {"get_numa_node_id", "4.10"}, {"skb_change_head", "4.10"}, {"xdp_adjust_head", "4.10"}, {"probe_read_str", "4.11"}, {"get_socket_cookie", "4.12"}, {"get_socket_uid", "4.12"}, {"set_hash", "4.13"}, {"setsockopt", "4.13"}, {"skb_adjust_room", "4.13"}, {"redirect_map", "4.14"}, {"sk_redirect_map", "4.14"}, {"sock_map_update", "4.14"}, {"xdp_adjust_meta", "4.15"}, {"perf_event_read_value", "4.15"}, {"perf_prog_read_value", "4.15"}, {"getsockopt", "4.15"}, {"override_return", "4.16"}, {"sock_ops_cb_flags_set", "4.16"}, {"msg_redirect_map", "4.17"}, {"msg_apply_bytes", "4.17"}, {"msg_cork_bytes", "4.17"}, {"msg_pull_data", "4.17"}, {"bind", "4.17"}, {"xdp_adjust_tail", "4.18"}, {"skb_get_xfrm_state", "4.18"}, {"get_stack", "4.18"}, {"skb_load_bytes_relative", "4.18"}, {"fib_lookup", "4.18"}, {"sock_hash_update", "4.18"}, {"msg_redirect_hash", "4.18"}, {"sk_redirect_hash", "4.18"}, {"lwt_push_encap", "4.18"}, {"lwt_seg6_store_bytes", "4.18"}, {"lwt_seg6_adjust_srh", "4.18"}, {"lwt_seg6_action", "4.18"}, {"rc_repeat", "4.18"}, {"rc_keydown", "4.18"}, {"skb_cgroup_id", "4.18"}, {"get_current_cgroup_id", "4.18"}, {"get_local_storage", "4.19"}, {"sk_select_reuseport", "4.19"}, {"skb_ancestor_cgroup_id", "4.19"}, {"sk_lookup_tcp", "4.20"}, {"sk_lookup_udp", "4.20"}, {"sk_release", "4.20"}, {"map_push_elem", "4.20"}, {"map_pop_elem", "4.20"}, {"map_peak_elem", "4.20"}, {"msg_push_data", "4.20"}, {"msg_pop_data", "5.0"}, {"rc_pointer_rel", "5.0"}, {"spin_lock", "5.1"}, {"spin_unlock", "5.1"}, {"sk_fullsock", "5.1"}, {"tcp_sock", "5.1"}, {"skb_ecn_set_ce", "5.1"}, {"get_listener_sock", "5.1"}, {"skc_lookup_tcp", "5.2"}, {"tcp_check_syncookie", "5.2"}, {"sysctl_get_name", "5.2"}, {"sysctl_get_current_value", "5.2"}, {"sysctl_get_new_value", "5.2"}, {"sysctl_set_new_value", "5.2"}, {"strtol", "5.2"}, {"strtoul", "5.2"}, {"sk_storage_get", "5.2"}, {"sk_storage_delete", "5.2"}, {"send_signal", "5.3"}, {"tcp_gen_syncookie", "5.3"}, {"skb_output", "5.5"}, {"probe_read_user", "5.5"}, {"probe_read_kernel", "5.5"}, {"probe_read_user_str", "5.5"}, {"probe_read_kernel_str", "5.5"}, }; static uint64_t ptr_to_u64(void *ptr) { return (uint64_t) (unsigned long) ptr; } int bcc_create_map_xattr(struct bpf_create_map_attr *attr, bool allow_rlimit) { unsigned name_len = attr->name ? strlen(attr->name) : 0; char map_name[BPF_OBJ_NAME_LEN] = {}; memcpy(map_name, attr->name, min(name_len, BPF_OBJ_NAME_LEN - 1)); attr->name = map_name; int ret = bpf_create_map_xattr(attr); if (ret < 0 && errno == EPERM) { if (!allow_rlimit) return ret; // see note below about the rationale for this retry struct rlimit rl = {}; if (getrlimit(RLIMIT_MEMLOCK, &rl) == 0) { rl.rlim_max = RLIM_INFINITY; rl.rlim_cur = rl.rlim_max; if (setrlimit(RLIMIT_MEMLOCK, &rl) == 0) ret = bpf_create_map_xattr(attr); } } // kernel already supports btf if its loading is successful, // but this map type may not support pretty print yet. if (ret < 0 && attr->btf_key_type_id && errno == 524 /* ENOTSUPP */) { attr->btf_fd = 0; attr->btf_key_type_id = 0; attr->btf_value_type_id = 0; ret = bpf_create_map_xattr(attr); } if (ret < 0 && name_len && (errno == E2BIG || errno == EINVAL)) { map_name[0] = '\0'; ret = bpf_create_map_xattr(attr); } if (ret < 0 && errno == EPERM) { if (!allow_rlimit) return ret; // see note below about the rationale for this retry struct rlimit rl = {}; if (getrlimit(RLIMIT_MEMLOCK, &rl) == 0) { rl.rlim_max = RLIM_INFINITY; rl.rlim_cur = rl.rlim_max; if (setrlimit(RLIMIT_MEMLOCK, &rl) == 0) ret = bpf_create_map_xattr(attr); } } return ret; } int bcc_create_map(enum bpf_map_type map_type, const char *name, int key_size, int value_size, int max_entries, int map_flags) { struct bpf_create_map_attr attr = {}; attr.map_type = map_type; attr.name = name; attr.key_size = key_size; attr.value_size = value_size; attr.max_entries = max_entries; attr.map_flags = map_flags; return bcc_create_map_xattr(&attr, true); } int bpf_update_elem(int fd, void *key, void *value, unsigned long long flags) { return bpf_map_update_elem(fd, key, value, flags); } int bpf_lookup_elem(int fd, void *key, void *value) { return bpf_map_lookup_elem(fd, key, value); } int bpf_delete_elem(int fd, void *key) { return bpf_map_delete_elem(fd, key); } int bpf_get_first_key(int fd, void *key, size_t key_size) { int i, res; // 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. res = bpf_map_get_next_key(fd, 0, key); if (res < 0 && errno == EFAULT) { // Fall back to try to find a non-existing key. static unsigned char try_values[3] = {0, 0xff, 0x55}; for (i = 0; i < 3; i++) { memset(key, try_values[i], key_size); // We want to check the existence of the key but we don't know the size // of map's value. So we pass an invalid pointer for value, expect // the call to fail and check if the error is ENOENT indicating the // key doesn't exist. If we use NULL for the invalid pointer, it might // trigger a page fault in kernel and affect performance. Hence we use // ~0 which will fail and return fast. // This should fail since we pass an invalid pointer for value. if (bpf_map_lookup_elem(fd, key, (void *)~0) >= 0) return -1; // This means the key doesn't exist. if (errno == ENOENT) return bpf_map_get_next_key(fd, (void*)&try_values[i], key); } return -1; } else { return res; } } int bpf_get_next_key(int fd, void *key, void *next_key) { return bpf_map_get_next_key(fd, key, next_key); } static void bpf_print_hints(int ret, char *log) { if (ret < 0) fprintf(stderr, "bpf: Failed to load program: %s\n", strerror(errno)); if (log == NULL) return; else fprintf(stderr, "%s\n", log); if (ret >= 0) return; // The following error strings will need maintenance to match LLVM. // stack busting if (strstr(log, "invalid stack off=-") != NULL) { fprintf(stderr, "HINT: Looks like you exceeded the BPF stack limit. " "This can happen if you allocate too much local variable storage. " "For example, if you allocated a 1 Kbyte struct (maybe for " "BPF_PERF_OUTPUT), busting a max stack of 512 bytes.\n\n"); } // didn't check NULL on map lookup if (strstr(log, "invalid mem access 'map_value_or_null'") != NULL) { fprintf(stderr, "HINT: The 'map_value_or_null' error can happen if " "you dereference a pointer value from a map lookup without first " "checking if that pointer is NULL.\n\n"); } // lacking a bpf_probe_read if (strstr(log, "invalid mem access 'inv'") != NULL) { fprintf(stderr, "HINT: The invalid mem access 'inv' error can happen " "if you try to dereference memory without first using " "bpf_probe_read() to copy it to the BPF stack. Sometimes the " "bpf_probe_read is automatic by the bcc rewriter, other times " "you'll need to be explicit.\n\n"); } // referencing global/static variables or read only data if (strstr(log, "unknown opcode") != NULL) { fprintf(stderr, "HINT: The 'unknown opcode' can happen if you reference " "a global or static variable, or data in read-only section. For example," " 'char *p = \"hello\"' will result in p referencing a read-only section," " and 'char p[] = \"hello\"' will have \"hello\" stored on the stack.\n\n"); } // helper function not found in kernel char *helper_str = strstr(log, "invalid func "); if (helper_str != NULL) { helper_str += strlen("invalid func "); char *str = strchr(helper_str, '#'); if (str != NULL) { helper_str = str + 1; } int helper_id = atoi(helper_str); if (helper_id && helper_id < sizeof(helpers) / sizeof(struct bpf_helper)) { struct bpf_helper helper = helpers[helper_id - 1]; fprintf(stderr, "HINT: bpf_%s missing (added in Linux %s).\n\n", helper.name, helper.required_version); } } } #define ROUND_UP(x, n) (((x) + (n) - 1u) & ~((n) - 1u)) int bpf_obj_get_info(int prog_map_fd, void *info, uint32_t *info_len) { return bpf_obj_get_info_by_fd(prog_map_fd, info, info_len); } int bpf_prog_compute_tag(const struct bpf_insn *insns, int prog_len, unsigned long long *ptag) { struct sockaddr_alg alg = { .salg_family = AF_ALG, .salg_type = "hash", .salg_name = "sha1", }; int shafd = socket(AF_ALG, SOCK_SEQPACKET, 0); if (shafd < 0) { fprintf(stderr, "sha1 socket not available %s\n", strerror(errno)); return -1; } int ret = bind(shafd, (struct sockaddr *)&alg, sizeof(alg)); if (ret < 0) { fprintf(stderr, "sha1 bind fail %s\n", strerror(errno)); close(shafd); return ret; } int shafd2 = accept(shafd, NULL, 0); if (shafd2 < 0) { fprintf(stderr, "sha1 accept fail %s\n", strerror(errno)); close(shafd); return -1; } struct bpf_insn prog[prog_len / 8]; bool map_ld_seen = false; int i; for (i = 0; i < prog_len / 8; i++) { prog[i] = insns[i]; if (insns[i].code == (BPF_LD | BPF_DW | BPF_IMM) && insns[i].src_reg == BPF_PSEUDO_MAP_FD && !map_ld_seen) { prog[i].imm = 0; map_ld_seen = true; } else if (insns[i].code == 0 && map_ld_seen) { prog[i].imm = 0; map_ld_seen = false; } else { map_ld_seen = false; } } ret = write(shafd2, prog, prog_len); if (ret != prog_len) { fprintf(stderr, "sha1 write fail %s\n", strerror(errno)); close(shafd2); close(shafd); return -1; } union { unsigned char sha[20]; unsigned long long tag; } u = {}; ret = read(shafd2, u.sha, 20); if (ret != 20) { fprintf(stderr, "sha1 read fail %s\n", strerror(errno)); close(shafd2); close(shafd); return -1; } *ptag = __builtin_bswap64(u.tag); close(shafd2); close(shafd); return 0; } int bpf_prog_get_tag(int fd, unsigned long long *ptag) { char fmt[64]; snprintf(fmt, sizeof(fmt), "/proc/self/fdinfo/%d", fd); FILE * f = fopen(fmt, "r"); if (!f) { /* fprintf(stderr, "failed to open fdinfo %s\n", strerror(errno));*/ return -1; } fgets(fmt, sizeof(fmt), f); // pos fgets(fmt, sizeof(fmt), f); // flags fgets(fmt, sizeof(fmt), f); // mnt_id fgets(fmt, sizeof(fmt), f); // prog_type fgets(fmt, sizeof(fmt), f); // prog_jited fgets(fmt, sizeof(fmt), f); // prog_tag fclose(f); char *p = strchr(fmt, ':'); if (!p) { /* fprintf(stderr, "broken fdinfo %s\n", fmt);*/ return -2; } unsigned long long tag = 0; sscanf(p + 1, "%llx", &tag); *ptag = tag; return 0; } int bcc_prog_load_xattr(struct bpf_load_program_attr *attr, int prog_len, char *log_buf, unsigned log_buf_size, bool allow_rlimit) { unsigned name_len = attr->name ? strlen(attr->name) : 0; char *tmp_log_buf = NULL, *attr_log_buf = NULL; unsigned tmp_log_buf_size = 0, attr_log_buf_size = 0; int ret = 0, name_offset = 0; char prog_name[BPF_OBJ_NAME_LEN] = {}; unsigned insns_cnt = prog_len / sizeof(struct bpf_insn); attr->insns_cnt = insns_cnt; if (attr->log_level > 0) { if (log_buf_size > 0) { // Use user-provided log buffer if availiable. log_buf[0] = 0; attr_log_buf = log_buf; attr_log_buf_size = log_buf_size; } else { // Create and use temporary log buffer if user didn't provide one. tmp_log_buf_size = LOG_BUF_SIZE; tmp_log_buf = malloc(tmp_log_buf_size); if (!tmp_log_buf) { fprintf(stderr, "bpf: Failed to allocate temporary log buffer: %s\n\n", strerror(errno)); attr->log_level = 0; } else { tmp_log_buf[0] = 0; attr_log_buf = tmp_log_buf; attr_log_buf_size = tmp_log_buf_size; } } } if (name_len) { if (strncmp(attr->name, "kprobe__", 8) == 0) name_offset = 8; else if (strncmp(attr->name, "tracepoint__", 12) == 0) name_offset = 12; else if (strncmp(attr->name, "raw_tracepoint__", 16) == 0) name_offset = 16; memcpy(prog_name, attr->name + name_offset, min(name_len - name_offset, BPF_OBJ_NAME_LEN - 1)); attr->name = prog_name; } ret = bpf_load_program_xattr(attr, attr_log_buf, attr_log_buf_size); // func_info/line_info may not be supported in old kernels. if (ret < 0 && attr->func_info && errno == EINVAL) { attr->prog_btf_fd = 0; attr->func_info = NULL; attr->func_info_cnt = 0; attr->func_info_rec_size = 0; attr->line_info = NULL; attr->line_info_cnt = 0; attr->line_info_rec_size = 0; ret = bpf_load_program_xattr(attr, attr_log_buf, attr_log_buf_size); } // BPF object name is not supported on older Kernels. // If we failed due to this, clear the name and try again. if (ret < 0 && name_len && (errno == E2BIG || errno == EINVAL)) { prog_name[0] = '\0'; ret = bpf_load_program_xattr(attr, attr_log_buf, attr_log_buf_size); } if (ret < 0 && errno == EPERM) { if (!allow_rlimit) return ret; // When EPERM is returned, two reasons are possible: // 1. user has no permissions for bpf() // 2. user has insufficent rlimit for locked memory // Unfortunately, there is no api to inspect the current usage of locked // mem for the user, so an accurate calculation of how much memory to lock // for this new program is difficult to calculate. As a hack, bump the limit // to unlimited. If program load fails again, return the error. struct rlimit rl = {}; if (getrlimit(RLIMIT_MEMLOCK, &rl) == 0) { rl.rlim_max = RLIM_INFINITY; rl.rlim_cur = rl.rlim_max; if (setrlimit(RLIMIT_MEMLOCK, &rl) == 0) ret = bpf_load_program_xattr(attr, attr_log_buf, attr_log_buf_size); } } if (ret < 0 && errno == E2BIG) { fprintf(stderr, "bpf: %s. Program %s too large (%u insns), at most %d insns\n\n", strerror(errno), attr->name, insns_cnt, BPF_MAXINSNS); return -1; } // The load has failed. Handle log message. if (ret < 0) { // User has provided a log buffer. if (log_buf_size) { // If logging is not already enabled, enable it and do the syscall again. if (attr->log_level == 0) { attr->log_level = 1; ret = bpf_load_program_xattr(attr, log_buf, log_buf_size); } // Print the log message and return. bpf_print_hints(ret, log_buf); if (errno == ENOSPC) fprintf(stderr, "bpf: log_buf size may be insufficient\n"); goto return_result; } // User did not provide log buffer. We will try to increase size of // our temporary log buffer to get full error message. if (tmp_log_buf) free(tmp_log_buf); tmp_log_buf_size = LOG_BUF_SIZE; if (attr->log_level == 0) attr->log_level = 1; for (;;) { tmp_log_buf = malloc(tmp_log_buf_size); if (!tmp_log_buf) { fprintf(stderr, "bpf: Failed to allocate temporary log buffer: %s\n\n", strerror(errno)); goto return_result; } tmp_log_buf[0] = 0; ret = bpf_load_program_xattr(attr, tmp_log_buf, tmp_log_buf_size); if (ret < 0 && errno == ENOSPC) { // Temporary buffer size is not enough. Double it and try again. free(tmp_log_buf); tmp_log_buf = NULL; tmp_log_buf_size <<= 1; } else { break; } } } // Check if we should print the log message if log_level is not 0, // either specified by user or set due to error. if (attr->log_level > 0) { // Don't print if user enabled logging and provided log buffer, // but there is no error. if (log_buf && ret < 0) bpf_print_hints(ret, log_buf); else if (tmp_log_buf) bpf_print_hints(ret, tmp_log_buf); } return_result: if (tmp_log_buf) free(tmp_log_buf); return ret; } int bcc_prog_load(enum bpf_prog_type prog_type, const char *name, const struct bpf_insn *insns, int prog_len, const char *license, unsigned kern_version, int log_level, char *log_buf, unsigned log_buf_size) { struct bpf_load_program_attr attr = {}; attr.prog_type = prog_type; attr.name = name; attr.insns = insns; attr.license = license; attr.kern_version = kern_version; attr.log_level = log_level; return bcc_prog_load_xattr(&attr, prog_len, log_buf, log_buf_size, true); } int bpf_open_raw_sock(const char *name) { struct sockaddr_ll sll; int sock; sock = socket(PF_PACKET, SOCK_RAW | SOCK_NONBLOCK | SOCK_CLOEXEC, htons(ETH_P_ALL)); if (sock < 0) { fprintf(stderr, "cannot create raw socket\n"); return -1; } /* Do not bind on empty interface names */ if (!name || *name == '\0') return sock; memset(&sll, 0, sizeof(sll)); sll.sll_family = AF_PACKET; sll.sll_ifindex = if_nametoindex(name); if (sll.sll_ifindex == 0) { fprintf(stderr, "bpf: Resolving device name to index: %s\n", strerror(errno)); close(sock); return -1; } sll.sll_protocol = htons(ETH_P_ALL); if (bind(sock, (struct sockaddr *)&sll, sizeof(sll)) < 0) { fprintf(stderr, "bind to %s: %s\n", name, strerror(errno)); close(sock); return -1; } return sock; } int bpf_attach_socket(int sock, int prog) { return setsockopt(sock, SOL_SOCKET, SO_ATTACH_BPF, &prog, sizeof(prog)); } #define PMU_TYPE_FILE "/sys/bus/event_source/devices/%s/type" static int bpf_find_probe_type(const char *event_type) { int fd; int ret; char buf[PATH_MAX]; ret = snprintf(buf, sizeof(buf), PMU_TYPE_FILE, event_type); if (ret < 0 || ret >= sizeof(buf)) return -1; fd = open(buf, O_RDONLY); if (fd < 0) return -1; ret = read(fd, buf, sizeof(buf)); close(fd); if (ret < 0 || ret >= sizeof(buf)) return -1; errno = 0; ret = (int)strtol(buf, NULL, 10); return errno ? -1 : ret; } #define PMU_RETPROBE_FILE "/sys/bus/event_source/devices/%s/format/retprobe" static int bpf_get_retprobe_bit(const char *event_type) { int fd; int ret; char buf[PATH_MAX]; ret = snprintf(buf, sizeof(buf), PMU_RETPROBE_FILE, event_type); if (ret < 0 || ret >= sizeof(buf)) return -1; fd = open(buf, O_RDONLY); if (fd < 0) return -1; ret = read(fd, buf, sizeof(buf)); close(fd); if (ret < 0 || ret >= sizeof(buf)) return -1; if (strncmp(buf, "config:", strlen("config:"))) return -1; errno = 0; ret = (int)strtol(buf + strlen("config:"), NULL, 10); return errno ? -1 : ret; } /* * Kernel API with e12f03d ("perf/core: Implement the 'perf_kprobe' PMU") allows * creating [k,u]probe with perf_event_open, which makes it easier to clean up * the [k,u]probe. This function tries to create pfd with the perf_kprobe PMU. */ static int bpf_try_perf_event_open_with_probe(const char *name, uint64_t offs, int pid, const char *event_type, int is_return) { struct perf_event_attr attr = {}; int type = bpf_find_probe_type(event_type); int is_return_bit = bpf_get_retprobe_bit(event_type); int cpu = 0; if (type < 0 || is_return_bit < 0) return -1; attr.sample_period = 1; attr.wakeup_events = 1; if (is_return) attr.config |= 1 << is_return_bit; /* * struct perf_event_attr in latest perf_event.h has the following * extension to config1 and config2. To keep bcc compatibe with * older perf_event.h, we use config1 and config2 here instead of * kprobe_func, uprobe_path, kprobe_addr, and probe_offset. * * union { * __u64 bp_addr; * __u64 kprobe_func; * __u64 uprobe_path; * __u64 config1; * }; * union { * __u64 bp_len; * __u64 kprobe_addr; * __u64 probe_offset; * __u64 config2; * }; */ attr.config2 = offs; /* config2 here is kprobe_addr or probe_offset */ attr.size = sizeof(attr); attr.type = type; /* config1 here is kprobe_func or uprobe_path */ attr.config1 = ptr_to_u64((void *)name); // PID filter is only possible for uprobe events. if (pid < 0) pid = -1; // perf_event_open API doesn't allow both pid and cpu to be -1. // So only set it to -1 when PID is not -1. // Tracing events do not do CPU filtering in any cases. if (pid != -1) cpu = -1; return syscall(__NR_perf_event_open, &attr, pid, cpu, -1 /* group_fd */, PERF_FLAG_FD_CLOEXEC); } // When a valid Perf Event FD provided through pfd, it will be used to enable // and attach BPF program to the event, and event_path will be ignored. // Otherwise, event_path is expected to contain the path to the event in debugfs // and it will be used to open the Perf Event FD. // In either case, if the attach partially failed (such as issue with the // ioctl operations), the **caller** need to clean up the Perf Event FD, either // provided by the caller or opened here. static int bpf_attach_tracing_event(int progfd, const char *event_path, int pid, int *pfd) { int efd, cpu = 0; ssize_t bytes; char buf[PATH_MAX]; struct perf_event_attr attr = {}; // Caller did not provided a valid Perf Event FD. Create one with the debugfs // event path provided. if (*pfd < 0) { snprintf(buf, sizeof(buf), "%s/id", event_path); efd = open(buf, O_RDONLY, 0); if (efd < 0) { fprintf(stderr, "open(%s): %s\n", buf, strerror(errno)); return -1; } bytes = read(efd, buf, sizeof(buf)); if (bytes <= 0 || bytes >= sizeof(buf)) { fprintf(stderr, "read(%s): %s\n", buf, strerror(errno)); close(efd); return -1; } close(efd); buf[bytes] = '\0'; attr.config = strtol(buf, NULL, 0); attr.type = PERF_TYPE_TRACEPOINT; attr.sample_period = 1; attr.wakeup_events = 1; // PID filter is only possible for uprobe events. if (pid < 0) pid = -1; // perf_event_open API doesn't allow both pid and cpu to be -1. // So only set it to -1 when PID is not -1. // Tracing events do not do CPU filtering in any cases. if (pid != -1) cpu = -1; *pfd = syscall(__NR_perf_event_open, &attr, pid, cpu, -1 /* group_fd */, PERF_FLAG_FD_CLOEXEC); if (*pfd < 0) { fprintf(stderr, "perf_event_open(%s/id): %s\n", event_path, strerror(errno)); return -1; } } if (ioctl(*pfd, PERF_EVENT_IOC_SET_BPF, progfd) < 0) { perror("ioctl(PERF_EVENT_IOC_SET_BPF)"); return -1; } if (ioctl(*pfd, PERF_EVENT_IOC_ENABLE, 0) < 0) { perror("ioctl(PERF_EVENT_IOC_ENABLE)"); return -1; } return 0; } static int enter_mount_ns(int pid) { struct stat self_stat, target_stat; int self_fd = -1, target_fd = -1; char buf[64]; if (pid < 0) return -1; if ((size_t)snprintf(buf, sizeof(buf), "/proc/%d/ns/mnt", pid) >= sizeof(buf)) return -1; self_fd = open("/proc/self/ns/mnt", O_RDONLY); if (self_fd < 0) { perror("open(/proc/self/ns/mnt)"); return -1; } target_fd = open(buf, O_RDONLY); if (target_fd < 0) { perror("open(/proc//ns/mnt)"); goto error; } if (fstat(self_fd, &self_stat)) { perror("fstat(self_fd)"); goto error; } if (fstat(target_fd, &target_stat)) { perror("fstat(target_fd)"); goto error; } // both target and current ns are same, avoid setns and close all fds if (self_stat.st_ino == target_stat.st_ino) goto error; if (setns(target_fd, CLONE_NEWNS)) { perror("setns(target)"); goto error; } close(target_fd); return self_fd; error: if (self_fd >= 0) close(self_fd); if (target_fd >= 0) close(target_fd); return -1; } static void exit_mount_ns(int fd) { if (fd < 0) return; if (setns(fd, CLONE_NEWNS)) perror("setns"); close(fd); } /* Creates an [uk]probe using debugfs. * On success, the path to the probe is placed in buf (which is assumed to be of size PATH_MAX). */ static int create_probe_event(char *buf, const char *ev_name, enum bpf_probe_attach_type attach_type, const char *config1, uint64_t offset, const char *event_type, pid_t pid, int maxactive) { int kfd = -1, res = -1, ns_fd = -1; char ev_alias[256]; bool is_kprobe = strncmp("kprobe", event_type, 6) == 0; snprintf(buf, PATH_MAX, "/sys/kernel/debug/tracing/%s_events", event_type); kfd = open(buf, O_WRONLY | O_APPEND, 0); if (kfd < 0) { fprintf(stderr, "%s: open(%s): %s\n", __func__, buf, strerror(errno)); return -1; } res = snprintf(ev_alias, sizeof(ev_alias), "%s_bcc_%d", ev_name, getpid()); if (res < 0 || res >= sizeof(ev_alias)) { fprintf(stderr, "Event name (%s) is too long for buffer\n", ev_name); close(kfd); goto error; } if (is_kprobe) { if (offset > 0 && attach_type == BPF_PROBE_ENTRY) snprintf(buf, PATH_MAX, "p:kprobes/%s %s+%"PRIu64, ev_alias, config1, offset); else if (maxactive > 0 && attach_type == BPF_PROBE_RETURN) snprintf(buf, PATH_MAX, "r%d:kprobes/%s %s", maxactive, ev_alias, config1); else snprintf(buf, PATH_MAX, "%c:kprobes/%s %s", attach_type == BPF_PROBE_ENTRY ? 'p' : 'r', ev_alias, config1); } else { res = snprintf(buf, PATH_MAX, "%c:%ss/%s %s:0x%lx", attach_type==BPF_PROBE_ENTRY ? 'p' : 'r', event_type, ev_alias, config1, (unsigned long)offset); if (res < 0 || res >= PATH_MAX) { fprintf(stderr, "Event alias (%s) too long for buffer\n", ev_alias); close(kfd); return -1; } ns_fd = enter_mount_ns(pid); } if (write(kfd, buf, strlen(buf)) < 0) { if (errno == ENOENT) fprintf(stderr, "cannot attach %s, probe entry may not exist\n", event_type); else fprintf(stderr, "cannot attach %s, %s\n", event_type, strerror(errno)); close(kfd); goto error; } close(kfd); if (!is_kprobe) exit_mount_ns(ns_fd); snprintf(buf, PATH_MAX, "/sys/kernel/debug/tracing/events/%ss/%s", event_type, ev_alias); return 0; error: if (!is_kprobe) exit_mount_ns(ns_fd); return -1; } // config1 could be either kprobe_func or uprobe_path, // see bpf_try_perf_event_open_with_probe(). static int bpf_attach_probe(int progfd, enum bpf_probe_attach_type attach_type, const char *ev_name, const char *config1, const char* event_type, uint64_t offset, pid_t pid, int maxactive) { int kfd, pfd = -1; char buf[PATH_MAX], fname[256]; bool is_kprobe = strncmp("kprobe", event_type, 6) == 0; if (maxactive <= 0) // Try create the [k,u]probe Perf Event with perf_event_open API. pfd = bpf_try_perf_event_open_with_probe(config1, offset, pid, event_type, attach_type != BPF_PROBE_ENTRY); // If failed, most likely Kernel doesn't support the perf_kprobe PMU // (e12f03d "perf/core: Implement the 'perf_kprobe' PMU") yet. // Try create the event using debugfs. if (pfd < 0) { if (create_probe_event(buf, ev_name, attach_type, config1, offset, event_type, pid, maxactive) < 0) goto error; // If we're using maxactive, we need to check that the event was created // under the expected name. If debugfs doesn't support maxactive yet // (kernel < 4.12), the event is created under a different name; we need to // delete that event and start again without maxactive. if (is_kprobe && maxactive > 0 && attach_type == BPF_PROBE_RETURN) { if (snprintf(fname, sizeof(fname), "%s/id", buf) >= sizeof(fname)) { fprintf(stderr, "filename (%s) is too long for buffer\n", buf); goto error; } if (access(fname, F_OK) == -1) { // Deleting kprobe event with incorrect name. kfd = open("/sys/kernel/debug/tracing/kprobe_events", O_WRONLY | O_APPEND, 0); if (kfd < 0) { fprintf(stderr, "open(/sys/kernel/debug/tracing/kprobe_events): %s\n", strerror(errno)); return -1; } snprintf(fname, sizeof(fname), "-:kprobes/%s_0", ev_name); if (write(kfd, fname, strlen(fname)) < 0) { if (errno == ENOENT) fprintf(stderr, "cannot detach kprobe, probe entry may not exist\n"); else fprintf(stderr, "cannot detach kprobe, %s\n", strerror(errno)); close(kfd); goto error; } close(kfd); // Re-creating kprobe event without maxactive. if (create_probe_event(buf, ev_name, attach_type, config1, offset, event_type, pid, 0) < 0) goto error; } } } // If perf_event_open succeeded, bpf_attach_tracing_event will use the created // Perf Event FD directly and buf would be empty and unused. // Otherwise it will read the event ID from the path in buf, create the // Perf Event event using that ID, and updated value of pfd. if (bpf_attach_tracing_event(progfd, buf, pid, &pfd) == 0) return pfd; error: bpf_close_perf_event_fd(pfd); return -1; } int bpf_attach_kprobe(int progfd, enum bpf_probe_attach_type attach_type, const char *ev_name, const char *fn_name, uint64_t fn_offset, int maxactive) { return bpf_attach_probe(progfd, attach_type, ev_name, fn_name, "kprobe", fn_offset, -1, maxactive); } int bpf_attach_uprobe(int progfd, enum bpf_probe_attach_type attach_type, const char *ev_name, const char *binary_path, uint64_t offset, pid_t pid) { return bpf_attach_probe(progfd, attach_type, ev_name, binary_path, "uprobe", offset, pid, -1); } static int bpf_detach_probe(const char *ev_name, const char *event_type) { int kfd = -1, res; char buf[PATH_MAX]; int found_event = 0; size_t bufsize = 0; char *cptr = NULL; FILE *fp; /* * For [k,u]probe created with perf_event_open (on newer kernel), it is * not necessary to clean it up in [k,u]probe_events. We first look up * the %s_bcc_%d line in [k,u]probe_events. If the event is not found, * it is safe to skip the cleaning up process (write -:... to the file). */ snprintf(buf, sizeof(buf), "/sys/kernel/debug/tracing/%s_events", event_type); fp = fopen(buf, "r"); if (!fp) { fprintf(stderr, "open(%s): %s\n", buf, strerror(errno)); goto error; } res = snprintf(buf, sizeof(buf), "%ss/%s_bcc_%d", event_type, ev_name, getpid()); if (res < 0 || res >= sizeof(buf)) { fprintf(stderr, "snprintf(%s): %d\n", ev_name, res); goto error; } while (getline(&cptr, &bufsize, fp) != -1) if (strstr(cptr, buf) != NULL) { found_event = 1; break; } free(cptr); fclose(fp); fp = NULL; if (!found_event) return 0; snprintf(buf, sizeof(buf), "/sys/kernel/debug/tracing/%s_events", event_type); kfd = open(buf, O_WRONLY | O_APPEND, 0); if (kfd < 0) { fprintf(stderr, "open(%s): %s\n", buf, strerror(errno)); goto error; } res = snprintf(buf, sizeof(buf), "-:%ss/%s_bcc_%d", event_type, ev_name, getpid()); if (res < 0 || res >= sizeof(buf)) { fprintf(stderr, "snprintf(%s): %d\n", ev_name, res); goto error; } if (write(kfd, buf, strlen(buf)) < 0) { fprintf(stderr, "write(%s): %s\n", buf, strerror(errno)); goto error; } close(kfd); return 0; error: if (kfd >= 0) close(kfd); if (fp) fclose(fp); return -1; } int bpf_detach_kprobe(const char *ev_name) { return bpf_detach_probe(ev_name, "kprobe"); } int bpf_detach_uprobe(const char *ev_name) { return bpf_detach_probe(ev_name, "uprobe"); } int bpf_attach_tracepoint(int progfd, const char *tp_category, const char *tp_name) { char buf[256]; int pfd = -1; snprintf(buf, sizeof(buf), "/sys/kernel/debug/tracing/events/%s/%s", tp_category, tp_name); if (bpf_attach_tracing_event(progfd, buf, -1 /* PID */, &pfd) == 0) return pfd; bpf_close_perf_event_fd(pfd); return -1; } int bpf_detach_tracepoint(const char *tp_category, const char *tp_name) { UNUSED(tp_category); UNUSED(tp_name); // Right now, there is nothing to do, but it's a good idea to encourage // callers to detach anything they attach. return 0; } int bpf_attach_raw_tracepoint(int progfd, char *tp_name) { int ret; ret = bpf_raw_tracepoint_open(tp_name, progfd); if (ret < 0) fprintf(stderr, "bpf_attach_raw_tracepoint (%s): %s\n", tp_name, strerror(errno)); return ret; } void * bpf_open_perf_buffer(perf_reader_raw_cb raw_cb, perf_reader_lost_cb lost_cb, void *cb_cookie, int pid, int cpu, int page_cnt) { int pfd; struct perf_event_attr attr = {}; struct perf_reader *reader = NULL; reader = perf_reader_new(raw_cb, lost_cb, cb_cookie, page_cnt); if (!reader) goto error; attr.config = 10;//PERF_COUNT_SW_BPF_OUTPUT; attr.type = PERF_TYPE_SOFTWARE; attr.sample_type = PERF_SAMPLE_RAW; attr.sample_period = 1; attr.wakeup_events = 1; pfd = syscall(__NR_perf_event_open, &attr, pid, cpu, -1, PERF_FLAG_FD_CLOEXEC); if (pfd < 0) { fprintf(stderr, "perf_event_open: %s\n", strerror(errno)); fprintf(stderr, " (check your kernel for PERF_COUNT_SW_BPF_OUTPUT support, 4.4 or newer)\n"); goto error; } perf_reader_set_fd(reader, pfd); if (perf_reader_mmap(reader) < 0) goto error; if (ioctl(pfd, PERF_EVENT_IOC_ENABLE, 0) < 0) { perror("ioctl(PERF_EVENT_IOC_ENABLE)"); goto error; } return reader; error: if (reader) perf_reader_free(reader); return NULL; } static int invalid_perf_config(uint32_t type, uint64_t config) { switch (type) { case PERF_TYPE_HARDWARE: if (config >= PERF_COUNT_HW_MAX) { fprintf(stderr, "HARDWARE perf event config out of range\n"); goto is_invalid; } return 0; case PERF_TYPE_SOFTWARE: if (config >= PERF_COUNT_SW_MAX) { fprintf(stderr, "SOFTWARE perf event config out of range\n"); goto is_invalid; } else if (config == 10 /* PERF_COUNT_SW_BPF_OUTPUT */) { fprintf(stderr, "Unable to open or attach perf event for BPF_OUTPUT\n"); goto is_invalid; } return 0; case PERF_TYPE_HW_CACHE: if (((config >> 16) >= PERF_COUNT_HW_CACHE_RESULT_MAX) || (((config >> 8) & 0xff) >= PERF_COUNT_HW_CACHE_OP_MAX) || ((config & 0xff) >= PERF_COUNT_HW_CACHE_MAX)) { fprintf(stderr, "HW_CACHE perf event config out of range\n"); goto is_invalid; } return 0; case PERF_TYPE_TRACEPOINT: case PERF_TYPE_BREAKPOINT: fprintf(stderr, "Unable to open or attach TRACEPOINT or BREAKPOINT events\n"); goto is_invalid; default: return 0; } is_invalid: fprintf(stderr, "Invalid perf event type %" PRIu32 " config %" PRIu64 "\n", type, config); return 1; } int bpf_open_perf_event(uint32_t type, uint64_t config, int pid, int cpu) { int fd; struct perf_event_attr attr = {}; if (invalid_perf_config(type, config)) { return -1; } attr.sample_period = LONG_MAX; attr.type = type; attr.config = config; fd = syscall(__NR_perf_event_open, &attr, pid, cpu, -1, PERF_FLAG_FD_CLOEXEC); if (fd < 0) { fprintf(stderr, "perf_event_open: %s\n", strerror(errno)); return -1; } if (ioctl(fd, PERF_EVENT_IOC_ENABLE, 0) < 0) { perror("ioctl(PERF_EVENT_IOC_ENABLE)"); close(fd); return -1; } return fd; } int bpf_attach_xdp(const char *dev_name, int progfd, uint32_t flags) { int ifindex = if_nametoindex(dev_name); char err_buf[256]; int ret = -1; if (ifindex == 0) { fprintf(stderr, "bpf: Resolving device name to index: %s\n", strerror(errno)); return -1; } ret = bpf_set_link_xdp_fd(ifindex, progfd, flags); if (ret) { libbpf_strerror(ret, err_buf, sizeof(err_buf)); fprintf(stderr, "bpf: Attaching prog to %s: %s", dev_name, err_buf); return -1; } return 0; } int bpf_attach_perf_event_raw(int progfd, void *perf_event_attr, pid_t pid, int cpu, int group_fd, unsigned long extra_flags) { int fd = syscall(__NR_perf_event_open, perf_event_attr, pid, cpu, group_fd, PERF_FLAG_FD_CLOEXEC | extra_flags); if (fd < 0) { perror("perf_event_open failed"); return -1; } if (ioctl(fd, PERF_EVENT_IOC_SET_BPF, progfd) != 0) { perror("ioctl(PERF_EVENT_IOC_SET_BPF) failed"); close(fd); return -1; } if (ioctl(fd, PERF_EVENT_IOC_ENABLE, 0) != 0) { perror("ioctl(PERF_EVENT_IOC_ENABLE) failed"); close(fd); return -1; } return fd; } int bpf_attach_perf_event(int progfd, uint32_t ev_type, uint32_t ev_config, uint64_t sample_period, uint64_t sample_freq, pid_t pid, int cpu, int group_fd) { if (invalid_perf_config(ev_type, ev_config)) { return -1; } if (!((sample_period > 0) ^ (sample_freq > 0))) { fprintf( stderr, "Exactly one of sample_period / sample_freq should be set\n" ); return -1; } struct perf_event_attr attr = {}; attr.type = ev_type; attr.config = ev_config; if (pid > 0) attr.inherit = 1; if (sample_freq > 0) { attr.freq = 1; attr.sample_freq = sample_freq; } else { attr.sample_period = sample_period; } return bpf_attach_perf_event_raw(progfd, &attr, pid, cpu, group_fd, 0); } int bpf_close_perf_event_fd(int fd) { int res, error = 0; if (fd >= 0) { res = ioctl(fd, PERF_EVENT_IOC_DISABLE, 0); if (res != 0) { perror("ioctl(PERF_EVENT_IOC_DISABLE) failed"); error = res; } res = close(fd); if (res != 0) { perror("close perf event FD failed"); error = (res && !error) ? res : error; } } return error; } bpfcc-0.12.0/src/cc/libbpf.h000066400000000000000000000232211357404205000154320ustar00rootroot00000000000000/* * Copyright (c) 2015 PLUMgrid, Inc. * * 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. */ /* eBPF mini library */ #ifndef LIBBPF_H #define LIBBPF_H #include "linux/bpf.h" #include #include #include #ifdef __cplusplus extern "C" { #endif struct bpf_create_map_attr; struct bpf_load_program_attr; enum bpf_probe_attach_type { BPF_PROBE_ENTRY, BPF_PROBE_RETURN }; int bcc_create_map(enum bpf_map_type map_type, const char *name, int key_size, int value_size, int max_entries, int map_flags); int bcc_create_map_xattr(struct bpf_create_map_attr *attr, bool allow_rlimit); int bpf_update_elem(int fd, void *key, void *value, unsigned long long flags); int bpf_lookup_elem(int fd, void *key, void *value); int bpf_delete_elem(int fd, void *key); int bpf_get_first_key(int fd, void *key, size_t key_size); int bpf_get_next_key(int fd, void *key, void *next_key); /* * Load a BPF program, and return the FD of the loaded program. * * On newer Kernels, the parameter name is used to identify the loaded program * for inspection and debugging. It could be different from the function name. * * If log_level has value greater than 0, or the load failed, it will enable * extra logging of loaded BPF bytecode and register status, and will print the * logging message to stderr. In such cases: * - If log_buf and log_buf_size are provided, it will use and also write the * log messages to the provided log_buf. If log_buf is insufficient in size, * it will not to any additional memory allocation. * - Otherwise, it will allocate an internal temporary buffer for log message * printing, and continue to attempt increase that allocated buffer size if * initial attemp was insufficient in size. */ int bcc_prog_load(enum bpf_prog_type prog_type, const char *name, const struct bpf_insn *insns, int prog_len, const char *license, unsigned kern_version, int log_level, char *log_buf, unsigned log_buf_size); int bcc_prog_load_xattr(struct bpf_load_program_attr *attr, int prog_len, char *log_buf, unsigned log_buf_size, bool allow_rlimit); int bpf_attach_socket(int sockfd, int progfd); /* create RAW socket. If name is not NULL/a non-empty null-terminated string, * bind the raw socket to the interface 'name' */ int bpf_open_raw_sock(const char *name); typedef void (*perf_reader_raw_cb)(void *cb_cookie, void *raw, int raw_size); typedef void (*perf_reader_lost_cb)(void *cb_cookie, uint64_t lost); int bpf_attach_kprobe(int progfd, enum bpf_probe_attach_type attach_type, const char *ev_name, const char *fn_name, uint64_t fn_offset, int maxactive); int bpf_detach_kprobe(const char *ev_name); int bpf_attach_uprobe(int progfd, enum bpf_probe_attach_type attach_type, const char *ev_name, const char *binary_path, uint64_t offset, pid_t pid); int bpf_detach_uprobe(const char *ev_name); int bpf_attach_tracepoint(int progfd, const char *tp_category, const char *tp_name); int bpf_detach_tracepoint(const char *tp_category, const char *tp_name); int bpf_attach_raw_tracepoint(int progfd, char *tp_name); void * bpf_open_perf_buffer(perf_reader_raw_cb raw_cb, perf_reader_lost_cb lost_cb, void *cb_cookie, int pid, int cpu, int page_cnt); /* attached a prog expressed by progfd to the device specified in dev_name */ int bpf_attach_xdp(const char *dev_name, int progfd, uint32_t flags); // attach a prog expressed by progfd to run on a specific perf event. The perf // event will be created using the perf_event_attr pointer provided. int bpf_attach_perf_event_raw(int progfd, void *perf_event_attr, pid_t pid, int cpu, int group_fd, unsigned long extra_flags); // attach a prog expressed by progfd to run on a specific perf event, with // certain sample period or sample frequency int bpf_attach_perf_event(int progfd, uint32_t ev_type, uint32_t ev_config, uint64_t sample_period, uint64_t sample_freq, pid_t pid, int cpu, int group_fd); int bpf_open_perf_event(uint32_t type, uint64_t config, int pid, int cpu); int bpf_close_perf_event_fd(int fd); int bpf_obj_pin(int fd, const char *pathname); int bpf_obj_get(const char *pathname); int bpf_obj_get_info(int prog_map_fd, void *info, uint32_t *info_len); int bpf_prog_compute_tag(const struct bpf_insn *insns, int prog_len, unsigned long long *tag); int bpf_prog_get_tag(int fd, unsigned long long *tag); int bpf_prog_get_next_id(uint32_t start_id, uint32_t *next_id); int bpf_prog_get_fd_by_id(uint32_t id); int bpf_map_get_fd_by_id(uint32_t id); int bpf_obj_get_info_by_fd(int prog_fd, void *info, uint32_t *info_len); #define LOG_BUF_SIZE 65536 // Put non-static/inline functions in their own section with this prefix + // fn_name to enable discovery by the bcc library. #define BPF_FN_PREFIX ".bpf.fn." /* ALU ops on registers, bpf_add|sub|...: dst_reg += src_reg */ #define BPF_ALU64_REG(OP, DST, SRC) \ ((struct bpf_insn) { \ .code = BPF_ALU64 | BPF_OP(OP) | BPF_X, \ .dst_reg = DST, \ .src_reg = SRC, \ .off = 0, \ .imm = 0 }) #define BPF_ALU32_REG(OP, DST, SRC) \ ((struct bpf_insn) { \ .code = BPF_ALU | BPF_OP(OP) | BPF_X, \ .dst_reg = DST, \ .src_reg = SRC, \ .off = 0, \ .imm = 0 }) /* ALU ops on immediates, bpf_add|sub|...: dst_reg += imm32 */ #define BPF_ALU64_IMM(OP, DST, IMM) \ ((struct bpf_insn) { \ .code = BPF_ALU64 | BPF_OP(OP) | BPF_K, \ .dst_reg = DST, \ .src_reg = 0, \ .off = 0, \ .imm = IMM }) #define BPF_ALU32_IMM(OP, DST, IMM) \ ((struct bpf_insn) { \ .code = BPF_ALU | BPF_OP(OP) | BPF_K, \ .dst_reg = DST, \ .src_reg = 0, \ .off = 0, \ .imm = IMM }) /* Short form of mov, dst_reg = src_reg */ #define BPF_MOV64_REG(DST, SRC) \ ((struct bpf_insn) { \ .code = BPF_ALU64 | BPF_MOV | BPF_X, \ .dst_reg = DST, \ .src_reg = SRC, \ .off = 0, \ .imm = 0 }) /* Short form of mov, dst_reg = imm32 */ #define BPF_MOV64_IMM(DST, IMM) \ ((struct bpf_insn) { \ .code = BPF_ALU64 | BPF_MOV | BPF_K, \ .dst_reg = DST, \ .src_reg = 0, \ .off = 0, \ .imm = IMM }) /* BPF_LD_IMM64 macro encodes single 'load 64-bit immediate' insn */ #define BPF_LD_IMM64(DST, IMM) \ BPF_LD_IMM64_RAW(DST, 0, IMM) #define BPF_LD_IMM64_RAW(DST, SRC, IMM) \ ((struct bpf_insn) { \ .code = BPF_LD | BPF_DW | BPF_IMM, \ .dst_reg = DST, \ .src_reg = SRC, \ .off = 0, \ .imm = (__u32) (IMM) }), \ ((struct bpf_insn) { \ .code = 0, /* zero is reserved opcode */ \ .dst_reg = 0, \ .src_reg = 0, \ .off = 0, \ .imm = ((__u64) (IMM)) >> 32 }) #define BPF_PSEUDO_MAP_FD 1 /* pseudo BPF_LD_IMM64 insn used to refer to process-local map_fd */ #define BPF_LD_MAP_FD(DST, MAP_FD) \ BPF_LD_IMM64_RAW(DST, BPF_PSEUDO_MAP_FD, MAP_FD) /* Direct packet access, R0 = *(uint *) (skb->data + imm32) */ #define BPF_LD_ABS(SIZE, IMM) \ ((struct bpf_insn) { \ .code = BPF_LD | BPF_SIZE(SIZE) | BPF_ABS, \ .dst_reg = 0, \ .src_reg = 0, \ .off = 0, \ .imm = IMM }) /* Memory load, dst_reg = *(uint *) (src_reg + off16) */ #define BPF_LDX_MEM(SIZE, DST, SRC, OFF) \ ((struct bpf_insn) { \ .code = BPF_LDX | BPF_SIZE(SIZE) | BPF_MEM, \ .dst_reg = DST, \ .src_reg = SRC, \ .off = OFF, \ .imm = 0 }) /* Memory store, *(uint *) (dst_reg + off16) = src_reg */ #define BPF_STX_MEM(SIZE, DST, SRC, OFF) \ ((struct bpf_insn) { \ .code = BPF_STX | BPF_SIZE(SIZE) | BPF_MEM, \ .dst_reg = DST, \ .src_reg = SRC, \ .off = OFF, \ .imm = 0 }) /* Memory store, *(uint *) (dst_reg + off16) = imm32 */ #define BPF_ST_MEM(SIZE, DST, OFF, IMM) \ ((struct bpf_insn) { \ .code = BPF_ST | BPF_SIZE(SIZE) | BPF_MEM, \ .dst_reg = DST, \ .src_reg = 0, \ .off = OFF, \ .imm = IMM }) /* Conditional jumps against registers, if (dst_reg 'op' src_reg) goto pc + off16 */ #define BPF_JMP_REG(OP, DST, SRC, OFF) \ ((struct bpf_insn) { \ .code = BPF_JMP | BPF_OP(OP) | BPF_X, \ .dst_reg = DST, \ .src_reg = SRC, \ .off = OFF, \ .imm = 0 }) /* Conditional jumps against immediates, if (dst_reg 'op' imm32) goto pc + off16 */ #define BPF_JMP_IMM(OP, DST, IMM, OFF) \ ((struct bpf_insn) { \ .code = BPF_JMP | BPF_OP(OP) | BPF_K, \ .dst_reg = DST, \ .src_reg = 0, \ .off = OFF, \ .imm = IMM }) /* Raw code statement block */ #define BPF_RAW_INSN(CODE, DST, SRC, OFF, IMM) \ ((struct bpf_insn) { \ .code = CODE, \ .dst_reg = DST, \ .src_reg = SRC, \ .off = OFF, \ .imm = IMM }) /* Program exit */ #define BPF_EXIT_INSN() \ ((struct bpf_insn) { \ .code = BPF_JMP | BPF_EXIT, \ .dst_reg = 0, \ .src_reg = 0, \ .off = 0, \ .imm = 0 }) #ifdef __cplusplus } #endif #endif bpfcc-0.12.0/src/cc/link_all.cc000066400000000000000000000011651357404205000161220ustar00rootroot00000000000000// Copyright (c) 2017 VMware, Inc. // Licensed under the Apache License, Version 2.0 (the "License") #include #include "bcc_usdt.h" namespace { // Take this trick from llvm for forcing exported functions in helper // libraries to be included in the final .so struct LinkAll { LinkAll() { // getenv never returns -1, but compiler doesn't know! if (::getenv("bar") != (char *)-1) return; (void)bcc_usdt_new_frompid(-1, nullptr); (void)bcc_usdt_new_frompath(nullptr); (void)bcc_usdt_close(nullptr); } } LinkAll; // declare one instance to invoke the constructor } bpfcc-0.12.0/src/cc/perf_reader.c000066400000000000000000000156451357404205000164600ustar00rootroot00000000000000/* * Copyright (c) 2015 PLUMgrid, Inc. * * 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "libbpf.h" #include "perf_reader.h" enum { RB_NOT_USED = 0, // ring buffer not usd RB_USED_IN_MUNMAP = 1, // used in munmap RB_USED_IN_READ = 2, // used in read }; struct perf_reader { perf_reader_raw_cb raw_cb; perf_reader_lost_cb lost_cb; void *cb_cookie; // to be returned in the cb void *buf; // for keeping segmented data size_t buf_size; void *base; int rb_use_state; pid_t rb_read_tid; int page_size; int page_cnt; int fd; }; struct perf_reader * perf_reader_new(perf_reader_raw_cb raw_cb, perf_reader_lost_cb lost_cb, void *cb_cookie, int page_cnt) { struct perf_reader *reader = calloc(1, sizeof(struct perf_reader)); if (!reader) return NULL; reader->raw_cb = raw_cb; reader->lost_cb = lost_cb; reader->cb_cookie = cb_cookie; reader->fd = -1; reader->page_size = getpagesize(); reader->page_cnt = page_cnt; return reader; } void perf_reader_free(void *ptr) { if (ptr) { struct perf_reader *reader = ptr; pid_t tid = syscall(__NR_gettid); while (!__sync_bool_compare_and_swap(&reader->rb_use_state, RB_NOT_USED, RB_USED_IN_MUNMAP)) { // If the same thread, it is called from call back handler, no locking needed if (tid == reader->rb_read_tid) break; } munmap(reader->base, reader->page_size * (reader->page_cnt + 1)); if (reader->fd >= 0) { ioctl(reader->fd, PERF_EVENT_IOC_DISABLE, 0); close(reader->fd); } free(reader->buf); free(ptr); } } int perf_reader_mmap(struct perf_reader *reader) { int mmap_size = reader->page_size * (reader->page_cnt + 1); if (reader->fd < 0) { fprintf(stderr, "%s: reader fd is not set\n", __FUNCTION__); return -1; } reader->base = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE , MAP_SHARED, reader->fd, 0); if (reader->base == MAP_FAILED) { perror("mmap"); return -1; } return 0; } struct perf_sample_trace_common { uint16_t id; uint8_t flags; uint8_t preempt_count; int pid; }; struct perf_sample_trace_kprobe { struct perf_sample_trace_common common; uint64_t ip; }; static void parse_sw(struct perf_reader *reader, void *data, int size) { uint8_t *ptr = data; struct perf_event_header *header = (void *)data; struct { uint32_t size; char data[0]; } *raw = NULL; ptr += sizeof(*header); if (ptr > (uint8_t *)data + size) { fprintf(stderr, "%s: corrupt sample header\n", __FUNCTION__); return; } raw = (void *)ptr; ptr += sizeof(raw->size) + raw->size; if (ptr > (uint8_t *)data + size) { fprintf(stderr, "%s: corrupt raw sample\n", __FUNCTION__); return; } // sanity check if (ptr != (uint8_t *)data + size) { fprintf(stderr, "%s: extra data at end of sample\n", __FUNCTION__); return; } if (reader->raw_cb) reader->raw_cb(reader->cb_cookie, raw->data, raw->size); } static uint64_t read_data_head(volatile struct perf_event_mmap_page *perf_header) { uint64_t data_head = perf_header->data_head; asm volatile("" ::: "memory"); return data_head; } static void write_data_tail(volatile struct perf_event_mmap_page *perf_header, uint64_t data_tail) { asm volatile("" ::: "memory"); perf_header->data_tail = data_tail; } void perf_reader_event_read(struct perf_reader *reader) { volatile struct perf_event_mmap_page *perf_header = reader->base; uint64_t buffer_size = (uint64_t)reader->page_size * reader->page_cnt; uint64_t data_head; uint8_t *base = (uint8_t *)reader->base + reader->page_size; uint8_t *sentinel = (uint8_t *)reader->base + buffer_size + reader->page_size; uint8_t *begin, *end; reader->rb_read_tid = syscall(__NR_gettid); if (!__sync_bool_compare_and_swap(&reader->rb_use_state, RB_NOT_USED, RB_USED_IN_READ)) return; // Consume all the events on this ring, calling the cb function for each one. // The message may fall on the ring boundary, in which case copy the message // into a malloced buffer. for (data_head = read_data_head(perf_header); perf_header->data_tail != data_head; data_head = read_data_head(perf_header)) { uint64_t data_tail = perf_header->data_tail; uint8_t *ptr; begin = base + data_tail % buffer_size; // event header is u64, won't wrap struct perf_event_header *e = (void *)begin; ptr = begin; end = base + (data_tail + e->size) % buffer_size; if (end < begin) { // perf event wraps around the ring, make a contiguous copy reader->buf = realloc(reader->buf, e->size); size_t len = sentinel - begin; memcpy(reader->buf, begin, len); memcpy((void *)((unsigned long)reader->buf + len), base, e->size - len); ptr = reader->buf; } if (e->type == PERF_RECORD_LOST) { /* * struct { * struct perf_event_header header; * u64 id; * u64 lost; * struct sample_id sample_id; * }; */ uint64_t lost = *(uint64_t *)(ptr + sizeof(*e) + sizeof(uint64_t)); if (reader->lost_cb) { reader->lost_cb(reader->cb_cookie, lost); } else { fprintf(stderr, "Possibly lost %" PRIu64 " samples\n", lost); } } else if (e->type == PERF_RECORD_SAMPLE) { parse_sw(reader, ptr, e->size); } else { fprintf(stderr, "%s: unknown sample type %d\n", __FUNCTION__, e->type); } write_data_tail(perf_header, perf_header->data_tail + e->size); } reader->rb_use_state = RB_NOT_USED; __sync_synchronize(); reader->rb_read_tid = 0; } int perf_reader_poll(int num_readers, struct perf_reader **readers, int timeout) { struct pollfd pfds[num_readers]; int i; for (i = 0; i fd; pfds[i].events = POLLIN; } if (poll(pfds, num_readers, timeout) > 0) { for (i = 0; i < num_readers; ++i) { if (pfds[i].revents & POLLIN) perf_reader_event_read(readers[i]); } } return 0; } void perf_reader_set_fd(struct perf_reader *reader, int fd) { reader->fd = fd; } int perf_reader_fd(struct perf_reader *reader) { return reader->fd; } bpfcc-0.12.0/src/cc/perf_reader.h000066400000000000000000000024161357404205000164550ustar00rootroot00000000000000/* * Copyright (c) 2015 PLUMgrid, Inc. * * 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. */ #ifndef PERF_READER_H #define PERF_READER_H #include "libbpf.h" #ifdef __cplusplus extern "C" { #endif struct perf_reader; struct perf_reader * perf_reader_new(perf_reader_raw_cb raw_cb, perf_reader_lost_cb lost_cb, void *cb_cookie, int page_cnt); void perf_reader_free(void *ptr); int perf_reader_mmap(struct perf_reader *reader); void perf_reader_event_read(struct perf_reader *reader); int perf_reader_poll(int num_readers, struct perf_reader **readers, int timeout); int perf_reader_fd(struct perf_reader *reader); void perf_reader_set_fd(struct perf_reader *reader, int fd); #ifdef __cplusplus } #endif #endif bpfcc-0.12.0/src/cc/setns.h000066400000000000000000000004731357404205000153340ustar00rootroot00000000000000// This file is only needed to support build for CentOS 6 // Remove it when no longer needed. // File is trivial and therefore is in public domain. #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include #include #define setns(FD, NSTYPE) syscall(__NR_setns, (int)(FD), (int)(NSTYPE)) bpfcc-0.12.0/src/cc/shared_table.cc000066400000000000000000000074621357404205000167600ustar00rootroot00000000000000/* * Copyright (c) 2016 PLUMgrid, Inc. * * 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. */ #include #include #include "common.h" #include "linux/bpf.h" #include "table_storage.h" #include "table_storage_impl.h" namespace ebpf { using std::string; using std::unique_ptr; /// A process-wide singleton of shared tables class SharedTableStorage : public TableStorageImpl { public: class iterator : public TableStorageIteratorImpl { std::map::iterator it_; public: explicit iterator(const std::map::iterator &it) : it_(it) {} virtual ~iterator() {} virtual unique_ptr clone() const override { return make_unique(it_); } virtual self_type &operator++() override { ++it_; return *this; } virtual value_type &operator*() const override { return *it_; } virtual pointer operator->() const override { return &*it_; } }; virtual ~SharedTableStorage() {} virtual bool Find(const string &name, TableStorage::iterator &result) const override; virtual bool Insert(const string &name, TableDesc &&desc) override; virtual bool Delete(const string &name) override; virtual unique_ptr begin() override; virtual unique_ptr end() override; virtual unique_ptr lower_bound(const string &k) override; virtual unique_ptr upper_bound(const string &k) override; virtual unique_ptr erase(const TableStorageIteratorImpl &it) override; private: static std::map tables_; }; bool SharedTableStorage::Find(const string &name, TableStorage::iterator &result) const { auto it = tables_.find(name); if (it == tables_.end()) return false; result = TableStorage::iterator(make_unique(it)); return true; } bool SharedTableStorage::Insert(const string &name, TableDesc &&desc) { auto it = tables_.find(name); if (it != tables_.end()) return false; tables_[name] = std::move(desc); return true; } bool SharedTableStorage::Delete(const string &name) { auto it = tables_.find(name); if (it == tables_.end()) return false; tables_.erase(it); return true; } unique_ptr SharedTableStorage::begin() { return make_unique(tables_.begin()); } unique_ptr SharedTableStorage::end() { return make_unique(tables_.end()); } unique_ptr SharedTableStorage::lower_bound(const string &k) { return make_unique(tables_.lower_bound(k)); } unique_ptr SharedTableStorage::upper_bound(const string &k) { return make_unique(tables_.upper_bound(k)); } unique_ptr SharedTableStorage::erase(const TableStorageIteratorImpl &it) { auto i = tables_.find((*it).first); if (i == tables_.end()) return unique_ptr(); return make_unique(tables_.erase(i)); } // All maps for this process are kept in global static storage. std::map SharedTableStorage::tables_; unique_ptr createSharedTableStorage() { auto t = make_unique(); t->Init(make_unique()); t->AddMapTypesVisitor(createJsonMapTypesVisitor()); return t; } } bpfcc-0.12.0/src/cc/syms.h000066400000000000000000000140041357404205000151660ustar00rootroot00000000000000/* * Copyright (c) 2016 GitHub, Inc. * * 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. */ #pragma once #include #include #include #include #include #include #include #include "bcc_proc.h" #include "bcc_syms.h" #include "file_desc.h" class ProcStat { std::string procfs_; ino_t inode_; ino_t getinode_(); public: ProcStat(int pid); bool is_stale(); void reset() { inode_ = getinode_(); } }; class SymbolCache { public: virtual ~SymbolCache() = default; virtual void refresh() = 0; virtual bool resolve_addr(uint64_t addr, struct bcc_symbol *sym, bool demangle = true) = 0; virtual bool resolve_name(const char *module, const char *name, uint64_t *addr) = 0; }; class KSyms : SymbolCache { struct Symbol { Symbol(const char *name, const char *mod, uint64_t addr) : name(name), mod(mod), addr(addr) {} std::string name; std::string mod; uint64_t addr; bool operator<(const Symbol &rhs) const { return addr < rhs.addr; } }; std::vector syms_; std::unordered_map symnames_; static void _add_symbol(const char *, const char *, uint64_t, void *); public: virtual bool resolve_addr(uint64_t addr, struct bcc_symbol *sym, bool demangle = true) override; virtual bool resolve_name(const char *unused, const char *name, uint64_t *addr) override; virtual void refresh() override; }; class ProcSyms : SymbolCache { struct NameIdx { size_t section_idx; size_t str_table_idx; size_t str_len; bool debugfile; }; struct Symbol { Symbol(const std::string *name, uint64_t start, uint64_t size) : is_name_resolved(true), start(start), size(size) { data.name = name; } Symbol(size_t section_idx, size_t str_table_idx, size_t str_len, uint64_t start, uint64_t size, bool debugfile) : is_name_resolved(false), start(start), size(size) { data.name_idx.section_idx = section_idx; data.name_idx.str_table_idx = str_table_idx; data.name_idx.str_len = str_len; data.name_idx.debugfile = debugfile; } bool is_name_resolved; union { struct NameIdx name_idx; const std::string *name{nullptr}; } data; uint64_t start; uint64_t size; bool operator<(const struct Symbol& rhs) const { return start < rhs.start; } }; enum class ModuleType { UNKNOWN, EXEC, SO, PERF_MAP, VDSO }; struct Module { struct Range { uint64_t start; uint64_t end; uint64_t file_offset; Range(uint64_t s, uint64_t e, uint64_t f) : start(s), end(e), file_offset(f) {} }; Module(const char *name, const char *path, struct bcc_symbol_option *option); std::string name_; std::string path_; std::vector ranges_; bool loaded_; bcc_symbol_option *symbol_option_; ModuleType type_; // The file offset within the ELF of the SO's first text section. uint64_t elf_so_offset_; uint64_t elf_so_addr_; std::unordered_set symnames_; std::vector syms_; void load_sym_table(); bool contains(uint64_t addr, uint64_t &offset) const; uint64_t start() const { return ranges_.begin()->start; } bool find_addr(uint64_t offset, struct bcc_symbol *sym); bool find_name(const char *symname, uint64_t *addr); static int _add_symbol(const char *symname, uint64_t start, uint64_t size, void *p); static int _add_symbol_lazy(size_t section_idx, size_t str_table_idx, size_t str_len, uint64_t start, uint64_t size, int debugfile, void *p); }; int pid_; std::vector modules_; ProcStat procstat_; bcc_symbol_option symbol_option_; static int _add_load_sections(uint64_t v_addr, uint64_t mem_sz, uint64_t file_offset, void *payload); static int _add_module(mod_info *, int, void *); void load_exe(); void load_modules(); public: ProcSyms(int pid, struct bcc_symbol_option *option = nullptr); virtual void refresh() override; virtual bool resolve_addr(uint64_t addr, struct bcc_symbol *sym, bool demangle = true) override; virtual bool resolve_name(const char *module, const char *name, uint64_t *addr) override; }; class BuildSyms { struct Symbol { Symbol(const std::string *name, uint64_t start, uint64_t size) :name(name), start(start), size(size) {} const std::string *name; uint64_t start; uint64_t size; bool operator<(const struct Symbol &rhs) const { return start < rhs.start; } }; struct Module { Module(const char *module_name): module_name_(module_name), loaded_(false) {} const std::string module_name_; const std::string build_id_; bool loaded_; std::unordered_set symnames_; std::vector syms_; bcc_symbol_option symbol_option_; bool load_sym_table(); static int _add_symbol(const char *symname, uint64_t start, uint64_t size, void *p); bool resolve_addr(uint64_t offset, struct bcc_symbol*, bool demangle=true); }; std::unordered_map > buildmap_; public: BuildSyms() {} virtual ~BuildSyms() = default; virtual bool add_module(const std::string module_name); virtual bool resolve_addr(std::string build_id, uint64_t offset, struct bcc_symbol *sym, bool demangle = true); }; bpfcc-0.12.0/src/cc/table_desc.h000066400000000000000000000071561357404205000162720ustar00rootroot00000000000000/* * Copyright (c) 2015 PLUMgrid, Inc. * * 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. */ #pragma once #include #include #include #include #include #include "bcc_exception.h" #include "file_desc.h" namespace clang { class ASTContext; class QualType; } namespace ebpf { typedef std::function sscanf_fn; typedef std::function snprintf_fn; /// TableDesc uniquely stores all of the runtime state for an active bpf table. /// The copy constructor/assign operator are disabled since the file handles /// owned by this table are not implicitly copyable. One should call the dup() /// method if an explicit new handle is required. We define the move operators /// so that objects of this class can reside in stl containers. class TableDesc { private: TableDesc(const TableDesc &that) : name(that.name), fd(that.fd.dup()), fake_fd(that.fake_fd), type(that.type), key_size(that.key_size), leaf_size(that.leaf_size), max_entries(that.max_entries), flags(that.flags), key_desc(that.key_desc), leaf_desc(that.leaf_desc), key_sscanf(that.key_sscanf), leaf_sscanf(that.leaf_sscanf), key_snprintf(that.key_snprintf), leaf_snprintf(that.leaf_snprintf), is_shared(that.is_shared), is_extern(that.is_extern) {} public: TableDesc() : fake_fd(0), type(0), key_size(0), leaf_size(0), max_entries(0), flags(0), is_shared(false), is_extern(false) {} TableDesc(const std::string &name, FileDesc &&fd, int type, size_t key_size, size_t leaf_size, size_t max_entries, int flags) : name(name), fd(std::move(fd)), type(type), key_size(key_size), leaf_size(leaf_size), max_entries(max_entries), flags(flags), is_shared(false), is_extern(false) {} TableDesc(TableDesc &&that) = default; TableDesc &operator=(TableDesc &&that) = default; TableDesc &operator=(const TableDesc &that) = delete; TableDesc dup() const { return TableDesc(*this); } std::string name; FileDesc fd; int fake_fd; int type; size_t key_size; // sizes are in bytes size_t leaf_size; size_t max_entries; int flags; std::string key_desc; std::string leaf_desc; sscanf_fn key_sscanf; sscanf_fn leaf_sscanf; snprintf_fn key_snprintf; snprintf_fn leaf_snprintf; bool is_shared; bool is_extern; }; /// MapTypesVisitor gets notified of new bpf tables, and has a chance to parse /// the key and leaf types for their own usage. Subclass this abstract class and /// implement the Visit method, then add an instance of this class to the /// StorageTable instance to be notified of each new key/leaf type. class MapTypesVisitor { public: virtual ~MapTypesVisitor() {} virtual void Visit(TableDesc &desc, clang::ASTContext &C, clang::QualType key_type, clang::QualType leaf_type) = 0; }; std::unique_ptr createJsonMapTypesVisitor(); } // namespace ebpf bpfcc-0.12.0/src/cc/table_storage.cc000066400000000000000000000065501357404205000171530ustar00rootroot00000000000000/* * Copyright (c) 2017 VMware, Inc. * * 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. */ #include #include #include "table_storage_impl.h" namespace ebpf { using std::move; using std::string; using std::unique_ptr; const string Path::DELIM = "/"; TableStorage::TableStorage() {} TableStorage::~TableStorage() {} void TableStorage::Init(unique_ptr impl) { impl_ = move(impl); } bool TableStorage::Find(const Path &path, TableStorage::iterator &result) const { return impl_->Find(path.to_string(), result); } bool TableStorage::Insert(const Path &path, TableDesc &&desc) { return impl_->Insert(path.to_string(), move(desc)); } bool TableStorage::Delete(const Path &path) { return impl_->Delete(path.to_string()); } size_t TableStorage::DeletePrefix(const Path &path) { size_t i = 0; auto it = lower_bound(path); auto upper = upper_bound(path); while (it != upper) { it = impl_->erase(*it.impl_); ++i; } return i; } void TableStorage::AddMapTypesVisitor(unique_ptr visitor) { visitors_.push_back(move(visitor)); } void TableStorage::VisitMapType(TableDesc &desc, clang::ASTContext &C, clang::QualType key_type, clang::QualType leaf_type) { for (auto &v : visitors_) v->Visit(desc, C, key_type, leaf_type); } TableStorage::iterator TableStorage::begin() { return impl_->begin(); } TableStorage::iterator TableStorage::end() { return impl_->end(); } TableStorage::iterator TableStorage::lower_bound(const Path &p) { return impl_->lower_bound(p.to_string()); } TableStorage::iterator TableStorage::upper_bound(const Path &p) { return impl_->upper_bound(p.to_string() + "\x7f"); } /// TableStorage::iterator implementation TableStorage::iterator::iterator() {} TableStorage::iterator::iterator(unique_ptr impl) : impl_(move(impl)) {} TableStorage::iterator::iterator(const iterator &that) : impl_(that.impl_->clone()) {} TableStorage::iterator::~iterator() {} TableStorage::iterator::iterator(iterator &&that) { *this = move(that); } TableStorage::iterator &TableStorage::iterator::operator=(iterator &&that) { impl_ = move(that.impl_); return *this; } TableStorage::iterator &TableStorage::iterator::operator++() { ++*impl_; return *this; } TableStorage::iterator TableStorage::iterator::operator++(int) { iterator tmp(*this); operator++(); return tmp; } bool TableStorage::iterator::operator==(const iterator &rhs) const { // assumes that the underlying pair is stored in only one place return &**impl_ == &**rhs.impl_; } bool TableStorage::iterator::operator!=(const iterator &rhs) const { return &**impl_ != &**rhs.impl_; } TableStorage::iterator::reference TableStorage::iterator::operator*() const { return **impl_; } TableStorage::iterator::pointer TableStorage::iterator::operator->() const { return &**impl_; } } // namespace ebpf bpfcc-0.12.0/src/cc/table_storage.h000066400000000000000000000062121357404205000170100ustar00rootroot00000000000000/* * Copyright (c) 2017 VMware, Inc. * * 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. */ #pragma once #include #include #include #include #include #include #include "table_desc.h" namespace ebpf { typedef std::map> fake_fd_map_def; class TableStorageImpl; class TableStorageIteratorImpl; class Path { public: static const std::string DELIM; Path() = default; Path(const Path &other) = default; Path &operator=(const Path &other) = default; Path(std::initializer_list parts) { size_t len = parts.size() * DELIM.size(); for (const auto &s : parts) len += s.size(); path_.reserve(len); for (const auto &s : parts) path_ += DELIM + s; } const std::string &to_string() const { return path_; } private: std::string path_; }; class TableStorage { public: /// iterator is an abstract class for traversing the map entries in a table /// storage object. class iterator { private: friend class TableStorage; iterator(const iterator &); public: typedef std::pair value_type; typedef std::ptrdiff_t difference_type; typedef value_type *pointer; typedef value_type &reference; typedef std::forward_iterator_tag iterator_category; typedef iterator self_type; iterator(); iterator(std::unique_ptr); ~iterator(); iterator(iterator &&); iterator &operator=(iterator &&); self_type &operator++(); self_type operator++(int); bool operator==(const self_type &) const; bool operator!=(const self_type &) const; value_type &operator*() const; pointer operator->() const; private: std::unique_ptr impl_; }; TableStorage(); ~TableStorage(); void Init(std::unique_ptr); bool Find(const Path &path, TableStorage::iterator &result) const; bool Insert(const Path &path, TableDesc &&desc); bool Delete(const Path &path); size_t DeletePrefix(const Path &path); void AddMapTypesVisitor(std::unique_ptr); void VisitMapType(TableDesc &desc, clang::ASTContext &C, clang::QualType key_type, clang::QualType leaf_type); iterator begin(); iterator end(); iterator lower_bound(const Path &p); iterator upper_bound(const Path &p); private: std::unique_ptr impl_; std::vector> visitors_; }; std::unique_ptr createSharedTableStorage(); std::unique_ptr createBpfFsTableStorage(); } bpfcc-0.12.0/src/cc/table_storage_impl.h000066400000000000000000000034341357404205000200340ustar00rootroot00000000000000/* * Copyright (c) 2017 VMware, Inc. * * 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. */ #pragma once #include "table_storage.h" namespace ebpf { class TableStorageIteratorImpl { public: typedef std::pair value_type; typedef value_type *pointer; typedef value_type &reference; typedef TableStorageIteratorImpl self_type; virtual ~TableStorageIteratorImpl() {} virtual std::unique_ptr clone() const = 0; virtual self_type &operator++() = 0; virtual value_type &operator*() const = 0; virtual pointer operator->() const = 0; private: }; class TableStorageImpl { public: virtual ~TableStorageImpl(){}; virtual bool Find(const std::string &name, TableStorage::iterator &result) const = 0; virtual bool Insert(const std::string &name, TableDesc &&desc) = 0; virtual bool Delete(const std::string &name) = 0; virtual std::unique_ptr begin() = 0; virtual std::unique_ptr end() = 0; virtual std::unique_ptr lower_bound(const std::string &k) = 0; virtual std::unique_ptr upper_bound(const std::string &k) = 0; virtual std::unique_ptr erase(const TableStorageIteratorImpl &it) = 0; }; } // namespace ebpf bpfcc-0.12.0/src/cc/usdt.h000066400000000000000000000214051357404205000151550ustar00rootroot00000000000000/* * Copyright (c) 2016 GitHub, Inc. * * 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. */ #pragma once #include #include #include #include #include "bcc_proc.h" #include "syms.h" #include "vendor/optional.hpp" struct bcc_usdt; namespace ebpf { class BPF; class USDT; } namespace USDT { using std::experimental::optional; using std::experimental::nullopt; class ArgumentParser; static const std::string USDT_PROGRAM_HEADER = "#include \n"; static const std::string COMPILER_BARRIER = "__asm__ __volatile__(\"\": : :\"memory\");"; class Argument { private: optional arg_size_; optional constant_; optional deref_offset_; optional deref_ident_; optional base_register_name_; optional index_register_name_; optional scale_; bool get_global_address(uint64_t *address, const std::string &binpath, const optional &pid) const; public: Argument(); ~Argument(); bool assign_to_local(std::ostream &stream, const std::string &local_name, const std::string &binpath, const optional &pid = nullopt) const; int arg_size() const { return arg_size_.value_or(sizeof(void *)); } std::string ctype() const; const optional &deref_ident() const { return deref_ident_; } const optional &base_register_name() const { return base_register_name_; } const optional &index_register_name() const { return index_register_name_; } const optional scale() const { return scale_; } const optional constant() const { return constant_; } const optional deref_offset() const { return deref_offset_; } friend class ArgumentParser; friend class ArgumentParser_aarch64; friend class ArgumentParser_powerpc64; friend class ArgumentParser_s390x; friend class ArgumentParser_x64; }; class ArgumentParser { protected: const char *arg_; ssize_t cur_pos_; void skip_whitespace_from(size_t pos); void skip_until_whitespace_from(size_t pos); void print_error(ssize_t pos); ssize_t parse_number(ssize_t pos, optional *result) { char *endp; int number = strtol(arg_ + pos, &endp, 0); if (endp > arg_ + pos) *result = number; return endp - arg_; } bool error_return(ssize_t error_start, ssize_t skip_start) { print_error(error_start); skip_until_whitespace_from(skip_start); return false; } public: virtual bool parse(Argument *dest) = 0; bool done() { return cur_pos_ < 0 || arg_[cur_pos_] == '\0'; } ArgumentParser(const char *arg) : arg_(arg), cur_pos_(0) {} }; class ArgumentParser_aarch64 : public ArgumentParser { private: bool parse_register(ssize_t pos, ssize_t &new_pos, optional *reg_num); bool parse_size(ssize_t pos, ssize_t &new_pos, optional *arg_size); bool parse_mem(ssize_t pos, ssize_t &new_pos, optional *reg_num, optional *offset); public: bool parse(Argument *dest); ArgumentParser_aarch64(const char *arg) : ArgumentParser(arg) {} }; class ArgumentParser_powerpc64 : public ArgumentParser { public: bool parse(Argument *dest); ArgumentParser_powerpc64(const char *arg) : ArgumentParser(arg) {} }; class ArgumentParser_s390x : public ArgumentParser { public: bool parse(Argument *dest); ArgumentParser_s390x(const char *arg) : ArgumentParser(arg) {} }; class ArgumentParser_x64 : public ArgumentParser { private: enum Register { REG_A, REG_B, REG_C, REG_D, REG_SI, REG_DI, REG_BP, REG_SP, REG_8, REG_9, REG_10, REG_11, REG_12, REG_13, REG_14, REG_15, REG_RIP, }; struct RegInfo { Register reg; int size; }; static const std::unordered_map registers_; bool normalize_register(std::string *reg, int *reg_size); void reg_to_name(std::string *norm, Register reg); ssize_t parse_register(ssize_t pos, std::string &name, int &size); ssize_t parse_identifier(ssize_t pos, optional *ident); ssize_t parse_base_register(ssize_t pos, Argument *dest); ssize_t parse_index_register(ssize_t pos, Argument *dest); ssize_t parse_scale(ssize_t pos, Argument *dest); ssize_t parse_expr(ssize_t pos, Argument *dest); ssize_t parse_1(ssize_t pos, Argument *dest); public: bool parse(Argument *dest); ArgumentParser_x64(const char *arg) : ArgumentParser(arg) {} }; struct Location { uint64_t address_; std::string bin_path_; std::vector arguments_; Location(uint64_t addr, const std::string &bin_path, const char *arg_fmt); }; class Probe { std::string bin_path_; // initial bin_path when Probe is created std::string provider_; std::string name_; uint64_t semaphore_; std::vector locations_; optional pid_; std::unordered_map object_type_map_; // bin_path => is shared lib? optional attached_to_; optional attached_semaphore_; uint8_t mod_match_inode_only_; std::string largest_arg_type(size_t arg_n); bool add_to_semaphore(int16_t val); bool resolve_global_address(uint64_t *global, const std::string &bin_path, const uint64_t addr); bool lookup_semaphore_addr(uint64_t *address); void add_location(uint64_t addr, const std::string &bin_path, const char *fmt); public: Probe(const char *bin_path, const char *provider, const char *name, uint64_t semaphore, const optional &pid, uint8_t mod_match_inode_only = 0); size_t num_locations() const { return locations_.size(); } size_t num_arguments() const { return locations_.front().arguments_.size(); } uint64_t semaphore() const { return semaphore_; } uint64_t address(size_t n = 0) const { return locations_[n].address_; } const char *location_bin_path(size_t n = 0) const { return locations_[n].bin_path_.c_str(); } const Location &location(size_t n) const { return locations_[n]; } bool usdt_getarg(std::ostream &stream); bool usdt_getarg(std::ostream &stream, const std::string& probe_func); std::string get_arg_ctype(int arg_index) { return largest_arg_type(arg_index); } void finalize_locations(); bool need_enable() const { return semaphore_ != 0x0; } bool enable(const std::string &fn_name); bool disable(); bool enabled() const { return !!attached_to_; } bool in_shared_object(const std::string &bin_path); const std::string &name() { return name_; } const std::string &bin_path() { return bin_path_; } const std::string &provider() { return provider_; } friend class Context; friend class ::ebpf::BPF; friend class ::ebpf::USDT; }; class Context { std::vector> probes_; std::unordered_set modules_; optional pid_; optional pid_stat_; std::string cmd_bin_path_; bool loaded_; static void _each_probe(const char *binpath, const struct bcc_elf_usdt *probe, void *p); static int _each_module(mod_info *, int enter_ns, void *p); void add_probe(const char *binpath, const struct bcc_elf_usdt *probe); std::string resolve_bin_path(const std::string &bin_path); private: uint8_t mod_match_inode_only_; public: Context(const std::string &bin_path, uint8_t mod_match_inode_only = 0); Context(int pid, uint8_t mod_match_inode_only = 0); Context(int pid, const std::string &bin_path, uint8_t mod_match_inode_only = 0); ~Context(); optional pid() const { return pid_; } bool loaded() const { return loaded_; } size_t num_probes() const { return probes_.size(); } const std::string & cmd_bin_path() const { return cmd_bin_path_; } Probe *get(const std::string &probe_name); Probe *get(const std::string &provider_name, const std::string &probe_name); Probe *get(int pos) { return probes_[pos].get(); } bool enable_probe(const std::string &probe_name, const std::string &fn_name); bool enable_probe(const std::string &provider_name, const std::string &probe_name, const std::string &fn_name); typedef void (*each_cb)(struct bcc_usdt *); void each(each_cb callback); typedef void (*each_uprobe_cb)(const char *, const char *, uint64_t, int); void each_uprobe(each_uprobe_cb callback); friend class ::ebpf::BPF; friend class ::ebpf::USDT; }; } bpfcc-0.12.0/src/cc/usdt/000077500000000000000000000000001357404205000150025ustar00rootroot00000000000000bpfcc-0.12.0/src/cc/usdt/CMakeLists.txt000066400000000000000000000000651357404205000175430ustar00rootroot00000000000000add_library(usdt-static STATIC usdt_args.cc usdt.cc) bpfcc-0.12.0/src/cc/usdt/usdt.cc000066400000000000000000000407271357404205000163020ustar00rootroot00000000000000/* * Copyright (c) 2016 GitHub, Inc. * * 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. */ #include #include #include #include #include #include #include #include #include "bcc_elf.h" #include "bcc_proc.h" #include "common.h" #include "usdt.h" #include "vendor/tinyformat.hpp" #include "bcc_usdt.h" namespace USDT { Location::Location(uint64_t addr, const std::string &bin_path, const char *arg_fmt) : address_(addr), bin_path_(bin_path) { #ifdef __aarch64__ ArgumentParser_aarch64 parser(arg_fmt); #elif __powerpc64__ ArgumentParser_powerpc64 parser(arg_fmt); #elif __s390x__ ArgumentParser_s390x parser(arg_fmt); #else ArgumentParser_x64 parser(arg_fmt); #endif while (!parser.done()) { Argument arg; if (!parser.parse(&arg)) continue; arguments_.push_back(std::move(arg)); } } Probe::Probe(const char *bin_path, const char *provider, const char *name, uint64_t semaphore, const optional &pid, uint8_t mod_match_inode_only) : bin_path_(bin_path), provider_(provider), name_(name), semaphore_(semaphore), pid_(pid), mod_match_inode_only_(mod_match_inode_only) {} bool Probe::in_shared_object(const std::string &bin_path) { if (object_type_map_.find(bin_path) == object_type_map_.end()) { return (object_type_map_[bin_path] = bcc_elf_is_shared_obj(bin_path.c_str())); } return object_type_map_[bin_path]; } bool Probe::resolve_global_address(uint64_t *global, const std::string &bin_path, const uint64_t addr) { if (in_shared_object(bin_path)) { return (pid_ && !bcc_resolve_global_addr(*pid_, bin_path.c_str(), addr, mod_match_inode_only_, global)); } *global = addr; return true; } bool Probe::add_to_semaphore(int16_t val) { assert(pid_); if (!attached_semaphore_) { uint64_t addr; if (!resolve_global_address(&addr, bin_path_, semaphore_)) return false; attached_semaphore_ = addr; } off_t address = static_cast(attached_semaphore_.value()); std::string procmem = tfm::format("/proc/%d/mem", pid_.value()); int memfd = ::open(procmem.c_str(), O_RDWR); if (memfd < 0) return false; int16_t original; if (::lseek(memfd, address, SEEK_SET) < 0 || ::read(memfd, &original, 2) != 2) { ::close(memfd); return false; } original = original + val; if (::lseek(memfd, address, SEEK_SET) < 0 || ::write(memfd, &original, 2) != 2) { ::close(memfd); return false; } ::close(memfd); return true; } bool Probe::enable(const std::string &fn_name) { if (attached_to_) return false; if (need_enable()) { if (!pid_) return false; if (!add_to_semaphore(+1)) return false; } attached_to_ = fn_name; return true; } bool Probe::disable() { if (!attached_to_) return false; attached_to_ = nullopt; if (need_enable()) { assert(pid_); return add_to_semaphore(-1); } return true; } std::string Probe::largest_arg_type(size_t arg_n) { Argument *largest = nullptr; for (Location &location : locations_) { Argument *candidate = &location.arguments_[arg_n]; if (!largest || std::abs(candidate->arg_size()) > std::abs(largest->arg_size())) largest = candidate; } assert(largest); return largest->ctype(); } bool Probe::usdt_getarg(std::ostream &stream) { if (!attached_to_ || attached_to_->empty()) return false; return usdt_getarg(stream, attached_to_.value()); } bool Probe::usdt_getarg(std::ostream &stream, const std::string& probe_func) { const size_t arg_count = locations_[0].arguments_.size(); if (arg_count == 0) return true; for (size_t arg_n = 0; arg_n < arg_count; ++arg_n) { std::string ctype = largest_arg_type(arg_n); std::string cptr = tfm::format("*((%s *)dest)", ctype); tfm::format(stream, "static __always_inline int _bpf_readarg_%s_%d(" "struct pt_regs *ctx, void *dest, size_t len) {\n" " if (len != sizeof(%s)) return -1;\n", probe_func, arg_n + 1, ctype); if (locations_.size() == 1) { Location &location = locations_.front(); stream << " "; if (!location.arguments_[arg_n].assign_to_local(stream, cptr, location.bin_path_, pid_)) return false; stream << "\n return 0;\n}\n"; } else { stream << " switch(PT_REGS_IP(ctx)) {\n"; for (Location &location : locations_) { uint64_t global_address; if (!resolve_global_address(&global_address, location.bin_path_, location.address_)) return false; tfm::format(stream, " case 0x%xULL: ", global_address); if (!location.arguments_[arg_n].assign_to_local(stream, cptr, location.bin_path_, pid_)) return false; stream << " return 0;\n"; } stream << " }\n"; stream << " return -1;\n}\n"; } } return true; } void Probe::add_location(uint64_t addr, const std::string &bin_path, const char *fmt) { locations_.emplace_back(addr, bin_path, fmt); } void Probe::finalize_locations() { std::sort(locations_.begin(), locations_.end(), [](const Location &a, const Location &b) { return a.bin_path_ < b.bin_path_ || a.address_ < b.address_; }); auto last = std::unique(locations_.begin(), locations_.end(), [](const Location &a, const Location &b) { return a.bin_path_ == b.bin_path_ && a.address_ == b.address_; }); locations_.erase(last, locations_.end()); } void Context::_each_probe(const char *binpath, const struct bcc_elf_usdt *probe, void *p) { Context *ctx = static_cast(p); ctx->add_probe(binpath, probe); } int Context::_each_module(mod_info *mod, int enter_ns, void *p) { Context *ctx = static_cast(p); std::string path = mod->name; if (ctx->pid_ && *ctx->pid_ != -1 && enter_ns) { path = tfm::format("/proc/%d/root%s", *ctx->pid_, path); } // Modules may be reported multiple times if they contain more than one // executable region. We are going to parse the ELF on disk anyway, so we // don't need these duplicates. if (ctx->modules_.insert(path).second /*inserted new?*/) { bcc_elf_foreach_usdt(path.c_str(), _each_probe, p); } return 0; } void Context::add_probe(const char *binpath, const struct bcc_elf_usdt *probe) { for (auto &p : probes_) { if (p->provider_ == probe->provider && p->name_ == probe->name) { p->add_location(probe->pc, binpath, probe->arg_fmt); return; } } probes_.emplace_back( new Probe(binpath, probe->provider, probe->name, probe->semaphore, pid_, mod_match_inode_only_) ); probes_.back()->add_location(probe->pc, binpath, probe->arg_fmt); } std::string Context::resolve_bin_path(const std::string &bin_path) { std::string result; if (char *which = bcc_procutils_which(bin_path.c_str())) { result = which; ::free(which); } else if (char *which_so = bcc_procutils_which_so(bin_path.c_str(), 0)) { result = which_so; ::free(which_so); } if (!result.empty() && pid_ && *pid_ != -1 && result.find("/proc") != 0) { result = tfm::format("/proc/%d/root%s", *pid_, result); } return result; } Probe *Context::get(const std::string &probe_name) { for (auto &p : probes_) { if (p->name_ == probe_name) return p.get(); } return nullptr; } Probe *Context::get(const std::string &provider_name, const std::string &probe_name) { for (auto &p : probes_) { if (p->provider_ == provider_name && p->name_ == probe_name) return p.get(); } return nullptr; } bool Context::enable_probe(const std::string &probe_name, const std::string &fn_name) { return enable_probe("", probe_name, fn_name); } bool Context::enable_probe(const std::string &provider_name, const std::string &probe_name, const std::string &fn_name) { if (pid_stat_ && pid_stat_->is_stale()) return false; Probe *found_probe = nullptr; for (auto &p : probes_) { if (p->name_ == probe_name && (provider_name.empty() || p->provider() == provider_name)) { if (found_probe != nullptr) { fprintf(stderr, "Two same-name probes (%s) but different providers\n", probe_name.c_str()); return false; } found_probe = p.get(); } } if (found_probe != nullptr) return found_probe->enable(fn_name); return false; } void Context::each(each_cb callback) { for (const auto &probe : probes_) { struct bcc_usdt info = {0}; info.provider = probe->provider().c_str(); info.bin_path = probe->bin_path().c_str(); info.name = probe->name().c_str(); info.semaphore = probe->semaphore(); info.num_locations = probe->num_locations(); info.num_arguments = probe->num_arguments(); callback(&info); } } void Context::each_uprobe(each_uprobe_cb callback) { for (auto &p : probes_) { if (!p->enabled()) continue; for (Location &loc : p->locations_) { callback(loc.bin_path_.c_str(), p->attached_to_->c_str(), loc.address_, pid_.value_or(-1)); } } } Context::Context(const std::string &bin_path, uint8_t mod_match_inode_only) : loaded_(false), mod_match_inode_only_(mod_match_inode_only) { std::string full_path = resolve_bin_path(bin_path); if (!full_path.empty()) { if (bcc_elf_foreach_usdt(full_path.c_str(), _each_probe, this) == 0) { cmd_bin_path_ = full_path; loaded_ = true; } } for (const auto &probe : probes_) probe->finalize_locations(); } Context::Context(int pid, uint8_t mod_match_inode_only) : pid_(pid), pid_stat_(pid), loaded_(false), mod_match_inode_only_(mod_match_inode_only) { if (bcc_procutils_each_module(pid, _each_module, this) == 0) { cmd_bin_path_ = ebpf::get_pid_exe(pid); if (cmd_bin_path_.empty()) return; loaded_ = true; } for (const auto &probe : probes_) probe->finalize_locations(); } Context::Context(int pid, const std::string &bin_path, uint8_t mod_match_inode_only) : pid_(pid), pid_stat_(pid), loaded_(false), mod_match_inode_only_(mod_match_inode_only) { std::string full_path = resolve_bin_path(bin_path); if (!full_path.empty()) { int res = bcc_elf_foreach_usdt(full_path.c_str(), _each_probe, this); if (res == 0) { cmd_bin_path_ = ebpf::get_pid_exe(pid); if (cmd_bin_path_.empty()) return; loaded_ = true; } } for (const auto &probe : probes_) probe->finalize_locations(); } Context::~Context() { if (pid_stat_ && !pid_stat_->is_stale()) { for (auto &p : probes_) p->disable(); } } } extern "C" { void *bcc_usdt_new_frompid(int pid, const char *path) { USDT::Context *ctx; if (!path) { ctx = new USDT::Context(pid); } else { struct stat buffer; if (strlen(path) >= 1 && path[0] != '/') { fprintf(stderr, "HINT: Binary path should be absolute.\n\n"); return nullptr; } else if (stat(path, &buffer) == -1) { fprintf(stderr, "HINT: Specified binary doesn't exist.\n\n"); return nullptr; } ctx = new USDT::Context(pid, path); } if (!ctx->loaded()) { delete ctx; return nullptr; } return static_cast(ctx); } void *bcc_usdt_new_frompath(const char *path) { USDT::Context *ctx = new USDT::Context(path); if (!ctx->loaded()) { delete ctx; return nullptr; } return static_cast(ctx); } void bcc_usdt_close(void *usdt) { if (usdt) { USDT::Context *ctx = static_cast(usdt); delete ctx; } } int bcc_usdt_enable_probe(void *usdt, const char *probe_name, const char *fn_name) { USDT::Context *ctx = static_cast(usdt); return ctx->enable_probe(probe_name, fn_name) ? 0 : -1; } int bcc_usdt_enable_fully_specified_probe(void *usdt, const char *provider_name, const char *probe_name, const char *fn_name) { USDT::Context *ctx = static_cast(usdt); return ctx->enable_probe(provider_name, probe_name, fn_name) ? 0 : -1; } const char *bcc_usdt_genargs(void **usdt_array, int len) { static std::string storage_; std::ostringstream stream; if (!len) return ""; stream << USDT::USDT_PROGRAM_HEADER; // Generate genargs codes for an array of USDT Contexts. // // Each cmd_bin_path + probe_provider + probe_name // uniquely identifies a probe. std::unordered_set generated_probes; for (int i = 0; i < len; i++) { USDT::Context *ctx = static_cast(usdt_array[i]); for (size_t j = 0; j < ctx->num_probes(); j++) { USDT::Probe *p = ctx->get(j); if (p->enabled()) { std::string key = ctx->cmd_bin_path() + "*" + p->provider() + "*" + p->name(); if (generated_probes.find(key) != generated_probes.end()) continue; if (!p->usdt_getarg(stream)) return nullptr; generated_probes.insert(key); } } } storage_ = stream.str(); return storage_.c_str(); } const char *bcc_usdt_get_probe_argctype( void *ctx, const char* probe_name, const int arg_index ) { USDT::Probe *p = static_cast(ctx)->get(probe_name); if (p) return p->get_arg_ctype(arg_index).c_str(); return ""; } void bcc_usdt_foreach(void *usdt, bcc_usdt_cb callback) { USDT::Context *ctx = static_cast(usdt); ctx->each(callback); } int bcc_usdt_get_location(void *usdt, const char *provider_name, const char *probe_name, int index, struct bcc_usdt_location *location) { USDT::Context *ctx = static_cast(usdt); USDT::Probe *probe = ctx->get(provider_name, probe_name); if (!probe) return -1; if (index < 0 || (size_t)index >= probe->num_locations()) return -1; location->address = probe->address(index); location->bin_path = probe->location_bin_path(index); return 0; } int bcc_usdt_get_argument(void *usdt, const char *provider_name, const char *probe_name, int location_index, int argument_index, struct bcc_usdt_argument *argument) { USDT::Context *ctx = static_cast(usdt); USDT::Probe *probe = ctx->get(provider_name, probe_name); if (!probe) return -1; if (argument_index < 0 || (size_t)argument_index >= probe->num_arguments()) return -1; if (location_index < 0 || (size_t)location_index >= probe->num_locations()) return -1; auto const &location = probe->location(location_index); auto const &arg = location.arguments_[argument_index]; argument->size = arg.arg_size(); argument->valid = BCC_USDT_ARGUMENT_NONE; if (arg.constant()) { argument->valid |= BCC_USDT_ARGUMENT_CONSTANT; argument->constant = *(arg.constant()); } if (arg.deref_offset()) { argument->valid |= BCC_USDT_ARGUMENT_DEREF_OFFSET; argument->deref_offset = *(arg.deref_offset()); } if (arg.deref_ident()) { argument->valid |= BCC_USDT_ARGUMENT_DEREF_IDENT; argument->deref_ident = arg.deref_ident()->c_str(); } if (arg.base_register_name()) { argument->valid |= BCC_USDT_ARGUMENT_BASE_REGISTER_NAME; argument->base_register_name = arg.base_register_name()->c_str(); } if (arg.index_register_name()) { argument->valid |= BCC_USDT_ARGUMENT_INDEX_REGISTER_NAME; argument->index_register_name = arg.index_register_name()->c_str(); } if (arg.scale()) { argument->valid |= BCC_USDT_ARGUMENT_SCALE; argument->scale = *(arg.scale()); } return 0; } void bcc_usdt_foreach_uprobe(void *usdt, bcc_usdt_uprobe_cb callback) { USDT::Context *ctx = static_cast(usdt); ctx->each_uprobe(callback); } } bpfcc-0.12.0/src/cc/usdt/usdt_args.cc000066400000000000000000000415341357404205000173130ustar00rootroot00000000000000/* * Copyright (c) 2016 GitHub, Inc. * * 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. */ #include #include #include "syms.h" #include "usdt.h" #include "vendor/tinyformat.hpp" #include "bcc_elf.h" #include "bcc_syms.h" namespace USDT { Argument::Argument() {} Argument::~Argument() {} std::string Argument::ctype() const { const int s = arg_size() * 8; return (s < 0) ? tfm::format("int%d_t", -s) : tfm::format("uint%d_t", s); } bool Argument::get_global_address(uint64_t *address, const std::string &binpath, const optional &pid) const { if (pid) { static struct bcc_symbol_option default_option = { .use_debug_file = 1, .check_debug_file_crc = 1, .lazy_symbolize = 1, .use_symbol_type = BCC_SYM_ALL_TYPES }; return ProcSyms(*pid, &default_option) .resolve_name(binpath.c_str(), deref_ident_->c_str(), address); } if (!bcc_elf_is_shared_obj(binpath.c_str())) { struct bcc_symbol sym; if (bcc_resolve_symname(binpath.c_str(), deref_ident_->c_str(), 0x0, -1, nullptr, &sym) == 0) { *address = sym.offset; if (sym.module) ::free(const_cast(sym.module)); return true; } } return false; } bool Argument::assign_to_local(std::ostream &stream, const std::string &local_name, const std::string &binpath, const optional &pid) const { if (constant_) { tfm::format(stream, "%s = %d;", local_name, *constant_); return true; } if (!deref_offset_) { tfm::format(stream, "%s = ctx->%s;", local_name, *base_register_name_); // Put a compiler barrier to prevent optimization // like llvm SimplifyCFG SinkThenElseCodeToEnd // Volatile marking is not sufficient to prevent such optimization. tfm::format(stream, " %s", COMPILER_BARRIER); return true; } if (deref_offset_ && !deref_ident_) { tfm::format(stream, "{ u64 __addr = ctx->%s + %d", *base_register_name_, *deref_offset_); if (index_register_name_) { int scale = scale_.value_or(1); tfm::format(stream, " + (ctx->%s * %d);", *index_register_name_, scale); } else { tfm::format(stream, ";"); } // Theoretically, llvm SimplifyCFG SinkThenElseCodeToEnd may still // sink bpf_probe_read call, so put a barrier here to prevent sinking // of ctx->#fields. tfm::format(stream, " %s ", COMPILER_BARRIER); tfm::format(stream, "%s __res = 0x0; " "bpf_probe_read(&__res, sizeof(__res), (void *)__addr); " "%s = __res; }", ctype(), local_name); return true; } if (deref_offset_ && deref_ident_ && *base_register_name_ == "ip") { uint64_t global_address; if (!get_global_address(&global_address, binpath, pid)) return false; tfm::format(stream, "{ u64 __addr = 0x%xull + %d; %s __res = 0x0; " "bpf_probe_read(&__res, sizeof(__res), (void *)__addr); " "%s = __res; }", global_address, *deref_offset_, ctype(), local_name); return true; } return false; } void ArgumentParser::print_error(ssize_t pos) { fprintf(stderr, "Parse error:\n %s\n", arg_); for (ssize_t i = 0; i < pos + 4; ++i) fputc('-', stderr); fputc('^', stderr); fputc('\n', stderr); } void ArgumentParser::skip_whitespace_from(size_t pos) { while (isspace(arg_[pos])) pos++; cur_pos_ = pos; } void ArgumentParser::skip_until_whitespace_from(size_t pos) { while (arg_[pos] != '\0' && !isspace(arg_[pos])) pos++; cur_pos_ = pos; } bool ArgumentParser_aarch64::parse_register(ssize_t pos, ssize_t &new_pos, optional *reg_num) { new_pos = parse_number(pos, reg_num); if (new_pos == pos || *reg_num < 0 || *reg_num > 31) return error_return(pos, pos); return true; } bool ArgumentParser_aarch64::parse_size(ssize_t pos, ssize_t &new_pos, optional *arg_size) { int abs_arg_size; new_pos = parse_number(pos, arg_size); if (new_pos == pos) return error_return(pos, pos); abs_arg_size = abs(arg_size->value()); if (abs_arg_size != 1 && abs_arg_size != 2 && abs_arg_size != 4 && abs_arg_size != 8) return error_return(pos, pos); return true; } bool ArgumentParser_aarch64::parse_mem(ssize_t pos, ssize_t &new_pos, optional *reg_num, optional *offset) { if (arg_[pos] != 'x') return error_return(pos, pos); if (parse_register(pos + 1, new_pos, reg_num) == false) return false; if (arg_[new_pos] == ',') { pos = new_pos + 1; new_pos = parse_number(pos, offset); if (new_pos == pos) return error_return(pos, pos); } if (arg_[new_pos] != ']') return error_return(new_pos, new_pos); new_pos++; return true; } bool ArgumentParser_aarch64::parse(Argument *dest) { if (done()) return false; // Support the following argument patterns: // [-]@, [-]@, [-]@[], or // [-]@[,] ssize_t cur_pos = cur_pos_, new_pos; optional arg_size; // Parse [-] if (parse_size(cur_pos, new_pos, &arg_size) == false) return false; dest->arg_size_ = arg_size; // Make sure '@' present if (arg_[new_pos] != '@') return error_return(new_pos, new_pos); cur_pos = new_pos + 1; if (arg_[cur_pos] == 'x') { // Parse ...@ optional reg_num; if (parse_register(cur_pos + 1, new_pos, ®_num) == false) return false; cur_pos_ = new_pos; dest->base_register_name_ = "regs[" + std::to_string(reg_num.value()) + "]"; } else if (arg_[cur_pos] == '[') { // Parse ...@[] and ...@[] optional reg_num, offset = 0; if (parse_mem(cur_pos + 1, new_pos, ®_num, &offset) == false) return false; cur_pos_ = new_pos; dest->base_register_name_ = "regs[" + std::to_string(reg_num.value()) + "]"; dest->deref_offset_ = offset; } else { // Parse ...@ optional val; new_pos = parse_number(cur_pos, &val); if (cur_pos == new_pos) return error_return(cur_pos, cur_pos); cur_pos_ = new_pos; dest->constant_ = val; } skip_whitespace_from(cur_pos_); return true; } bool ArgumentParser_powerpc64::parse(Argument *dest) { if (done()) return false; bool matched; std::smatch matches; std::string arg_str(&arg_[cur_pos_]); std::regex arg_n_regex("^(\\-?[1248])\\@"); // Operands with constants of form iNUM or i-NUM std::regex arg_op_regex_const("^i(\\-?[0-9]+)( +|$)"); // Operands with register only of form REG or %rREG std::regex arg_op_regex_reg("^(?:%r)?([1-2]?[0-9]|3[0-1])( +|$)"); // Operands with a base register and an offset of form // NUM(REG) or -NUM(REG) or NUM(%rREG) or -NUM(%rREG) std::regex arg_op_regex_breg_off( "^(\\-?[0-9]+)\\((?:%r)?([1-2]?[0-9]|3[0-1])\\)( +|$)"); // Operands with a base register and an index register // of form REG,REG or %rREG,%rREG std::regex arg_op_regex_breg_ireg( "^(?:%r)?([1-2]?[0-9]|3[0-1])\\,(?:%r)?([1-2]?[0-9]|3[0-1])( +|$)"); matched = std::regex_search(arg_str, matches, arg_n_regex); if (matched) { dest->arg_size_ = stoi(matches.str(1)); cur_pos_ += matches.length(0); arg_str = &arg_[cur_pos_]; if (std::regex_search(arg_str, matches, arg_op_regex_const)) { dest->constant_ = stoi(matches.str(1)); } else if (std::regex_search(arg_str, matches, arg_op_regex_reg)) { dest->base_register_name_ = "gpr[" + matches.str(1) + "]"; } else if (std::regex_search(arg_str, matches, arg_op_regex_breg_off)) { dest->deref_offset_ = stoi(matches.str(1)); dest->base_register_name_ = "gpr[" + matches.str(2) + "]"; } else if (std::regex_search(arg_str, matches, arg_op_regex_breg_ireg)) { dest->deref_offset_ = 0; // In powerpc64, such operands contain a base // register and an index register which are // part of an indexed load/store operation. // Even if no offset value is present, this // is required by Argument::assign_to_local() // in order to generate code for reading the // argument. So, this is set to zero. dest->base_register_name_ = "gpr[" + matches.str(1) + "]"; dest->index_register_name_ = "gpr[" + matches.str(2) + "]"; dest->scale_ = abs(*dest->arg_size_); } else { matched = false; } } if (!matched) { print_error(cur_pos_); skip_until_whitespace_from(cur_pos_); skip_whitespace_from(cur_pos_); return false; } cur_pos_ += matches.length(0); skip_whitespace_from(cur_pos_); return true; } bool ArgumentParser_s390x::parse(Argument *dest) { if (done()) return false; bool matched; std::cmatch matches; #define S390X_IMM "(-?[0-9]+)" std::regex arg_n_regex("^" S390X_IMM "@"); // std::regex arg_op_regex_imm("^" S390X_IMM "(?: +|$)"); // %r #define S390X_REG "%r([0-9]|1[0-5])" std::regex arg_op_regex_reg("^" S390X_REG "(?: +|$)"); // (%r,%r) std::regex arg_op_regex_mem("^" S390X_IMM "?\\(" S390X_REG "(?:," S390X_REG ")?\\)(?: +|$)"); #undef S390X_IMM #undef S390X_REG matched = std::regex_search(arg_ + cur_pos_, matches, arg_n_regex); if (matched) { dest->arg_size_ = stoi(matches.str(1)); cur_pos_ += matches.length(0); if (std::regex_search(arg_ + cur_pos_, matches, arg_op_regex_imm)) { dest->constant_ = stoi(matches.str(1)); } else if (std::regex_search(arg_ + cur_pos_, matches, arg_op_regex_reg)) { dest->base_register_name_ = "gprs[" + matches.str(1) + "]"; } else if (std::regex_search(arg_ + cur_pos_, matches, arg_op_regex_mem)) { if (matches.length(1) > 0) { dest->deref_offset_ = stoi(matches.str(1)); } dest->base_register_name_ = "gprs[" + matches.str(2) + "]"; if (matches.length(3) > 0) { dest->index_register_name_ = "gprs[" + matches.str(3) + "]"; } } else { matched = false; } } if (!matched) { print_error(cur_pos_); skip_until_whitespace_from(cur_pos_); skip_whitespace_from(cur_pos_); return false; } cur_pos_ += matches.length(0); skip_whitespace_from(cur_pos_); return true; } ssize_t ArgumentParser_x64::parse_identifier(ssize_t pos, optional *result) { if (isalpha(arg_[pos]) || arg_[pos] == '_') { ssize_t start = pos++; while (isalnum(arg_[pos]) || arg_[pos] == '_') pos++; if (pos - start) result->emplace(arg_ + start, pos - start); } return pos; } ssize_t ArgumentParser_x64::parse_register(ssize_t pos, std::string &name, int &size) { ssize_t start = ++pos; if (arg_[start - 1] != '%') return -start; while (isalnum(arg_[pos])) pos++; std::string regname(arg_ + start, pos - start); if (!normalize_register(®name, &size)) return -start; name = regname; return pos; } ssize_t ArgumentParser_x64::parse_base_register(ssize_t pos, Argument *dest) { int size; std::string name; ssize_t res = parse_register(pos, name, size); if (res < 0) return res; dest->base_register_name_ = name; if (!dest->arg_size_) dest->arg_size_ = size; return res; } ssize_t ArgumentParser_x64::parse_index_register(ssize_t pos, Argument *dest) { int size; std::string name; ssize_t res = parse_register(pos, name, size); if (res < 0) return res; dest->index_register_name_ = name; return res; } ssize_t ArgumentParser_x64::parse_scale(ssize_t pos, Argument *dest) { return parse_number(pos, &dest->scale_); } ssize_t ArgumentParser_x64::parse_expr(ssize_t pos, Argument *dest) { if (arg_[pos] == '$') return parse_number(pos + 1, &dest->constant_); if (arg_[pos] == '%') return parse_base_register(pos, dest); if (isdigit(arg_[pos]) || arg_[pos] == '-') { pos = parse_number(pos, &dest->deref_offset_); if (arg_[pos] == '+') { pos = parse_identifier(pos + 1, &dest->deref_ident_); if (!dest->deref_ident_) return -pos; } } else { dest->deref_offset_ = 0; pos = parse_identifier(pos, &dest->deref_ident_); if (arg_[pos] == '+' || arg_[pos] == '-') { pos = parse_number(pos, &dest->deref_offset_); } } if (arg_[pos] != '(') return -pos; pos = parse_base_register(pos + 1, dest); if (pos < 0) return pos; if (arg_[pos] == ',') { pos = parse_index_register(pos + 1, dest); if (pos < 0) return pos; if (arg_[pos] == ',') { pos = parse_scale(pos + 1, dest); if (pos < 0) return pos; } } return (arg_[pos] == ')') ? pos + 1 : -pos; } ssize_t ArgumentParser_x64::parse_1(ssize_t pos, Argument *dest) { if (isdigit(arg_[pos]) || arg_[pos] == '-') { optional asize; ssize_t m = parse_number(pos, &asize); if (arg_[m] == '@' && asize) { dest->arg_size_ = asize; return parse_expr(m + 1, dest); } } return parse_expr(pos, dest); } bool ArgumentParser_x64::parse(Argument *dest) { if (done()) return false; ssize_t res = parse_1(cur_pos_, dest); if (res < 0) return error_return(-res, -res + 1); if (!isspace(arg_[res]) && arg_[res] != '\0') return error_return(res, res); skip_whitespace_from(res); return true; } const std::unordered_map ArgumentParser_x64::registers_ = { {"rax", {REG_A, 8}}, {"eax", {REG_A, 4}}, {"ax", {REG_A, 2}}, {"al", {REG_A, 1}}, {"rbx", {REG_B, 8}}, {"ebx", {REG_B, 4}}, {"bx", {REG_B, 2}}, {"bl", {REG_B, 1}}, {"rcx", {REG_C, 8}}, {"ecx", {REG_C, 4}}, {"cx", {REG_C, 2}}, {"cl", {REG_C, 1}}, {"rdx", {REG_D, 8}}, {"edx", {REG_D, 4}}, {"dx", {REG_D, 2}}, {"dl", {REG_D, 1}}, {"rsi", {REG_SI, 8}}, {"esi", {REG_SI, 4}}, {"si", {REG_SI, 2}}, {"sil", {REG_SI, 1}}, {"rdi", {REG_DI, 8}}, {"edi", {REG_DI, 4}}, {"di", {REG_DI, 2}}, {"dil", {REG_DI, 1}}, {"rbp", {REG_BP, 8}}, {"ebp", {REG_BP, 4}}, {"bp", {REG_BP, 2}}, {"bpl", {REG_BP, 1}}, {"rsp", {REG_SP, 8}}, {"esp", {REG_SP, 4}}, {"sp", {REG_SP, 2}}, {"spl", {REG_SP, 1}}, {"r8", {REG_8, 8}}, {"r8d", {REG_8, 4}}, {"r8w", {REG_8, 2}}, {"r8b", {REG_8, 1}}, {"r9", {REG_9, 8}}, {"r9d", {REG_9, 4}}, {"r9w", {REG_9, 2}}, {"r9b", {REG_9, 1}}, {"r10", {REG_10, 8}}, {"r10d", {REG_10, 4}}, {"r10w", {REG_10, 2}}, {"r10b", {REG_10, 1}}, {"r11", {REG_11, 8}}, {"r11d", {REG_11, 4}}, {"r11w", {REG_11, 2}}, {"r11b", {REG_11, 1}}, {"r12", {REG_12, 8}}, {"r12d", {REG_12, 4}}, {"r12w", {REG_12, 2}}, {"r12b", {REG_12, 1}}, {"r13", {REG_13, 8}}, {"r13d", {REG_13, 4}}, {"r13w", {REG_13, 2}}, {"r13b", {REG_13, 1}}, {"r14", {REG_14, 8}}, {"r14d", {REG_14, 4}}, {"r14w", {REG_14, 2}}, {"r14b", {REG_14, 1}}, {"r15", {REG_15, 8}}, {"r15d", {REG_15, 4}}, {"r15w", {REG_15, 2}}, {"r15b", {REG_15, 1}}, {"rip", {REG_RIP, 8}}, }; void ArgumentParser_x64::reg_to_name(std::string *norm, Register reg) { switch (reg) { case REG_A: *norm = "ax"; break; case REG_B: *norm = "bx"; break; case REG_C: *norm = "cx"; break; case REG_D: *norm = "dx"; break; case REG_SI: *norm = "si"; break; case REG_DI: *norm = "di"; break; case REG_BP: *norm = "bp"; break; case REG_SP: *norm = "sp"; break; case REG_8: *norm = "r8"; break; case REG_9: *norm = "r9"; break; case REG_10: *norm = "r10"; break; case REG_11: *norm = "r11"; break; case REG_12: *norm = "r12"; break; case REG_13: *norm = "r13"; break; case REG_14: *norm = "r14"; break; case REG_15: *norm = "r15"; break; case REG_RIP: *norm = "ip"; break; } } bool ArgumentParser_x64::normalize_register(std::string *reg, int *reg_size) { auto it = registers_.find(*reg); if (it == registers_.end()) return false; *reg_size = it->second.size; reg_to_name(reg, it->second.reg); return true; } } bpfcc-0.12.0/src/cc/vendor/000077500000000000000000000000001357404205000153205ustar00rootroot00000000000000bpfcc-0.12.0/src/cc/vendor/optional.hpp000066400000000000000000000666731357404205000177000ustar00rootroot00000000000000// Copyright (C) 2011 - 2012 Andrzej Krzemienski. // // Use, modification, and distribution is subject to the Boost Software // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // // The idea and interface is based on Boost.Optional library // authored by Fernando Luis Cacciola Carballal # ifndef ___OPTIONAL_HPP___ # define ___OPTIONAL_HPP___ # include # include # include # include # include # include # include # define TR2_OPTIONAL_REQUIRES(...) typename enable_if<__VA_ARGS__::value, bool>::type = false # if defined __GNUC__ // NOTE: GNUC is also defined for Clang # if (__GNUC__ == 4) && (__GNUC_MINOR__ >= 8) # define TR2_OPTIONAL_GCC_4_8_AND_HIGHER___ # elif (__GNUC__ > 4) # define TR2_OPTIONAL_GCC_4_8_AND_HIGHER___ # endif # # if (__GNUC__ == 4) && (__GNUC_MINOR__ >= 7) # define TR2_OPTIONAL_GCC_4_7_AND_HIGHER___ # elif (__GNUC__ > 4) # define TR2_OPTIONAL_GCC_4_7_AND_HIGHER___ # endif # # if (__GNUC__ == 4) && (__GNUC_MINOR__ == 8) && (__GNUC_PATCHLEVEL__ >= 1) # define TR2_OPTIONAL_GCC_4_8_1_AND_HIGHER___ # elif (__GNUC__ == 4) && (__GNUC_MINOR__ >= 9) # define TR2_OPTIONAL_GCC_4_8_1_AND_HIGHER___ # elif (__GNUC__ > 4) # define TR2_OPTIONAL_GCC_4_8_1_AND_HIGHER___ # endif # endif # # if defined __clang_major__ # if (__clang_major__ == 3 && __clang_minor__ >= 5) # define TR2_OPTIONAL_CLANG_3_5_AND_HIGHTER_ # elif (__clang_major__ > 3) # define TR2_OPTIONAL_CLANG_3_5_AND_HIGHTER_ # endif # if defined TR2_OPTIONAL_CLANG_3_5_AND_HIGHTER_ # define TR2_OPTIONAL_CLANG_3_4_2_AND_HIGHER_ # elif (__clang_major__ == 3 && __clang_minor__ == 4 && __clang_patchlevel__ >= 2) # define TR2_OPTIONAL_CLANG_3_4_2_AND_HIGHER_ # endif # endif # # if defined _MSC_VER # if (_MSC_VER >= 1900) # define TR2_OPTIONAL_MSVC_2015_AND_HIGHER___ # endif # endif # if defined __clang__ # if (__clang_major__ > 2) || (__clang_major__ == 2) && (__clang_minor__ >= 9) # define OPTIONAL_HAS_THIS_RVALUE_REFS 1 # else # define OPTIONAL_HAS_THIS_RVALUE_REFS 0 # endif # elif defined TR2_OPTIONAL_GCC_4_8_1_AND_HIGHER___ # define OPTIONAL_HAS_THIS_RVALUE_REFS 1 # elif defined TR2_OPTIONAL_MSVC_2015_AND_HIGHER___ # define OPTIONAL_HAS_THIS_RVALUE_REFS 1 # else # define OPTIONAL_HAS_THIS_RVALUE_REFS 0 # endif # if defined TR2_OPTIONAL_GCC_4_8_1_AND_HIGHER___ # define OPTIONAL_HAS_CONSTEXPR_INIT_LIST 1 # define OPTIONAL_CONSTEXPR_INIT_LIST constexpr # else # define OPTIONAL_HAS_CONSTEXPR_INIT_LIST 0 # define OPTIONAL_CONSTEXPR_INIT_LIST # endif # if defined TR2_OPTIONAL_CLANG_3_5_AND_HIGHTER_ && (defined __cplusplus) && (__cplusplus != 201103L) # define OPTIONAL_HAS_MOVE_ACCESSORS 1 # else # define OPTIONAL_HAS_MOVE_ACCESSORS 0 # endif # // In C++11 constexpr implies const, so we need to make non-const members also non-constexpr # if (defined __cplusplus) && (__cplusplus == 201103L) # define OPTIONAL_MUTABLE_CONSTEXPR # else # define OPTIONAL_MUTABLE_CONSTEXPR constexpr # endif namespace std{ namespace experimental{ // BEGIN workaround for missing is_trivially_destructible # if defined TR2_OPTIONAL_GCC_4_8_AND_HIGHER___ // leave it: it is already there # elif defined TR2_OPTIONAL_CLANG_3_4_2_AND_HIGHER_ // leave it: it is already there # elif defined TR2_OPTIONAL_MSVC_2015_AND_HIGHER___ // leave it: it is already there # elif defined TR2_OPTIONAL_DISABLE_EMULATION_OF_TYPE_TRAITS // leave it: the user doesn't want it # else template using is_trivially_destructible = std::has_trivial_destructor; # endif // END workaround for missing is_trivially_destructible # if (defined TR2_OPTIONAL_GCC_4_7_AND_HIGHER___) // leave it; our metafunctions are already defined. # elif defined TR2_OPTIONAL_CLANG_3_4_2_AND_HIGHER_ // leave it; our metafunctions are already defined. # elif defined TR2_OPTIONAL_MSVC_2015_AND_HIGHER___ // leave it: it is already there # elif defined TR2_OPTIONAL_DISABLE_EMULATION_OF_TYPE_TRAITS // leave it: the user doesn't want it # else // workaround for missing traits in GCC and CLANG template struct is_nothrow_move_constructible { constexpr static bool value = std::is_nothrow_constructible::value; }; template struct is_assignable { template constexpr static bool has_assign(...) { return false; } template () = std::declval(), true)) > // the comma operator is necessary for the cases where operator= returns void constexpr static bool has_assign(bool) { return true; } constexpr static bool value = has_assign(true); }; template struct is_nothrow_move_assignable { template struct has_nothrow_move_assign { constexpr static bool value = false; }; template struct has_nothrow_move_assign { constexpr static bool value = noexcept( std::declval() = std::declval() ); }; constexpr static bool value = has_nothrow_move_assign::value>::value; }; // end workaround # endif // 20.5.4, optional for object types template class optional; // 20.5.5, optional for lvalue reference types template class optional; // workaround: std utility functions aren't constexpr yet template inline constexpr T&& constexpr_forward(typename std::remove_reference::type& t) noexcept { return static_cast(t); } template inline constexpr T&& constexpr_forward(typename std::remove_reference::type&& t) noexcept { static_assert(!std::is_lvalue_reference::value, "!!"); return static_cast(t); } template inline constexpr typename std::remove_reference::type&& constexpr_move(T&& t) noexcept { return static_cast::type&&>(t); } #if defined NDEBUG # define TR2_OPTIONAL_ASSERTED_EXPRESSION(CHECK, EXPR) (EXPR) #else # define TR2_OPTIONAL_ASSERTED_EXPRESSION(CHECK, EXPR) ((CHECK) ? (EXPR) : ([]{assert(!#CHECK);}(), (EXPR))) #endif namespace detail_ { // static_addressof: a constexpr version of addressof template struct has_overloaded_addressof { template constexpr static bool has_overload(...) { return false; } template ().operator&()) > constexpr static bool has_overload(bool) { return true; } constexpr static bool value = has_overload(true); }; template )> constexpr T* static_addressof(T& ref) { return &ref; } template )> T* static_addressof(T& ref) { return std::addressof(ref); } // the call to convert(b) has return type A and converts b to type A iff b decltype(b) is implicitly convertible to A template U convert(U v) { return v; } } // namespace detail constexpr struct trivial_init_t{} trivial_init{}; // 20.5.6, In-place construction constexpr struct in_place_t{} in_place{}; // 20.5.7, Disengaged state indicator struct nullopt_t { struct init{}; constexpr explicit nullopt_t(init){} }; constexpr nullopt_t nullopt{nullopt_t::init()}; // 20.5.8, class bad_optional_access class bad_optional_access : public logic_error { public: explicit bad_optional_access(const string& what_arg) : logic_error{what_arg} {} explicit bad_optional_access(const char* what_arg) : logic_error{what_arg} {} }; template union storage_t { unsigned char dummy_; T value_; constexpr storage_t( trivial_init_t ) noexcept : dummy_() {}; template constexpr storage_t( Args&&... args ) : value_(constexpr_forward(args)...) {} ~storage_t(){} }; template union constexpr_storage_t { unsigned char dummy_; T value_; constexpr constexpr_storage_t( trivial_init_t ) noexcept : dummy_() {}; template constexpr constexpr_storage_t( Args&&... args ) : value_(constexpr_forward(args)...) {} ~constexpr_storage_t() = default; }; template struct optional_base { bool init_; storage_t storage_; constexpr optional_base() noexcept : init_(false), storage_(trivial_init) {}; explicit constexpr optional_base(const T& v) : init_(true), storage_(v) {} explicit constexpr optional_base(T&& v) : init_(true), storage_(constexpr_move(v)) {} template explicit optional_base(in_place_t, Args&&... args) : init_(true), storage_(constexpr_forward(args)...) {} template >)> explicit optional_base(in_place_t, std::initializer_list il, Args&&... args) : init_(true), storage_(il, std::forward(args)...) {} ~optional_base() { if (init_) storage_.value_.T::~T(); } }; template struct constexpr_optional_base { bool init_; constexpr_storage_t storage_; constexpr constexpr_optional_base() noexcept : init_(false), storage_(trivial_init) {}; explicit constexpr constexpr_optional_base(const T& v) : init_(true), storage_(v) {} explicit constexpr constexpr_optional_base(T&& v) : init_(true), storage_(constexpr_move(v)) {} template explicit constexpr constexpr_optional_base(in_place_t, Args&&... args) : init_(true), storage_(constexpr_forward(args)...) {} template >)> OPTIONAL_CONSTEXPR_INIT_LIST explicit constexpr_optional_base(in_place_t, std::initializer_list il, Args&&... args) : init_(true), storage_(il, std::forward(args)...) {} ~constexpr_optional_base() = default; }; template using OptionalBase = typename std::conditional< is_trivially_destructible::value, constexpr_optional_base, optional_base >::type; template class optional : private OptionalBase { static_assert( !std::is_same::type, nullopt_t>::value, "bad T" ); static_assert( !std::is_same::type, in_place_t>::value, "bad T" ); constexpr bool initialized() const noexcept { return OptionalBase::init_; } T* dataptr() { return std::addressof(OptionalBase::storage_.value_); } constexpr const T* dataptr() const { return detail_::static_addressof(OptionalBase::storage_.value_); } # if OPTIONAL_HAS_THIS_RVALUE_REFS == 1 constexpr const T& contained_val() const& { return OptionalBase::storage_.value_; } # if OPTIONAL_HAS_MOVE_ACCESSORS == 1 OPTIONAL_MUTABLE_CONSTEXPR T&& contained_val() && { return std::move(OptionalBase::storage_.value_); } OPTIONAL_MUTABLE_CONSTEXPR T& contained_val() & { return OptionalBase::storage_.value_; } # else T& contained_val() & { return OptionalBase::storage_.value_; } T&& contained_val() && { return std::move(OptionalBase::storage_.value_); } # endif # else constexpr const T& contained_val() const { return OptionalBase::storage_.value_; } T& contained_val() { return OptionalBase::storage_.value_; } # endif void clear() noexcept { if (initialized()) dataptr()->T::~T(); OptionalBase::init_ = false; } template void initialize(Args&&... args) noexcept(noexcept(T(std::forward(args)...))) { assert(!OptionalBase::init_); ::new (static_cast(dataptr())) T(std::forward(args)...); OptionalBase::init_ = true; } template void initialize(std::initializer_list il, Args&&... args) noexcept(noexcept(T(il, std::forward(args)...))) { assert(!OptionalBase::init_); ::new (static_cast(dataptr())) T(il, std::forward(args)...); OptionalBase::init_ = true; } public: typedef T value_type; // 20.5.5.1, constructors constexpr optional() noexcept : OptionalBase() {}; constexpr optional(nullopt_t) noexcept : OptionalBase() {}; optional(const optional& rhs) : OptionalBase() { if (rhs.initialized()) { ::new (static_cast(dataptr())) T(*rhs); OptionalBase::init_ = true; } } optional(optional&& rhs) noexcept(is_nothrow_move_constructible::value) : OptionalBase() { if (rhs.initialized()) { ::new (static_cast(dataptr())) T(std::move(*rhs)); OptionalBase::init_ = true; } } constexpr optional(const T& v) : OptionalBase(v) {} constexpr optional(T&& v) : OptionalBase(constexpr_move(v)) {} template explicit constexpr optional(in_place_t, Args&&... args) : OptionalBase(in_place_t{}, constexpr_forward(args)...) {} template >)> OPTIONAL_CONSTEXPR_INIT_LIST explicit optional(in_place_t, std::initializer_list il, Args&&... args) : OptionalBase(in_place_t{}, il, constexpr_forward(args)...) {} // 20.5.4.2, Destructor ~optional() = default; // 20.5.4.3, assignment optional& operator=(nullopt_t) noexcept { clear(); return *this; } optional& operator=(const optional& rhs) { if (initialized() == true && rhs.initialized() == false) clear(); else if (initialized() == false && rhs.initialized() == true) initialize(*rhs); else if (initialized() == true && rhs.initialized() == true) contained_val() = *rhs; return *this; } optional& operator=(optional&& rhs) noexcept(is_nothrow_move_assignable::value && is_nothrow_move_constructible::value) { if (initialized() == true && rhs.initialized() == false) clear(); else if (initialized() == false && rhs.initialized() == true) initialize(std::move(*rhs)); else if (initialized() == true && rhs.initialized() == true) contained_val() = std::move(*rhs); return *this; } template auto operator=(U&& v) -> typename enable_if < is_same::type, T>::value, optional& >::type { if (initialized()) { contained_val() = std::forward(v); } else { initialize(std::forward(v)); } return *this; } template void emplace(Args&&... args) { clear(); initialize(std::forward(args)...); } template void emplace(initializer_list il, Args&&... args) { clear(); initialize(il, std::forward(args)...); } // 20.5.4.4, Swap void swap(optional& rhs) noexcept(is_nothrow_move_constructible::value && noexcept(swap(declval(), declval()))) { if (initialized() == true && rhs.initialized() == false) { rhs.initialize(std::move(**this)); clear(); } else if (initialized() == false && rhs.initialized() == true) { initialize(std::move(*rhs)); rhs.clear(); } else if (initialized() == true && rhs.initialized() == true) { using std::swap; swap(**this, *rhs); } } // 20.5.4.5, Observers explicit constexpr operator bool() const noexcept { return initialized(); } constexpr T const* operator ->() const { return TR2_OPTIONAL_ASSERTED_EXPRESSION(initialized(), dataptr()); } # if OPTIONAL_HAS_MOVE_ACCESSORS == 1 OPTIONAL_MUTABLE_CONSTEXPR T* operator ->() { assert (initialized()); return dataptr(); } constexpr T const& operator *() const& { return TR2_OPTIONAL_ASSERTED_EXPRESSION(initialized(), contained_val()); } OPTIONAL_MUTABLE_CONSTEXPR T& operator *() & { assert (initialized()); return contained_val(); } OPTIONAL_MUTABLE_CONSTEXPR T&& operator *() && { assert (initialized()); return constexpr_move(contained_val()); } constexpr T const& value() const& { return initialized() ? contained_val() : (throw bad_optional_access("bad optional access"), contained_val()); } OPTIONAL_MUTABLE_CONSTEXPR T& value() & { return initialized() ? contained_val() : (throw bad_optional_access("bad optional access"), contained_val()); } OPTIONAL_MUTABLE_CONSTEXPR T&& value() && { if (!initialized()) throw bad_optional_access("bad optional access"); return std::move(contained_val()); } # else T* operator ->() { assert (initialized()); return dataptr(); } constexpr T const& operator *() const { return TR2_OPTIONAL_ASSERTED_EXPRESSION(initialized(), contained_val()); } T& operator *() { assert (initialized()); return contained_val(); } constexpr T const& value() const { return initialized() ? contained_val() : (throw bad_optional_access("bad optional access"), contained_val()); } T& value() { return initialized() ? contained_val() : (throw bad_optional_access("bad optional access"), contained_val()); } # endif # if OPTIONAL_HAS_THIS_RVALUE_REFS == 1 template constexpr T value_or(V&& v) const& { return *this ? **this : detail_::convert(constexpr_forward(v)); } # if OPTIONAL_HAS_MOVE_ACCESSORS == 1 template OPTIONAL_MUTABLE_CONSTEXPR T value_or(V&& v) && { return *this ? constexpr_move(const_cast&>(*this).contained_val()) : detail_::convert(constexpr_forward(v)); } # else template T value_or(V&& v) && { return *this ? constexpr_move(const_cast&>(*this).contained_val()) : detail_::convert(constexpr_forward(v)); } # endif # else template constexpr T value_or(V&& v) const { return *this ? **this : detail_::convert(constexpr_forward(v)); } # endif }; template class optional { static_assert( !std::is_same::value, "bad T" ); static_assert( !std::is_same::value, "bad T" ); T* ref; public: // 20.5.5.1, construction/destruction constexpr optional() noexcept : ref(nullptr) {} constexpr optional(nullopt_t) noexcept : ref(nullptr) {} constexpr optional(T& v) noexcept : ref(detail_::static_addressof(v)) {} optional(T&&) = delete; constexpr optional(const optional& rhs) noexcept : ref(rhs.ref) {} explicit constexpr optional(in_place_t, T& v) noexcept : ref(detail_::static_addressof(v)) {} explicit optional(in_place_t, T&&) = delete; ~optional() = default; // 20.5.5.2, mutation optional& operator=(nullopt_t) noexcept { ref = nullptr; return *this; } // optional& operator=(const optional& rhs) noexcept { // ref = rhs.ref; // return *this; // } // optional& operator=(optional&& rhs) noexcept { // ref = rhs.ref; // return *this; // } template auto operator=(U&& rhs) noexcept -> typename enable_if < is_same::type, optional>::value, optional& >::type { ref = rhs.ref; return *this; } template auto operator=(U&& rhs) noexcept -> typename enable_if < !is_same::type, optional>::value, optional& >::type = delete; void emplace(T& v) noexcept { ref = detail_::static_addressof(v); } void emplace(T&&) = delete; void swap(optional& rhs) noexcept { std::swap(ref, rhs.ref); } // 20.5.5.3, observers constexpr T* operator->() const { return TR2_OPTIONAL_ASSERTED_EXPRESSION(ref, ref); } constexpr T& operator*() const { return TR2_OPTIONAL_ASSERTED_EXPRESSION(ref, *ref); } constexpr T& value() const { return ref ? *ref : (throw bad_optional_access("bad optional access"), *ref); } explicit constexpr operator bool() const noexcept { return ref != nullptr; } template constexpr typename decay::type value_or(V&& v) const { return *this ? **this : detail_::convert::type>(constexpr_forward(v)); } }; template class optional { static_assert( sizeof(T) == 0, "optional rvalue references disallowed" ); }; // 20.5.8, Relational operators template constexpr bool operator==(const optional& x, const optional& y) { return bool(x) != bool(y) ? false : bool(x) == false ? true : *x == *y; } template constexpr bool operator!=(const optional& x, const optional& y) { return !(x == y); } template constexpr bool operator<(const optional& x, const optional& y) { return (!y) ? false : (!x) ? true : *x < *y; } template constexpr bool operator>(const optional& x, const optional& y) { return (y < x); } template constexpr bool operator<=(const optional& x, const optional& y) { return !(y < x); } template constexpr bool operator>=(const optional& x, const optional& y) { return !(x < y); } // 20.5.9, Comparison with nullopt template constexpr bool operator==(const optional& x, nullopt_t) noexcept { return (!x); } template constexpr bool operator==(nullopt_t, const optional& x) noexcept { return (!x); } template constexpr bool operator!=(const optional& x, nullopt_t) noexcept { return bool(x); } template constexpr bool operator!=(nullopt_t, const optional& x) noexcept { return bool(x); } template constexpr bool operator<(const optional&, nullopt_t) noexcept { return false; } template constexpr bool operator<(nullopt_t, const optional& x) noexcept { return bool(x); } template constexpr bool operator<=(const optional& x, nullopt_t) noexcept { return (!x); } template constexpr bool operator<=(nullopt_t, const optional&) noexcept { return true; } template constexpr bool operator>(const optional& x, nullopt_t) noexcept { return bool(x); } template constexpr bool operator>(nullopt_t, const optional&) noexcept { return false; } template constexpr bool operator>=(const optional&, nullopt_t) noexcept { return true; } template constexpr bool operator>=(nullopt_t, const optional& x) noexcept { return (!x); } // 20.5.10, Comparison with T template constexpr bool operator==(const optional& x, const T& v) { return bool(x) ? *x == v : false; } template constexpr bool operator==(const T& v, const optional& x) { return bool(x) ? v == *x : false; } template constexpr bool operator!=(const optional& x, const T& v) { return bool(x) ? *x != v : true; } template constexpr bool operator!=(const T& v, const optional& x) { return bool(x) ? v != *x : true; } template constexpr bool operator<(const optional& x, const T& v) { return bool(x) ? *x < v : true; } template constexpr bool operator>(const T& v, const optional& x) { return bool(x) ? v > *x : true; } template constexpr bool operator>(const optional& x, const T& v) { return bool(x) ? *x > v : false; } template constexpr bool operator<(const T& v, const optional& x) { return bool(x) ? v < *x : false; } template constexpr bool operator>=(const optional& x, const T& v) { return bool(x) ? *x >= v : false; } template constexpr bool operator<=(const T& v, const optional& x) { return bool(x) ? v <= *x : false; } template constexpr bool operator<=(const optional& x, const T& v) { return bool(x) ? *x <= v : true; } template constexpr bool operator>=(const T& v, const optional& x) { return bool(x) ? v >= *x : true; } // Comparison of optional with T template constexpr bool operator==(const optional& x, const T& v) { return bool(x) ? *x == v : false; } template constexpr bool operator==(const T& v, const optional& x) { return bool(x) ? v == *x : false; } template constexpr bool operator!=(const optional& x, const T& v) { return bool(x) ? *x != v : true; } template constexpr bool operator!=(const T& v, const optional& x) { return bool(x) ? v != *x : true; } template constexpr bool operator<(const optional& x, const T& v) { return bool(x) ? *x < v : true; } template constexpr bool operator>(const T& v, const optional& x) { return bool(x) ? v > *x : true; } template constexpr bool operator>(const optional& x, const T& v) { return bool(x) ? *x > v : false; } template constexpr bool operator<(const T& v, const optional& x) { return bool(x) ? v < *x : false; } template constexpr bool operator>=(const optional& x, const T& v) { return bool(x) ? *x >= v : false; } template constexpr bool operator<=(const T& v, const optional& x) { return bool(x) ? v <= *x : false; } template constexpr bool operator<=(const optional& x, const T& v) { return bool(x) ? *x <= v : true; } template constexpr bool operator>=(const T& v, const optional& x) { return bool(x) ? v >= *x : true; } // Comparison of optional with T template constexpr bool operator==(const optional& x, const T& v) { return bool(x) ? *x == v : false; } template constexpr bool operator==(const T& v, const optional& x) { return bool(x) ? v == *x : false; } template constexpr bool operator!=(const optional& x, const T& v) { return bool(x) ? *x != v : true; } template constexpr bool operator!=(const T& v, const optional& x) { return bool(x) ? v != *x : true; } template constexpr bool operator<(const optional& x, const T& v) { return bool(x) ? *x < v : true; } template constexpr bool operator>(const T& v, const optional& x) { return bool(x) ? v > *x : true; } template constexpr bool operator>(const optional& x, const T& v) { return bool(x) ? *x > v : false; } template constexpr bool operator<(const T& v, const optional& x) { return bool(x) ? v < *x : false; } template constexpr bool operator>=(const optional& x, const T& v) { return bool(x) ? *x >= v : false; } template constexpr bool operator<=(const T& v, const optional& x) { return bool(x) ? v <= *x : false; } template constexpr bool operator<=(const optional& x, const T& v) { return bool(x) ? *x <= v : true; } template constexpr bool operator>=(const T& v, const optional& x) { return bool(x) ? v >= *x : true; } // 20.5.12, Specialized algorithms template void swap(optional& x, optional& y) noexcept(noexcept(x.swap(y))) { x.swap(y); } template constexpr optional::type> make_optional(T&& v) { return optional::type>(constexpr_forward(v)); } template constexpr optional make_optional(reference_wrapper v) { return optional(v.get()); } } // namespace experimental } // namespace std namespace std { template struct hash> { typedef typename hash::result_type result_type; typedef std::experimental::optional argument_type; constexpr result_type operator()(argument_type const& arg) const { return arg ? std::hash{}(*arg) : result_type{}; } }; template struct hash> { typedef typename hash::result_type result_type; typedef std::experimental::optional argument_type; constexpr result_type operator()(argument_type const& arg) const { return arg ? std::hash{}(*arg) : result_type{}; } }; } # undef TR2_OPTIONAL_REQUIRES # undef TR2_OPTIONAL_ASSERTED_EXPRESSION # endif //___OPTIONAL_HPP___ bpfcc-0.12.0/src/cc/vendor/tinyformat.hpp000066400000000000000000001234221357404205000202310ustar00rootroot00000000000000// tinyformat.h // Copyright (C) 2011, Chris Foster [chris42f (at) gmail (d0t) com] // // Boost Software License - Version 1.0 // // Permission is hereby granted, free of charge, to any person or organization // obtaining a copy of the software and accompanying documentation covered by // this license (the "Software") to use, reproduce, display, distribute, // execute, and transmit the Software, and to prepare derivative works of the // Software, and to permit third-parties to whom the Software is furnished to // do so, all subject to the following: // // The copyright notices in the Software and this entire statement, including // the above license grant, this restriction and the following disclaimer, // must be included in all copies of the Software, in whole or in part, and // all derivative works of the Software, unless such copies or derivative // works are solely in the form of machine-executable object code generated by // a source language processor. // // 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, TITLE AND NON-INFRINGEMENT. IN NO EVENT // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. //------------------------------------------------------------------------------ // Tinyformat: A minimal type safe printf replacement // // tinyformat.h is a type safe printf replacement library in a single C++ // header file. Design goals include: // // * Type safety and extensibility for user defined types. // * C99 printf() compatibility, to the extent possible using std::ostream // * Simplicity and minimalism. A single header file to include and distribute // with your projects. // * Augment rather than replace the standard stream formatting mechanism // * C++98 support, with optional C++11 niceties // // // Main interface example usage // ---------------------------- // // To print a date to std::cout: // // std::string weekday = "Wednesday"; // const char* month = "July"; // size_t day = 27; // long hour = 14; // int min = 44; // // tfm::printf("%s, %s %d, %.2d:%.2d\n", weekday, month, day, hour, min); // // The strange types here emphasize the type safety of the interface; it is // possible to print a std::string using the "%s" conversion, and a // size_t using the "%d" conversion. A similar result could be achieved // using either of the tfm::format() functions. One prints on a user provided // stream: // // tfm::format(std::cerr, "%s, %s %d, %.2d:%.2d\n", // weekday, month, day, hour, min); // // The other returns a std::string: // // std::string date = tfm::format("%s, %s %d, %.2d:%.2d\n", // weekday, month, day, hour, min); // std::cout << date; // // These are the three primary interface functions. There is also a // convenience function printfln() which appends a newline to the usual result // of printf() for super simple logging. // // // User defined format functions // ----------------------------- // // Simulating variadic templates in C++98 is pretty painful since it requires // writing out the same function for each desired number of arguments. To make // this bearable tinyformat comes with a set of macros which are used // internally to generate the API, but which may also be used in user code. // // The three macros TINYFORMAT_ARGTYPES(n), TINYFORMAT_VARARGS(n) and // TINYFORMAT_PASSARGS(n) will generate a list of n argument types, // type/name pairs and argument names respectively when called with an integer // n between 1 and 16. We can use these to define a macro which generates the // desired user defined function with n arguments. To generate all 16 user // defined function bodies, use the macro TINYFORMAT_FOREACH_ARGNUM. For an // example, see the implementation of printf() at the end of the source file. // // Sometimes it's useful to be able to pass a list of format arguments through // to a non-template function. The FormatList class is provided as a way to do // this by storing the argument list in a type-opaque way. Continuing the // example from above, we construct a FormatList using makeFormatList(): // // FormatListRef formatList = tfm::makeFormatList(weekday, month, day, hour, min); // // The format list can now be passed into any non-template function and used // via a call to the vformat() function: // // tfm::vformat(std::cout, "%s, %s %d, %.2d:%.2d\n", formatList); // // // Additional API information // -------------------------- // // Error handling: Define TINYFORMAT_ERROR to customize the error handling for // format strings which are unsupported or have the wrong number of format // specifiers (calls assert() by default). // // User defined types: Uses operator<< for user defined types by default. // Overload formatValue() for more control. #ifndef TINYFORMAT_H_INCLUDED #define TINYFORMAT_H_INCLUDED namespace tinyformat {} //------------------------------------------------------------------------------ // Config section. Customize to your liking! // Namespace alias to encourage brevity namespace tfm = tinyformat; // Error handling; calls assert() by default. // #define TINYFORMAT_ERROR(reasonString) your_error_handler(reasonString) // Define for C++11 variadic templates which make the code shorter & more // general. If you don't define this, C++11 support is autodetected below. // #define TINYFORMAT_USE_VARIADIC_TEMPLATES //------------------------------------------------------------------------------ // Implementation details. #include #include #include #include #ifndef TINYFORMAT_ERROR # define TINYFORMAT_ERROR(reason) assert(0 && reason) #endif #if !defined(TINYFORMAT_USE_VARIADIC_TEMPLATES) && !defined(TINYFORMAT_NO_VARIADIC_TEMPLATES) # ifdef __GXX_EXPERIMENTAL_CXX0X__ # define TINYFORMAT_USE_VARIADIC_TEMPLATES # endif #endif #if defined(__GLIBCXX__) && __GLIBCXX__ < 20080201 // std::showpos is broken on old libstdc++ as provided with OSX. See // http://gcc.gnu.org/ml/libstdc++/2007-11/msg00075.html # define TINYFORMAT_OLD_LIBSTDCPLUSPLUS_WORKAROUND #endif #ifdef __APPLE__ // Workaround OSX linker warning: xcode uses different default symbol // visibilities for static libs vs executables (see issue #25) # define TINYFORMAT_HIDDEN __attribute__((visibility("hidden"))) #else # define TINYFORMAT_HIDDEN #endif namespace tinyformat { //------------------------------------------------------------------------------ namespace detail { // Test whether type T1 is convertible to type T2 template struct is_convertible { private: // two types of different size struct fail { char dummy[2]; }; struct succeed { char dummy; }; // Try to convert a T1 to a T2 by plugging into tryConvert static fail tryConvert(...); static succeed tryConvert(const T2&); static const T1& makeT1(); public: # ifdef _MSC_VER // Disable spurious loss of precision warnings in tryConvert(makeT1()) # pragma warning(push) # pragma warning(disable:4244) # pragma warning(disable:4267) # endif // Standard trick: the (...) version of tryConvert will be chosen from // the overload set only if the version taking a T2 doesn't match. // Then we compare the sizes of the return types to check which // function matched. Very neat, in a disgusting kind of way :) static const bool value = sizeof(tryConvert(makeT1())) == sizeof(succeed); # ifdef _MSC_VER # pragma warning(pop) # endif }; // Detect when a type is not a wchar_t string template struct is_wchar { typedef int tinyformat_wchar_is_not_supported; }; template<> struct is_wchar {}; template<> struct is_wchar {}; template struct is_wchar {}; template struct is_wchar {}; // Format the value by casting to type fmtT. This default implementation // should never be called. template::value> struct formatValueAsType { static void invoke(std::ostream& /*out*/, const T& /*value*/) { assert(0); } }; // Specialized version for types that can actually be converted to fmtT, as // indicated by the "convertible" template parameter. template struct formatValueAsType { static void invoke(std::ostream& out, const T& value) { out << static_cast(value); } }; #ifdef TINYFORMAT_OLD_LIBSTDCPLUSPLUS_WORKAROUND template::value> struct formatZeroIntegerWorkaround { static bool invoke(std::ostream& /**/, const T& /**/) { return false; } }; template struct formatZeroIntegerWorkaround { static bool invoke(std::ostream& out, const T& value) { if (static_cast(value) == 0 && out.flags() & std::ios::showpos) { out << "+0"; return true; } return false; } }; #endif // TINYFORMAT_OLD_LIBSTDCPLUSPLUS_WORKAROUND // Convert an arbitrary type to integer. The version with convertible=false // throws an error. template::value> struct convertToInt { static int invoke(const T& /*value*/) { TINYFORMAT_ERROR("tinyformat: Cannot convert from argument type to " "integer for use as variable width or precision"); return 0; } }; // Specialization for convertToInt when conversion is possible template struct convertToInt { static int invoke(const T& value) { return static_cast(value); } }; // Format at most ntrunc characters to the given stream. template inline void formatTruncated(std::ostream& out, const T& value, int ntrunc) { std::ostringstream tmp; tmp << value; std::string result = tmp.str(); out.write(result.c_str(), std::min(ntrunc, static_cast(result.size()))); } #define TINYFORMAT_DEFINE_FORMAT_TRUNCATED_CSTR(type) \ inline void formatTruncated(std::ostream& out, type* value, int ntrunc) \ { \ std::streamsize len = 0; \ while(len < ntrunc && value[len] != 0) \ ++len; \ out.write(value, len); \ } // Overload for const char* and char*. Could overload for signed & unsigned // char too, but these are technically unneeded for printf compatibility. TINYFORMAT_DEFINE_FORMAT_TRUNCATED_CSTR(const char) TINYFORMAT_DEFINE_FORMAT_TRUNCATED_CSTR(char) #undef TINYFORMAT_DEFINE_FORMAT_TRUNCATED_CSTR } // namespace detail //------------------------------------------------------------------------------ // Variable formatting functions. May be overridden for user-defined types if // desired. /// Format a value into a stream, delegating to operator<< by default. /// /// Users may override this for their own types. When this function is called, /// the stream flags will have been modified according to the format string. /// The format specification is provided in the range [fmtBegin, fmtEnd). For /// truncating conversions, ntrunc is set to the desired maximum number of /// characters, for example "%.7s" calls formatValue with ntrunc = 7. /// /// By default, formatValue() uses the usual stream insertion operator /// operator<< to format the type T, with special cases for the %c and %p /// conversions. template inline void formatValue(std::ostream& out, const char* /*fmtBegin*/, const char* fmtEnd, int ntrunc, const T& value) { #ifndef TINYFORMAT_ALLOW_WCHAR_STRINGS // Since we don't support printing of wchar_t using "%ls", make it fail at // compile time in preference to printing as a void* at runtime. typedef typename detail::is_wchar::tinyformat_wchar_is_not_supported DummyType; (void) DummyType(); // avoid unused type warning with gcc-4.8 #endif // The mess here is to support the %c and %p conversions: if these // conversions are active we try to convert the type to a char or const // void* respectively and format that instead of the value itself. For the // %p conversion it's important to avoid dereferencing the pointer, which // could otherwise lead to a crash when printing a dangling (const char*). bool canConvertToChar = detail::is_convertible::value; bool canConvertToVoidPtr = detail::is_convertible::value; if(canConvertToChar && *(fmtEnd-1) == 'c') detail::formatValueAsType::invoke(out, value); else if(canConvertToVoidPtr && *(fmtEnd-1) == 'p') detail::formatValueAsType::invoke(out, value); #ifdef TINYFORMAT_OLD_LIBSTDCPLUSPLUS_WORKAROUND else if(detail::formatZeroIntegerWorkaround::invoke(out, value)) /**/; #endif else if(ntrunc >= 0) { // Take care not to overread C strings in truncating conversions like // "%.4s" where at most 4 characters may be read. detail::formatTruncated(out, value, ntrunc); } else out << value; } // Overloaded version for char types to support printing as an integer #define TINYFORMAT_DEFINE_FORMATVALUE_CHAR(charType) \ inline void formatValue(std::ostream& out, const char* /*fmtBegin*/, \ const char* fmtEnd, int /**/, charType value) \ { \ switch(*(fmtEnd-1)) \ { \ case 'u': case 'd': case 'i': case 'o': case 'X': case 'x': \ out << static_cast(value); break; \ default: \ out << value; break; \ } \ } // per 3.9.1: char, signed char and unsigned char are all distinct types TINYFORMAT_DEFINE_FORMATVALUE_CHAR(char) TINYFORMAT_DEFINE_FORMATVALUE_CHAR(signed char) TINYFORMAT_DEFINE_FORMATVALUE_CHAR(unsigned char) #undef TINYFORMAT_DEFINE_FORMATVALUE_CHAR //------------------------------------------------------------------------------ // Tools for emulating variadic templates in C++98. The basic idea here is // stolen from the boost preprocessor metaprogramming library and cut down to // be just general enough for what we need. #define TINYFORMAT_ARGTYPES(n) TINYFORMAT_ARGTYPES_ ## n #define TINYFORMAT_VARARGS(n) TINYFORMAT_VARARGS_ ## n #define TINYFORMAT_PASSARGS(n) TINYFORMAT_PASSARGS_ ## n #define TINYFORMAT_PASSARGS_TAIL(n) TINYFORMAT_PASSARGS_TAIL_ ## n // To keep it as transparent as possible, the macros below have been generated // using python via the excellent cog.py code generation script. This avoids // the need for a bunch of complex (but more general) preprocessor tricks as // used in boost.preprocessor. // // To rerun the code generation in place, use `cog.py -r tinyformat.h` // (see http://nedbatchelder.com/code/cog). Alternatively you can just create // extra versions by hand. /*[[[cog maxParams = 16 def makeCommaSepLists(lineTemplate, elemTemplate, startInd=1): for j in range(startInd,maxParams+1): list = ', '.join([elemTemplate % {'i':i} for i in range(startInd,j+1)]) cog.outl(lineTemplate % {'j':j, 'list':list}) makeCommaSepLists('#define TINYFORMAT_ARGTYPES_%(j)d %(list)s', 'class T%(i)d') cog.outl() makeCommaSepLists('#define TINYFORMAT_VARARGS_%(j)d %(list)s', 'const T%(i)d& v%(i)d') cog.outl() makeCommaSepLists('#define TINYFORMAT_PASSARGS_%(j)d %(list)s', 'v%(i)d') cog.outl() cog.outl('#define TINYFORMAT_PASSARGS_TAIL_1') makeCommaSepLists('#define TINYFORMAT_PASSARGS_TAIL_%(j)d , %(list)s', 'v%(i)d', startInd = 2) cog.outl() cog.outl('#define TINYFORMAT_FOREACH_ARGNUM(m) \\\n ' + ' '.join(['m(%d)' % (j,) for j in range(1,maxParams+1)])) ]]]*/ #define TINYFORMAT_ARGTYPES_1 class T1 #define TINYFORMAT_ARGTYPES_2 class T1, class T2 #define TINYFORMAT_ARGTYPES_3 class T1, class T2, class T3 #define TINYFORMAT_ARGTYPES_4 class T1, class T2, class T3, class T4 #define TINYFORMAT_ARGTYPES_5 class T1, class T2, class T3, class T4, class T5 #define TINYFORMAT_ARGTYPES_6 class T1, class T2, class T3, class T4, class T5, class T6 #define TINYFORMAT_ARGTYPES_7 class T1, class T2, class T3, class T4, class T5, class T6, class T7 #define TINYFORMAT_ARGTYPES_8 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8 #define TINYFORMAT_ARGTYPES_9 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9 #define TINYFORMAT_ARGTYPES_10 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10 #define TINYFORMAT_ARGTYPES_11 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11 #define TINYFORMAT_ARGTYPES_12 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12 #define TINYFORMAT_ARGTYPES_13 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13 #define TINYFORMAT_ARGTYPES_14 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13, class T14 #define TINYFORMAT_ARGTYPES_15 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13, class T14, class T15 #define TINYFORMAT_ARGTYPES_16 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13, class T14, class T15, class T16 #define TINYFORMAT_VARARGS_1 const T1& v1 #define TINYFORMAT_VARARGS_2 const T1& v1, const T2& v2 #define TINYFORMAT_VARARGS_3 const T1& v1, const T2& v2, const T3& v3 #define TINYFORMAT_VARARGS_4 const T1& v1, const T2& v2, const T3& v3, const T4& v4 #define TINYFORMAT_VARARGS_5 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5 #define TINYFORMAT_VARARGS_6 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6 #define TINYFORMAT_VARARGS_7 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7 #define TINYFORMAT_VARARGS_8 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8 #define TINYFORMAT_VARARGS_9 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9 #define TINYFORMAT_VARARGS_10 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9, const T10& v10 #define TINYFORMAT_VARARGS_11 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9, const T10& v10, const T11& v11 #define TINYFORMAT_VARARGS_12 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9, const T10& v10, const T11& v11, const T12& v12 #define TINYFORMAT_VARARGS_13 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9, const T10& v10, const T11& v11, const T12& v12, const T13& v13 #define TINYFORMAT_VARARGS_14 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9, const T10& v10, const T11& v11, const T12& v12, const T13& v13, const T14& v14 #define TINYFORMAT_VARARGS_15 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9, const T10& v10, const T11& v11, const T12& v12, const T13& v13, const T14& v14, const T15& v15 #define TINYFORMAT_VARARGS_16 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9, const T10& v10, const T11& v11, const T12& v12, const T13& v13, const T14& v14, const T15& v15, const T16& v16 #define TINYFORMAT_PASSARGS_1 v1 #define TINYFORMAT_PASSARGS_2 v1, v2 #define TINYFORMAT_PASSARGS_3 v1, v2, v3 #define TINYFORMAT_PASSARGS_4 v1, v2, v3, v4 #define TINYFORMAT_PASSARGS_5 v1, v2, v3, v4, v5 #define TINYFORMAT_PASSARGS_6 v1, v2, v3, v4, v5, v6 #define TINYFORMAT_PASSARGS_7 v1, v2, v3, v4, v5, v6, v7 #define TINYFORMAT_PASSARGS_8 v1, v2, v3, v4, v5, v6, v7, v8 #define TINYFORMAT_PASSARGS_9 v1, v2, v3, v4, v5, v6, v7, v8, v9 #define TINYFORMAT_PASSARGS_10 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10 #define TINYFORMAT_PASSARGS_11 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11 #define TINYFORMAT_PASSARGS_12 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12 #define TINYFORMAT_PASSARGS_13 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13 #define TINYFORMAT_PASSARGS_14 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14 #define TINYFORMAT_PASSARGS_15 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15 #define TINYFORMAT_PASSARGS_16 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16 #define TINYFORMAT_PASSARGS_TAIL_1 #define TINYFORMAT_PASSARGS_TAIL_2 , v2 #define TINYFORMAT_PASSARGS_TAIL_3 , v2, v3 #define TINYFORMAT_PASSARGS_TAIL_4 , v2, v3, v4 #define TINYFORMAT_PASSARGS_TAIL_5 , v2, v3, v4, v5 #define TINYFORMAT_PASSARGS_TAIL_6 , v2, v3, v4, v5, v6 #define TINYFORMAT_PASSARGS_TAIL_7 , v2, v3, v4, v5, v6, v7 #define TINYFORMAT_PASSARGS_TAIL_8 , v2, v3, v4, v5, v6, v7, v8 #define TINYFORMAT_PASSARGS_TAIL_9 , v2, v3, v4, v5, v6, v7, v8, v9 #define TINYFORMAT_PASSARGS_TAIL_10 , v2, v3, v4, v5, v6, v7, v8, v9, v10 #define TINYFORMAT_PASSARGS_TAIL_11 , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11 #define TINYFORMAT_PASSARGS_TAIL_12 , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12 #define TINYFORMAT_PASSARGS_TAIL_13 , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13 #define TINYFORMAT_PASSARGS_TAIL_14 , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14 #define TINYFORMAT_PASSARGS_TAIL_15 , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15 #define TINYFORMAT_PASSARGS_TAIL_16 , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16 #define TINYFORMAT_FOREACH_ARGNUM(m) \ m(1) m(2) m(3) m(4) m(5) m(6) m(7) m(8) m(9) m(10) m(11) m(12) m(13) m(14) m(15) m(16) //[[[end]]] namespace detail { // Type-opaque holder for an argument to format(), with associated actions on // the type held as explicit function pointers. This allows FormatArg's for // each argument to be allocated as a homogenous array inside FormatList // whereas a naive implementation based on inheritance does not. class FormatArg { public: FormatArg() {} template FormatArg(const T& value) : m_value(static_cast(&value)), m_formatImpl(&formatImpl), m_toIntImpl(&toIntImpl) { } void format(std::ostream& out, const char* fmtBegin, const char* fmtEnd, int ntrunc) const { m_formatImpl(out, fmtBegin, fmtEnd, ntrunc, m_value); } int toInt() const { return m_toIntImpl(m_value); } private: template TINYFORMAT_HIDDEN static void formatImpl(std::ostream& out, const char* fmtBegin, const char* fmtEnd, int ntrunc, const void* value) { formatValue(out, fmtBegin, fmtEnd, ntrunc, *static_cast(value)); } template TINYFORMAT_HIDDEN static int toIntImpl(const void* value) { return convertToInt::invoke(*static_cast(value)); } const void* m_value; void (*m_formatImpl)(std::ostream& out, const char* fmtBegin, const char* fmtEnd, int ntrunc, const void* value); int (*m_toIntImpl)(const void* value); }; // Parse and return an integer from the string c, as atoi() // On return, c is set to one past the end of the integer. inline int parseIntAndAdvance(const char*& c) { int i = 0; for(;*c >= '0' && *c <= '9'; ++c) i = 10*i + (*c - '0'); return i; } // Print literal part of format string and return next format spec // position. // // Skips over any occurrences of '%%', printing a literal '%' to the // output. The position of the first % character of the next // nontrivial format spec is returned, or the end of string. inline const char* printFormatStringLiteral(std::ostream& out, const char* fmt) { const char* c = fmt; for(;; ++c) { switch(*c) { case '\0': out.write(fmt, c - fmt); return c; case '%': out.write(fmt, c - fmt); if(*(c+1) != '%') return c; // for "%%", tack trailing % onto next literal section. fmt = ++c; break; default: break; } } } // Parse a format string and set the stream state accordingly. // // The format mini-language recognized here is meant to be the one from C99, // with the form "%[flags][width][.precision][length]type". // // Formatting options which can't be natively represented using the ostream // state are returned in spacePadPositive (for space padded positive numbers) // and ntrunc (for truncating conversions). argIndex is incremented if // necessary to pull out variable width and precision . The function returns a // pointer to the character after the end of the current format spec. inline const char* streamStateFromFormat(std::ostream& out, bool& spacePadPositive, int& ntrunc, const char* fmtStart, const detail::FormatArg* formatters, int& argIndex, int numFormatters) { if(*fmtStart != '%') { TINYFORMAT_ERROR("tinyformat: Not enough conversion specifiers in format string"); return fmtStart; } // Reset stream state to defaults. out.width(0); out.precision(6); out.fill(' '); // Reset most flags; ignore irrelevant unitbuf & skipws. out.unsetf(std::ios::adjustfield | std::ios::basefield | std::ios::floatfield | std::ios::showbase | std::ios::boolalpha | std::ios::showpoint | std::ios::showpos | std::ios::uppercase); bool precisionSet = false; bool widthSet = false; int widthExtra = 0; const char* c = fmtStart + 1; // 1) Parse flags for(;; ++c) { switch(*c) { case '#': out.setf(std::ios::showpoint | std::ios::showbase); continue; case '0': // overridden by left alignment ('-' flag) if(!(out.flags() & std::ios::left)) { // Use internal padding so that numeric values are // formatted correctly, eg -00010 rather than 000-10 out.fill('0'); out.setf(std::ios::internal, std::ios::adjustfield); } continue; case '-': out.fill(' '); out.setf(std::ios::left, std::ios::adjustfield); continue; case ' ': // overridden by show positive sign, '+' flag. if(!(out.flags() & std::ios::showpos)) spacePadPositive = true; continue; case '+': out.setf(std::ios::showpos); spacePadPositive = false; widthExtra = 1; continue; default: break; } break; } // 2) Parse width if(*c >= '0' && *c <= '9') { widthSet = true; out.width(parseIntAndAdvance(c)); } if(*c == '*') { widthSet = true; int width = 0; if(argIndex < numFormatters) width = formatters[argIndex++].toInt(); else TINYFORMAT_ERROR("tinyformat: Not enough arguments to read variable width"); if(width < 0) { // negative widths correspond to '-' flag set out.fill(' '); out.setf(std::ios::left, std::ios::adjustfield); width = -width; } out.width(width); ++c; } // 3) Parse precision if(*c == '.') { ++c; int precision = 0; if(*c == '*') { ++c; if(argIndex < numFormatters) precision = formatters[argIndex++].toInt(); else TINYFORMAT_ERROR("tinyformat: Not enough arguments to read variable precision"); } else { if(*c >= '0' && *c <= '9') precision = parseIntAndAdvance(c); else if(*c == '-') // negative precisions ignored, treated as zero. parseIntAndAdvance(++c); } out.precision(precision); precisionSet = true; } // 4) Ignore any C99 length modifier while(*c == 'l' || *c == 'h' || *c == 'L' || *c == 'j' || *c == 'z' || *c == 't') ++c; // 5) We're up to the conversion specifier character. // Set stream flags based on conversion specifier (thanks to the // boost::format class for forging the way here). bool intConversion = false; switch(*c) { case 'u': case 'd': case 'i': out.setf(std::ios::dec, std::ios::basefield); intConversion = true; break; case 'o': out.setf(std::ios::oct, std::ios::basefield); intConversion = true; break; case 'X': out.setf(std::ios::uppercase); case 'x': case 'p': out.setf(std::ios::hex, std::ios::basefield); intConversion = true; break; case 'E': out.setf(std::ios::uppercase); case 'e': out.setf(std::ios::scientific, std::ios::floatfield); out.setf(std::ios::dec, std::ios::basefield); break; case 'F': out.setf(std::ios::uppercase); case 'f': out.setf(std::ios::fixed, std::ios::floatfield); break; case 'G': out.setf(std::ios::uppercase); case 'g': out.setf(std::ios::dec, std::ios::basefield); // As in boost::format, let stream decide float format. out.flags(out.flags() & ~std::ios::floatfield); break; case 'a': case 'A': TINYFORMAT_ERROR("tinyformat: the %a and %A conversion specs " "are not supported"); break; case 'c': // Handled as special case inside formatValue() break; case 's': if(precisionSet) ntrunc = static_cast(out.precision()); // Make %s print booleans as "true" and "false" out.setf(std::ios::boolalpha); break; case 'n': // Not supported - will cause problems! TINYFORMAT_ERROR("tinyformat: %n conversion spec not supported"); break; case '\0': TINYFORMAT_ERROR("tinyformat: Conversion spec incorrectly " "terminated by end of string"); return c; default: break; } if(intConversion && precisionSet && !widthSet) { // "precision" for integers gives the minimum number of digits (to be // padded with zeros on the left). This isn't really supported by the // iostreams, but we can approximately simulate it with the width if // the width isn't otherwise used. out.width(out.precision() + widthExtra); out.setf(std::ios::internal, std::ios::adjustfield); out.fill('0'); } return c+1; } //------------------------------------------------------------------------------ inline void formatImpl(std::ostream& out, const char* fmt, const detail::FormatArg* formatters, int numFormatters) { // Saved stream state std::streamsize origWidth = out.width(); std::streamsize origPrecision = out.precision(); std::ios::fmtflags origFlags = out.flags(); char origFill = out.fill(); for (int argIndex = 0; argIndex < numFormatters; ++argIndex) { // Parse the format string fmt = printFormatStringLiteral(out, fmt); bool spacePadPositive = false; int ntrunc = -1; const char* fmtEnd = streamStateFromFormat(out, spacePadPositive, ntrunc, fmt, formatters, argIndex, numFormatters); if (argIndex >= numFormatters) { // Check args remain after reading any variable width/precision TINYFORMAT_ERROR("tinyformat: Not enough format arguments"); return; } const FormatArg& arg = formatters[argIndex]; // Format the arg into the stream. if(!spacePadPositive) arg.format(out, fmt, fmtEnd, ntrunc); else { // The following is a special case with no direct correspondence // between stream formatting and the printf() behaviour. Simulate // it crudely by formatting into a temporary string stream and // munging the resulting string. std::ostringstream tmpStream; tmpStream.copyfmt(out); tmpStream.setf(std::ios::showpos); arg.format(tmpStream, fmt, fmtEnd, ntrunc); std::string result = tmpStream.str(); // allocates... yuck. for(size_t i = 0, iend = result.size(); i < iend; ++i) if(result[i] == '+') result[i] = ' '; out << result; } fmt = fmtEnd; } // Print remaining part of format string. fmt = printFormatStringLiteral(out, fmt); if(*fmt != '\0') TINYFORMAT_ERROR("tinyformat: Too many conversion specifiers in format string"); // Restore stream state out.width(origWidth); out.precision(origPrecision); out.flags(origFlags); out.fill(origFill); } } // namespace detail /// List of template arguments format(), held in a type-opaque way. /// /// A const reference to FormatList (typedef'd as FormatListRef) may be /// conveniently used to pass arguments to non-template functions: All type /// information has been stripped from the arguments, leaving just enough of a /// common interface to perform formatting as required. class FormatList { public: FormatList(detail::FormatArg* formatters, int N) : m_formatters(formatters), m_N(N) { } friend void vformat(std::ostream& out, const char* fmt, const FormatList& list); private: const detail::FormatArg* m_formatters; int m_N; }; /// Reference to type-opaque format list for passing to vformat() typedef const FormatList& FormatListRef; namespace detail { // Format list subclass with fixed storage to avoid dynamic allocation template class FormatListN : public FormatList { public: #ifdef TINYFORMAT_USE_VARIADIC_TEMPLATES template FormatListN(const Args&... args) : FormatList(&m_formatterStore[0], N), m_formatterStore { FormatArg(args)... } { static_assert(sizeof...(args) == N, "Number of args must be N"); } #else // C++98 version void init(int) {} # define TINYFORMAT_MAKE_FORMATLIST_CONSTRUCTOR(n) \ \ template \ FormatListN(TINYFORMAT_VARARGS(n)) \ : FormatList(&m_formatterStore[0], n) \ { assert(n == N); init(0, TINYFORMAT_PASSARGS(n)); } \ \ template \ void init(int i, TINYFORMAT_VARARGS(n)) \ { \ m_formatterStore[i] = FormatArg(v1); \ init(i+1 TINYFORMAT_PASSARGS_TAIL(n)); \ } TINYFORMAT_FOREACH_ARGNUM(TINYFORMAT_MAKE_FORMATLIST_CONSTRUCTOR) # undef TINYFORMAT_MAKE_FORMATLIST_CONSTRUCTOR #endif private: FormatArg m_formatterStore[N]; }; // Special 0-arg version - MSVC says zero-sized C array in struct is nonstandard template<> class FormatListN<0> : public FormatList { public: FormatListN() : FormatList(0, 0) {} }; } // namespace detail //------------------------------------------------------------------------------ // Primary API functions #ifdef TINYFORMAT_USE_VARIADIC_TEMPLATES /// Make type-agnostic format list from list of template arguments. /// /// The exact return type of this function is an implementation detail and /// shouldn't be relied upon. Instead it should be stored as a FormatListRef: /// /// FormatListRef formatList = makeFormatList( /*...*/ ); template detail::FormatListN makeFormatList(const Args&... args) { return detail::FormatListN(args...); } #else // C++98 version inline detail::FormatListN<0> makeFormatList() { return detail::FormatListN<0>(); } #define TINYFORMAT_MAKE_MAKEFORMATLIST(n) \ template \ detail::FormatListN makeFormatList(TINYFORMAT_VARARGS(n)) \ { \ return detail::FormatListN(TINYFORMAT_PASSARGS(n)); \ } TINYFORMAT_FOREACH_ARGNUM(TINYFORMAT_MAKE_MAKEFORMATLIST) #undef TINYFORMAT_MAKE_MAKEFORMATLIST #endif /// Format list of arguments to the stream according to the given format string. /// /// The name vformat() is chosen for the semantic similarity to vprintf(): the /// list of format arguments is held in a single function argument. inline void vformat(std::ostream& out, const char* fmt, FormatListRef list) { detail::formatImpl(out, fmt, list.m_formatters, list.m_N); } #ifdef TINYFORMAT_USE_VARIADIC_TEMPLATES /// Format list of arguments to the stream according to given format string. template void format(std::ostream& out, const char* fmt, const Args&... args) { vformat(out, fmt, makeFormatList(args...)); } /// Format list of arguments according to the given format string and return /// the result as a string. template std::string format(const char* fmt, const Args&... args) { std::ostringstream oss; format(oss, fmt, args...); return oss.str(); } /// Format list of arguments to std::cout, according to the given format string template void printf(const char* fmt, const Args&... args) { format(std::cout, fmt, args...); } template void printfln(const char* fmt, const Args&... args) { format(std::cout, fmt, args...); std::cout << '\n'; } #else // C++98 version inline void format(std::ostream& out, const char* fmt) { vformat(out, fmt, makeFormatList()); } inline std::string format(const char* fmt) { std::ostringstream oss; format(oss, fmt); return oss.str(); } inline void printf(const char* fmt) { format(std::cout, fmt); } inline void printfln(const char* fmt) { format(std::cout, fmt); std::cout << '\n'; } #define TINYFORMAT_MAKE_FORMAT_FUNCS(n) \ \ template \ void format(std::ostream& out, const char* fmt, TINYFORMAT_VARARGS(n)) \ { \ vformat(out, fmt, makeFormatList(TINYFORMAT_PASSARGS(n))); \ } \ \ template \ std::string format(const char* fmt, TINYFORMAT_VARARGS(n)) \ { \ std::ostringstream oss; \ format(oss, fmt, TINYFORMAT_PASSARGS(n)); \ return oss.str(); \ } \ \ template \ void printf(const char* fmt, TINYFORMAT_VARARGS(n)) \ { \ format(std::cout, fmt, TINYFORMAT_PASSARGS(n)); \ } \ \ template \ void printfln(const char* fmt, TINYFORMAT_VARARGS(n)) \ { \ format(std::cout, fmt, TINYFORMAT_PASSARGS(n)); \ std::cout << '\n'; \ } TINYFORMAT_FOREACH_ARGNUM(TINYFORMAT_MAKE_FORMAT_FUNCS) #undef TINYFORMAT_MAKE_FORMAT_FUNCS #endif } // namespace tinyformat #endif // TINYFORMAT_H_INCLUDED bpfcc-0.12.0/src/lua/000077500000000000000000000000001357404205000142175ustar00rootroot00000000000000bpfcc-0.12.0/src/lua/.busted000066400000000000000000000003051357404205000155040ustar00rootroot00000000000000-- Configuration for unit tests -- See: http://olivinelabs.com/busted/ return { default = { lpath = "./?.lua;./?/init.lua", helper = "./bpf/spec/helper.lua", ["auto-insulate"] = false, } } bpfcc-0.12.0/src/lua/.luacheckrc000066400000000000000000000005301357404205000163220ustar00rootroot00000000000000std = 'luajit' new_read_globals = { 'assert', 'describe', 'it', } new_globals = { 'math', } -- Luacheck < 0.18 doesn't support new_read_globals for _, v in ipairs(new_read_globals) do table.insert(new_globals, v) end -- Ignore some pedantic checks ignore = { '4.1/err', -- Shadowing err '4.1/.', -- Shadowing one letter variables } bpfcc-0.12.0/src/lua/CMakeLists.txt000066400000000000000000000016511357404205000167620ustar00rootroot00000000000000find_package(LuaJIT) find_program(LUAJIT luajit) if (LUAJIT_LIBRARIES AND LUAJIT) FILE(GLOB_RECURSE SRC_LUA ${CMAKE_CURRENT_SOURCE_DIR}/bcc/*.lua ${CMAKE_CURRENT_SOURCE_DIR}/bcc/vendor/*.lua ${CMAKE_CURRENT_SOURCE_DIR}/bpf/*.lua) ADD_CUSTOM_COMMAND( OUTPUT bcc.lua COMMAND ${LUAJIT} ${CMAKE_CURRENT_SOURCE_DIR}/src/squish.lua ${CMAKE_CURRENT_SOURCE_DIR} DEPENDS ${SRC_LUA} ${CMAKE_CURRENT_SOURCE_DIR}/squishy ) ADD_CUSTOM_COMMAND( OUTPUT bcc.o COMMAND ${LUAJIT} -bg bcc.lua bcc.o DEPENDS bcc.lua ) include_directories(${LUAJIT_INCLUDE_DIR}) add_executable(bcc-lua src/main.c bcc.o) set_target_properties(bcc-lua PROPERTIES LINKER_LANGUAGE C) target_link_libraries(bcc-lua ${LUAJIT_LIBRARIES}) target_link_libraries(bcc-lua ${bcc-lua-static}) if (NOT COMPILER_NOPIE_FLAG EQUAL "") target_link_libraries(bcc-lua ${COMPILER_NOPIE_FLAG}) endif() install(TARGETS bcc-lua RUNTIME DESTINATION bin) endif() bpfcc-0.12.0/src/lua/README.md000066400000000000000000000165161357404205000155070ustar00rootroot00000000000000Lua Tools for BCC ----------------- This directory contains Lua tooling for [BCC][bcc] (the BPF Compiler Collection). BCC is a toolkit for creating userspace and kernel tracing programs. By default, it comes with a library `libbcc`, some example tooling and a Python frontend for the library. Here we present an alternate frontend for `libbcc` implemented in LuaJIT. This lets you write the userspace part of your tracer in Lua instead of Python. Since LuaJIT is a JIT compiled language, tracers implemented in `bcc-lua` exhibit significantly reduced overhead compared to their Python equivalents. This is particularly noticeable in tracers that actively use the table APIs to get information from the kernel. If your tracer makes extensive use of `BPF_MAP_TYPE_PERF_EVENT_ARRAY` or `BPF_MAP_TYPE_HASH`, you may find the performance characteristics of this implementation very appealing, as LuaJIT can compile to native code a lot of the callchain to process the events, and this wrapper has been designed to benefit from such JIT compilation. ## Quickstart Guide The following instructions assume Ubuntu 18.04 LTS. 1. Clone this repository ``` $ git clone https://github.com/iovisor/bcc.git $ cd bcc/ ``` 2. As per the [Ubuntu - Binary](https://github.com/iovisor/bcc/blob/master/INSTALL.md#ubuntu---binary) installation istructions, install the required upstream stable and signed packages ``` $ sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 4052245BD4284CDD $ echo "deb https://repo.iovisor.org/apt/$(lsb_release -cs) $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/iovisor.list $ sudo apt-get update $ sudo apt-get install bcc-tools libbcc-examples linux-headers-$(uname -r) ``` 3. Install LuaJit and the corresponding development files ``` $ sudo apt-get install luajit luajit-5.1-dev ``` 4. Test one of the examples to ensure `libbcc` is properly installed ``` $ sudo src/lua/bcc-probe examples/lua/task_switch.lua ``` ## LuaJIT BPF compiler Now it is also possible to write Lua functions and compile them transparently to BPF bytecode, here is a simple socket filter example: ```lua local S = require('syscall') local bpf = require('bpf') local map = bpf.map('array', 256) -- Kernel-space part of the program local prog = assert(bpf(function () local proto = pkt.ip.proto -- Get byte (ip.proto) from frame at [23] xadd(map[proto], 1) -- Increment packet count end)) -- User-space part of the program local sock = assert(bpf.socket('lo', prog)) for i=1,10 do local icmp, udp, tcp = map[1], map[17], map[6] print('TCP', tcp, 'UDP', udp, 'ICMP', icmp, 'packets') S.sleep(1) end ``` The other application of BPF programs is attaching to probes for [perf event tracing][tracing]. That means you can trace events inside the kernel (or user-space), and then collect results - for example histogram of `sendto()` latency, off-cpu time stack traces, syscall latency, and so on. While kernel probes and perf events have unstable ABI, with a dynamic language we can create and use proper type based on the tracepoint ABI on runtime. Runtime automatically recognizes reads that needs a helper to be accessed. The type casts denote source of the objects, for example the [bashreadline][bashreadline] example that prints entered bash commands from all running shells: ```lua local ffi = require('ffi') local bpf = require('bpf') -- Perf event map local sample_t = 'struct { uint64_t pid; char str[80]; }' local events = bpf.map('perf_event_array') -- Kernel-space part of the program bpf.uprobe('/bin/bash:readline' function (ptregs) local sample = ffi.new(sample_t) sample.pid = pid_tgid() ffi.copy(sample.str, ffi.cast('char *', req.ax)) -- Cast `ax` to string pointer and copy to buffer perf_submit(events, sample) -- Write sample to perf event map end, true, -1, 0) -- User-space part of the program local log = events:reader(nil, 0, sample_t) -- Must specify PID or CPU_ID to observe while true do log:block() -- Wait until event reader is readable for _,e in log:read() do -- Collect available reader events print(tonumber(e.pid), ffi.string(e.str)) end end ``` Where cast to `struct pt_regs` flags the source of data as probe arguments, which means any pointer derived from this structure points to kernel and a helper is needed to access it. Casting `req.ax` to pointer is then required for `ffi.copy` semantics, otherwise it would be treated as `u64` and only it's value would be copied. The type detection is automatic most of the times (socket filters and `bpf.tracepoint`), but not with uprobes and kprobes. ### Installation ```bash $ luarocks install bpf ``` ### Examples See `examples/lua` directory. ### Helpers * `print(...)` is a wrapper for `bpf_trace_printk`, the output is captured in `cat /sys/kernel/debug/tracing/trace_pipe` * `bit.*` library **is** supported (`lshift, rshift, arshift, bnot, band, bor, bxor`) * `math.*` library *partially* supported (`log2, log, log10`) * `ffi.cast()` is implemented (including structures and arrays) * `ffi.new(...)` allocates memory on stack, initializers are NYI * `ffi.copy(...)` copies memory (possibly using helpers) between stack/kernel/registers * `ntoh(x[, width])` - convert from network to host byte order. * `hton(x[, width])` - convert from host to network byte order. * `xadd(dst, inc)` - exclusive add, a synchronous `*dst += b` if Lua had `+=` operator Below is a list of BPF-specific helpers: * `time()` - return current monotonic time in nanoseconds (uses `bpf_ktime_get_ns`) * `cpu()` - return current CPU number (uses `bpf_get_smp_processor_id`) * `pid_tgid()` - return caller `tgid << 32 | pid` (uses `bpf_get_current_pid_tgid`) * `uid_gid()` - return caller `gid << 32 | uid` (uses `bpf_get_current_uid_gid`) * `comm(var)` - write current process name (uses `bpf_get_current_comm`) * `perf_submit(map, var)` - submit variable to perf event array BPF map * `stack_id(map, flags)` - return stack trace identifier from stack trace BPF map * `load_bytes(off, var)` - helper for direct packet access with `skb_load_bytes()` ### Current state * Not all LuaJIT bytecode opcodes are supported *(notable mentions below)* * Closures `UCLO` will probably never be supported, although you can use upvalues inside compiled function. * Type narrowing is opportunistic. Numbers are 64-bit by default, but 64-bit immediate loads are not supported (e.g. `local x = map[ffi.cast('uint64_t', 1000)]`) * Tail calls `CALLT`, and iterators `ITERI` are NYI (as of now) * Arbitrary ctype **is** supported both for map keys and values * Basic optimisations like: constant propagation, partial DCE, liveness analysis and speculative register allocation are implement, but there's no control flow analysis yet. This means the compiler has the visibility when things are used and dead-stores occur, but there's no rewriter pass to eliminate them. * No register sub-allocations, no aggressive use of caller-saved `R1-5`, no aggressive narrowing (this would require variable range assertions and variable relationships) * Slices with not 1/2/4/8 length are NYI (requires allocating a memory on stack and using pointer type) [bcc]: https://github.com/iovisor/bcc [tracing]: http://www.brendangregg.com/blog/2016-03-05/linux-bpf-superpowers.html [bashreadline]: http://www.brendangregg.com/blog/2016-02-08/linux-ebpf-bcc-uprobes.htmlbpfcc-0.12.0/src/lua/bcc-probe000077500000000000000000000013551357404205000160050ustar00rootroot00000000000000#!/usr/bin/env luajit --[[ Copyright 2016 GitHub, Inc 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. ]] local str = require("debug").getinfo(1, "S").source:sub(2) local script_path = str:match("(.*/)").."/?.lua;" package.path = script_path..package.path require("bcc.run")() bpfcc-0.12.0/src/lua/bcc/000077500000000000000000000000001357404205000147465ustar00rootroot00000000000000bpfcc-0.12.0/src/lua/bcc/bpf.lua000066400000000000000000000200431357404205000162170ustar00rootroot00000000000000--[[ Copyright 2016 GitHub, Inc 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. ]] local ffi = require("ffi") local libbcc = require("bcc.libbcc") local TracerPipe = require("bcc.tracerpipe") local Table = require("bcc.table") local Sym = require("bcc.sym") local Bpf = class("BPF") Bpf.static.open_kprobes = {} Bpf.static.open_uprobes = {} Bpf.static.perf_buffers = {} Bpf.static.KPROBE_LIMIT = 1000 Bpf.static.tracer_pipe = nil Bpf.static.DEFAULT_CFLAGS = { '-D__HAVE_BUILTIN_BSWAP16__', '-D__HAVE_BUILTIN_BSWAP32__', '-D__HAVE_BUILTIN_BSWAP64__', } function Bpf.static.check_probe_quota(n) local cur = table.count(Bpf.static.open_kprobes) + table.count(Bpf.static.open_uprobes) assert(cur + n <= Bpf.static.KPROBE_LIMIT, "number of open probes would exceed quota") end function Bpf.static.cleanup() local function detach_all(probe_type, all_probes) for key, fd in pairs(all_probes) do libbcc.bpf_close_perf_event_fd(fd) -- skip bcc-specific kprobes if not key:starts("bcc:") then if probe_type == "kprobes" then libbcc.bpf_detach_kprobe(key) elseif probe_type == "uprobes" then libbcc.bpf_detach_uprobe(key) end end all_probes[key] = nil end end detach_all("kprobes", Bpf.static.open_kprobes) detach_all("uprobes", Bpf.static.open_uprobes) for key, perf_buffer in pairs(Bpf.static.perf_buffers) do libbcc.perf_reader_free(perf_buffer) Bpf.static.perf_buffers[key] = nil end if Bpf.static.tracer_pipe ~= nil then Bpf.static.tracer_pipe:close() end end function Bpf.static.SymbolCache(pid) return Sym.create_cache(pid) end function Bpf.static.num_open_uprobes() return table.count(Bpf.static.open_uprobes) end function Bpf.static.num_open_kprobes() return table.count(Bpf.static.open_kprobes) end Bpf.static.SCRIPT_ROOT = "./" function Bpf.static.script_root(root) local dir, file = root:match'(.*/)(.*)' Bpf.static.SCRIPT_ROOT = dir or "./" return Bpf end local function _find_file(script_root, filename) if filename == nil then return nil end if os.exists(filename) then return filename end if not filename:starts("/") then filename = script_root .. filename if os.exists(filename) then return filename end end assert(nil, "failed to find file "..filename.." (root="..script_root..")") end function Bpf:initialize(args) self.funcs = {} self.tables = {} if args.usdt and args.text then args.text = args.usdt:_get_text() .. args.text end local cflags = table.join(Bpf.DEFAULT_CFLAGS, args.cflags) local cflags_ary = ffi.new("const char *[?]", #cflags, cflags) local llvm_debug = rawget(_G, "LIBBCC_LLVM_DEBUG") or args.debug or 0 assert(type(llvm_debug) == "number") if args.text then log.info("\n%s\n", args.text) self.module = libbcc.bpf_module_create_c_from_string(args.text, llvm_debug, cflags_ary, #cflags, true) elseif args.src_file then local src = _find_file(Bpf.SCRIPT_ROOT, args.src_file) if src:ends(".b") then local hdr = _find_file(Bpf.SCRIPT_ROOT, args.hdr_file) self.module = libbcc.bpf_module_create_b(src, hdr, llvm_debug) else self.module = libbcc.bpf_module_create_c(src, llvm_debug, cflags_ary, #cflags, true) end end assert(self.module ~= nil, "failed to compile BPF module") if args.usdt then args.usdt:_attach_uprobes(self) end end function Bpf:load_funcs(prog_type) prog_type = prog_type or "BPF_PROG_TYPE_KPROBE" local result = {} local fn_count = tonumber(libbcc.bpf_num_functions(self.module)) for i = 0,fn_count-1 do local name = ffi.string(libbcc.bpf_function_name(self.module, i)) table.insert(result, self:load_func(name, prog_type)) end return result end function Bpf:load_func(fn_name, prog_type) if self.funcs[fn_name] ~= nil then return self.funcs[fn_name] end assert(libbcc.bpf_function_start(self.module, fn_name) ~= nil, "unknown program: "..fn_name) local fd = libbcc.bcc_prog_load(prog_type, fn_name, libbcc.bpf_function_start(self.module, fn_name), libbcc.bpf_function_size(self.module, fn_name), libbcc.bpf_module_license(self.module), libbcc.bpf_module_kern_version(self.module), 0, nil, 0) assert(fd >= 0, "failed to load BPF program "..fn_name) log.info("loaded %s (%d)", fn_name, fd) local fn = {bpf=self, name=fn_name, fd=fd} self.funcs[fn_name] = fn return fn end function Bpf:dump_func(fn_name) local start = libbcc.bpf_function_start(self.module, fn_name) assert(start ~= nil, "unknown program") local len = libbcc.bpf_function_size(self.module, fn_name) return ffi.string(start, tonumber(len)) end function Bpf:attach_uprobe(args) Bpf.check_probe_quota(1) local path, addr = Sym.check_path_symbol(args.name, args.sym, args.addr, args.pid, args.sym_off) local fn = self:load_func(args.fn_name, 'BPF_PROG_TYPE_KPROBE') local ptype = args.retprobe and "r" or "p" local ev_name = string.format("%s_%s_0x%p", ptype, path:gsub("[^%a%d]", "_"), addr) local retprobe = args.retprobe and 1 or 0 local res = libbcc.bpf_attach_uprobe(fn.fd, retprobe, ev_name, path, addr, args.pid or -1) assert(res >= 0, "failed to attach BPF to uprobe") self:probe_store("uprobe", ev_name, res) return self end function Bpf:attach_kprobe(args) -- TODO: allow the caller to glob multiple functions together Bpf.check_probe_quota(1) local fn = self:load_func(args.fn_name, 'BPF_PROG_TYPE_KPROBE') local event = args.event or "" local ptype = args.retprobe and "r" or "p" local ev_name = string.format("%s_%s", ptype, event:gsub("[%+%.]", "_")) local offset = args.fn_offset or 0 local retprobe = args.retprobe and 1 or 0 local maxactive = args.maxactive or 0 local res = libbcc.bpf_attach_kprobe(fn.fd, retprobe, ev_name, event, offset, maxactive) assert(res >= 0, "failed to attach BPF to kprobe") self:probe_store("kprobe", ev_name, res) return self end function Bpf:pipe() if Bpf.tracer_pipe == nil then Bpf.tracer_pipe = TracerPipe:new() end return Bpf.tracer_pipe end function Bpf:get_table(name, key_type, leaf_type) if self.tables[name] == nil then self.tables[name] = Table(self, name, key_type, leaf_type) end return self.tables[name] end function Bpf:probe_store(t, id, fd) if t == "kprobe" then Bpf.open_kprobes[id] = fd elseif t == "uprobe" then Bpf.open_uprobes[id] = fd else error("unknown probe type '%s'" % t) end log.info("%s -> %s", id, fd) end function Bpf:perf_buffer_store(id, reader) Bpf.perf_buffers[id] = reader log.info("%s -> %s", id, reader) end function Bpf:probe_lookup(t, id) if t == "kprobe" then return Bpf.open_kprobes[id] elseif t == "uprobe" then return Bpf.open_uprobes[id] else return nil end end function Bpf:_perf_buffer_array() local perf_buffer_count = table.count(Bpf.perf_buffers) local readers = ffi.new("struct perf_reader*[?]", perf_buffer_count) local n = 0 for _, r in pairs(Bpf.perf_buffers) do readers[n] = r n = n + 1 end assert(n == perf_buffer_count) return readers, n end function Bpf:perf_buffer_poll_loop() local perf_buffers, perf_buffer_count = self:_perf_buffer_array() return pcall(function() while true do libbcc.perf_reader_poll(perf_buffer_count, perf_buffers, -1) end end) end function Bpf:kprobe_poll_loop() return self:perf_buffer_poll_loop() end function Bpf:perf_buffer_poll(timeout) local perf_buffers, perf_buffer_count = self:_perf_buffer_array() libbcc.perf_reader_poll(perf_buffer_count, perf_buffers, timeout or -1) end function Bpf:kprobe_poll(timeout) self:perf_buffer_poll(timeout) end return Bpf bpfcc-0.12.0/src/lua/bcc/init.lua000066400000000000000000000011561357404205000164170ustar00rootroot00000000000000--[[ Copyright 2016 GitHub, Inc 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. ]] require("bcc.vendor.helpers") return { BPF = require("bcc.bpf") } bpfcc-0.12.0/src/lua/bcc/libbcc.lua000066400000000000000000000136541357404205000167000ustar00rootroot00000000000000--[[ Copyright 2016 GitHub, Inc 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. ]] local ffi = require("ffi") ffi.cdef[[ 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, }; int bcc_create_map(enum bpf_map_type map_type, int key_size, int value_size, int max_entries, int map_flags); int bpf_update_elem(int fd, void *key, void *value, unsigned long long flags); int bpf_lookup_elem(int fd, void *key, void *value); int bpf_delete_elem(int fd, void *key); int bpf_get_next_key(int fd, void *key, void *next_key); int bcc_prog_load(enum bpf_prog_type prog_type, const char *name, const struct bpf_insn *insns, int insn_len, const char *license, unsigned kern_version, int log_level, char *log_buf, unsigned log_buf_size); int bpf_attach_socket(int sockfd, int progfd); /* create RAW socket and bind to interface 'name' */ int bpf_open_raw_sock(const char *name); typedef void (*perf_reader_raw_cb)(void *cb_cookie, void *raw, int raw_size); typedef void (*perf_reader_lost_cb)(void *cb_cookie, uint64_t lost); int bpf_attach_kprobe(int progfd, int attach_type, const char *ev_name, const char *fn_name, uint64_t fn_offset, int maxactive); int bpf_detach_kprobe(const char *ev_name); int bpf_attach_uprobe(int progfd, int attach_type, const char *ev_name, const char *binary_path, uint64_t offset, int pid); int bpf_detach_uprobe(const char *ev_name); void * bpf_open_perf_buffer(perf_reader_raw_cb raw_cb, perf_reader_lost_cb lost_cb, void *cb_cookie, int pid, int cpu, int page_cnt); int bpf_close_perf_event_fd(int fd); ]] ffi.cdef[[ void * bpf_module_create_b(const char *filename, const char *proto_filename, unsigned flags); void * bpf_module_create_c(const char *filename, unsigned flags, const char *cflags[], int ncflags, bool allow_rlimit); void * bpf_module_create_c_from_string(const char *text, unsigned flags, const char *cflags[], int ncflags, bool allow_rlimit); void bpf_module_destroy(void *program); char * bpf_module_license(void *program); unsigned bpf_module_kern_version(void *program); size_t bpf_num_functions(void *program); const char * bpf_function_name(void *program, size_t id); void * bpf_function_start_id(void *program, size_t id); void * bpf_function_start(void *program, const char *name); size_t bpf_function_size_id(void *program, size_t id); size_t bpf_function_size(void *program, const char *name); size_t bpf_num_tables(void *program); size_t bpf_table_id(void *program, const char *table_name); int bpf_table_fd(void *program, const char *table_name); int bpf_table_fd_id(void *program, size_t id); int bpf_table_type(void *program, const char *table_name); int bpf_table_type_id(void *program, size_t id); size_t bpf_table_max_entries(void *program, const char *table_name); size_t bpf_table_max_entries_id(void *program, size_t id); int bpf_table_flags(void *program, const char *table_name); int bpf_table_flags_id(void *program, size_t id); const char * bpf_table_name(void *program, size_t id); const char * bpf_table_key_desc(void *program, const char *table_name); const char * bpf_table_key_desc_id(void *program, size_t id); const char * bpf_table_leaf_desc(void *program, const char *table_name); const char * bpf_table_leaf_desc_id(void *program, size_t id); size_t bpf_table_key_size(void *program, const char *table_name); size_t bpf_table_key_size_id(void *program, size_t id); size_t bpf_table_leaf_size(void *program, const char *table_name); size_t bpf_table_leaf_size_id(void *program, size_t id); int bpf_table_key_snprintf(void *program, size_t id, char *buf, size_t buflen, const void *key); int bpf_table_leaf_snprintf(void *program, size_t id, char *buf, size_t buflen, const void *leaf); int bpf_table_key_sscanf(void *program, size_t id, const char *buf, void *key); int bpf_table_leaf_sscanf(void *program, size_t id, const char *buf, void *leaf); ]] ffi.cdef[[ struct perf_reader; void perf_reader_free(void *ptr); int perf_reader_mmap(struct perf_reader *reader); int perf_reader_poll(int num_readers, struct perf_reader **readers, int timeout); int perf_reader_fd(struct perf_reader *reader); void perf_reader_set_fd(struct perf_reader *reader, int fd); ]] ffi.cdef[[ struct bcc_symbol { const char *name; const char *demangle_name; const char *module; uint64_t offset; }; struct bcc_symbol_option { int use_debug_file; int check_debug_file_crc; int lazy_symbolize; uint32_t use_symbol_type; }; int bcc_resolve_symname(const char *module, const char *symname, const uint64_t addr, int pid, struct bcc_symbol_option *option, struct bcc_symbol *sym); void bcc_procutils_free(const char *ptr); void *bcc_symcache_new(int pid, struct bcc_symbol_option *option); void bcc_symbol_free_demangle_name(struct bcc_symbol *sym); int bcc_symcache_resolve(void *symcache, uint64_t addr, struct bcc_symbol *sym); void bcc_symcache_refresh(void *resolver); ]] ffi.cdef[[ void *bcc_usdt_new_frompid(int pid); void *bcc_usdt_new_frompath(const char *path); void bcc_usdt_close(void *usdt); int bcc_usdt_enable_probe(void *, const char *, const char *); char *bcc_usdt_genargs(void *); typedef void (*bcc_usdt_uprobe_cb)(const char *, const char *, uint64_t, int); void bcc_usdt_foreach_uprobe(void *usdt, bcc_usdt_uprobe_cb callback); ]] if rawget(_G, "BCC_STANDALONE") then return ffi.C else return ffi.load( os.getenv("LIBBCC_SO_PATH") or rawget(_G, "LIBBCC_SO_PATH") or "bcc") end bpfcc-0.12.0/src/lua/bcc/run.lua000066400000000000000000000042161357404205000162600ustar00rootroot00000000000000--[[ Copyright 2016 GitHub, Inc 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. ]] return function() require("bcc.vendor.helpers") local standalone = rawget(_G, "BCC_STANDALONE") local progname = standalone or "bcc-probe" local function print_usage() io.stderr:write(string.format( "usage: %s [[--version|--verbose] --] path_to_script.lua [...]\n", progname)) os.exit(1) end local function print_version() local jit = require("jit") print(string.format("%s %s -- Running on %s (%s/%s)", progname, rawget(_G, "BCC_VERSION") or "HEAD", jit.version, jit.os, jit.arch)) os.exit(0) end while arg[1] and string.starts(arg[1], "-") do local k = table.remove(arg, 1) if k == "--" then break elseif standalone == nil and string.starts(k, "--so-path=") then rawset(_G, "LIBBCC_SO_PATH", string.lstrip(k, "--so-path=")) elseif k == "--llvm-debug" then rawset(_G, "LIBBCC_LLVM_DEBUG", 1) elseif k == "-V" or k == "--verbose" then log.enabled = true elseif k == "-v" or k == "--version" then print_version() else print_usage() end end local tracefile = table.remove(arg, 1) if not tracefile then print_usage() end local BPF = require("bcc.bpf") BPF.script_root(tracefile) local USDT = require("bcc.usdt") local utils = { argparse = require("bcc.vendor.argparse"), posix = require("bcc.vendor.posix"), USDT = USDT, } local command = dofile(tracefile) local res, err = xpcall(command, debug.traceback, BPF, utils) if not res and err ~= "interrupted!" then io.stderr:write("[ERROR] "..err.."\n") end BPF.cleanup() USDT.cleanup() return res, err end bpfcc-0.12.0/src/lua/bcc/sym.lua000066400000000000000000000035051357404205000162640ustar00rootroot00000000000000--[[ Copyright 2016 GitHub, Inc 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. ]] local ffi = require("ffi") local libbcc = require("bcc.libbcc") local SYM = ffi.typeof("struct bcc_symbol[1]") local function create_cache(pid) return { _CACHE = libbcc.bcc_symcache_new(pid or -1, nil), resolve = function(self, addr) local sym = SYM() if libbcc.bcc_symcache_resolve(self._CACHE, addr, sym) < 0 then return "[unknown]", 0x0 end local name_res = ffi.string(sym[0].demangle_name) libbcc.bcc_symbol_free_demangle_name(sym); return name_res, sym[0].offset end } end local function check_path_symbol(module, symname, addr, pid, sym_off) local sym = SYM() local module_path local new_addr if libbcc.bcc_resolve_symname(module, symname, addr or 0x0, pid or 0, nil, sym) < 0 then if sym[0].module == nil then error("could not find library '%s' in the library path" % module) else module_path = ffi.string(sym[0].module) libbcc.bcc_procutils_free(sym[0].module) error("failed to resolve symbol '%s' in '%s'" % { symname, module_path}) end end new_addr = sym[0].offset + (sym_off or 0) module_path = ffi.string(sym[0].module) libbcc.bcc_procutils_free(sym[0].module) return module_path, new_addr end return { create_cache=create_cache, check_path_symbol=check_path_symbol } bpfcc-0.12.0/src/lua/bcc/table.lua000066400000000000000000000251341357404205000165450ustar00rootroot00000000000000--[[ Copyright 2016 GitHub, Inc 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. ]] local ffi = require("ffi") local libbcc = require("bcc.libbcc") local Posix = require("bcc.vendor.posix") local BaseTable = class("BaseTable") BaseTable.static.BPF_MAP_TYPE_HASH = 1 BaseTable.static.BPF_MAP_TYPE_ARRAY = 2 BaseTable.static.BPF_MAP_TYPE_PROG_ARRAY = 3 BaseTable.static.BPF_MAP_TYPE_PERF_EVENT_ARRAY = 4 BaseTable.static.BPF_MAP_TYPE_PERCPU_HASH = 5 BaseTable.static.BPF_MAP_TYPE_PERCPU_ARRAY = 6 BaseTable.static.BPF_MAP_TYPE_STACK_TRACE = 7 BaseTable.static.BPF_MAP_TYPE_CGROUP_ARRAY = 8 BaseTable.static.BPF_MAP_TYPE_LRU_HASH = 9 BaseTable.static.BPF_MAP_TYPE_LRU_PERCPU_HASH = 10 BaseTable.static.BPF_MAP_TYPE_LPM_TRIE = 11 function BaseTable:initialize(t_type, bpf, map_id, map_fd, key_type, leaf_type) assert(t_type == libbcc.bpf_table_type_id(bpf.module, map_id)) self.t_type = t_type self.bpf = bpf self.map_id = map_id self.map_fd = map_fd self.c_key = ffi.typeof(key_type.."[1]") self.c_leaf = ffi.typeof(leaf_type.."[1]") end function BaseTable:key_sprintf(key) local pkey = self.c_key(key) local buf_len = ffi.sizeof(self.c_key) * 8 local pbuf = ffi.new("char[?]", buf_len) local res = libbcc.bpf_table_key_snprintf( self.bpf.module, self.map_id, pbuf, buf_len, pkey) assert(res == 0, "could not print key") return ffi.string(pbuf) end function BaseTable:leaf_sprintf(leaf) local pleaf = self.c_leaf(leaf) local buf_len = ffi.sizeof(self.c_leaf) * 8 local pbuf = ffi.new("char[?]", buf_len) local res = libbcc.bpf_table_leaf_snprintf( self.bpf.module, self.map_id, pbuf, buf_len, pleaf) assert(res == 0, "could not print leaf") return ffi.string(pbuf) end function BaseTable:key_scanf(key_str) local pkey = self.c_key() local res = libbcc.bpf_table_key_sscanf( self.bpf.module, self.map_id, key_str, pkey) assert(res == 0, "could not scanf key") return pkey[0] end function BaseTable:leaf_scanf(leaf_str) local pleaf = self.c_leaf() local res = libbcc.bpf_table_leaf_sscanf( self.bpf.module, self.map_id, leaf_str, pleaf) assert(res == 0, "could not scanf leaf") return pleaf[0] end function BaseTable:get(key) local pkey = self.c_key(key) local pvalue = self.c_leaf() if libbcc.bpf_lookup_elem(self.map_fd, pkey, pvalue) < 0 then return nil end return pvalue[0] end function BaseTable:set(key, value) local pkey = self.c_key(key) local pvalue = self.c_leaf(value) assert(libbcc.bpf_update_elem(self.map_fd, pkey, pvalue, 0) == 0, "could not update table") end function BaseTable:_empty_key() local pkey = self.c_key() local pvalue = self.c_leaf() for _, v in ipairs({0x0, 0x55, 0xff}) do ffi.fill(pkey, ffi.sizeof(pkey[0]), v) if libbcc.bpf_lookup_elem(self.map_fd, pkey, pvalue) < 0 then return pkey end end error("failed to find an empty key for table iteration") end function BaseTable:keys() local pkey = self:_empty_key() return function() local pkey_next = self.c_key() if libbcc.bpf_get_next_key(self.map_fd, pkey, pkey_next) < 0 then return nil end pkey = pkey_next return pkey[0] end end function BaseTable:items() local pkey = self:_empty_key() return function() local pkey_next = self.c_key() local pvalue = self.c_leaf() if libbcc.bpf_get_next_key(self.map_fd, pkey, pkey_next) < 0 then return nil end pkey = pkey_next assert(libbcc.bpf_lookup_elem(self.map_fd, pkey, pvalue) == 0) return pkey[0], pvalue[0] end end local HashTable = class("HashTable", BaseTable) function HashTable:initialize(bpf, map_id, map_fd, key_type, leaf_type) BaseTable.initialize(self, BaseTable.BPF_MAP_TYPE_HASH, bpf, map_id, map_fd, key_type, leaf_type) end function HashTable:delete(key) local pkey = self.c_key(key) return libbcc.bpf_delete_elem(self.map_fd, pkey) == 0 end function HashTable:size() local n = 0 self:each(function() n = n + 1 end) return n end local BaseArray = class("BaseArray", BaseTable) function BaseArray:initialize(t_type, bpf, map_id, map_fd, key_type, leaf_type) BaseTable.initialize(self, t_type, bpf, map_id, map_fd, key_type, leaf_type) self.max_entries = tonumber(libbcc.bpf_table_max_entries_id(self.bpf.module, self.map_id)) end function BaseArray:_normalize_key(key) assert(type(key) == "number", "invalid key (expected a number") if key < 0 then key = self.max_entries + key end assert(key < self.max_entries, string.format("out of range (%d >= %d)", key, self.max_entries)) return key end function BaseArray:get(key) return BaseTable.get(self, self:_normalize_key(key)) end function BaseArray:set(key, value) return BaseTable.set(self, self:_normalize_key(key), value) end function BaseArray:delete(key) assert(nil, "unsupported") end function BaseArray:items(with_index) local pkey = self.c_key() local max = self.max_entries local n = 0 -- TODO return function() local pvalue = self.c_leaf() if n == max then return nil end pkey[0] = n n = n + 1 if libbcc.bpf_lookup_elem(self.map_fd, pkey, pvalue) ~= 0 then return nil end if with_index then return n, pvalue[0] -- return 1-based index else return pvalue[0] end end end local Array = class("Array", BaseArray) function Array:initialize(bpf, map_id, map_fd, key_type, leaf_type) BaseArray.initialize(self, BaseTable.BPF_MAP_TYPE_ARRAY, bpf, map_id, map_fd, key_type, leaf_type) end local PerfEventArray = class("PerfEventArray", BaseArray) function PerfEventArray:initialize(bpf, map_id, map_fd, key_type, leaf_type) BaseArray.initialize(self, BaseTable.BPF_MAP_TYPE_PERF_EVENT_ARRAY, bpf, map_id, map_fd, key_type, leaf_type) self._callbacks = {} end local function _perf_id(id, cpu) return string.format("bcc:perf_event_array:%d:%d", tonumber(id), cpu or 0) end function PerfEventArray:_open_perf_buffer(cpu, callback, ctype, page_cnt, lost_cb) local _cb = ffi.cast("perf_reader_raw_cb", function (cookie, data, size) callback(cpu, ctype(data)[0]) end) local _lost_cb = nil if lost_cb then _lost_cb = ffi.cast("perf_reader_lost_cb", function (cookie, lost) lost_cb(cookie, lost) end) end -- default to 8 pages per buffer local reader = libbcc.bpf_open_perf_buffer(_cb, _lost_cb, nil, -1, cpu, page_cnt or 8) assert(reader, "failed to open perf buffer") local fd = libbcc.perf_reader_fd(reader) self:set(cpu, fd) self.bpf:perf_buffer_store(_perf_id(self.map_id, cpu), reader) self._callbacks[cpu] = _cb end function PerfEventArray:open_perf_buffer(callback, data_type, data_params, page_cnt, lost_cb) assert(data_type, "a data type is needed for callback conversion") local ctype = ffi.typeof(data_type.."*", unpack(data_params or {})) for i = 0, Posix.cpu_count() - 1 do self:_open_perf_buffer(i, callback, ctype, page_cnt, lost_cb) end end local StackTrace = class("StackTrace", BaseTable) StackTrace.static.MAX_STACK = 127 function StackTrace:initialize(bpf, map_id, map_fd, key_type, leaf_type) BaseTable.initialize(self, BaseTable.BPF_MAP_TYPE_STACK_TRACE, bpf, map_id, map_fd, key_type, leaf_type) self._stackp = self.c_leaf() -- FIXME: not threadsafe end function StackTrace:walk(id) local pkey = self.c_key(id) local pstack = self._stackp local i = 0 if libbcc.bpf_lookup_elem(self.map_fd, pkey, pstack) < 0 then return nil end return function() if i >= StackTrace.MAX_STACK then return nil end local addr = pstack[0].ip[i] if addr == 0 then return nil end i = i + 1 return addr end end function StackTrace:get(id, resolver) local stack = {} for addr in self:walk(id) do table.insert(stack, resolver and resolver(addr) or addr) end return stack end local function _decode_table_type(desc) local json = require("bcc.vendor.json") local json_desc = ffi.string(desc) local function _dec(t) if type(t) == "string" then return t end local fields = {} local struct = t[3] or "struct" for _, value in ipairs(t[2]) do local f = nil if #value == 2 then f = string.format("%s %s;", _dec(value[2]), value[1]) elseif #value == 3 then if type(value[3]) == "table" then f = string.format("%s %s[%d];", _dec(value[2]), value[1], value[3][1]) elseif type(value[3]) == "number" then local t = _dec(value[2]) assert(t == "int" or t == "unsigned int", "bitfields can only appear in [unsigned] int types") f = string.format("%s %s:%d;", t, value[1], value[3]) end end assert(f ~= nil, "failed to decode type "..json_desc) table.insert(fields, f) end assert(struct == "struct" or struct == "struct_packed" or struct == "union", "unknown complex type: "..struct) if struct == "union" then return string.format("union { %s }", table.concat(fields, " ")) else return string.format("struct { %s }", table.concat(fields, " ")) end end return _dec(json.parse(json_desc)) end local function NewTable(bpf, name, key_type, leaf_type) local id = libbcc.bpf_table_id(bpf.module, name) local fd = libbcc.bpf_table_fd(bpf.module, name) if fd < 0 then return nil end local t_type = libbcc.bpf_table_type_id(bpf.module, id) local table = nil if t_type == BaseTable.BPF_MAP_TYPE_HASH then table = HashTable elseif t_type == BaseTable.BPF_MAP_TYPE_ARRAY then table = Array elseif t_type == BaseTable.BPF_MAP_TYPE_PERF_EVENT_ARRAY then table = PerfEventArray elseif t_type == BaseTable.BPF_MAP_TYPE_STACK_TRACE then table = StackTrace end assert(table, "unsupported table type %d" % t_type) if key_type == nil then local desc = libbcc.bpf_table_key_desc(bpf.module, name) assert(desc, "Failed to load BPF table description for "..name) key_type = _decode_table_type(desc) end if leaf_type == nil then local desc = libbcc.bpf_table_leaf_desc(bpf.module, name) assert(desc, "Failed to load BPF table description for "..name) leaf_type = _decode_table_type(desc) end log.info("key = %s value = %s", key_type, leaf_type) return table:new(bpf, id, fd, key_type, leaf_type) end return NewTable bpfcc-0.12.0/src/lua/bcc/tracerpipe.lua000066400000000000000000000030041357404205000176040ustar00rootroot00000000000000--[[ Copyright 2016 GitHub, Inc 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. ]] local TracerPipe = class("TracerPipe") TracerPipe.static.TRACEFS = "/sys/kernel/debug/tracing" TracerPipe.static.fields = "%s+(.-)%-(%d+)%s+%[(%d+)%]%s+(....)%s+([%d%.]+):.-:%s+(.+)" function TracerPipe:close() if self.pipe ~= nil then self.pipe:close() end end function TracerPipe:open() if self.pipe == nil then self.pipe = assert(io.open(TracerPipe.TRACEFS .. "/trace_pipe")) end return self.pipe end function TracerPipe:readline() return self:open():read() end function TracerPipe:trace_fields() while true do local line = self:readline() if not line and self.nonblocking then return nil end if not line:starts("CPU:") then local task, pid, cpu, flags, ts, msg = line:match(TracerPipe.fields) if task ~= nil then return task, tonumber(pid), tonumber(cpu), flags, tonumber(ts), msg end end end end function TracerPipe:initialize(nonblocking) self.nonblocking = nonblocking end return TracerPipe bpfcc-0.12.0/src/lua/bcc/usdt.lua000066400000000000000000000037111357404205000164320ustar00rootroot00000000000000--[[ Copyright 2016 GitHub, Inc 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. ]] local ffi = require("ffi") local libbcc = require("bcc.libbcc") local Usdt = class("USDT") Usdt.static.open_contexts = {} function Usdt.static.cleanup() for _, context in ipairs(Usdt.static.open_contexts) do context:_cleanup() end end function Usdt:initialize(args) assert(args.pid or args.path) if args.pid then self.pid = args.pid self.context = libbcc.bcc_usdt_new_frompid(args.pid) elseif args.path then self.path = args.path self.context = libbcc.bcc_usdt_new_frompath(args.path) end assert(self.context ~= nil, "failed to create USDT context") table.insert(Usdt.open_contexts, self) end function Usdt:enable_probe(args) assert(args.probe and args.fn_name) assert(libbcc.bcc_usdt_enable_probe( self.context, args.probe, args.fn_name) == 0) end function Usdt:_cleanup() libbcc.bcc_usdt_close(self.context) self.context = nil end function Usdt:_get_text() local argc = libbcc.bcc_usdt_genargs(self.context) assert(argc ~= nil) return ffi.string(argc) end function Usdt:_attach_uprobes(bpf) local uprobes = {} local cb = ffi.cast("bcc_usdt_uprobe_cb", function(binpath, fn_name, addr, pid) table.insert(uprobes, {name=ffi.string(binpath), addr=addr, fn_name=ffi.string(fn_name), pid=pid}) end) libbcc.bcc_usdt_foreach_uprobe(self.context, cb) cb:free() for _, args in ipairs(uprobes) do bpf:attach_uprobe(args) end end return Usdt bpfcc-0.12.0/src/lua/bcc/vendor/000077500000000000000000000000001357404205000162435ustar00rootroot00000000000000bpfcc-0.12.0/src/lua/bcc/vendor/argparse.lua000066400000000000000000000707501357404205000205630ustar00rootroot00000000000000-- The MIT License (MIT) -- Copyright (c) 2013 - 2015 Peter Melnichenko -- 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. local function deep_update(t1, t2) for k, v in pairs(t2) do if type(v) == "table" then v = deep_update({}, v) end t1[k] = v end return t1 end -- A property is a tuple {name, callback}. -- properties.args is number of properties that can be set as arguments -- when calling an object. local function class(prototype, properties, parent) -- Class is the metatable of its instances. local cl = {} cl.__index = cl if parent then cl.__prototype = deep_update(deep_update({}, parent.__prototype), prototype) else cl.__prototype = prototype end if properties then local names = {} -- Create setter methods and fill set of property names. for _, property in ipairs(properties) do local name, callback = property[1], property[2] cl[name] = function(self, value) if not callback(self, value) then self["_" .. name] = value end return self end names[name] = true end function cl.__call(self, ...) -- When calling an object, if the first argument is a table, -- interpret keys as property names, else delegate arguments -- to corresponding setters in order. if type((...)) == "table" then for name, value in pairs((...)) do if names[name] then self[name](self, value) end end else local nargs = select("#", ...) for i, property in ipairs(properties) do if i > nargs or i > properties.args then break end local arg = select(i, ...) if arg ~= nil then self[property[1]](self, arg) end end end return self end end -- If indexing class fails, fallback to its parent. local class_metatable = {} class_metatable.__index = parent function class_metatable.__call(self, ...) -- Calling a class returns its instance. -- Arguments are delegated to the instance. local object = deep_update({}, self.__prototype) setmetatable(object, self) return object(...) end return setmetatable(cl, class_metatable) end local function typecheck(name, types, value) for _, type_ in ipairs(types) do if type(value) == type_ then return true end end error(("bad property '%s' (%s expected, got %s)"):format(name, table.concat(types, " or "), type(value))) end local function typechecked(name, ...) local types = {...} return {name, function(_, value) typecheck(name, types, value) end} end local multiname = {"name", function(self, value) typecheck("name", {"string"}, value) for alias in value:gmatch("%S+") do self._name = self._name or alias table.insert(self._aliases, alias) end -- Do not set _name as with other properties. return true end} local function parse_boundaries(str) if tonumber(str) then return tonumber(str), tonumber(str) end if str == "*" then return 0, math.huge end if str == "+" then return 1, math.huge end if str == "?" then return 0, 1 end if str:match "^%d+%-%d+$" then local min, max = str:match "^(%d+)%-(%d+)$" return tonumber(min), tonumber(max) end if str:match "^%d+%+$" then local min = str:match "^(%d+)%+$" return tonumber(min), math.huge end end local function boundaries(name) return {name, function(self, value) typecheck(name, {"number", "string"}, value) local min, max = parse_boundaries(value) if not min then error(("bad property '%s'"):format(name)) end self["_min" .. name], self["_max" .. name] = min, max end} end local actions = {} local option_action = {"action", function(_, value) typecheck("action", {"function", "string"}, value) if type(value) == "string" and not actions[value] then error(("unknown action '%s'"):format(value)) end end} local option_init = {"init", function(self) self._has_init = true end} local option_default = {"default", function(self, value) if type(value) ~= "string" then self._init = value self._has_init = true return true end end} local add_help = {"add_help", function(self, value) typecheck("add_help", {"boolean", "string", "table"}, value) if self._has_help then table.remove(self._options) self._has_help = false end if value then local help = self:flag() :description "Show this help message and exit." :action(function() print(self:get_help()) os.exit(0) end) if value ~= true then help = help(value) end if not help._name then help "-h" "--help" end self._has_help = true end end} local Parser = class({ _arguments = {}, _options = {}, _commands = {}, _mutexes = {}, _require_command = true, _handle_options = true }, { args = 3, typechecked("name", "string"), typechecked("description", "string"), typechecked("epilog", "string"), typechecked("usage", "string"), typechecked("help", "string"), typechecked("require_command", "boolean"), typechecked("handle_options", "boolean"), typechecked("action", "function"), typechecked("command_target", "string"), add_help }) local Command = class({ _aliases = {} }, { args = 3, multiname, typechecked("description", "string"), typechecked("epilog", "string"), typechecked("target", "string"), typechecked("usage", "string"), typechecked("help", "string"), typechecked("require_command", "boolean"), typechecked("handle_options", "boolean"), typechecked("action", "function"), typechecked("command_target", "string"), add_help }, Parser) local Argument = class({ _minargs = 1, _maxargs = 1, _mincount = 1, _maxcount = 1, _defmode = "unused", _show_default = true }, { args = 5, typechecked("name", "string"), typechecked("description", "string"), option_default, typechecked("convert", "function", "table"), boundaries("args"), typechecked("target", "string"), typechecked("defmode", "string"), typechecked("show_default", "boolean"), typechecked("argname", "string", "table"), option_action, option_init }) local Option = class({ _aliases = {}, _mincount = 0, _overwrite = true }, { args = 6, multiname, typechecked("description", "string"), option_default, typechecked("convert", "function", "table"), boundaries("args"), boundaries("count"), typechecked("target", "string"), typechecked("defmode", "string"), typechecked("show_default", "boolean"), typechecked("overwrite", "boolean"), typechecked("argname", "string", "table"), option_action, option_init }, Argument) function Argument:_get_argument_list() local buf = {} local i = 1 while i <= math.min(self._minargs, 3) do local argname = self:_get_argname(i) if self._default and self._defmode:find "a" then argname = "[" .. argname .. "]" end table.insert(buf, argname) i = i+1 end while i <= math.min(self._maxargs, 3) do table.insert(buf, "[" .. self:_get_argname(i) .. "]") i = i+1 if self._maxargs == math.huge then break end end if i < self._maxargs then table.insert(buf, "...") end return buf end function Argument:_get_usage() local usage = table.concat(self:_get_argument_list(), " ") if self._default and self._defmode:find "u" then if self._maxargs > 1 or (self._minargs == 1 and not self._defmode:find "a") then usage = "[" .. usage .. "]" end end return usage end function actions.store_true(result, target) result[target] = true end function actions.store_false(result, target) result[target] = false end function actions.store(result, target, argument) result[target] = argument end function actions.count(result, target, _, overwrite) if not overwrite then result[target] = result[target] + 1 end end function actions.append(result, target, argument, overwrite) result[target] = result[target] or {} table.insert(result[target], argument) if overwrite then table.remove(result[target], 1) end end function actions.concat(result, target, arguments, overwrite) if overwrite then error("'concat' action can't handle too many invocations") end result[target] = result[target] or {} for _, argument in ipairs(arguments) do table.insert(result[target], argument) end end function Argument:_get_action() local action, init if self._maxcount == 1 then if self._maxargs == 0 then action, init = "store_true", nil else action, init = "store", nil end else if self._maxargs == 0 then action, init = "count", 0 else action, init = "append", {} end end if self._action then action = self._action end if self._has_init then init = self._init end if type(action) == "string" then action = actions[action] end return action, init end -- Returns placeholder for `narg`-th argument. function Argument:_get_argname(narg) local argname = self._argname or self:_get_default_argname() if type(argname) == "table" then return argname[narg] else return argname end end function Argument:_get_default_argname() return "<" .. self._name .. ">" end function Option:_get_default_argname() return "<" .. self:_get_default_target() .. ">" end -- Returns label to be shown in the help message. function Argument:_get_label() return self._name end function Option:_get_label() local variants = {} local argument_list = self:_get_argument_list() table.insert(argument_list, 1, nil) for _, alias in ipairs(self._aliases) do argument_list[1] = alias table.insert(variants, table.concat(argument_list, " ")) end return table.concat(variants, ", ") end function Command:_get_label() return table.concat(self._aliases, ", ") end function Argument:_get_description() if self._default and self._show_default then if self._description then return ("%s (default: %s)"):format(self._description, self._default) else return ("default: %s"):format(self._default) end else return self._description or "" end end function Command:_get_description() return self._description or "" end function Option:_get_usage() local usage = self:_get_argument_list() table.insert(usage, 1, self._name) usage = table.concat(usage, " ") if self._mincount == 0 or self._default then usage = "[" .. usage .. "]" end return usage end function Argument:_get_default_target() return self._name end function Option:_get_default_target() local res for _, alias in ipairs(self._aliases) do if alias:sub(1, 1) == alias:sub(2, 2) then res = alias:sub(3) break end end res = res or self._name:sub(2) return (res:gsub("-", "_")) end function Option:_is_vararg() return self._maxargs ~= self._minargs end function Parser:_get_fullname() local parent = self._parent local buf = {self._name} while parent do table.insert(buf, 1, parent._name) parent = parent._parent end return table.concat(buf, " ") end function Parser:_update_charset(charset) charset = charset or {} for _, command in ipairs(self._commands) do command:_update_charset(charset) end for _, option in ipairs(self._options) do for _, alias in ipairs(option._aliases) do charset[alias:sub(1, 1)] = true end end return charset end function Parser:argument(...) local argument = Argument(...) table.insert(self._arguments, argument) return argument end function Parser:option(...) local option = Option(...) if self._has_help then table.insert(self._options, #self._options, option) else table.insert(self._options, option) end return option end function Parser:flag(...) return self:option():args(0)(...) end function Parser:command(...) local command = Command():add_help(true)(...) command._parent = self table.insert(self._commands, command) return command end function Parser:mutex(...) local options = {...} for i, option in ipairs(options) do assert(getmetatable(option) == Option, ("bad argument #%d to 'mutex' (Option expected)"):format(i)) end table.insert(self._mutexes, options) return self end local max_usage_width = 70 local usage_welcome = "Usage: " function Parser:get_usage() if self._usage then return self._usage end local lines = {usage_welcome .. self:_get_fullname()} local function add(s) if #lines[#lines]+1+#s <= max_usage_width then lines[#lines] = lines[#lines] .. " " .. s else lines[#lines+1] = (" "):rep(#usage_welcome) .. s end end -- This can definitely be refactored into something cleaner local mutex_options = {} local vararg_mutexes = {} -- First, put mutexes which do not contain vararg options and remember those which do for _, mutex in ipairs(self._mutexes) do local buf = {} local is_vararg = false for _, option in ipairs(mutex) do if option:_is_vararg() then is_vararg = true end table.insert(buf, option:_get_usage()) mutex_options[option] = true end local repr = "(" .. table.concat(buf, " | ") .. ")" if is_vararg then table.insert(vararg_mutexes, repr) else add(repr) end end -- Second, put regular options for _, option in ipairs(self._options) do if not mutex_options[option] and not option:_is_vararg() then add(option:_get_usage()) end end -- Put positional arguments for _, argument in ipairs(self._arguments) do add(argument:_get_usage()) end -- Put mutexes containing vararg options for _, mutex_repr in ipairs(vararg_mutexes) do add(mutex_repr) end for _, option in ipairs(self._options) do if not mutex_options[option] and option:_is_vararg() then add(option:_get_usage()) end end if #self._commands > 0 then if self._require_command then add("") else add("[]") end add("...") end return table.concat(lines, "\n") end local margin_len = 3 local margin_len2 = 25 local margin = (" "):rep(margin_len) local margin2 = (" "):rep(margin_len2) local function make_two_columns(s1, s2) if s2 == "" then return margin .. s1 end s2 = s2:gsub("\n", "\n" .. margin2) if #s1 < (margin_len2-margin_len) then return margin .. s1 .. (" "):rep(margin_len2-margin_len-#s1) .. s2 else return margin .. s1 .. "\n" .. margin2 .. s2 end end function Parser:get_help() if self._help then return self._help end local blocks = {self:get_usage()} if self._description then table.insert(blocks, self._description) end local labels = {"Arguments:", "Options:", "Commands:"} for i, elements in ipairs{self._arguments, self._options, self._commands} do if #elements > 0 then local buf = {labels[i]} for _, element in ipairs(elements) do table.insert(buf, make_two_columns(element:_get_label(), element:_get_description())) end table.insert(blocks, table.concat(buf, "\n")) end end if self._epilog then table.insert(blocks, self._epilog) end return table.concat(blocks, "\n\n") end local function get_tip(context, wrong_name) local context_pool = {} local possible_name local possible_names = {} for name in pairs(context) do if type(name) == "string" then for i = 1, #name do possible_name = name:sub(1, i - 1) .. name:sub(i + 1) if not context_pool[possible_name] then context_pool[possible_name] = {} end table.insert(context_pool[possible_name], name) end end end for i = 1, #wrong_name + 1 do possible_name = wrong_name:sub(1, i - 1) .. wrong_name:sub(i + 1) if context[possible_name] then possible_names[possible_name] = true elseif context_pool[possible_name] then for _, name in ipairs(context_pool[possible_name]) do possible_names[name] = true end end end local first = next(possible_names) if first then if next(possible_names, first) then local possible_names_arr = {} for name in pairs(possible_names) do table.insert(possible_names_arr, "'" .. name .. "'") end table.sort(possible_names_arr) return "\nDid you mean one of these: " .. table.concat(possible_names_arr, " ") .. "?" else return "\nDid you mean '" .. first .. "'?" end else return "" end end local ElementState = class({ invocations = 0 }) function ElementState:__call(state, element) self.state = state self.result = state.result self.element = element self.target = element._target or element:_get_default_target() self.action, self.result[self.target] = element:_get_action() return self end function ElementState:error(fmt, ...) self.state:error(fmt, ...) end function ElementState:convert(argument) local converter = self.element._convert if converter then local ok, err if type(converter) == "function" then ok, err = converter(argument) else ok = converter[argument] end if ok == nil then self:error(err and "%s" or "malformed argument '%s'", err or argument) end argument = ok end return argument end function ElementState:default(mode) return self.element._defmode:find(mode) and self.element._default end local function bound(noun, min, max, is_max) local res = "" if min ~= max then res = "at " .. (is_max and "most" or "least") .. " " end local number = is_max and max or min return res .. tostring(number) .. " " .. noun .. (number == 1 and "" or "s") end function ElementState:invoke(alias) self.open = true self.name = ("%s '%s'"):format(alias and "option" or "argument", alias or self.element._name) self.overwrite = false if self.invocations >= self.element._maxcount then if self.element._overwrite then self.overwrite = true else self:error("%s must be used %s", self.name, bound("time", self.element._mincount, self.element._maxcount, true)) end else self.invocations = self.invocations + 1 end self.args = {} if self.element._maxargs <= 0 then self:close() end return self.open end function ElementState:pass(argument) argument = self:convert(argument) table.insert(self.args, argument) if #self.args >= self.element._maxargs then self:close() end return self.open end function ElementState:complete_invocation() while #self.args < self.element._minargs do self:pass(self.element._default) end end function ElementState:close() if self.open then self.open = false if #self.args < self.element._minargs then if self:default("a") then self:complete_invocation() else if #self.args == 0 then if getmetatable(self.element) == Argument then self:error("missing %s", self.name) elseif self.element._maxargs == 1 then self:error("%s requires an argument", self.name) end end self:error("%s requires %s", self.name, bound("argument", self.element._minargs, self.element._maxargs)) end end local args = self.args if self.element._maxargs <= 1 then args = args[1] end if self.element._maxargs == 1 and self.element._minargs == 0 and self.element._mincount ~= self.element._maxcount then args = self.args end self.action(self.result, self.target, args, self.overwrite) end end local ParseState = class({ result = {}, options = {}, arguments = {}, argument_i = 1, element_to_mutexes = {}, mutex_to_used_option = {}, command_actions = {} }) function ParseState:__call(parser, error_handler) self.parser = parser self.error_handler = error_handler self.charset = parser:_update_charset() self:switch(parser) return self end function ParseState:error(fmt, ...) self.error_handler(self.parser, fmt:format(...)) end function ParseState:switch(parser) self.parser = parser if parser._action then table.insert(self.command_actions, {action = parser._action, name = parser._name}) end for _, option in ipairs(parser._options) do option = ElementState(self, option) table.insert(self.options, option) for _, alias in ipairs(option.element._aliases) do self.options[alias] = option end end for _, mutex in ipairs(parser._mutexes) do for _, option in ipairs(mutex) do if not self.element_to_mutexes[option] then self.element_to_mutexes[option] = {} end table.insert(self.element_to_mutexes[option], mutex) end end for _, argument in ipairs(parser._arguments) do argument = ElementState(self, argument) table.insert(self.arguments, argument) argument:invoke() end self.handle_options = parser._handle_options self.argument = self.arguments[self.argument_i] self.commands = parser._commands for _, command in ipairs(self.commands) do for _, alias in ipairs(command._aliases) do self.commands[alias] = command end end end function ParseState:get_option(name) local option = self.options[name] if not option then self:error("unknown option '%s'%s", name, get_tip(self.options, name)) else return option end end function ParseState:get_command(name) local command = self.commands[name] if not command then if #self.commands > 0 then self:error("unknown command '%s'%s", name, get_tip(self.commands, name)) else self:error("too many arguments") end else return command end end function ParseState:invoke(option, name) self:close() if self.element_to_mutexes[option.element] then for _, mutex in ipairs(self.element_to_mutexes[option.element]) do local used_option = self.mutex_to_used_option[mutex] if used_option and used_option ~= option then self:error("option '%s' can not be used together with %s", name, used_option.name) else self.mutex_to_used_option[mutex] = option end end end if option:invoke(name) then self.option = option end end function ParseState:pass(arg) if self.option then if not self.option:pass(arg) then self.option = nil end elseif self.argument then if not self.argument:pass(arg) then self.argument_i = self.argument_i + 1 self.argument = self.arguments[self.argument_i] end else local command = self:get_command(arg) self.result[command._target or command._name] = true if self.parser._command_target then self.result[self.parser._command_target] = command._name end self:switch(command) end end function ParseState:close() if self.option then self.option:close() self.option = nil end end function ParseState:finalize() self:close() for i = self.argument_i, #self.arguments do local argument = self.arguments[i] if #argument.args == 0 and argument:default("u") then argument:complete_invocation() else argument:close() end end if self.parser._require_command and #self.commands > 0 then self:error("a command is required") end for _, option in ipairs(self.options) do local name = option.name or ("option '%s'"):format(option.element._name) if option.invocations == 0 then if option:default("u") then option:invoke(name) option:complete_invocation() option:close() end end local mincount = option.element._mincount if option.invocations < mincount then if option:default("a") then while option.invocations < mincount do option:invoke(name) option:close() end elseif option.invocations == 0 then self:error("missing %s", name) else self:error("%s must be used %s", name, bound("time", mincount, option.element._maxcount)) end end end for i = #self.command_actions, 1, -1 do self.command_actions[i].action(self.result, self.command_actions[i].name) end end function ParseState:parse(args) for _, arg in ipairs(args) do local plain = true if self.handle_options then local first = arg:sub(1, 1) if self.charset[first] then if #arg > 1 then plain = false if arg:sub(2, 2) == first then if #arg == 2 then self:close() self.handle_options = false else local equals = arg:find "=" if equals then local name = arg:sub(1, equals - 1) local option = self:get_option(name) if option.element._maxargs <= 0 then self:error("option '%s' does not take arguments", name) end self:invoke(option, name) self:pass(arg:sub(equals + 1)) else local option = self:get_option(arg) self:invoke(option, arg) end end else for i = 2, #arg do local name = first .. arg:sub(i, i) local option = self:get_option(name) self:invoke(option, name) if i ~= #arg and option.element._maxargs > 0 then self:pass(arg:sub(i + 1)) break end end end end end end if plain then self:pass(arg) end end self:finalize() return self.result end function Parser:error(msg) io.stderr:write(("%s\n\nError: %s\n"):format(self:get_usage(), msg)) os.exit(1) end -- Compatibility with strict.lua and other checkers: local default_cmdline = rawget(_G, "arg") or {} function Parser:_parse(args, error_handler) return ParseState(self, error_handler):parse(args or default_cmdline) end function Parser:parse(args) return self:_parse(args, self.error) end local function xpcall_error_handler(err) return tostring(err) .. "\noriginal " .. debug.traceback("", 2):sub(2) end function Parser:pparse(args) local parse_error local ok, result = xpcall(function() return self:_parse(args, function(_, err) parse_error = err error(err, 0) end) end, xpcall_error_handler) if ok then return true, result elseif not parse_error then error(result, 0) else return false, parse_error end end return function(...) return Parser(default_cmdline[0]):add_help(true)(...) end bpfcc-0.12.0/src/lua/bcc/vendor/helpers.lua000066400000000000000000000106461357404205000204170ustar00rootroot00000000000000do local ffi = require("ffi") local ptrtype = ffi.typeof("uint64_t") local strformat = string.format function string.format(format, ...) local args = {...} local match_no = 1 local newfmt, count = string.gsub(format, "()%%(.-)(%a)", function(_, mods, t) local n = match_no match_no = match_no + 1 if t == 'p' and ffi.istype(ptrtype, args[n]) then local lo = tonumber(args[n] % 4294967296ULL) local hi = tonumber(args[n] / 4294967296ULL) args[n] = (hi == 0) and strformat("%x", lo) or strformat("%x%08x", hi, lo) return "%"..mods.."s" end end) if count == 0 then return strformat(format, ...) else return strformat(newfmt, unpack(args,1,select('#',...))) end end end function string.starts(s, p) return string.sub(s, 1, string.len(p)) == p end function string.lstrip(s, p) return string.sub(s, string.len(p) + 1) end function string.ends(s, e) return e == '' or string.sub(s, -string.len(e))==e end function string.escape(s) return s:gsub('[%-%.%+%[%]%(%)%$%^%%%?%*]','%%%1') end --- split a string into a list of strings separated by a delimiter. -- @param s The input string -- @param re A Lua string pattern; defaults to '%s+' -- @param plain don't use Lua patterns -- @param n optional maximum number of splits -- @return a list-like table -- @raise error if s is not a string function string.split(s,re,plain,n) local find,sub,append = string.find, string.sub, table.insert local i1,ls = 1,{} if not re then re = '%s+' end if re == '' then return {s} end while true do local i2,i3 = find(s,re,i1,plain) if not i2 then local last = sub(s,i1) if last ~= '' then append(ls,last) end if #ls == 1 and ls[1] == '' then return {} else return ls end end append(ls,sub(s,i1,i2-1)) if n and #ls == n then ls[#ls] = sub(s,i1) return ls end i1 = i3+1 end end function table.count(T) local count = 0 for _ in pairs(T) do count = count + 1 end return count end function table.bsearch(list, value, mkval) local low = 1 local high = #list while low <= high do local mid = math.floor((low+high)/2) local this = mkval and mkval(list[mid]) or list[mid] if this > value then high = mid - 1 elseif this < value then low = mid + 1 else return mid end end return low - 1 end function table.join(a, b) assert(a) if b == nil or #b == 0 then return a end local res = {} for _, v in ipairs(a) do table.insert(res, v) end for _, v in ipairs(b) do table.insert(res, v) end return res end function table.build(iterator_fn, build_fn) build_fn = (build_fn or function(arg) return arg end) local res = {} while true do local vars = {iterator_fn()} if vars[1] == nil then break end table.insert(res, build_fn(vars)) end return res end function table.values(T) local V = {} for k, v in pairs(T) do table.insert(V, v) end return V end function table.tuples(T) local i = 0 local n = table.getn(t) return function () i = i + 1 if i <= n then return t[i][1], t[i][2] end end end getmetatable("").__mod = function(a, b) if not b then return a elseif type(b) == "table" then return string.format(a, unpack(b)) else return string.format(a, b) end end function os.exists(path) local f=io.open(path,"r") if f~=nil then io.close(f) return true else return false end end function os.spawn(...) local cmd = string.format(...) local proc = assert(io.popen(cmd)) local out = proc:read("*a") proc:close() return out end local function logline(...) if not log.enabled then return end local c_green = "\27[32m" local c_grey = "\27[1;30m" local c_clear = "\27[0m" local msg = string.format(...) local info = debug.getinfo(2, "Sln") local line = string.format("%s[%s:%s]%s %s", c_grey, info.short_src:match("^.+/(.+)$"), info.currentline, c_clear, info.name) io.stderr:write( string.format("%s[%s]%s %s: %s\n", c_green, os.date("%H:%M:%S"), c_clear, line, msg)) end setmetatable(_G, { __newindex = function (_, n) error("attempt to write to undeclared variable "..n, 2) end, __index = function (_, n) error("attempt to read undeclared variable "..n, 2) end, }) rawset(_G, "log", { info = logline, enabled = false }) rawset(_G, "class", require("bcc.vendor.middleclass")) bpfcc-0.12.0/src/lua/bcc/vendor/json.lua000066400000000000000000000161261357404205000177250ustar00rootroot00000000000000--[[ json.lua A compact pure-Lua JSON library. This code is in the public domain: https://gist.github.com/tylerneylon/59f4bcf316be525b30ab The main functions are: json.stringify, json.parse. ## json.stringify: This expects the following to be true of any tables being encoded: * They only have string or number keys. Number keys must be represented as strings in json; this is part of the json spec. * They are not recursive. Such a structure cannot be specified in json. A Lua table is considered to be an array if and only if its set of keys is a consecutive sequence of positive integers starting at 1. Arrays are encoded like so: `[2, 3, false, "hi"]`. Any other type of Lua table is encoded as a json object, encoded like so: `{"key1": 2, "key2": false}`. Because the Lua nil value cannot be a key, and as a table value is considered equivalent to a missing key, there is no way to express the json "null" value in a Lua table. The only way this will output "null" is if your entire input obj is nil itself. An empty Lua table, {}, could be considered either a json object or array - it's an ambiguous edge case. We choose to treat this as an object as it is the more general type. To be clear, none of the above considerations is a limitation of this code. Rather, it is what we get when we completely observe the json specification for as arbitrary a Lua object as json is capable of expressing. ## json.parse: This function parses json, with the exception that it does not pay attention to \u-escaped unicode code points in strings. It is difficult for Lua to return null as a value. In order to prevent the loss of keys with a null value in a json string, this function uses the one-off table value json.null (which is just an empty table) to indicate null values. This way you can check if a value is null with the conditional `val == json.null`. If you have control over the data and are using Lua, I would recommend just avoiding null values in your data to begin with. --]] local json = {} -- Internal functions. local function kind_of(obj) if type(obj) ~= 'table' then return type(obj) end local i = 1 for _ in pairs(obj) do if obj[i] ~= nil then i = i + 1 else return 'table' end end if i == 1 then return 'table' else return 'array' end end local function escape_str(s) local in_char = {'\\', '"', '/', '\b', '\f', '\n', '\r', '\t'} local out_char = {'\\', '"', '/', 'b', 'f', 'n', 'r', 't'} for i, c in ipairs(in_char) do s = s:gsub(c, '\\' .. out_char[i]) end return s end -- Returns pos, did_find; there are two cases: -- 1. Delimiter found: pos = pos after leading space + delim; did_find = true. -- 2. Delimiter not found: pos = pos after leading space; did_find = false. -- This throws an error if err_if_missing is true and the delim is not found. local function skip_delim(str, pos, delim, err_if_missing) pos = pos + #str:match('^%s*', pos) if str:sub(pos, pos) ~= delim then if err_if_missing then error('Expected ' .. delim .. ' near position ' .. pos) end return pos, false end return pos + 1, true end -- Expects the given pos to be the first character after the opening quote. -- Returns val, pos; the returned pos is after the closing quote character. local function parse_str_val(str, pos, val) val = val or '' local early_end_error = 'End of input found while parsing string.' if pos > #str then error(early_end_error) end local c = str:sub(pos, pos) if c == '"' then return val, pos + 1 end if c ~= '\\' then return parse_str_val(str, pos + 1, val .. c) end -- We must have a \ character. local esc_map = {b = '\b', f = '\f', n = '\n', r = '\r', t = '\t'} local nextc = str:sub(pos + 1, pos + 1) if not nextc then error(early_end_error) end return parse_str_val(str, pos + 2, val .. (esc_map[nextc] or nextc)) end -- Returns val, pos; the returned pos is after the number's final character. local function parse_num_val(str, pos) local num_str = str:match('^-?%d+%.?%d*[eE]?[+-]?%d*', pos) local val = tonumber(num_str) if not val then error('Error parsing number at position ' .. pos .. '.') end return val, pos + #num_str end -- Public values and functions. function json.stringify(obj, as_key) local s = {} -- We'll build the string as an array of strings to be concatenated. local kind = kind_of(obj) -- This is 'array' if it's an array or type(obj) otherwise. if kind == 'array' then if as_key then error('Can\'t encode array as key.') end s[#s + 1] = '[' for i, val in ipairs(obj) do if i > 1 then s[#s + 1] = ', ' end s[#s + 1] = json.stringify(val) end s[#s + 1] = ']' elseif kind == 'table' then if as_key then error('Can\'t encode table as key.') end s[#s + 1] = '{' for k, v in pairs(obj) do if #s > 1 then s[#s + 1] = ', ' end s[#s + 1] = json.stringify(k, true) s[#s + 1] = ':' s[#s + 1] = json.stringify(v) end s[#s + 1] = '}' elseif kind == 'string' then return '"' .. escape_str(obj) .. '"' elseif kind == 'number' then if as_key then return '"' .. tostring(obj) .. '"' end return tostring(obj) elseif kind == 'boolean' then return tostring(obj) elseif kind == 'nil' then return 'null' else error('Unjsonifiable type: ' .. kind .. '.') end return table.concat(s) end json.null = {} -- This is a one-off table to represent the null value. function json.parse(str, pos, end_delim) pos = pos or 1 if pos > #str then error('Reached unexpected end of input.') end local pos = pos + #str:match('^%s*', pos) -- Skip whitespace. local first = str:sub(pos, pos) if first == '{' then -- Parse an object. local obj, key, delim_found = {}, true, true pos = pos + 1 while true do key, pos = json.parse(str, pos, '}') if key == nil then return obj, pos end if not delim_found then error('Comma missing between object items.') end pos = skip_delim(str, pos, ':', true) -- true -> error if missing. obj[key], pos = json.parse(str, pos) pos, delim_found = skip_delim(str, pos, ',') end elseif first == '[' then -- Parse an array. local arr, val, delim_found = {}, true, true pos = pos + 1 while true do val, pos = json.parse(str, pos, ']') if val == nil then return arr, pos end if not delim_found then error('Comma missing between array items.') end arr[#arr + 1] = val pos, delim_found = skip_delim(str, pos, ',') end elseif first == '"' then -- Parse a string. return parse_str_val(str, pos + 1) elseif first == '-' or first:match('%d') then -- Parse a number. return parse_num_val(str, pos) elseif first == end_delim then -- End of an object or array. return nil, pos + 1 else -- Parse true, false, or null. local literals = {['true'] = true, ['false'] = false, ['null'] = json.null} for lit_str, lit_val in pairs(literals) do local lit_end = pos + #lit_str - 1 if str:sub(pos, lit_end) == lit_str then return lit_val, lit_end + 1 end end local pos_info_str = 'position ' .. pos .. ': ' .. str:sub(pos, pos + 10) error('Invalid json syntax starting at ' .. pos_info_str) end end return json bpfcc-0.12.0/src/lua/bcc/vendor/middleclass.lua000066400000000000000000000135251357404205000212400ustar00rootroot00000000000000local middleclass = { _VERSION = 'middleclass v4.0.0', _DESCRIPTION = 'Object Orientation for Lua', _URL = 'https://github.com/kikito/middleclass', _LICENSE = [[ MIT LICENSE Copyright (c) 2011 Enrique García Cota 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. ]] } local function _createIndexWrapper(aClass, f) if f == nil then return aClass.__instanceDict else return function(self, name) local value = aClass.__instanceDict[name] if value ~= nil then return value elseif type(f) == "function" then return (f(self, name)) else return f[name] end end end end local function _propagateInstanceMethod(aClass, name, f) f = name == "__index" and _createIndexWrapper(aClass, f) or f aClass.__instanceDict[name] = f for subclass in pairs(aClass.subclasses) do if rawget(subclass.__declaredMethods, name) == nil then _propagateInstanceMethod(subclass, name, f) end end end local function _declareInstanceMethod(aClass, name, f) aClass.__declaredMethods[name] = f if f == nil and aClass.super then f = aClass.super.__instanceDict[name] end _propagateInstanceMethod(aClass, name, f) end local function _tostring(self) return "class " .. self.name end local function _call(self, ...) return self:new(...) end local function _createClass(name, super) local dict = {} dict.__index = dict local aClass = { name = name, super = super, static = {}, __instanceDict = dict, __declaredMethods = {}, subclasses = setmetatable({}, {__mode='k'}) } if super then setmetatable(aClass.static, { __index = function(_,k) return rawget(dict,k) or super.static[k] end }) else setmetatable(aClass.static, { __index = function(_,k) return rawget(dict,k) end }) end setmetatable(aClass, { __index = aClass.static, __tostring = _tostring, __call = _call, __newindex = _declareInstanceMethod }) return aClass end local function _includeMixin(aClass, mixin) assert(type(mixin) == 'table', "mixin must be a table") for name,method in pairs(mixin) do if name ~= "included" and name ~= "static" then aClass[name] = method end end for name,method in pairs(mixin.static or {}) do aClass.static[name] = method end if type(mixin.included)=="function" then mixin:included(aClass) end return aClass end local DefaultMixin = { __tostring = function(self) return "instance of " .. tostring(self.class) end, initialize = function(self, ...) end, isInstanceOf = function(self, aClass) return type(self) == 'table' and type(self.class) == 'table' and type(aClass) == 'table' and ( aClass == self.class or type(aClass.isSubclassOf) == 'function' and self.class:isSubclassOf(aClass) ) end, static = { allocate = function(self) assert(type(self) == 'table', "Make sure that you are using 'Class:allocate' instead of 'Class.allocate'") return setmetatable({ class = self }, self.__instanceDict) end, new = function(self, ...) assert(type(self) == 'table', "Make sure that you are using 'Class:new' instead of 'Class.new'") local instance = self:allocate() instance:initialize(...) return instance end, subclass = function(self, name) assert(type(self) == 'table', "Make sure that you are using 'Class:subclass' instead of 'Class.subclass'") assert(type(name) == "string", "You must provide a name(string) for your class") local subclass = _createClass(name, self) for methodName, f in pairs(self.__instanceDict) do _propagateInstanceMethod(subclass, methodName, f) end subclass.initialize = function(instance, ...) return self.initialize(instance, ...) end self.subclasses[subclass] = true self:subclassed(subclass) return subclass end, subclassed = function(self, other) end, isSubclassOf = function(self, other) return type(other) == 'table' and type(self) == 'table' and type(self.super) == 'table' and ( self.super == other or type(self.super.isSubclassOf) == 'function' and self.super:isSubclassOf(other) ) end, include = function(self, ...) assert(type(self) == 'table', "Make sure you that you are using 'Class:include' instead of 'Class.include'") for _,mixin in ipairs({...}) do _includeMixin(self, mixin) end return self end } } function middleclass.class(name, super) assert(type(name) == 'string', "A name (string) is needed for the new class") return super and super:subclass(name) or _includeMixin(_createClass(name), DefaultMixin) end setmetatable(middleclass, { __call = function(_, ...) return middleclass.class(...) end }) return middleclass bpfcc-0.12.0/src/lua/bcc/vendor/posix.lua000066400000000000000000000041431357404205000201120ustar00rootroot00000000000000--[[ Copyright 2016 GitHub, Inc 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. ]] local ffi = require("ffi") -- Avoid duplicate declarations if syscall library is present local has_syscall, _ = pcall(require, "syscall") if not has_syscall then ffi.cdef [[ typedef int clockid_t; typedef long time_t; struct timespec { time_t tv_sec; long tv_nsec; }; int clock_gettime(clockid_t clk_id, struct timespec *tp); int clock_nanosleep(clockid_t clock_id, int flags, const struct timespec *request, struct timespec *remain); ]] end ffi.cdef [[ int get_nprocs(void); uint64_t strtoull(const char *nptr, char **endptr, int base); ]] local CLOCK = { REALTIME = 0, MONOTONIC = 1, PROCESS_CPUTIME_ID = 2, THREAD_CPUTIME_ID = 3, MONOTONIC_RAW = 4, REALTIME_COARSE = 5, MONOTONIC_COARSE = 6, } local function time_ns(clock) local ts = ffi.new("struct timespec[1]") assert(ffi.C.clock_gettime(clock or CLOCK.MONOTONIC_RAW, ts) == 0, "clock_gettime() failed: "..ffi.errno()) return tonumber(ts[0].tv_sec * 1e9 + ts[0].tv_nsec) end local function sleep(seconds, clock) local s, ns = math.modf(seconds) local ts = ffi.new("struct timespec[1]") ts[0].tv_sec = s ts[0].tv_nsec = ns / 1e9 ffi.C.clock_nanosleep(clock or CLOCK.MONOTONIC, 0, ts, nil) end local function cpu_count() return tonumber(ffi.C.get_nprocs()) end local function tonumber64(n, base) assert(type(n) == "string") return ffi.C.strtoull(n, nil, base or 10) end return { time_ns=time_ns, sleep=sleep, CLOCK=CLOCK, cpu_count=cpu_count, tonumber64=tonumber64, } bpfcc-0.12.0/src/lua/bpf-scm-1.rockspec000066400000000000000000000014101357404205000174330ustar00rootroot00000000000000package = "bpf" version = "scm-1" source = { url = "git://github.com/iovisor/bcc.git" } description = { summary = "BCC - LuaJIT to BPF compiler.", detailed = [[ ]], homepage = "https://github.com/iovisor/bcc", license = "BSD" } dependencies = { "lua >= 5.1", "ljsyscall >= 0.12", } external_dependencies = { LIBELF = { library = "elf" } } build = { type = "builtin", install = { bin = { } }, modules = { bpf = "src/lua/bpf/bpf.lua", ["bpf.builtins"] = "src/lua/bpf/builtins.lua", ["bpf.cdef"] = "src/lua/bpf/cdef.lua", ["bpf.elf"] = "src/lua/bpf/elf.lua", ["bpf.init"] = "src/lua/bpf/init.lua", ["bpf.ljbytecode"] = "src/lua/bpf/ljbytecode.lua", ["bpf.proto"] = "src/lua/bpf/proto.lua", } } bpfcc-0.12.0/src/lua/bpf/000077500000000000000000000000001357404205000147665ustar00rootroot00000000000000bpfcc-0.12.0/src/lua/bpf/bpf.lua000066400000000000000000001727251357404205000162560ustar00rootroot00000000000000--[[ Copyright 2016 Marek Vavrusa 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. ]] -- LuaJIT to BPF bytecode compiler. -- -- The code generation phase is currently one-pass and produces: -- * Compiled code in BPF bytecode format (https://www.kernel.org/doc/Documentation/networking/filter.txt) -- * Variables with liveness analysis and other meta (spill information, compile-time value) -- -- The code generator optimises as much as possible in single pass: -- * Fold compile-time expressions and constant propagation -- * Basic control flow analysis with dead code elimination (based on compile-time expressions) -- * Single-pass optimistic register allocation -- -- The first pass doesn't have variable lifetime visibility yet, so it relies on rewriter for further -- optimisations such as: -- * Dead store elimination (first-pass doesn't know if/when the variable is going to be used) -- * Common sub-expression elimination (relies on DCE and liveness analysis) -- * Orphan JMP elimination (removing this in first pass would break previous JMP targets) -- * Better register allocation (needs to be recomputed after optimisations) local ffi = require('ffi') local bit = require('bit') local S = require('syscall') local bytecode = require('bpf.ljbytecode') local cdef = require('bpf.cdef') local proto = require('bpf.proto') local builtins = require('bpf.builtins') -- Constants local ALWAYS, NEVER = -1, -2 local BPF = ffi.typeof('struct bpf') local HELPER = ffi.typeof('struct bpf_func_id') -- Symbolic table of constant expressions over numbers local const_expr = { ADD = function (a, b) return a + b end, SUB = function (a, b) return a - b end, DIV = function (a, b) return a / b end, MOD = function (a, b) return a % b end, JEQ = function (a, b) return a == b end, JNE = function (a, b) return a ~= b end, JGE = function (a, b) return a >= b end, JGT = function (a, b) return a > b end, } local const_width = { [1] = BPF.B, [2] = BPF.H, [4] = BPF.W, [8] = BPF.DW, } -- Built-ins that are strict only (never compile-time expandable) local builtins_strict = { [ffi.new] = true, [print] = true, } -- Deep copy a table local function table_copy(t) local copy = {} for n,v in pairs(t) do if type(v) == 'table' then v = table_copy(v) end copy[n] = v end return copy end -- Return true if the constant part is a proxy local function is_proxy(x) return type(x) == 'table' and (x.__dissector or x.__map or x.__base) end -- Create compiler closure local function create_emitter(env, stackslots, params, param_types) local V = {} -- Variable tracking / register allocator local code = { -- Generated code pc = 0, bc_pc = 0, insn = ffi.new('struct bpf_insn[4096]'), fixup = {}, reachable = true, seen_cmp = nil, } local Vstate = {} -- Track variable layout at basic block exits -- Anything below this stack offset is free to use by caller -- @note: There is no tracking memory allocator, so the caller may -- lower it for persistent objects, but such memory will never -- be reclaimed and the caller is responsible for resetting stack -- top whenever the memory below is free to be reused local stack_top = (stackslots + 1) * ffi.sizeof('uint64_t') local function emit(op, dst, src, off, imm) local ins = code.insn[code.pc] ins.code = op ins.dst_reg = dst ins.src_reg = src ins.off = off ins.imm = imm code.pc = code.pc + 1 end local function reg_spill(var) local vinfo = V[var] assert(vinfo.reg, 'attempt to spill VAR that doesn\'t have an allocated register') vinfo.spill = (var + 1) * ffi.sizeof('uint64_t') -- Index by (variable number) * (register width) emit(BPF.MEM + BPF.STX + BPF.DW, 10, vinfo.reg, -vinfo.spill, 0) vinfo.reg = nil end local function reg_fill(var, reg) local vinfo = V[var] assert(reg, 'attempt to fill variable to register but not register is allocated') assert(vinfo.spill, 'attempt to fill register with a VAR that isn\'t spilled') emit(BPF.MEM + BPF.LDX + BPF.DW, reg, 10, -vinfo.spill, 0) vinfo.reg = reg vinfo.spill = nil end -- Allocate a register (lazy simple allocator) local function reg_alloc(var, reg) -- Specific register requested, must spill/move existing variable if reg then for k,v in pairs(V) do -- Spill any variable that has this register if v.reg == reg and not v.shadow then reg_spill(k) break end end return reg end -- Find free or least recently used slot local last, last_seen, used = nil, 0xffff, 0 for k,v in pairs(V) do if v.reg then if not v.live_to or v.live_to < last_seen then last, last_seen = k, v.live_to or last_seen end used = bit.bor(used, bit.lshift(1, v.reg)) end end -- Attempt to select a free register from R7-R9 (callee saved) local free = bit.bnot(used) if bit.band(free, 0x80) ~= 0 then reg = 7 elseif bit.band(free,0x100) ~= 0 then reg = 8 elseif bit.band(free,0x200) ~= 0 then reg = 9 end -- Select another variable to be spilled if not reg then assert(last) reg = V[last].reg reg_spill(last) end assert(reg, 'VAR '..var..'fill/spill failed') return reg end -- Set new variable local function vset(var, reg, const, vtype) -- Must materialise all variables shadowing this variable slot, as it will be overwritten if V[var] and V[var].reg then for _, vinfo in pairs(V) do -- Shadowing variable MUST share the same type and attributes, -- but the register assignment may have changed if vinfo.shadow == var then vinfo.reg = V[var].reg vinfo.shadow = nil end end end -- Get precise type for CDATA or attempt to narrow numeric constant if not vtype and type(const) == 'cdata' then vtype = ffi.typeof(const) end V[var] = {reg=reg, const=const, type=vtype} -- Track variable source if V[var].const and type(const) == 'table' then V[var].source = V[var].const.source end end -- Materialize (or register) a variable in a register -- If the register is nil, then the a new register is assigned (if not already assigned) local function vreg(var, reg, reserve, vtype) local vinfo = V[var] assert(vinfo, 'VAR '..var..' not registered') vinfo.live_to = code.pc-1 if (vinfo.reg and not reg) and not vinfo.shadow then return vinfo.reg end reg = reg_alloc(var, reg) -- Materialize variable shadow copy local src = vinfo while src.shadow do src = V[src.shadow] end if reserve then -- luacheck: ignore -- No load to register occurs elseif src.reg then emit(BPF.ALU64 + BPF.MOV + BPF.X, reg, src.reg, 0, 0) elseif src.spill then vinfo.spill = src.spill reg_fill(var, reg) elseif src.const then vtype = vtype or src.type if type(src.const) == 'table' and src.const.__base then -- Load pointer type emit(BPF.ALU64 + BPF.MOV + BPF.X, reg, 10, 0, 0) emit(BPF.ALU64 + BPF.ADD + BPF.K, reg, 0, 0, -src.const.__base) elseif type(src.const) == 'table' and src.const.__dissector then -- Load dissector offset (imm32), but keep the constant part (dissector proxy) emit(BPF.ALU64 + BPF.MOV + BPF.K, reg, 0, 0, src.const.off or 0) elseif vtype and ffi.sizeof(vtype) == 8 then -- IMM64 must be done in two instructions with imm64 = (lo(imm32), hi(imm32)) emit(BPF.LD + BPF.DW, reg, 0, 0, ffi.cast('uint32_t', src.const)) emit(0, 0, 0, 0, ffi.cast('uint32_t', bit.rshift(bit.rshift(src.const, 16), 16))) vinfo.const = nil -- The variable is live else emit(BPF.ALU64 + BPF.MOV + BPF.K, reg, 0, 0, src.const) vinfo.const = nil -- The variable is live end else assert(false, 'VAR '..var..' has neither register nor constant value') end vinfo.reg = reg vinfo.shadow = nil vinfo.live_from = code.pc-1 vinfo.type = vtype or vinfo.type return reg end -- Copy variable local function vcopy(dst, src) if dst == src then return end V[dst] = {reg=V[src].reg, const=V[src].const, shadow=src, source=V[src].source, type=V[src].type} end -- Dereference variable of pointer type local function vderef(dst_reg, src_reg, vinfo) -- Dereference map pointers for primitive types -- BPF doesn't allow pointer arithmetics, so use the entry value assert(type(vinfo.const) == 'table' and vinfo.const.__dissector, 'cannot dereference a non-pointer variable') local vtype = vinfo.const.__dissector local w = ffi.sizeof(vtype) assert(const_width[w], 'NYI: sizeof('..tostring(vtype)..') not 1/2/4/8 bytes') if dst_reg ~= src_reg then emit(BPF.ALU64 + BPF.MOV + BPF.X, dst_reg, src_reg, 0, 0) -- dst = src end -- Optimize the NULL check away if provably not NULL if not vinfo.source or vinfo.source:find('_or_null', 1, true) then emit(BPF.JMP + BPF.JEQ + BPF.K, src_reg, 0, 1, 0) -- if (src != NULL) end emit(BPF.MEM + BPF.LDX + const_width[w], dst_reg, src_reg, 0, 0) -- dst = *src; end -- Allocate a space for variable local function valloc(size, blank) local base = stack_top assert(stack_top + size < 512 * 1024, 'exceeded maximum stack size of 512kB') stack_top = stack_top + size -- Align to 8 byte boundary stack_top = math.ceil(stack_top/8)*8 -- Current kernel version doesn't support ARG_PTR_TO_RAW_STACK -- so we always need to have memory initialized, remove this when supported if blank then if type(blank) == 'string' then local sp = 0 while sp < size do -- TODO: no BPF_ST + BPF_DW instruction yet local as_u32 = ffi.new('uint32_t [1]') local sub = blank:sub(sp+1, sp+ffi.sizeof(as_u32)) ffi.copy(as_u32, sub, #sub) emit(BPF.MEM + BPF.ST + BPF.W, 10, 0, -(stack_top-sp), as_u32[0]) sp = sp + ffi.sizeof(as_u32) end elseif type(blank) == 'boolean' then reg_alloc(stackslots, 0) emit(BPF.ALU64 + BPF.MOV + BPF.K, 0, 0, 0, 0) for sp = base+8,stack_top,8 do emit(BPF.MEM + BPF.STX + BPF.DW, 10, 0, -sp, 0) end else error('NYI: will with unknown type '..type(blank)) end end return stack_top end -- Turn variable into scalar in register (or constant) local function vscalar(a, w) assert(const_width[w], 'sizeof(scalar variable) must be 1/2/4/8') local src_reg -- If source is a pointer, we must dereference it first if cdef.isptr(V[a].type) then src_reg = vreg(a) local tmp_reg = reg_alloc(stackslots, 1) -- Clone variable in tmp register emit(BPF.ALU64 + BPF.MOV + BPF.X, tmp_reg, src_reg, 0, 0) vderef(tmp_reg, tmp_reg, V[a]) src_reg = tmp_reg -- Materialize and dereference it -- Source is a value on stack, we must load it first elseif type(V[a].const) == 'table' and V[a].const.__base > 0 then src_reg = vreg(a) emit(BPF.MEM + BPF.LDX + const_width[w], src_reg, 10, -V[a].const.__base, 0) V[a].type = V[a].const.__dissector V[a].const = nil -- Value is dereferenced -- If source is an imm32 number, avoid register load elseif type(V[a].const) == 'number' and w < 8 then return nil, V[a].const -- Load variable from any other source else src_reg = vreg(a) end return src_reg, nil end -- Emit compensation code at the end of basic block to unify variable set layout on all block exits -- 1. we need to free registers by spilling -- 2. fill registers to match other exits from this BB local function bb_end(Vcomp) for i,v in pairs(V) do if Vcomp[i] and Vcomp[i].spill and not v.spill then -- Materialize constant or shadowing variable to be able to spill if not v.reg and (v.shadow or cdef.isimmconst(v)) then vreg(i) end reg_spill(i) end end for i,v in pairs(V) do if Vcomp[i] and Vcomp[i].reg and not v.reg then vreg(i, Vcomp[i].reg) end -- Compensate variable metadata change if Vcomp[i] and Vcomp[i].source then V[i].source = Vcomp[i].source end end end local function CMP_STR(a, b, op) assert(op == 'JEQ' or op == 'JNE', 'NYI: only equivallence stack/string only supports == or ~=') -- I have no better idea how to implement it than unrolled XOR loop, as we can fixup only one JMP -- So: X(a,b) = a[0] ^ b[0] | a[1] ^ b[1] | ... -- EQ(a,b) <=> X == 0 -- This could be optimised by placing early exits by rewriter in second phase for long strings local base, size = V[a].const.__base, math.min(#b, ffi.sizeof(V[a].type)) local acc, tmp = reg_alloc(stackslots, 0), reg_alloc(stackslots+1, 1) local sp = 0 emit(BPF.ALU64 + BPF.MOV + BPF.K, acc, 0, 0, 0) while sp < size do -- Load string chunk as imm32 local as_u32 = ffi.new('uint32_t [1]') local sub = b:sub(sp+1, sp+ffi.sizeof(as_u32)) ffi.copy(as_u32, sub, #sub) -- TODO: make this faster by interleaved load/compare steps with DW length emit(BPF.MEM + BPF.LDX + BPF.W, tmp, 10, -(base-sp), 0) emit(BPF.ALU64 + BPF.XOR + BPF.K, tmp, 0, 0, as_u32[0]) emit(BPF.ALU64 + BPF.OR + BPF.X, acc, tmp, 0, 0) sp = sp + ffi.sizeof(as_u32) end emit(BPF.JMP + BPF[op] + BPF.K, acc, 0, 0xffff, 0) code.seen_cmp = code.pc-1 end local function CMP_REG(a, b, op) -- Fold compile-time expressions if V[a].const and V[b].const and not (is_proxy(V[a].const) or is_proxy(V[b].const)) then code.seen_cmp = const_expr[op](V[a].const, V[b].const) and ALWAYS or NEVER else -- Comparison against compile-time string or stack memory if V[b].const and type(V[b].const) == 'string' then return CMP_STR(a, V[b].const, op) end -- The 0xFFFF target here has no significance, it's just a placeholder for -- compiler to replace it's absolute offset to LJ bytecode insn with a relative -- offset in BPF program code, verifier will accept only programs with valid JMP targets local a_reg, b_reg = vreg(a), vreg(b) emit(BPF.JMP + BPF[op] + BPF.X, a_reg, b_reg, 0xffff, 0) code.seen_cmp = code.pc-1 end end local function CMP_IMM(a, b, op) local c = V[a].const if c and not is_proxy(c) then -- Fold compile-time expressions code.seen_cmp = const_expr[op](c, b) and ALWAYS or NEVER else -- Convert imm32 to number if type(b) == 'string' then if #b == 1 then b = b:byte() elseif cdef.isptr(V[a].type) then -- String comparison between stack/constant string return CMP_STR(a, b, op) elseif #b <= 4 then -- Convert to u32 with network byte order local imm = ffi.new('uint32_t[1]') ffi.copy(imm, b, #b) b = builtins.hton(imm[0]) else error('NYI: compare register with string, where #string > sizeof(u32)') end end -- The 0xFFFF target here has no significance, it's just a placeholder for -- compiler to replace it's absolute offset to LJ bytecode insn with a relative -- offset in BPF program code, verifier will accept only programs with valid JMP targets local reg = vreg(a) emit(BPF.JMP + BPF[op] + BPF.K, reg, 0, 0xffff, b) code.seen_cmp = code.pc-1 -- Remember NULL pointer checks as BPF prohibits pointer comparisons -- and repeated checks wouldn't pass the verifier, only comparisons -- against constants are checked. if op == 'JEQ' and tonumber(b) == 0 and V[a].source then local pos = V[a].source:find('_or_null', 1, true) if pos then code.seen_null_guard = a end -- Inverse NULL pointer check (if a ~= nil) elseif op == 'JNE' and tonumber(b) == 0 and V[a].source then local pos = V[a].source:find('_or_null', 1, true) if pos then code.seen_null_guard = a code.seen_null_guard_inverse = true end end end end local function ALU_IMM(dst, a, b, op) -- Fold compile-time expressions if V[a].const and not is_proxy(V[a].const) then assert(cdef.isimmconst(V[a]), 'VAR '..a..' must be numeric') vset(dst, nil, const_expr[op](V[a].const, b)) -- Now we need to materialize dissected value at DST, and add it else vcopy(dst, a) local dst_reg = vreg(dst) if cdef.isptr(V[a].type) then vderef(dst_reg, dst_reg, V[a]) V[dst].type = V[a].const.__dissector else V[dst].type = V[a].type end emit(BPF.ALU64 + BPF[op] + BPF.K, dst_reg, 0, 0, b) end end local function ALU_REG(dst, a, b, op) -- Fold compile-time expressions if V[a].const and not (is_proxy(V[a].const) or is_proxy(V[b].const)) then assert(cdef.isimmconst(V[a]), 'VAR '..a..' must be numeric') assert(cdef.isimmconst(V[b]), 'VAR '..b..' must be numeric') if type(op) == 'string' then op = const_expr[op] end vcopy(dst, a) V[dst].const = op(V[a].const, V[b].const) else local src_reg = b and vreg(b) or 0 -- SRC is optional for unary operations if b and cdef.isptr(V[b].type) then -- We have to allocate a temporary register for dereferencing to preserve -- pointer in source variable that MUST NOT be altered reg_alloc(stackslots, 2) vderef(2, src_reg, V[b]) src_reg = 2 end vcopy(dst, a) -- DST may alias B, so copy must occur after we materialize B local dst_reg = vreg(dst) if cdef.isptr(V[a].type) then vderef(dst_reg, dst_reg, V[a]) V[dst].type = V[a].const.__dissector end emit(BPF.ALU64 + BPF[op] + BPF.X, dst_reg, src_reg, 0, 0) V[stackslots].reg = nil -- Free temporary registers end end local function ALU_IMM_NV(dst, a, b, op) -- Do DST = IMM(a) op VAR(b) where we can't invert because -- the registers are u64 but immediates are u32, so complement -- arithmetics wouldn't work vset(stackslots+1, nil, a) ALU_REG(dst, stackslots+1, b, op) end local function LD_ABS(dst, w, off) assert(off, 'LD_ABS called without offset') if w < 8 then local dst_reg = vreg(dst, 0, true, builtins.width_type(w)) -- Reserve R0 emit(BPF.LD + BPF.ABS + const_width[w], dst_reg, 0, 0, off) if w > 1 and ffi.abi('le') then -- LD_ABS has htonl() semantics, reverse emit(BPF.ALU + BPF.END + BPF.TO_BE, dst_reg, 0, 0, w * 8) end elseif w == 8 then -- LD_ABS|IND prohibits DW, we need to do two W loads and combine them local tmp_reg = vreg(stackslots, 0, true, builtins.width_type(w)) -- Reserve R0 emit(BPF.LD + BPF.ABS + const_width[4], tmp_reg, 0, 0, off + 4) if ffi.abi('le') then -- LD_ABS has htonl() semantics, reverse emit(BPF.ALU + BPF.END + BPF.TO_BE, tmp_reg, 0, 0, 32) end ALU_IMM(stackslots, stackslots, 32, 'LSH') local dst_reg = vreg(dst, 0, true, builtins.width_type(w)) -- Reserve R0, spill tmp variable emit(BPF.LD + BPF.ABS + const_width[4], dst_reg, 0, 0, off) if ffi.abi('le') then -- LD_ABS has htonl() semantics, reverse emit(BPF.ALU + BPF.END + BPF.TO_BE, dst_reg, 0, 0, 32) end ALU_REG(dst, dst, stackslots, 'OR') V[stackslots].reg = nil -- Free temporary registers else assert(w < 8, 'NYI: only LD_ABS of 1/2/4/8 is supported') end end local function LD_IND(dst, src, w, off) local src_reg = vreg(src) -- Must materialize first in case dst == src local dst_reg = vreg(dst, 0, true, builtins.width_type(w)) -- Reserve R0 emit(BPF.LD + BPF.IND + const_width[w], dst_reg, src_reg, 0, off or 0) if w > 1 and ffi.abi('le') then -- LD_ABS has htonl() semantics, reverse emit(BPF.ALU + BPF.END + BPF.TO_BE, dst_reg, 0, 0, w * 8) end end local function LD_MEM(dst, src, w, off) local src_reg = vreg(src) -- Must materialize first in case dst == src local dst_reg = vreg(dst, nil, true, builtins.width_type(w)) -- Reserve R0 emit(BPF.MEM + BPF.LDX + const_width[w], dst_reg, src_reg, off or 0, 0) end -- @note: This is specific now as it expects registers reserved local function LD_IMM_X(dst_reg, src_type, imm, w) if w == 8 then -- IMM64 must be done in two instructions with imm64 = (lo(imm32), hi(imm32)) emit(BPF.LD + const_width[w], dst_reg, src_type, 0, ffi.cast('uint32_t', imm)) -- Must shift in two steps as bit.lshift supports [0..31] emit(0, 0, 0, 0, ffi.cast('uint32_t', bit.lshift(bit.lshift(imm, 16), 16))) else emit(BPF.LD + const_width[w], dst_reg, src_type, 0, imm) end end local function BUILTIN(func, ...) local builtin_export = { -- Compiler primitives (work with variable slots, emit instructions) V=V, vreg=vreg, vset=vset, vcopy=vcopy, vderef=vderef, valloc=valloc, emit=emit, reg_alloc=reg_alloc, reg_spill=reg_spill, tmpvar=stackslots, const_width=const_width, -- Extensions and helpers (use with care) LD_IMM_X = LD_IMM_X, } func(builtin_export, ...) end local function LOAD(dst, src, off, vtype) local base = V[src].const assert(base and base.__dissector, 'NYI: load() on variable that doesn\'t have dissector') assert(V[src].source, 'NYI: load() on variable with unknown source') -- Cast to different type if requested vtype = vtype or base.__dissector local w = ffi.sizeof(vtype) assert(const_width[w], 'NYI: load() supports 1/2/4/8 bytes at a time only, wanted ' .. tostring(w)) -- Packet access with a dissector (use BPF_LD) if V[src].source:find('ptr_to_pkt', 1, true) then if base.off then -- Absolute address to payload LD_ABS(dst, w, off + base.off) else -- Indirect address to payload LD_IND(dst, src, w, off) end -- Direct access to first argument (skb fields, pt regs, ...) elseif V[src].source:find('ptr_to_ctx', 1, true) then LD_MEM(dst, src, w, off) -- Direct skb access with a dissector (use BPF_MEM) elseif V[src].source:find('ptr_to_skb', 1, true) then LD_MEM(dst, src, w, off) -- Pointer to map-backed memory (use BPF_MEM) elseif V[src].source:find('ptr_to_map_value', 1, true) then LD_MEM(dst, src, w, off) -- Indirect read using probe (uprobe or kprobe, uses helper) elseif V[src].source:find('ptr_to_probe', 1, true) then BUILTIN(builtins[builtins.probe_read], nil, dst, src, vtype, off) V[dst].source = V[src].source -- Builtin handles everything else error('NYI: load() on variable from ' .. V[src].source) end V[dst].type = vtype V[dst].const = nil -- Dissected value is not constant anymore end local function CALL(a, b, d) assert(b-1 <= 1, 'NYI: CALL with >1 return values') -- Perform either compile-time, helper, or builtin local func = V[a].const -- Gather all arguments and check if they're constant local args, const, nargs = {}, true, d - 1 for i = a+1, a+d-1 do table.insert(args, V[i].const) if not V[i].const or is_proxy(V[i].const) then const = false end end local builtin = builtins[func] if not const or nargs == 0 then if builtin and type(builtin) == 'function' then args = {a} for i = a+1, a+nargs do table.insert(args, i) end BUILTIN(builtin, unpack(args)) elseif V[a+2] and V[a+2].const then -- var OP imm ALU_IMM(a, a+1, V[a+2].const, builtin) elseif nargs <= 2 then -- var OP var ALU_REG(a, a+1, V[a+2] and a+2, builtin) else error('NYI: CALL non-builtin with 3 or more arguments') end -- Call on dissector implies slice retrieval elseif type(func) == 'table' and func.__dissector then assert(nargs >= 2, 'NYI: .slice(a, b) must have at least two arguments') assert(V[a+1].const and V[a+2].const, 'NYI: slice() arguments must be constant') local off = V[a+1].const local vtype = builtins.width_type(V[a+2].const - off) -- Access to packet via packet (use BPF_LD) if V[a].source and V[a].source:find('ptr_to_', 1, true) then LOAD(a, a, off, vtype) else error('NYI: .slice(a, b) on non-pointer memory ' .. (V[a].source or 'unknown')) end -- Strict builtins cannot be expanded on compile-time elseif builtins_strict[func] and builtin then args = {a} for i = a+1, a+nargs do table.insert(args, i) end BUILTIN(builtin, unpack(args)) -- Attempt compile-time call expansion (expects all argument compile-time known) else assert(const, 'NYI: CALL attempted on constant arguments, but at least one argument is not constant') V[a].const = func(unpack(args)) end end local function MAP_INIT(map_var, key, imm) local map = V[map_var].const vreg(map_var, 1, true, ffi.typeof('uint64_t')) -- Reserve R1 and load ptr for process-local map fd LD_IMM_X(1, BPF.PSEUDO_MAP_FD, map.fd, ffi.sizeof(V[map_var].type)) V[map_var].reg = nil -- R1 will be invalidated after CALL, forget register allocation -- Reserve R2 and load R2 = key pointer local key_size = ffi.sizeof(map.key_type) local w = const_width[key_size] or BPF.DW local pod_type = const_width[key_size] local sp = stack_top + key_size -- Must use stack below spill slots -- Store immediate value on stack reg_alloc(stackslots, 2) -- Spill anything in R2 (unnamed tmp variable) local key_base = key and V[key].const imm = imm or key_base if imm and (not key or not is_proxy(key_base)) then assert(pod_type, 'NYI: map[const K], K width must be 1/2/4/8') emit(BPF.MEM + BPF.ST + w, 10, 0, -sp, imm) -- Key is in register, spill it elseif V[key].reg and pod_type then if cdef.isptr(V[key].type) then -- There is already pointer in register, dereference before spilling emit(BPF.MEM + BPF.LDX + w, 2, V[key].reg, 0, 0) emit(BPF.MEM + BPF.STX + w, 10, 2, -sp, 0) else -- Variable in register is POD, spill it on the stack emit(BPF.MEM + BPF.STX + w, 10, V[key].reg, -sp, 0) end -- Key is spilled from register to stack elseif V[key].spill then sp = V[key].spill -- Key is already on stack, write to base-relative address elseif key_base.__base then assert(key_size == ffi.sizeof(V[key].type), 'VAR '..key..' type incompatible with BPF map key type') sp = key_base.__base else error('VAR '..key..' is neither const-expr/register/stack/spilled') end -- If [FP+K] addressing, emit it if sp then emit(BPF.ALU64 + BPF.MOV + BPF.X, 2, 10, 0, 0) emit(BPF.ALU64 + BPF.ADD + BPF.K, 2, 0, 0, -sp) end end local function MAP_GET(dst, map_var, key, imm) local map = V[map_var].const MAP_INIT(map_var, key, imm) -- Flag as pointer type and associate dissector for map value type vreg(dst, 0, true, ffi.typeof('uint8_t *')) V[dst].const = {__dissector=map.val_type} V[dst].source = 'ptr_to_map_value_or_null' emit(BPF.JMP + BPF.CALL, 0, 0, 0, HELPER.map_lookup_elem) V[stackslots].reg = nil -- Free temporary registers end local function MAP_DEL(map_var, key, key_imm) -- Set R0, R1 (map fd, preempt R0) reg_alloc(stackslots, 0) -- Spill anything in R0 (unnamed tmp variable) MAP_INIT(map_var, key, key_imm) emit(BPF.JMP + BPF.CALL, 0, 0, 0, HELPER.map_delete_elem) V[stackslots].reg = nil -- Free temporary registers end local function MAP_SET(map_var, key, key_imm, src) local map = V[map_var].const -- Delete when setting nil if V[src].type == ffi.typeof('void') then return MAP_DEL(map_var, key, key_imm) end -- Set R0, R1 (map fd, preempt R0) reg_alloc(stackslots, 0) -- Spill anything in R0 (unnamed tmp variable) MAP_INIT(map_var, key, key_imm) reg_alloc(stackslots, 4) -- Spill anything in R4 (unnamed tmp variable) emit(BPF.ALU64 + BPF.MOV + BPF.K, 4, 0, 0, 0) -- BPF_ANY, create new element or update existing -- Reserve R3 for value pointer reg_alloc(stackslots, 3) -- Spill anything in R3 (unnamed tmp variable) local val_size = ffi.sizeof(map.val_type) local w = const_width[val_size] or BPF.DW local pod_type = const_width[val_size] -- Stack pointer must be aligned to both key/value size and have enough headroom for (key, value) local sp = stack_top + ffi.sizeof(map.key_type) + val_size sp = sp + (sp % val_size) local base = V[src].const if base and not is_proxy(base) then assert(pod_type, 'NYI: MAP[K] = imm V; V width must be 1/2/4/8') emit(BPF.MEM + BPF.ST + w, 10, 0, -sp, base) -- Value is in register, spill it elseif V[src].reg and pod_type then -- Value is a pointer, derefernce it and spill it if cdef.isptr(V[src].type) then vderef(3, V[src].reg, V[src]) emit(BPF.MEM + BPF.STX + w, 10, 3, -sp, 0) else emit(BPF.MEM + BPF.STX + w, 10, V[src].reg, -sp, 0) end -- We get a pointer to spilled register on stack elseif V[src].spill then -- If variable is a pointer, we can load it to R3 directly (save "LEA") if cdef.isptr(V[src].type) then reg_fill(src, 3) -- If variable is a stack pointer, we don't have to check it if base.__base then emit(BPF.JMP + BPF.CALL, 0, 0, 0, HELPER.map_update_elem) return end vderef(3, V[src].reg, V[src]) emit(BPF.MEM + BPF.STX + w, 10, 3, -sp, 0) else sp = V[src].spill end -- Value is already on stack, write to base-relative address elseif base.__base then if val_size ~= ffi.sizeof(V[src].type) then local err = string.format('VAR %d type (%s) incompatible with BPF map value type (%s): expected %d, got %d', src, V[src].type, map.val_type, val_size, ffi.sizeof(V[src].type)) error(err) end sp = base.__base -- Value is constant, materialize it on stack else error('VAR '.. src ..' is neither const-expr/register/stack/spilled') end emit(BPF.ALU64 + BPF.MOV + BPF.X, 3, 10, 0, 0) emit(BPF.ALU64 + BPF.ADD + BPF.K, 3, 0, 0, -sp) emit(BPF.JMP + BPF.CALL, 0, 0, 0, HELPER.map_update_elem) V[stackslots].reg = nil -- Free temporary registers end -- Finally - this table translates LuaJIT bytecode into code emitter actions. local BC = { -- Constants KNUM = function(a, _, c, _) -- KNUM if c < 2147483648 then vset(a, nil, c, ffi.typeof('int32_t')) else vset(a, nil, c, ffi.typeof('uint64_t')) end end, KSHORT = function(a, _, _, d) -- KSHORT vset(a, nil, d, ffi.typeof('int16_t')) end, KCDATA = function(a, _, c, _) -- KCDATA -- Coerce numeric types if possible local ct = ffi.typeof(c) if ffi.istype(ct, ffi.typeof('uint64_t')) or ffi.istype(ct, ffi.typeof('int64_t')) then vset(a, nil, c, ct) elseif tonumber(c) ~= nil then -- TODO: this should not be possible vset(a, nil, tonumber(c), ct) else error('NYI: cannot use CDATA constant of type ' .. ct) end end, KPRI = function(a, _, _, d) -- KPRI -- KNIL is 0, must create a special type to identify it local vtype = (d < 1) and ffi.typeof('void') or ffi.typeof('uint8_t') vset(a, nil, (d < 2) and 0 or 1, vtype) end, KSTR = function(a, _, c, _) -- KSTR vset(a, nil, c, ffi.typeof('const char[?]')) end, MOV = function(a, _, _, d) -- MOV var, var vcopy(a, d) end, -- Comparison ops -- Note: comparisons are always followed by JMP opcode, that -- will fuse following JMP to JMP+CMP instruction in BPF -- Note: we're narrowed to integers, so operand/operator inversion is legit ISLT = function(a, _, _, d) return CMP_REG(d, a, 'JGE') end, -- (a < d) (inverted) ISGE = function(a, _, _, d) return CMP_REG(a, d, 'JGE') end, -- (a >= d) ISGT = function(a, _, _, d) return CMP_REG(a, d, 'JGT') end, -- (a > d) ISEQV = function(a, _, _, d) return CMP_REG(a, d, 'JEQ') end, -- (a == d) ISNEV = function(a, _, _, d) return CMP_REG(a, d, 'JNE') end, -- (a ~= d) ISEQS = function(a, _, c, _) return CMP_IMM(a, c, 'JEQ') end, -- (a == str(c)) ISNES = function(a, _, c, _) return CMP_IMM(a, c, 'JNE') end, -- (a ~= str(c)) ISEQN = function(a, _, c, _) return CMP_IMM(a, c, 'JEQ') end, -- (a == c) ISNEN = function(a, _, c, _) return CMP_IMM(a, c, 'JNE') end, -- (a ~= c) IST = function(_, _, _, d) return CMP_IMM(d, 0, 'JNE') end, -- (d) ISF = function(_, _, _, d) return CMP_IMM(d, 0, 'JEQ') end, -- (not d) ISEQP = function(a, _, c, _) return CMP_IMM(a, c, 'JEQ') end, -- ISEQP (a == c) -- Binary operations with RHS constants ADDVN = function(a, b, c, _) return ALU_IMM(a, b, c, 'ADD') end, SUBVN = function(a, b, c, _) return ALU_IMM(a, b, c, 'SUB') end, MULVN = function(a, b, c, _) return ALU_IMM(a, b, c, 'MUL') end, DIVVN = function(a, b, c, _) return ALU_IMM(a, b, c, 'DIV') end, MODVN = function(a, b, c, _) return ALU_IMM(a, b, c, 'MOD') end, -- Binary operations with LHS constants -- Cheat code: we're narrowed to integer arithmetic, so MUL+ADD are commutative ADDNV = function(a, b, c, _) return ALU_IMM(a, b, c, 'ADD') end, -- ADDNV MULNV = function(a, b, c, _) return ALU_IMM(a, b, c, 'MUL') end, -- MULNV SUBNV = function(a, b, c, _) return ALU_IMM_NV(a, c, b, 'SUB') end, -- SUBNV DIVNV = function(a, b, c, _) return ALU_IMM_NV(a, c, b, 'DIV') end, -- DIVNV -- Binary operations between registers ADDVV = function(a, b, _, d) return ALU_REG(a, b, d, 'ADD') end, SUBVV = function(a, b, _, d) return ALU_REG(a, b, d, 'SUB') end, MULVV = function(a, b, _, d) return ALU_REG(a, b, d, 'MUL') end, DIVVV = function(a, b, _, d) return ALU_REG(a, b, d, 'DIV') end, MODVV = function(a, b, _, d) return ALU_REG(a, b, d, 'MOD') end, -- Strings CAT = function(a, b, _, d) -- CAT A = B ~ D assert(V[b].const and V[d].const, 'NYI: CAT only works on compile-time expressions') assert(type(V[b].const) == 'string' and type(V[d].const) == 'string', 'NYI: CAT only works on compile-time strings') vset(a, nil, V[b].const .. V[d].const) end, -- Tables GGET = function (a, _, c, _) -- GGET (A = GLOBAL[c]) if env[c] ~= nil then vset(a, nil, env[c]) else error(string.format("undefined global '%s'", c)) end end, UGET = function (a, _, c, _) -- UGET (A = UPVALUE[c]) if env[c] ~= nil then vset(a, nil, env[c]) else error(string.format("undefined upvalue '%s'", c)) end end, TSETB = function (a, b, _, d) -- TSETB (B[D] = A) assert(V[b] and type(V[b].const) == 'table', 'NYI: B[D] where B is not Lua table, BPF map, or pointer') local vinfo = V[b].const if vinfo.__map then -- BPF map read (constant) return MAP_SET(b, nil, d, a) -- D is literal elseif vinfo.__dissector then assert(vinfo.__dissector, 'NYI: B[D] where B does not have a known element size') local w = ffi.sizeof(vinfo.__dissector) -- TODO: support vectorized moves larger than register width assert(const_width[w], 'B[C] = A, sizeof(A) must be 1/2/4/8') local src_reg, const = vscalar(a, w) -- If changing map value, write to absolute address + offset if V[b].source and V[b].source:find('ptr_to_map_value', 1, true) then local dst_reg = vreg(b) -- Optimization: immediate values (imm32) can be stored directly if type(const) == 'number' then emit(BPF.MEM + BPF.ST + const_width[w], dst_reg, 0, d, const) else emit(BPF.MEM + BPF.STX + const_width[w], dst_reg, src_reg, d, 0) end -- Table is already on stack, write to vinfo-relative address elseif vinfo.__base then -- Optimization: immediate values (imm32) can be stored directly if type(const) == 'number' then emit(BPF.MEM + BPF.ST + const_width[w], 10, 0, -vinfo.__base + (d * w), const) else emit(BPF.MEM + BPF.STX + const_width[w], 10, src_reg, -vinfo.__base + (d * w), 0) end else error('NYI: B[D] where B is not Lua table, BPF map, or pointer') end elseif vinfo and vinfo and V[a].const then vinfo[V[d].const] = V[a].const else error('NYI: B[D] where B is not Lua table, BPF map, or pointer') end end, TSETV = function (a, b, _, d) -- TSETV (B[D] = A) assert(V[b] and type(V[b].const) == 'table', 'NYI: B[D] where B is not Lua table, BPF map, or pointer') local vinfo = V[b].const if vinfo.__map then -- BPF map read (constant) return MAP_SET(b, d, nil, a) -- D is variable elseif vinfo.__dissector then assert(vinfo.__dissector, 'NYI: B[D] where B does not have a known element size') local w = ffi.sizeof(vinfo.__dissector) -- TODO: support vectorized moves larger than register width assert(const_width[w], 'B[C] = A, sizeof(A) must be 1/2/4/8') local src_reg, const = vscalar(a, w) -- If changing map value, write to absolute address + offset if V[b].source and V[b].source:find('ptr_to_map_value', 1, true) then -- Calculate variable address from two registers local tmp_var = stackslots + 1 vset(tmp_var, nil, d) ALU_REG(tmp_var, tmp_var, b, 'ADD') local dst_reg = vreg(tmp_var) V[tmp_var].reg = nil -- Only temporary allocation -- Optimization: immediate values (imm32) can be stored directly if type(const) == 'number' and w < 8 then emit(BPF.MEM + BPF.ST + const_width[w], dst_reg, 0, 0, const) else emit(BPF.MEM + BPF.STX + const_width[w], dst_reg, src_reg, 0, 0) end -- Table is already on stack, write to vinfo-relative address elseif vinfo.__base then -- Calculate variable address from two registers local tmp_var = stackslots + 1 vcopy(tmp_var, d) -- Element position if w > 1 then ALU_IMM(tmp_var, tmp_var, w, 'MUL') -- multiply by element size end local dst_reg = vreg(tmp_var) -- add R10 (stack pointer) emit(BPF.ALU64 + BPF.ADD + BPF.X, dst_reg, 10, 0, 0) V[tmp_var].reg = nil -- Only temporary allocation -- Optimization: immediate values (imm32) can be stored directly if type(const) == 'number' and w < 8 then emit(BPF.MEM + BPF.ST + const_width[w], dst_reg, 0, -vinfo.__base, const) else emit(BPF.MEM + BPF.STX + const_width[w], dst_reg, src_reg, -vinfo.__base, 0) end else error('NYI: B[D] where B is not Lua table, BPF map, or pointer') end elseif vinfo and V[d].const and V[a].const then vinfo[V[d].const] = V[a].const else error('NYI: B[D] where B is not Lua table, BPF map, or pointer') end end, TSETS = function (a, b, c, _) -- TSETS (B[C] = A) assert(V[b] and V[b].const, 'NYI: B[D] where B is not Lua table, BPF map, or pointer') local base = V[b].const if base.__dissector then local ofs,bpos = ffi.offsetof(base.__dissector, c) assert(not bpos, 'NYI: B[C] = A, where C is a bitfield') local w = builtins.sizeofattr(base.__dissector, c) -- TODO: support vectorized moves larger than register width assert(const_width[w], 'B[C] = A, sizeof(A) must be 1/2/4/8') local src_reg, const = vscalar(a, w) -- If changing map value, write to absolute address + offset if V[b].source and V[b].source:find('ptr_to_map_value', 1, true) then local dst_reg = vreg(b) -- Optimization: immediate values (imm32) can be stored directly if type(const) == 'number' and w < 8 then emit(BPF.MEM + BPF.ST + const_width[w], dst_reg, 0, ofs, const) else emit(BPF.MEM + BPF.STX + const_width[w], dst_reg, src_reg, ofs, 0) end -- Table is already on stack, write to base-relative address elseif base.__base then -- Optimization: immediate values (imm32) can be stored directly if type(const) == 'number' and w < 8 then emit(BPF.MEM + BPF.ST + const_width[w], 10, 0, -base.__base + ofs, const) else emit(BPF.MEM + BPF.STX + const_width[w], 10, src_reg, -base.__base + ofs, 0) end else error('NYI: B[C] where B is not Lua table, BPF map, or pointer') end elseif V[a].const then base[c] = V[a].const else error('NYI: B[C] where B is not Lua table, BPF map, or pointer') end end, TGETB = function (a, b, _, d) -- TGETB (A = B[D]) local base = V[b].const assert(type(base) == 'table', 'NYI: B[C] where C is string and B not Lua table or BPF map') if a ~= b then vset(a) end if base.__map then -- BPF map read (constant) MAP_GET(a, b, nil, d) -- Pointer access with a dissector (traditional uses BPF_LD, direct uses BPF_MEM) elseif V[b].source and V[b].source:find('ptr_to_') then local vtype = base.__dissector and base.__dissector or ffi.typeof('uint8_t') LOAD(a, b, d, vtype) -- Specialise PTR[0] as dereference operator elseif cdef.isptr(V[b].type) and d == 0 then vcopy(a, b) local dst_reg = vreg(a) vderef(dst_reg, dst_reg, V[a]) V[a].type = V[a].const.__dissector else error('NYI: A = B[D], where B is not Lua table or packet dissector or pointer dereference') end end, TGETV = function (a, b, _, d) -- TGETV (A = B[D]) local base = V[b].const assert(type(base) == 'table', 'NYI: B[C] where C is string and B not Lua table or BPF map') if a ~= b then vset(a) end if base.__map then -- BPF map read MAP_GET(a, b, d) -- Pointer access with a dissector (traditional uses BPF_LD, direct uses BPF_MEM) elseif V[b].source and V[b].source:find('ptr_to_') then local vtype = base.__dissector and base.__dissector or ffi.typeof('uint8_t') LOAD(a, b, d, vtype) -- Constant dereference elseif type(V[d].const) == 'number' then V[a].const = base[V[d].const] else error('NYI: A = B[D], where B is not Lua table or packet dissector or pointer dereference') end end, TGETS = function (a, b, c, _) -- TGETS (A = B[C]) local base = V[b].const assert(type(base) == 'table', 'NYI: B[C] where C is string and B not Lua table or BPF map') if a ~= b then vset(a) end if base.__dissector then local ofs,bpos,bsize = ffi.offsetof(base.__dissector, c) -- Resolve table key using metatable if not ofs and type(base.__dissector[c]) == 'string' then c = base.__dissector[c] ofs,bpos,bsize = ffi.offsetof(base.__dissector, c) end if not ofs and proto[c] then -- Load new dissector on given offset BUILTIN(proto[c], a, b, c) else -- Loading register from offset is a little bit tricky as there are -- several data sources and value loading modes with different restrictions -- such as checking pointer values for NULL compared to using stack. assert(ofs, tostring(base.__dissector)..'.'..c..' attribute not exists') if a ~= b then vset(a) end -- Dissected value is probably not constant anymore local new_const = nil local w, atype = builtins.sizeofattr(base.__dissector, c) -- [SP+K] addressing using R10 (stack pointer) -- Doesn't need to be checked for NULL if base.__base and base.__base > 0 then if cdef.isptr(atype) then -- If the member is pointer type, update base pointer with offset new_const = {__base = base.__base-ofs} else local dst_reg = vreg(a, nil, true) emit(BPF.MEM + BPF.LDX + const_width[w], dst_reg, 10, -base.__base+ofs, 0) end -- Pointer access with a dissector (traditional uses BPF_LD, direct uses BPF_MEM) elseif V[b].source and V[b].source:find('ptr_to_') then LOAD(a, b, ofs, atype) else error('NYI: B[C] where B is not Lua table, BPF map, or pointer') end -- Bitfield, must be further narrowed with a bitmask/shift if bpos then local mask = 0 for i=bpos+1,bpos+bsize do mask = bit.bor(mask, bit.lshift(1, w*8-i)) end emit(BPF.ALU64 + BPF.AND + BPF.K, vreg(a), 0, 0, mask) -- Free optimization: single-bit values need just boolean result if bsize > 1 then local shift = w*8-bsize-bpos if shift > 0 then emit(BPF.ALU64 + BPF.RSH + BPF.K, vreg(a), 0, 0, shift) end end end V[a].type = atype V[a].const = new_const V[a].source = V[b].source -- Track direct access to skb data -- see https://www.kernel.org/doc/Documentation/networking/filter.txt "Direct packet access" if ffi.istype(base.__dissector, ffi.typeof('struct sk_buff')) then -- Direct access to skb uses skb->data and skb->data_end -- which are encoded as u32, but are actually pointers if c == 'data' or c == 'data_end' then V[a].const = {__dissector = ffi.typeof('uint8_t')} V[a].source = 'ptr_to_skb' end end end else V[a].const = base[c] end end, -- Loops and branches CALLM = function (a, b, _, d) -- A = A(A+1, ..., A+D+MULTRES) -- NYI: Support single result only CALL(a, b, d+2) end, CALL = function (a, b, _, d) -- A = A(A+1, ..., A+D-1) CALL(a, b, d) end, JMP = function (a, _, c, _) -- JMP -- Discard unused slots after jump for i, _ in pairs(V) do if i >= a and i < stackslots then V[i] = nil end end -- Cross basic block boundary if the jump target isn't provably unreachable local val = code.fixup[c] or {} if code.seen_cmp and code.seen_cmp ~= ALWAYS then if code.seen_cmp ~= NEVER then -- Do not emit the jump or fixup -- Store previous CMP insn for reemitting after compensation code local jmpi = ffi.new('struct bpf_insn', code.insn[code.pc-1]) code.pc = code.pc - 1 -- First branch point, emit compensation code local Vcomp = Vstate[c] if not Vcomp then -- Select scratch register (R0-5) that isn't used as operand -- in the CMP instruction, as the variable may not be live, after -- the JMP, but it may be used in the JMP+CMP instruction itself local tmp_reg = 0 for reg = 0, 5 do if reg ~= jmpi.dst_reg and reg ~= jmpi.src_reg then tmp_reg = reg break end end -- Force materialization of constants at the end of BB for i, v in pairs(V) do if not v.reg and cdef.isimmconst(v) then vreg(i, tmp_reg) -- Load to TMP register (not saved) reg_spill(i) -- Spill caller-saved registers end end -- Record variable state Vstate[c] = V Vcomp = V V = table_copy(V) -- Variable state already set, emit specific compensation code else bb_end(Vcomp) end -- Record pointer NULL check from condition -- If the condition checks pointer variable against NULL, -- we can assume it will not be NULL in the fall-through block if code.seen_null_guard then local var = code.seen_null_guard -- The null guard can have two forms: -- if x == nil then goto -- if x ~= nil then goto -- First form guarantees that the variable will be non-nil on the following instruction -- Second form guarantees that the variable will be non-nil at the jump target local vinfo = code.seen_null_guard_inverse and Vcomp[var] or V[var] if vinfo.source then local pos = vinfo.source:find('_or_null', 1, true) if pos then vinfo.source = vinfo.source:sub(1, pos - 1) end end end -- Reemit CMP insn emit(jmpi.code, jmpi.dst_reg, jmpi.src_reg, jmpi.off, jmpi.imm) -- Fuse JMP into previous CMP opcode, mark JMP target for fixup -- as we don't knot the relative offset in generated code yet table.insert(val, code.pc-1) code.fixup[c] = val end code.seen_cmp = nil code.seen_null_guard = nil code.seen_null_guard_inverse = nil elseif c == code.bc_pc + 1 then -- luacheck: ignore 542 -- Eliminate jumps to next immediate instruction -- e.g. 0002 JMP 1 => 0003 else -- We need to synthesise a condition that's always true, however -- BPF prohibits pointer arithmetic to prevent pointer leaks -- so we have to clear out one register and use it for cmp that's always true local dst_reg = reg_alloc(stackslots) V[stackslots].reg = nil -- Only temporary allocation -- First branch point, emit compensation code local Vcomp = Vstate[c] if not Vcomp then -- Force materialization of constants at the end of BB for i, v in pairs(V) do if not v.reg and cdef.isimmconst(v) then vreg(i, dst_reg) -- Load to TMP register (not saved) reg_spill(i) -- Spill caller-saved registers end end -- Record variable state Vstate[c] = V V = table_copy(V) -- Variable state already set, emit specific compensation code else bb_end(Vcomp) end emit(BPF.ALU64 + BPF.MOV + BPF.K, dst_reg, 0, 0, 0) emit(BPF.JMP + BPF.JEQ + BPF.K, dst_reg, 0, 0xffff, 0) table.insert(val, code.pc-1) -- Fixup JMP target code.reachable = false -- Code following the JMP is not reachable code.fixup[c] = val end end, RET1 = function (a, _, _, _) -- RET1 -- Free optimisation: spilled variable will not be filled again for i, v in pairs(V) do if i ~= a then v.reg = nil end end if V[a].reg ~= 0 then vreg(a, 0) end -- Convenience: dereference pointer variables -- e.g. 'return map[k]' will return actual map value, not pointer if cdef.isptr(V[a].type) then vderef(0, 0, V[a]) end emit(BPF.JMP + BPF.EXIT, 0, 0, 0, 0) code.reachable = false end, RET0 = function (_, _, _, _) -- RET0 emit(BPF.ALU64 + BPF.MOV + BPF.K, 0, 0, 0, 0) emit(BPF.JMP + BPF.EXIT, 0, 0, 0, 0) code.reachable = false end, compile = function () return code end } -- Composite instructions function BC.CALLT(a, _, _, d) -- Tailcall: return A(A+1, ..., A+D-1) CALL(a, 1, d) BC.RET1(a) end -- Always initialize R6 with R1 context emit(BPF.ALU64 + BPF.MOV + BPF.X, 6, 1, 0, 0) -- Register R6 as context variable (first argument) if params and params > 0 then vset(0, 6, param_types[1] or proto.skb) assert(V[0].source == V[0].const.source) -- Propagate source annotation from typeinfo end -- Register tmpvars vset(stackslots) vset(stackslots+1) return setmetatable(BC, { __index = function (_, k, _) if type(k) == 'number' then local op_str = string.sub(require('jit.vmdef').bcnames, 6*k+1, 6*k+6) error(string.format("NYI: opcode '0x%02x' (%-04s)", k, op_str)) end end, __call = function (t, op, a, b, c, d) code.bc_pc = code.bc_pc + 1 -- Exitting BB straight through, emit compensation code if Vstate[code.bc_pc] then if code.reachable then -- Instruction is reachable from previous line -- so we must make the variable allocation consistent -- with the variable allocation at the jump source -- e.g. 0001 x:R0 = 5 -- 0002 if rand() then goto 0005 -- 0003 x:R0 -> x:stack -- 0004 y:R0 = 5 -- 0005 x:? = 10 <-- x was in R0 before jump, and stack after jump bb_end(Vstate[code.bc_pc]) else -- Instruction isn't reachable from previous line, restore variable layout -- e.g. RET or condition-less JMP on previous line V = table_copy(Vstate[code.bc_pc]) end end -- Perform fixup of jump targets -- We need to do this because the number of consumed and emitted -- bytecode instructions is different local fixup = code.fixup[code.bc_pc] if fixup ~= nil then -- Patch JMP source insn with relative offset for _,pc in ipairs(fixup) do code.insn[pc].off = code.pc - 1 - pc end code.fixup[code.bc_pc] = nil code.reachable = true end -- Execute if code.reachable then assert(t[op], string.format('NYI: instruction %s, parameters: %s,%s,%s,%s', op,a,b,c,d)) return t[op](a, b, c, d) end end, }) end -- Emitted code dump local function dump_mem(cls, ins, _, fuse) -- This is a very dense MEM instruction decoder without much explanation -- Refer to https://www.kernel.org/doc/Documentation/networking/filter.txt for instruction format local mode = bit.band(ins.code, 0xe0) if mode == BPF.XADD then cls = 5 end -- The only mode local op_1 = {'LD', 'LDX', 'ST', 'STX', '', 'XADD'} local op_2 = {[0]='W', [8]='H', [16]='B', [24]='DW'} local name = op_1[cls+1] .. op_2[bit.band(ins.code, 0x18)] local off = tonumber(ffi.cast('int16_t', ins.off)) -- Reinterpret as signed local dst = cls < 2 and 'R'..ins.dst_reg or string.format('[R%d%+d]', ins.dst_reg, off) local src = cls % 2 == 0 and '#'..ins.imm or 'R'..ins.src_reg if cls == BPF.LDX then src = string.format('[R%d%+d]', ins.src_reg, off) end if mode == BPF.ABS then src = string.format('skb[%d]', ins.imm) end if mode == BPF.IND then src = string.format('skb[R%d%+d]', ins.src_reg, ins.imm) end return string.format('%s\t%s\t%s', fuse and '' or name, fuse and '' or dst, src) end local function dump_alu(cls, ins, pc) local alu = {'ADD', 'SUB', 'MUL', 'DIV', 'OR', 'AND', 'LSH', 'RSH', 'NEG', 'MOD', 'XOR', 'MOV', 'ARSH', 'END' } local jmp = {'JA', 'JEQ', 'JGT', 'JGE', 'JSET', 'JNE', 'JSGT', 'JSGE', 'CALL', 'EXIT'} local helper = {'unspec', 'map_lookup_elem', 'map_update_elem', 'map_delete_elem', 'probe_read', 'ktime_get_ns', 'trace_printk', 'get_prandom_u32', 'get_smp_processor_id', 'skb_store_bytes', 'l3_csum_replace', 'l4_csum_replace', 'tail_call', 'clone_redirect', 'get_current_pid_tgid', 'get_current_uid_gid', 'get_current_comm', 'get_cgroup_classid', 'skb_vlan_push', 'skb_vlan_pop', 'skb_get_tunnel_key', 'skb_set_tunnel_key', 'perf_event_read', 'redirect', 'get_route_realm', 'perf_event_output', 'skb_load_bytes'} local op = 0 -- This is a very dense ALU instruction decoder without much explanation -- Refer to https://www.kernel.org/doc/Documentation/networking/filter.txt for instruction format for i = 0,13 do if 0x10 * i == bit.band(ins.code, 0xf0) then op = i + 1 break end end local name = (cls == 5) and jmp[op] or alu[op] local src = (bit.band(ins.code, 0x08) == BPF.X) and 'R'..ins.src_reg or '#'..ins.imm local target = (cls == 5 and op < 9) and string.format('\t=> %04d', pc + ins.off + 1) or '' if cls == 5 and op == 9 then target = string.format('\t; %s', helper[ins.imm + 1] or tostring(ins.imm)) end return string.format('%s\t%s\t%s%s', name, 'R'..ins.dst_reg, src, target) end local function dump_string(code, off, hide_counter) if not code then return end local cls_map = { [0] = dump_mem, [1] = dump_mem, [2] = dump_mem, [3] = dump_mem, [4] = dump_alu, [5] = dump_alu, [7] = dump_alu, } local result = {} local fused = false for i = off or 0, code.pc - 1 do local ins = code.insn[i] local cls = bit.band(ins.code, 0x07) local line = cls_map[cls](cls, ins, i, fused) if hide_counter then table.insert(result, line) else table.insert(result, string.format('%04u\t%s', i, line)) end fused = string.find(line, 'LDDW', 1) end return table.concat(result, '\n') end local function dump(code) if not code then return end print(string.format('-- BPF %s:0-%u', code.insn, code.pc)) print(dump_string(code)) end local function compile(prog, params) -- Create code emitter sandbox, include caller locals local env = { pkt=proto.pkt, eth=proto.pkt, BPF=BPF, ffi=ffi } -- Include upvalues up to 4 nested scopes back -- the narrower scope overrides broader scope for k = 5, 2, -1 do local i = 1 while true do local ok, n, v = pcall(debug.getlocal, k, i) if not ok or not n then break end env[n] = v i = i + 1 end end setmetatable(env, { __index = function (_, k) return proto[k] or builtins[k] or _G[k] end }) -- Create code emitter and compile LuaJIT bytecode if type(prog) == 'string' then prog = loadstring(prog) end -- Create error handler to print traceback local funci, pc = bytecode.funcinfo(prog), 0 local E = create_emitter(env, funci.stackslots, funci.params, params or {}) local on_err = function (e) funci = bytecode.funcinfo(prog, pc) local from, to = 0, 0 for _ = 1, funci.currentline do from = to to = string.find(funci.source, '\n', from+1, true) or 0 end print(funci.loc..':'..string.sub(funci.source, from+1, to-1)) print('error: '..e) print(debug.traceback()) end for _,op,a,b,c,d in bytecode.decoder(prog) do local ok, _, err = xpcall(E,on_err,op,a,b,c,d) if not ok then return nil, err end end return E:compile() end -- BPF map interface local bpf_map_mt = { __gc = function (map) S.close(map.fd) end, __len = function(map) return map.max_entries end, __index = function (map, k) if type(k) == 'string' then -- Return iterator if k == 'pairs' then return function(t, key) -- Get next key local next_key = ffi.new(ffi.typeof(t.key)) local cur_key if key then cur_key = t.key t.key[0] = key else cur_key = ffi.new(ffi.typeof(t.key)) end local ok, err = S.bpf_map_op(S.c.BPF_CMD.MAP_GET_NEXT_KEY, map.fd, cur_key, next_key) if not ok then return nil, err end -- Get next value assert(S.bpf_map_op(S.c.BPF_CMD.MAP_LOOKUP_ELEM, map.fd, next_key, map.val)) return next_key[0], map.val[0] end, map, nil -- Read for perf event map elseif k == 'reader' then return function (pmap, pid, cpu, event_type) -- Caller must either specify PID or CPU if not pid or pid < 0 then assert((cpu and cpu >= 0), 'NYI: creating composed reader for all CPUs') pid = -1 end -- Create BPF output reader local pe = S.t.perf_event_attr1() pe[0].type = 'software' pe[0].config = 'sw_bpf_output' pe[0].sample_type = 'raw' pe[0].sample_period = 1 pe[0].wakeup_events = 1 local reader, err = S.t.perf_reader(S.perf_event_open(pe, pid, cpu or -1)) if not reader then return nil, tostring(err) end -- Register event reader fd in BPF map assert(cpu < pmap.max_entries, string.format('BPF map smaller than read CPU %d', cpu)) pmap[cpu] = reader.fd -- Open memory map and start reading local ok, err = reader:start() assert(ok, tostring(err)) ok, err = reader:mmap() assert(ok, tostring(err)) return cdef.event_reader(reader, event_type) end -- Signalise this is a map type end return k == '__map' end -- Retrieve key map.key[0] = k local ok, err = S.bpf_map_op(S.c.BPF_CMD.MAP_LOOKUP_ELEM, map.fd, map.key, map.val) if not ok then return nil, err end return ffi.new(map.val_type, map.val[0]) end, __newindex = function (map, k, v) map.key[0] = k if v == nil then return S.bpf_map_op(map.fd, S.c.BPF_CMD.MAP_DELETE_ELEM, map.key, nil) end map.val[0] = v return S.bpf_map_op(S.c.BPF_CMD.MAP_UPDATE_ELEM, map.fd, map.key, map.val) end, } -- Linux tracing interface local function trace_check_enabled(path) path = path or '/sys/kernel/debug/tracing' if S.statfs(path) then return true end return nil, 'debugfs not accessible: "mount -t debugfs nodev /sys/kernel/debug"? missing sudo?' end -- Tracepoint interface local tracepoint_mt = { __index = { bpf = function (t, prog) if type(prog) ~= 'table' then -- Create protocol parser with source probe prog = compile(prog, {proto.type(t.type, {source='ptr_to_probe'})}) end -- Load the BPF program local prog_fd, err, log = S.bpf_prog_load(S.c.BPF_PROG.TRACEPOINT, prog.insn, prog.pc) assert(prog_fd, tostring(err)..': '..tostring(log)) -- Open tracepoint and attach t.reader:setbpf(prog_fd:getfd()) table.insert(t.progs, prog_fd) return prog_fd end, } } -- Open tracepoint local function tracepoint_open(path, pid, cpu, group_fd) -- Open tracepoint and compile tracepoint type local tp = assert(S.perf_tracepoint('/sys/kernel/debug/tracing/events/'..path)) local tp_type = assert(cdef.tracepoint_type(path)) -- Open tracepoint reader and create interface local reader = assert(S.perf_attach_tracepoint(tp, pid, cpu, group_fd)) return setmetatable({tp=tp,type=tp_type,reader=reader,progs={}}, tracepoint_mt) end local function trace_bpf(ptype, pname, pdef, retprobe, prog, pid, cpu, group_fd) -- Load BPF program if type(prog) ~= 'table' then prog = compile(prog, {proto.pt_regs}) end local prog_fd, err, log = S.bpf_prog_load(S.c.BPF_PROG.KPROBE, prog.insn, prog.pc) assert(prog_fd, tostring(err)..': '..tostring(log)) -- Open tracepoint and attach local tp, err = S.perf_probe(ptype, pname, pdef, retprobe) if not tp then prog_fd:close() return nil, tostring(err) end local reader, err = S.perf_attach_tracepoint(tp, pid, cpu, group_fd, {sample_type='raw, callchain'}) if not reader then prog_fd:close() S.perf_probe(ptype, pname, false) return nil, tostring(err) end local ok, err = reader:setbpf(prog_fd:getfd()) if not ok then prog_fd:close() reader:close() S.perf_probe(ptype, pname, false) return nil, tostring(err)..' (kernel version should be at least 4.1)' end -- Create GC closure for reader to close BPF program -- and detach probe in correct order ffi.gc(reader, function () prog_fd:close() reader:close() S.perf_probe(ptype, pname, false) end) return {reader=reader, prog=prog_fd, probe=pname, probe_type=ptype} end -- Module interface return setmetatable({ new = create_emitter, dump = dump, dump_string = dump_string, maps = {}, map = function (type, max_entries, key_ctype, val_ctype) if not key_ctype then key_ctype = ffi.typeof('uint32_t') end if not val_ctype then val_ctype = ffi.typeof('uint32_t') end if not max_entries then max_entries = 4096 end -- Special case for BPF_MAP_STACK_TRACE if S.c.BPF_MAP[type] == S.c.BPF_MAP.STACK_TRACE then key_ctype = ffi.typeof('int32_t') val_ctype = ffi.typeof('struct bpf_stacktrace') end local fd, err = S.bpf_map_create(S.c.BPF_MAP[type], ffi.sizeof(key_ctype), ffi.sizeof(val_ctype), max_entries) if not fd then return nil, tostring(err) end local map = setmetatable({ max_entries = max_entries, key = ffi.new(ffi.typeof('$ [1]', key_ctype)), val = ffi.new(ffi.typeof('$ [1]', val_ctype)), map_type = S.c.BPF_MAP[type], key_type = key_ctype, val_type = val_ctype, fd = fd:nogc():getfd(), }, bpf_map_mt) return map end, socket = function (sock, prog) -- Expect socket type, if sock is string then assume it's -- an interface name (e.g. 'lo'), if it's a number then typecast it as a socket local ok, err if type(sock) == 'string' then local iface = assert(S.nl.getlink())[sock] assert(iface, sock..' is not interface name') sock, err = S.socket('packet', 'raw') assert(sock, tostring(err)) ok, err = sock:bind(S.t.sockaddr_ll({protocol='all', ifindex=iface.index})) assert(ok, tostring(err)) elseif type(sock) == 'number' then sock = S.t.fd(sock):nogc() elseif ffi.istype(S.t.fd, sock) then -- luacheck: ignore -- No cast required else return nil, 'socket must either be an fd number, an interface name, or an ljsyscall socket' end -- Load program and attach it to socket if type(prog) ~= 'table' then prog = compile(prog, {proto.skb}) end local prog_fd, err, log = S.bpf_prog_load(S.c.BPF_PROG.SOCKET_FILTER, prog.insn, prog.pc) assert(prog_fd, tostring(err)..': '..tostring(log)) assert(sock:setsockopt('socket', 'attach_bpf', prog_fd:getfd())) return prog_fd, err end, tracepoint = function(tp, prog, pid, cpu, group_fd) assert(trace_check_enabled()) -- Return tracepoint instance if no program specified -- this allows free specialisation of arg0 to tracepoint type local probe = tracepoint_open(tp, pid, cpu, group_fd) -- Load the BPF program if prog then probe:bpf(prog) end return probe end, kprobe = function(tp, prog, retprobe, pid, cpu, group_fd) assert(trace_check_enabled()) -- Open tracepoint and attach local pname, pdef = tp:match('([^:]+):(.+)') return trace_bpf('kprobe', pname, pdef, retprobe, prog, pid, cpu, group_fd) end, uprobe = function(tp, prog, retprobe, pid, cpu, group_fd) assert(trace_check_enabled()) -- Translate symbol to address local obj, sym_want = tp:match('([^:]+):(.+)') if not S.statfs(obj) then return nil, S.t.error(S.c.E.NOENT) end -- Resolve Elf object (no support for anything else) local elf = require('bpf.elf').open(obj) local sym = elf:resolve(sym_want) if not sym then return nil, 'no such symbol' end sym = sym.st_value - elf:loadaddr() local sym_addr = string.format('%x%04x', tonumber(bit.rshift(sym, 32)), tonumber(ffi.cast('uint32_t', sym))) -- Convert it to expected uprobe format local pname = string.format('%s_%s', obj:gsub('.*/', ''), sym_addr) local pdef = obj..':0x'..sym_addr return trace_bpf('uprobe', pname, pdef, retprobe, prog, pid, cpu, group_fd) end, tracelog = function(path) assert(trace_check_enabled()) path = path or '/sys/kernel/debug/tracing/trace_pipe' return io.open(path, 'r') end, ntoh = builtins.ntoh, hton = builtins.hton, }, { __call = function (_, prog) return compile(prog) end, }) bpfcc-0.12.0/src/lua/bpf/builtins.lua000066400000000000000000000442301357404205000173250ustar00rootroot00000000000000--[[ Copyright 2016 Marek Vavrusa 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. ]] local ffi = require('ffi') local bit = require('bit') local cdef = require('bpf.cdef') local BPF, HELPER = ffi.typeof('struct bpf'), ffi.typeof('struct bpf_func_id') local const_width = { [1] = BPF.B, [2] = BPF.H, [4] = BPF.W, [8] = BPF.DW, } local const_width_type = { [1] = ffi.typeof('uint8_t'), [2] = ffi.typeof('uint16_t'), [4] = ffi.typeof('uint32_t'), [8] = ffi.typeof('uint64_t'), } -- Built-ins that will be translated into BPF instructions -- i.e. bit.bor(0xf0, 0x0f) becomes {'alu64, or, k', reg(0xf0), reg(0x0f), 0, 0} local builtins = { [bit.lshift] = 'LSH', [bit.rshift] = 'RSH', [bit.band] = 'AND', [bit.bnot] = 'NEG', [bit.bor] = 'OR', [bit.bxor] = 'XOR', [bit.arshift] = 'ARSH', -- Extensions and intrinsics } local function width_type(w) -- Note: ffi.typeof doesn't accept '?' as template return const_width_type[w] or ffi.typeof(string.format('uint8_t [%d]', w)) end builtins.width_type = width_type -- Return struct member size/type (requires LuaJIT 2.1+) -- I am ashamed that there's no easier way around it. local function sizeofattr(ct, name) if not ffi.typeinfo then error('LuaJIT 2.1+ is required for ffi.typeinfo') end local cinfo = ffi.typeinfo(ct) while true do cinfo = ffi.typeinfo(cinfo.sib) if not cinfo then return end if cinfo.name == name then break end end local size = math.max(1, ffi.typeinfo(cinfo.sib or ct).size - cinfo.size) -- Guess type name return size, builtins.width_type(size) end builtins.sizeofattr = sizeofattr -- Byte-order conversions for little endian local function ntoh(x, w) if w then x = ffi.cast(const_width_type[w/8], x) end return bit.bswap(x) end local function hton(x, w) return ntoh(x, w) end builtins.ntoh = ntoh builtins.hton = hton builtins[ntoh] = function (e, dst, a, w) -- This is trickery, but TO_LE means cpu_to_le(), -- and we want exactly the opposite as network is always 'be' w = w or ffi.sizeof(e.V[a].type)*8 if w == 8 then return end -- NOOP assert(w <= 64, 'NYI: hton(a[, width]) - operand larger than register width') -- Allocate registers and execute e.vcopy(dst, a) e.emit(BPF.ALU + BPF.END + BPF.TO_BE, e.vreg(dst), 0, 0, w) end builtins[hton] = function (e, dst, a, w) w = w or ffi.sizeof(e.V[a].type)*8 if w == 8 then return end -- NOOP assert(w <= 64, 'NYI: hton(a[, width]) - operand larger than register width') -- Allocate registers and execute e.vcopy(dst, a) e.emit(BPF.ALU + BPF.END + BPF.TO_LE, e.vreg(dst), 0, 0, w) end -- Byte-order conversions for big endian are no-ops if ffi.abi('be') then ntoh = function (x, w) return w and ffi.cast(const_width_type[w/8], x) or x end hton = ntoh builtins[ntoh] = function(_, _, _) return end builtins[hton] = function(_, _, _) return end end -- Other built-ins local function xadd() error('NYI') end builtins.xadd = xadd builtins[xadd] = function (e, ret, a, b, off) local vinfo = e.V[a].const assert(vinfo and vinfo.__dissector, 'xadd(a, b[, offset]) called on non-pointer') local w = ffi.sizeof(vinfo.__dissector) -- Calculate structure attribute offsets if e.V[off] and type(e.V[off].const) == 'string' then local ct, field = vinfo.__dissector, e.V[off].const off = ffi.offsetof(ct, field) assert(off, 'xadd(a, b, offset) - offset is not valid in given structure') w = sizeofattr(ct, field) end assert(w == 4 or w == 8, 'NYI: xadd() - 1 and 2 byte atomic increments are not supported') -- Allocate registers and execute local src_reg = e.vreg(b) local dst_reg = e.vreg(a) -- Set variable for return value and call e.vset(ret) e.vreg(ret, 0, true, ffi.typeof('int32_t')) -- Optimize the NULL check away if provably not NULL if not e.V[a].source or e.V[a].source:find('_or_null', 1, true) then e.emit(BPF.JMP + BPF.JEQ + BPF.K, dst_reg, 0, 1, 0) -- if (dst != NULL) end e.emit(BPF.XADD + BPF.STX + const_width[w], dst_reg, src_reg, off or 0, 0) end local function probe_read() error('NYI') end builtins.probe_read = probe_read builtins[probe_read] = function (e, ret, dst, src, vtype, ofs) e.reg_alloc(e.tmpvar, 1) -- Load stack pointer to dst, since only load to stack memory is supported -- we have to use allocated stack memory or create a new allocation and convert -- to pointer type e.emit(BPF.ALU64 + BPF.MOV + BPF.X, 1, 10, 0, 0) if not e.V[dst].const or not e.V[dst].const.__base > 0 then builtins[ffi.new](e, dst, vtype) -- Allocate stack memory end e.emit(BPF.ALU64 + BPF.ADD + BPF.K, 1, 0, 0, -e.V[dst].const.__base) -- Set stack memory maximum size bound e.reg_alloc(e.tmpvar, 2) if not vtype then vtype = cdef.typename(e.V[dst].type) -- Dereference pointer type to pointed type for size calculation if vtype:sub(-1) == '*' then vtype = vtype:sub(0, -2) end end local w = ffi.sizeof(vtype) e.emit(BPF.ALU64 + BPF.MOV + BPF.K, 2, 0, 0, w) -- Set source pointer if e.V[src].reg then e.reg_alloc(e.tmpvar, 3) -- Copy from original register e.emit(BPF.ALU64 + BPF.MOV + BPF.X, 3, e.V[src].reg, 0, 0) else e.vreg(src, 3) e.reg_spill(src) -- Spill to avoid overwriting end if ofs and ofs > 0 then e.emit(BPF.ALU64 + BPF.ADD + BPF.K, 3, 0, 0, ofs) end -- Call probe read helper ret = ret or e.tmpvar e.vset(ret) e.vreg(ret, 0, true, ffi.typeof('int32_t')) e.emit(BPF.JMP + BPF.CALL, 0, 0, 0, HELPER.probe_read) e.V[e.tmpvar].reg = nil -- Free temporary registers end builtins[ffi.cast] = function (e, dst, ct, x) assert(e.V[ct].const, 'ffi.cast(ctype, x) called with bad ctype') e.vcopy(dst, x) if e.V[x].const and type(e.V[x].const) == 'table' then e.V[dst].const.__dissector = ffi.typeof(e.V[ct].const) end e.V[dst].type = ffi.typeof(e.V[ct].const) -- Specific types also encode source of the data -- This is because BPF has different helpers for reading -- different data sources, so variables must track origins. -- struct pt_regs - source of the data is probe -- struct skb - source of the data is socket buffer -- struct X - source of the data is probe/tracepoint if ffi.typeof(e.V[ct].const) == ffi.typeof('struct pt_regs') then e.V[dst].source = 'ptr_to_probe' end end builtins[ffi.new] = function (e, dst, ct, x) if type(ct) == 'number' then ct = ffi.typeof(e.V[ct].const) -- Get ctype from variable end assert(not x, 'NYI: ffi.new(ctype, ...) - initializer is not supported') assert(not cdef.isptr(ct, true), 'NYI: ffi.new(ctype, ...) - ctype MUST NOT be a pointer') e.vset(dst, nil, ct) e.V[dst].source = 'ptr_to_stack' e.V[dst].const = {__base = e.valloc(ffi.sizeof(ct), true), __dissector = ct} -- Set array dissector if created an array -- e.g. if ct is 'char [2]', then dissector is 'char' local elem_type = tostring(ct):match('ctype<(.+)%s%[(%d+)%]>') if elem_type then e.V[dst].const.__dissector = ffi.typeof(elem_type) end end builtins[ffi.copy] = function (e, ret, dst, src) assert(cdef.isptr(e.V[dst].type), 'ffi.copy(dst, src) - dst MUST be a pointer type') assert(cdef.isptr(e.V[src].type), 'ffi.copy(dst, src) - src MUST be a pointer type') -- Specific types also encode source of the data -- struct pt_regs - source of the data is probe -- struct skb - source of the data is socket buffer if e.V[src].source and e.V[src].source:find('ptr_to_probe', 1, true) then e.reg_alloc(e.tmpvar, 1) -- Load stack pointer to dst, since only load to stack memory is supported -- we have to either use spilled variable or allocated stack memory offset e.emit(BPF.ALU64 + BPF.MOV + BPF.X, 1, 10, 0, 0) if e.V[dst].spill then e.emit(BPF.ALU64 + BPF.ADD + BPF.K, 1, 0, 0, -e.V[dst].spill) elseif e.V[dst].const.__base then e.emit(BPF.ALU64 + BPF.ADD + BPF.K, 1, 0, 0, -e.V[dst].const.__base) else error('ffi.copy(dst, src) - can\'t get stack offset of dst') end -- Set stack memory maximum size bound local dst_tname = cdef.typename(e.V[dst].type) if dst_tname:sub(-1) == '*' then dst_tname = dst_tname:sub(0, -2) end e.reg_alloc(e.tmpvar, 2) e.emit(BPF.ALU64 + BPF.MOV + BPF.K, 2, 0, 0, ffi.sizeof(dst_tname)) -- Set source pointer if e.V[src].reg then e.reg_alloc(e.tmpvar, 3) -- Copy from original register e.emit(BPF.ALU64 + BPF.MOV + BPF.X, 3, e.V[src].reg, 0, 0) else e.vreg(src, 3) e.reg_spill(src) -- Spill to avoid overwriting end -- Call probe read helper e.vset(ret) e.vreg(ret, 0, true, ffi.typeof('int32_t')) e.emit(BPF.JMP + BPF.CALL, 0, 0, 0, HELPER.probe_read) e.V[e.tmpvar].reg = nil -- Free temporary registers elseif e.V[src].const and e.V[src].const.__map then error('NYI: ffi.copy(dst, src) - src is backed by BPF map') elseif e.V[src].const and e.V[src].const.__dissector then error('NYI: ffi.copy(dst, src) - src is backed by socket buffer') else -- TODO: identify cheap register move -- TODO: identify copy to/from stack error('NYI: ffi.copy(dst, src) - src is neither BPF map/socket buffer or probe') end end -- print(format, ...) builtin changes semantics from Lua print(...) -- the first parameter has to be format and only reduced set of conversion specificers -- is allowed: %d %u %x %ld %lu %lx %lld %llu %llx %p %s builtins[print] = function (e, ret, fmt, a1, a2, a3) -- Load format string and length e.reg_alloc(e.V[e.tmpvar], 1) e.reg_alloc(e.V[e.tmpvar+1], 1) if type(e.V[fmt].const) == 'string' then local src = e.V[fmt].const local len = #src + 1 local dst = e.valloc(len, src) -- TODO: this is materialize step e.V[fmt].const = {__base=dst} e.V[fmt].type = ffi.typeof('char ['..len..']') elseif e.V[fmt].const.__base then -- luacheck: ignore -- NOP else error('NYI: print(fmt, ...) - format variable is not literal/stack memory') end -- Prepare helper call e.emit(BPF.ALU64 + BPF.MOV + BPF.X, 1, 10, 0, 0) e.emit(BPF.ALU64 + BPF.ADD + BPF.K, 1, 0, 0, -e.V[fmt].const.__base) e.emit(BPF.ALU64 + BPF.MOV + BPF.K, 2, 0, 0, ffi.sizeof(e.V[fmt].type)) if a1 then local args = {a1, a2, a3} assert(#args <= 3, 'print(fmt, ...) - maximum of 3 arguments supported') for i, arg in ipairs(args) do e.vcopy(e.tmpvar, arg) -- Copy variable e.vreg(e.tmpvar, 3+i-1) -- Materialize it in arg register end end -- Call helper e.vset(ret) e.vreg(ret, 0, true, ffi.typeof('int32_t')) -- Return is integer e.emit(BPF.JMP + BPF.CALL, 0, 0, 0, HELPER.trace_printk) e.V[e.tmpvar].reg = nil -- Free temporary registers end -- Implements bpf_perf_event_output(ctx, map, flags, var, vlen) on perf event map local function perf_submit(e, dst, map_var, src) -- Set R2 = map fd (indirect load) local map = e.V[map_var].const e.vcopy(e.tmpvar, map_var) e.vreg(e.tmpvar, 2, true, ffi.typeof('uint64_t')) e.LD_IMM_X(2, BPF.PSEUDO_MAP_FD, map.fd, ffi.sizeof('uint64_t')) -- Set R1 = ctx e.reg_alloc(e.tmpvar, 1) -- Spill anything in R1 (unnamed tmp variable) e.emit(BPF.ALU64 + BPF.MOV + BPF.X, 1, 6, 0, 0) -- CTX is always in R6, copy -- Set R3 = flags e.vset(e.tmpvar, nil, 0) -- BPF_F_CURRENT_CPU e.vreg(e.tmpvar, 3, false, ffi.typeof('uint64_t')) -- Set R4 = pointer to src on stack assert(e.V[src].const.__base, 'NYI: submit(map, var) - variable is not on stack') e.emit(BPF.ALU64 + BPF.MOV + BPF.X, 4, 10, 0, 0) e.emit(BPF.ALU64 + BPF.ADD + BPF.K, 4, 0, 0, -e.V[src].const.__base) -- Set R5 = src length e.emit(BPF.ALU64 + BPF.MOV + BPF.K, 5, 0, 0, ffi.sizeof(e.V[src].type)) -- Set R0 = ret and call e.vset(dst) e.vreg(dst, 0, true, ffi.typeof('int32_t')) -- Return is integer e.emit(BPF.JMP + BPF.CALL, 0, 0, 0, HELPER.perf_event_output) e.V[e.tmpvar].reg = nil -- Free temporary registers end -- Implements bpf_skb_load_bytes(ctx, off, var, vlen) on skb->data local function load_bytes(e, dst, off, var) -- Set R2 = offset e.vset(e.tmpvar, nil, off) e.vreg(e.tmpvar, 2, false, ffi.typeof('uint64_t')) -- Set R1 = ctx e.reg_alloc(e.tmpvar, 1) -- Spill anything in R1 (unnamed tmp variable) e.emit(BPF.ALU64 + BPF.MOV + BPF.X, 1, 6, 0, 0) -- CTX is always in R6, copy -- Set R3 = pointer to var on stack assert(e.V[var].const.__base, 'NYI: load_bytes(off, var, len) - variable is not on stack') e.emit(BPF.ALU64 + BPF.MOV + BPF.X, 3, 10, 0, 0) e.emit(BPF.ALU64 + BPF.ADD + BPF.K, 3, 0, 0, -e.V[var].const.__base) -- Set R4 = var length e.emit(BPF.ALU64 + BPF.MOV + BPF.K, 4, 0, 0, ffi.sizeof(e.V[var].type)) -- Set R0 = ret and call e.vset(dst) e.vreg(dst, 0, true, ffi.typeof('int32_t')) -- Return is integer e.emit(BPF.JMP + BPF.CALL, 0, 0, 0, HELPER.skb_load_bytes) e.V[e.tmpvar].reg = nil -- Free temporary registers end -- Implements bpf_get_stack_id() local function stack_id(e, ret, map_var, key) -- Set R2 = map fd (indirect load) local map = e.V[map_var].const e.vcopy(e.tmpvar, map_var) e.vreg(e.tmpvar, 2, true, ffi.typeof('uint64_t')) e.LD_IMM_X(2, BPF.PSEUDO_MAP_FD, map.fd, ffi.sizeof('uint64_t')) -- Set R1 = ctx e.reg_alloc(e.tmpvar, 1) -- Spill anything in R1 (unnamed tmp variable) e.emit(BPF.ALU64 + BPF.MOV + BPF.X, 1, 6, 0, 0) -- CTX is always in R6, copy -- Load flags in R2 (immediate value or key) local imm = e.V[key].const assert(tonumber(imm), 'NYI: stack_id(map, var), var must be constant number') e.reg_alloc(e.tmpvar, 3) -- Spill anything in R2 (unnamed tmp variable) e.LD_IMM_X(3, 0, imm, 8) -- Return R0 as signed integer e.vset(ret) e.vreg(ret, 0, true, ffi.typeof('int32_t')) e.emit(BPF.JMP + BPF.CALL, 0, 0, 0, HELPER.get_stackid) e.V[e.tmpvar].reg = nil -- Free temporary registers end -- table.insert(table, value) keeps semantics with the exception of BPF maps -- map `perf_event` -> submit inserted value builtins[table.insert] = function (e, dst, map_var, value) assert(e.V[map_var].const.__map, 'NYI: table.insert() supported only on BPF maps') return perf_submit(e, dst, map_var, value) end -- bpf_get_current_comm(buffer) - write current process name to byte buffer local function comm() error('NYI') end builtins[comm] = function (e, ret, dst) -- Set R1 = buffer assert(e.V[dst].const.__base, 'NYI: comm(buffer) - buffer variable is not on stack') e.reg_alloc(e.tmpvar, 1) -- Spill e.emit(BPF.ALU64 + BPF.MOV + BPF.X, 1, 10, 0, 0) e.emit(BPF.ALU64 + BPF.ADD + BPF.K, 1, 0, 0, -e.V[dst].const.__base) -- Set R2 = length e.reg_alloc(e.tmpvar, 2) -- Spill e.emit(BPF.ALU64 + BPF.MOV + BPF.K, 2, 0, 0, ffi.sizeof(e.V[dst].type)) -- Return is integer e.vset(ret) e.vreg(ret, 0, true, ffi.typeof('int32_t')) e.emit(BPF.JMP + BPF.CALL, 0, 0, 0, HELPER.get_current_comm) e.V[e.tmpvar].reg = nil -- Free temporary registers end -- Math library built-ins math.log2 = function () error('NYI') end builtins[math.log2] = function (e, dst, x) -- Classic integer bits subdivison algorithm to find the position -- of the highest bit set, adapted for BPF bytecode-friendly operations. -- https://graphics.stanford.edu/~seander/bithacks.html -- r = 0 local r = e.vreg(dst, nil, true) e.emit(BPF.ALU64 + BPF.MOV + BPF.K, r, 0, 0, 0) -- v = x e.vcopy(e.tmpvar, x) local v = e.vreg(e.tmpvar, 2) if cdef.isptr(e.V[x].const) then -- No pointer arithmetics, dereference e.vderef(v, v, {const = {__dissector=ffi.typeof('uint64_t')}}) end -- Invert value to invert all tests, otherwise we would need and+jnz e.emit(BPF.ALU64 + BPF.NEG + BPF.K, v, 0, 0, 0) -- v = ~v -- Unrolled test cases, converted masking to arithmetic as we don't have "if !(a & b)" -- As we're testing inverted value, we have to use arithmetic shift to copy MSB for i=4,0,-1 do local k = bit.lshift(1, i) e.emit(BPF.JMP + BPF.JGT + BPF.K, v, 0, 2, bit.bnot(bit.lshift(1, k))) -- if !upper_half(x) e.emit(BPF.ALU64 + BPF.ARSH + BPF.K, v, 0, 0, k) -- v >>= k e.emit(BPF.ALU64 + BPF.OR + BPF.K, r, 0, 0, k) -- r |= k end -- No longer constant, cleanup tmpvars e.V[dst].const = nil e.V[e.tmpvar].reg = nil end builtins[math.log10] = function (e, dst, x) -- Compute log2(x) and transform builtins[math.log2](e, dst, x) -- Relationship: log10(v) = log2(v) / log2(10) local r = e.V[dst].reg e.emit(BPF.ALU64 + BPF.ADD + BPF.K, r, 0, 0, 1) -- Compensate round-down e.emit(BPF.ALU64 + BPF.MUL + BPF.K, r, 0, 0, 1233) -- log2(10) ~ 1233>>12 e.emit(BPF.ALU64 + BPF.RSH + BPF.K, r, 0, 0, 12) end builtins[math.log] = function (e, dst, x) -- Compute log2(x) and transform builtins[math.log2](e, dst, x) -- Relationship: ln(v) = log2(v) / log2(e) local r = e.V[dst].reg e.emit(BPF.ALU64 + BPF.ADD + BPF.K, r, 0, 0, 1) -- Compensate round-down e.emit(BPF.ALU64 + BPF.MUL + BPF.K, r, 0, 0, 2839) -- log2(e) ~ 2839>>12 e.emit(BPF.ALU64 + BPF.RSH + BPF.K, r, 0, 0, 12) end -- Call-type helpers local function call_helper(e, dst, h, vtype) e.vset(dst) e.vreg(dst, 0, true, vtype or ffi.typeof('uint64_t')) e.emit(BPF.JMP + BPF.CALL, 0, 0, 0, h) e.V[dst].const = nil -- Target is not a function anymore end local function cpu() error('NYI') end local function rand() error('NYI') end local function time() error('NYI') end local function pid_tgid() error('NYI') end local function uid_gid() error('NYI') end -- Export helpers and builtin variants builtins.cpu = cpu builtins.time = time builtins.pid_tgid = pid_tgid builtins.uid_gid = uid_gid builtins.comm = comm builtins.perf_submit = perf_submit builtins.stack_id = stack_id builtins.load_bytes = load_bytes builtins[cpu] = function (e, dst) return call_helper(e, dst, HELPER.get_smp_processor_id) end builtins[rand] = function (e, dst) return call_helper(e, dst, HELPER.get_prandom_u32, ffi.typeof('uint32_t')) end builtins[time] = function (e, dst) return call_helper(e, dst, HELPER.ktime_get_ns) end builtins[pid_tgid] = function (e, dst) return call_helper(e, dst, HELPER.get_current_pid_tgid) end builtins[uid_gid] = function (e, dst) return call_helper(e, dst, HELPER.get_current_uid_gid) end builtins[perf_submit] = function (e, dst, map, value) return perf_submit(e, dst, map, value) end builtins[stack_id] = function (e, dst, map, key) return stack_id(e, dst, map, key) end builtins[load_bytes] = function (e, dst, off, var, len) return load_bytes(e, dst, off, var, len) end return builtins bpfcc-0.12.0/src/lua/bpf/cdef.lua000066400000000000000000000222601357404205000163740ustar00rootroot00000000000000--[[ Copyright 2016 Marek Vavrusa 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. ]] local ffi = require('ffi') local bit = require('bit') local has_syscall, S = pcall(require, 'syscall') local M = {} ffi.cdef [[ struct bpf { /* Instruction classes */ static const int LD = 0x00; static const int LDX = 0x01; static const int ST = 0x02; static const int STX = 0x03; static const int ALU = 0x04; static const int JMP = 0x05; static const int ALU64 = 0x07; /* ld/ldx fields */ static const int W = 0x00; static const int H = 0x08; static const int B = 0x10; static const int ABS = 0x20; static const int IND = 0x40; static const int MEM = 0x60; static const int LEN = 0x80; static const int MSH = 0xa0; /* alu/jmp fields */ static const int ADD = 0x00; static const int SUB = 0x10; static const int MUL = 0x20; static const int DIV = 0x30; static const int OR = 0x40; static const int AND = 0x50; static const int LSH = 0x60; static const int RSH = 0x70; static const int NEG = 0x80; static const int MOD = 0x90; static const int XOR = 0xa0; static const int JA = 0x00; static const int JEQ = 0x10; static const int JGT = 0x20; static const int JGE = 0x30; static const int JSET = 0x40; static const int K = 0x00; static const int X = 0x08; static const int JNE = 0x50; /* jump != */ static const int JSGT = 0x60; /* SGT is signed '>', GT in x86 */ static const int JSGE = 0x70; /* SGE is signed '>=', GE in x86 */ static const int CALL = 0x80; /* function call */ static const int EXIT = 0x90; /* function return */ /* ld/ldx fields */ static const int DW = 0x18; /* double word */ static const int XADD = 0xc0; /* exclusive add */ /* alu/jmp fields */ static const int MOV = 0xb0; /* mov reg to reg */ static const int ARSH = 0xc0; /* sign extending arithmetic shift right */ /* change endianness of a register */ static const int END = 0xd0; /* flags for endianness conversion: */ static const int TO_LE = 0x00; /* convert to little-endian */ static const int TO_BE = 0x08; /* convert to big-endian */ /* misc */ static const int PSEUDO_MAP_FD = 0x01; /* helper functions */ static const int F_CURRENT_CPU = 0xffffffff; static const int F_USER_STACK = 1 << 8; static const int F_FAST_STACK_CMP = 1 << 9; static const int F_REUSE_STACKID = 1 << 10; /* special offsets for ancillary data */ static const int NET_OFF = -0x100000; static const int LL_OFF = -0x200000; }; /* eBPF commands */ struct bpf_cmd { static const int MAP_CREATE = 0; static const int MAP_LOOKUP_ELEM = 1; static const int MAP_UPDATE_ELEM = 2; static const int MAP_DELETE_ELEM = 3; static const int MAP_GET_NEXT_KEY = 4; static const int PROG_LOAD = 5; static const int OBJ_PIN = 6; static const int OBJ_GET = 7; }; /* eBPF helpers */ struct bpf_func_id { static const int unspec = 0; static const int map_lookup_elem = 1; static const int map_update_elem = 2; static const int map_delete_elem = 3; static const int probe_read = 4; static const int ktime_get_ns = 5; static const int trace_printk = 6; static const int get_prandom_u32 = 7; static const int get_smp_processor_id = 8; static const int skb_store_bytes = 9; static const int l3_csum_replace = 10; static const int l4_csum_replace = 11; static const int tail_call = 12; static const int clone_redirect = 13; static const int get_current_pid_tgid = 14; static const int get_current_uid_gid = 15; static const int get_current_comm = 16; static const int get_cgroup_classid = 17; static const int skb_vlan_push = 18; static const int skb_vlan_pop = 19; static const int skb_get_tunnel_key = 20; static const int skb_set_tunnel_key = 21; static const int perf_event_read = 22; static const int redirect = 23; static const int get_route_realm = 24; static const int perf_event_output = 25; static const int skb_load_bytes = 26; static const int get_stackid = 27; }; /* BPF_MAP_STACK_TRACE structures and constants */ static const int BPF_MAX_STACK_DEPTH = 127; struct bpf_stacktrace { uint64_t ip[BPF_MAX_STACK_DEPTH]; }; ]] -- Compatibility: ljsyscall doesn't have support for BPF syscall if not has_syscall or not S.bpf then error("ljsyscall doesn't support bpf(), must be updated") else local strflag = require('syscall.helpers').strflag -- Compatibility: ljsyscall<=0.12 if not S.c.BPF_MAP.LRU_HASH then S.c.BPF_MAP = strflag { UNSPEC = 0, HASH = 1, ARRAY = 2, PROG_ARRAY = 3, PERF_EVENT_ARRAY = 4, PERCPU_HASH = 5, PERCPU_ARRAY = 6, STACK_TRACE = 7, CGROUP_ARRAY = 8, LRU_HASH = 9, LRU_PERCPU_HASH = 10, LPM_TRIE = 11, ARRAY_OF_MAPS = 12, HASH_OF_MAPS = 13, DEVMAP = 14, SOCKMAP = 15, CPUMAP = 16, } end if not S.c.BPF_PROG.TRACEPOINT then S.c.BPF_PROG = strflag { UNSPEC = 0, SOCKET_FILTER = 1, KPROBE = 2, SCHED_CLS = 3, SCHED_ACT = 4, TRACEPOINT = 5, XDP = 6, PERF_EVENT = 7, CGROUP_SKB = 8, CGROUP_SOCK = 9, LWT_IN = 10, LWT_OUT = 11, LWT_XMIT = 12, SOCK_OPS = 13, SK_SKB = 14, CGROUP_DEVICE = 15, SK_MSG = 16, RAW_TRACEPOINT = 17, CGROUP_SOCK_ADDR = 18, } end end -- Compatibility: metatype for stacktrace local function stacktrace_iter(t, i) i = i + 1 if i < #t and t.ip[i] > 0 then return i, t.ip[i] end end ffi.metatype('struct bpf_stacktrace', { __len = function (t) return ffi.sizeof(t.ip) / ffi.sizeof(t.ip[0]) end, __ipairs = function (t) return stacktrace_iter, t, -1 end, }) -- Reflect cdata type function M.typename(v) if not v or type(v) ~= 'cdata' then return nil end return string.match(tostring(ffi.typeof(v)), '<([^>]+)') end -- Reflect if cdata type can be pointer (accepts array or pointer) function M.isptr(v, noarray) local ctname = M.typename(v) if ctname then ctname = string.sub(ctname, -1) ctname = ctname == '*' or (not noarray and ctname == ']') end return ctname end -- Return true if variable is a non-nil constant that can be used as immediate value -- e.g. result of KSHORT and KNUM function M.isimmconst(v) return (type(v.const) == 'number' and not ffi.istype(v.type, ffi.typeof('void'))) or type(v.const) == 'cdata' and ffi.istype(v.type, ffi.typeof('uint64_t')) -- Lua numbers are at most 52 bits or type(v.const) == 'cdata' and ffi.istype(v.type, ffi.typeof('int64_t')) end function M.osversion() -- We have no better way to extract current kernel hex-string other -- than parsing headers, compiling a helper function or reading /proc local ver_str, count = S.sysctl('kernel.version'):match('%d+.%d+.%d+'), 2 if not ver_str then -- kernel.version is freeform, fallback to kernel.osrelease ver_str = S.sysctl('kernel.osrelease'):match('%d+.%d+.%d+') end local version = 0 for i in ver_str:gmatch('%d+') do -- Convert 'X.Y.Z' to 0xXXYYZZ version = bit.bor(version, bit.lshift(tonumber(i), 8*count)) count = count - 1 end return version end function M.event_reader(reader, event_type) -- Caller can specify event message binary format if event_type then assert(type(event_type) == 'string' and ffi.typeof(event_type), 'not a valid type for event reader') event_type = ffi.typeof(event_type .. '*') -- Convert type to pointer-to-type end -- Wrap reader in interface that can interpret read event messages return setmetatable({reader=reader,type=event_type}, {__index = { block = function(_ --[[self]]) return S.select { readfds = {reader.fd} } end, next = function(_ --[[self]], k) local len, ev = reader:next(k) -- Filter out only sample frames while ev and ev.type ~= S.c.PERF_RECORD.SAMPLE do len, ev = reader:next(len) end if ev and event_type then -- The perf event reader returns framed data with header and variable length -- This is going skip the frame header and cast data to given type ev = ffi.cast(event_type, ffi.cast('char *', ev) + ffi.sizeof('struct perf_event_header') + ffi.sizeof('uint32_t')) end return len, ev end, read = function(self) return self.next, self, nil end, }}) end function M.tracepoint_type(tp) -- Read tracepoint format string local fp = assert(io.open('/sys/kernel/debug/tracing/events/'..tp..'/format', 'r')) local fmt = fp:read '*a' fp:close() -- Parse struct fields local fields = {} for f in fmt:gmatch 'field:([^;]+;)' do table.insert(fields, f) end return string.format('struct { %s }', table.concat(fields)) end return M bpfcc-0.12.0/src/lua/bpf/elf.lua000066400000000000000000000222001357404205000162330ustar00rootroot00000000000000--[[ Copyright 2016 Marek Vavrusa 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. ]] -- This is a tiny wrapper over libelf to extract load address -- and offsets of dynamic symbols local S = require('syscall') local ffi = require('ffi') ffi.cdef [[ /* Type for a 16-bit quantity. */ typedef uint16_t Elf32_Half; typedef uint16_t Elf64_Half; /* Types for signed and unsigned 32-bit quantities. */ typedef uint32_t Elf32_Word; typedef int32_t Elf32_Sword; typedef uint32_t Elf64_Word; typedef int32_t Elf64_Sword; /* Types for signed and unsigned 64-bit quantities. */ typedef uint64_t Elf32_Xword; typedef int64_t Elf32_Sxword; typedef uint64_t Elf64_Xword; typedef int64_t Elf64_Sxword; /* Type of addresses. */ typedef uint32_t Elf32_Addr; typedef uint64_t Elf64_Addr; /* Type of file offsets. */ typedef uint32_t Elf32_Off; typedef uint64_t Elf64_Off; /* Type for section indices, which are 16-bit quantities. */ typedef uint16_t Elf32_Section; typedef uint16_t Elf64_Section; /* Constants */ struct Elf_Cmd { static const int READ = 1; static const int RDWR = 2; static const int WRITE = 3; static const int CLR = 4; static const int SET = 5; static const int FDDONE = 6; static const int FDREAD = 7; static const int READ_MMAP = 8; static const int RDWR_MMAP = 9; static const int WRITE_MMAP =10; static const int READ_MMAP_PRIVATE =11; static const int EMPTY =12; static const int NUM =13; }; /* Descriptor for the ELF file. */ typedef struct Elf Elf; /* Descriptor for ELF file section. */ typedef struct Elf_Scn Elf_Scn; /* Container type for metatable */ struct Elf_object { int fd; Elf *elf; }; /* Program segment header. */ typedef struct { Elf64_Word p_type; /* Segment type */ Elf64_Word p_flags; /* Segment flags */ Elf64_Off p_offset; /* Segment file offset */ Elf64_Addr p_vaddr; /* Segment virtual address */ Elf64_Addr p_paddr; /* Segment physical address */ Elf64_Xword p_filesz; /* Segment size in file */ Elf64_Xword p_memsz; /* Segment size in memory */ Elf64_Xword p_align; /* Segment alignment */ } Elf64_Phdr; typedef Elf64_Phdr GElf_Phdr; /* Section header. */ typedef struct { Elf64_Word sh_name; /* Section name (string tbl index) */ Elf64_Word sh_type; /* Section type */ Elf64_Xword sh_flags; /* Section flags */ Elf64_Addr sh_addr; /* Section virtual addr at execution */ Elf64_Off sh_offset; /* Section file offset */ Elf64_Xword sh_size; /* Section size in bytes */ Elf64_Word sh_link; /* Link to another section */ Elf64_Word sh_info; /* Additional section information */ Elf64_Xword sh_addralign; /* Section alignment */ Elf64_Xword sh_entsize; /* Entry size if section holds table */ } Elf64_Shdr; typedef Elf64_Shdr GElf_Shdr; /* Descriptor for data to be converted to or from memory format. */ typedef struct { void *d_buf; /* Pointer to the actual data. */ int d_type; /* Type of this piece of data. */ unsigned int d_version; /* ELF version. */ size_t d_size; /* Size in bytes. */ uint64_t d_off; /* Offset into section. */ size_t d_align; /* Alignment in section. */ } Elf_Data; /* Symbol table entry. */ typedef struct { Elf64_Word st_name; /* Symbol name (string tbl index) */ unsigned char st_info; /* Symbol type and binding */ unsigned char st_other; /* Symbol visibility */ Elf64_Section st_shndx; /* Section index */ Elf64_Addr st_value; /* Symbol value */ Elf64_Xword st_size; /* Symbol size */ } Elf64_Sym; typedef Elf64_Sym GElf_Sym; /* Coordinate ELF library and application versions. */ unsigned int elf_version (unsigned int __version); /* Return descriptor for ELF file to work according to CMD. */ Elf *elf_begin (int __fildes, int __cmd, Elf *__ref); /* Free resources allocated for ELF. */ int elf_end (Elf *__elf); /* Get the number of program headers in the ELF file. If the file uses more headers than can be represented in the e_phnum field of the ELF header the information from the sh_info field in the zeroth section header is used. */ int elf_getphdrnum (Elf *__elf, size_t *__dst); /* Retrieve program header table entry. */ GElf_Phdr *gelf_getphdr (Elf *__elf, int __ndx, GElf_Phdr *__dst); /* Retrieve section header. */ GElf_Shdr *gelf_getshdr (Elf_Scn *__scn, GElf_Shdr *__dst); /* Retrieve symbol information from the symbol table at the given index. */ GElf_Sym *gelf_getsym (Elf_Data *__data, int __ndx, GElf_Sym *__dst); /* Get section with next section index. */ Elf_Scn *elf_nextscn (Elf *__elf, Elf_Scn *__scn); /* Get data from section while translating from file representation to memory representation. */ Elf_Data *elf_getdata (Elf_Scn *__scn, Elf_Data *__data); /* Return pointer to string at OFFSET in section INDEX. */ char *elf_strptr (Elf *__elf, size_t __index, size_t __offset); ]] local elf = ffi.load('elf') local EV = { NONE=0, CURRENT=1, NUM=2 } local PT = { NULL=0, LOAD=1, DYNAMIC=2, INTERP=3, NOTE=4, SHLIB=5, PHDR=6, TLS=7, NUM=8 } local SHT = { NULL=0, PROGBITS=1, SYMTAB=2, STRTAB=3, RELA=4, HASH=5, DYNAMIC=6, NOTE=7, NOBITS=8, REL=9, SHLIB=10, DYNSYM=11, INIT_ARRAY=14, FINI_ARRAY=15, PREINIT_ARRAY=16, GROUP=17, SYMTAB_SHNDX=18, NUM=19 } local ELF_C = ffi.new('struct Elf_Cmd') local M = {} -- Optional poor man's C++ demangler local cpp_demangler = os.getenv('CPP_DEMANGLER') if not cpp_demangler then for prefix in string.gmatch(os.getenv('PATH'), '[^;:]+') do if S.statfs(prefix..'/c++filt') then cpp_demangler = prefix..'/c++filt' break end end end local cpp_demangle = function (name) return name end if cpp_demangler then cpp_demangle = function (name) local cmd = string.format('%s -p %s', cpp_demangler, name) local fp = assert(io.popen(cmd, 'r')) local output = fp:read('*all') fp:close() return output:match '^(.-)%s*$' end end -- Metatable for ELF object ffi.metatype('struct Elf_object', { __gc = function (t) t:close() end, __index = { close = function (t) if t.elf ~= nil then elf.elf_end(t.elf) S.close(t.fd) t.elf = nil end end, -- Load library load address loadaddr = function(t) local phnum = ffi.new('size_t [1]') if elf.elf_getphdrnum(t.elf, phnum) == nil then return nil, 'cannot get phdrnum' end local header = ffi.new('GElf_Phdr [1]') for i = 0, tonumber(phnum[0])-1 do if elf.gelf_getphdr(t.elf, i, header) ~= nil and header[0].p_type == PT.LOAD then return header[0].p_vaddr end end end, -- Resolve symbol address resolve = function (t, k, pattern) local section = elf.elf_nextscn(t.elf, nil) while section ~= nil do local header = ffi.new('GElf_Shdr [1]') if elf.gelf_getshdr(section, header) ~= nil then if header[0].sh_type == SHT.SYMTAB or header[0].sh_type == SHT.DYNSYM then local data = elf.elf_getdata(section, nil) while data ~= nil do if data.d_size % header[0].sh_entsize > 0 then return nil, 'bad section header entity size' end local symcount = tonumber(data.d_size / header[0].sh_entsize) local sym = ffi.new('GElf_Sym [1]') for i = 0, symcount - 1 do if elf.gelf_getsym(data, i, sym) ~= nil then local name = elf.elf_strptr(t.elf, header[0].sh_link, sym[0].st_name) if name ~= nil then -- Demangle C++ symbols if necessary name = ffi.string(name) if name:sub(1,2) == '_Z' then name = cpp_demangle(name) end -- Match symbol name against pattern if pattern and string.match(name, k) or k == name then return sym[0] end end end end data = elf.elf_getdata(section, data) end end end section = elf.elf_nextscn(t.elf, section) end end, } }) -- Open an ELF object function M.open(path) if elf.elf_version(EV.CURRENT) == EV.NONE then return nil, 'bad version' end local fd, err = S.open(path, 'rdonly') if not fd then return nil, err end local pt = ffi.new('Elf *') pt = elf.elf_begin(fd:getfd(), ELF_C.READ, pt) if not pt then fd:close() return nil, 'cannot open elf object' end return ffi.new('struct Elf_object', fd:nogc():getfd(), pt) end return Mbpfcc-0.12.0/src/lua/bpf/init.lua000066400000000000000000000011421357404205000164320ustar00rootroot00000000000000--[[ Copyright 2016 Marek Vavrusa 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. ]] return require('bpf.bpf') bpfcc-0.12.0/src/lua/bpf/ljbytecode.lua000066400000000000000000000042731357404205000176230ustar00rootroot00000000000000--[[ Copyright 2016 Marek Vavrusa 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. ]] local jutil = require("jit.util") local vmdef = require("jit.vmdef") local bit = require('bit') local shr, band = bit.rshift, bit.band -- Decode LuaJIT 2.0 Byte Format -- Reference: http://wiki.luajit.org/Bytecode-2.0 -- Thanks to LJ, we get code in portable bytecode with constants folded, basic -- virtual registers allocated etc. -- No SSA IR, type inference or advanced optimizations because the code wasn't traced yet. local function decode_ins(func, pc) local ins, m = jutil.funcbc(func, pc) if not ins then return nil end local op, ma, mb, mc = band(ins, 0xff), band(m, 7), band(m, 15*8), band(m, 15*128) local a, b, c, d = band(shr(ins, 8), 0xff), nil, nil, shr(ins, 16) if mb ~= 0 then d = band(d, 0xff) b = shr(ins, 24) end if ma == 5 then -- BCMuv a = jutil.funcuvname(func, a) end if mc == 13*128 then -- BCMjump c = pc+d-0x7fff elseif mc == 14*128 then -- BCMcdata c = jutil.funck(func, -d-1) elseif mc == 9*128 then -- BCMint c = jutil.funck(func, d) elseif mc == 10*128 then -- BCMstr c = jutil.funck(func, -d-1) elseif mc == 5*128 then -- BCMuv c = jutil.funcuvname(func, d) end -- Convert version-specific opcode to string op = 6*op op = string.sub(vmdef.bcnames, op+1, op+6):match('[^%s]+') return pc, op, a, b, c, d end -- Decoder closure local function decoder(func) local pc = 0 return function () pc = pc + 1 return decode_ins(func, pc) end end -- Hexdump generated code local function dump(func) return require('jit.bc').dump(func) end return { decode = decode_ins, decoder = decoder, dump = dump, funcinfo = function (...) return jutil.funcinfo(...) end, }bpfcc-0.12.0/src/lua/bpf/proto.lua000066400000000000000000000424661357404205000166500ustar00rootroot00000000000000--[[ Copyright 2016 Marek Vavrusa 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. ]] local ffi = require('ffi') local BPF = ffi.typeof('struct bpf') ffi.cdef [[ struct sk_buff { uint32_t len; uint32_t pkt_type; uint32_t mark; uint32_t queue_mapping; uint32_t protocol; uint32_t vlan_present; uint32_t vlan_tci; uint32_t vlan_proto; uint32_t priority; uint32_t ingress_ifindex; uint32_t ifindex; uint32_t tc_index; uint32_t cb[5]; uint32_t hash; uint32_t tc_classid; uint32_t data; uint32_t data_end; uint32_t napi_id; /* Accessed by BPF_PROG_TYPE_sk_skb types from here to ... */ uint32_t family; uint32_t remote_ip4; /* Stored in network byte order */ uint32_t local_ip4; /* Stored in network byte order */ uint32_t remote_ip6[4]; /* Stored in network byte order */ uint32_t local_ip6[4]; /* Stored in network byte order */ uint32_t remote_port; /* Stored in network byte order */ uint32_t local_port; /* stored in host byte order */ /* ... here. */ uint32_t data_meta; }; struct net_off_t { uint8_t ver:4; } __attribute__((packed)); struct eth_t { uint8_t dst[6]; uint8_t src[6]; uint16_t type; } __attribute__((packed)); struct dot1q_t { uint16_t pri:3; uint16_t cfi:1; uint16_t vlanid:12; uint16_t type; } __attribute__((packed)); struct arp_t { uint16_t htype; uint16_t ptype; uint8_t hlen; uint8_t plen; uint16_t oper; uint8_t sha[6]; uint32_t spa; uint8_t tha[6]; uint32_t tpa; } __attribute__((packed)); struct ip_t { uint8_t ver:4; uint8_t hlen:4; uint8_t tos; uint16_t tlen; uint16_t identification; uint16_t ffo_unused:1; uint16_t df:1; uint16_t mf:1; uint16_t foffset:13; uint8_t ttl; uint8_t proto; uint16_t hchecksum; uint32_t src; uint32_t dst; } __attribute__((packed)); struct icmp_t { uint8_t type; uint8_t code; uint16_t checksum; } __attribute__((packed)); struct ip6_t { uint32_t ver:4; uint32_t priority:8; uint32_t flow_label:20; uint16_t payload_len; uint8_t next_header; uint8_t hop_limit; uint64_t src_hi; uint64_t src_lo; uint64_t dst_hi; uint64_t dst_lo; } __attribute__((packed)); struct ip6_opt_t { uint8_t next_header; uint8_t ext_len; uint8_t pad[6]; } __attribute__((packed)); struct icmp6_t { uint8_t type; uint8_t code; uint16_t checksum; } __attribute__((packed)); struct udp_t { uint16_t src_port; uint16_t dst_port; uint16_t length; uint16_t crc; } __attribute__((packed)); struct tcp_t { uint16_t src_port; uint16_t dst_port; uint32_t seq_num; uint32_t ack_num; uint8_t offset:4; uint8_t reserved:4; uint8_t flag_cwr:1; uint8_t flag_ece:1; uint8_t flag_urg:1; uint8_t flag_ack:1; uint8_t flag_psh:1; uint8_t flag_rst:1; uint8_t flag_syn:1; uint8_t flag_fin:1; uint16_t rcv_wnd; uint16_t cksum; uint16_t urg_ptr; } __attribute__((packed)); struct vxlan_t { uint32_t rsv1:4; uint32_t iflag:1; uint32_t rsv2:3; uint32_t rsv3:24; uint32_t key:24; uint32_t rsv4:8; } __attribute__((packed)); ]] -- Architecture-specific ptrace register layout local S = require('syscall') local arch = S.abi.arch local parm_to_reg = {} if arch == 'x64' then ffi.cdef [[ struct pt_regs { unsigned long r15; unsigned long r14; unsigned long r13; unsigned long r12; unsigned long bp; unsigned long bx; unsigned long r11; unsigned long r10; unsigned long r9; unsigned long r8; unsigned long ax; unsigned long cx; unsigned long dx; unsigned long si; unsigned long di; unsigned long orig_ax; unsigned long ip; unsigned long cs; unsigned long flags; unsigned long sp; unsigned long ss; };]] parm_to_reg = {parm1='di', parm2='si', parm3='dx', parm4='cx', parm5='r8', ret='sp', fp='bp'} else ffi.cdef 'struct pt_regs {};' end -- Map symbolic registers to architecture ABI ffi.metatype('struct pt_regs', { __index = function (_ --[[t]],k) return assert(parm_to_reg[k], 'no such register: '..k) end, }) local M = {} -- Dissector interface local function dissector(type, e, dst, src, field) local parent = e.V[src].const -- Create new dissector variable e.vcopy(dst, src) -- Compute and materialize new dissector offset from parent e.V[dst].const = {off=e.V[src].const.off, __dissector=e.V[src].const.__dissector} parent.__dissector[field](e, dst) e.V[dst].const.__dissector = type end M.dissector = dissector -- Get current effective offset, load field value at an offset relative to it and -- add its value to compute next effective offset (e.g. udp_off = ip_off + pkt[ip_off].hlen) local function next_offset(e, var, type, off, mask, shift) local d = e.V[var].const -- Materialize relative offset value in R0 local dst_reg, tmp_reg if d.off then dst_reg = e.vreg(var, 0, true) tmp_reg = dst_reg -- Use target register to avoid copy e.emit(BPF.LD + BPF.ABS + e.const_width[ffi.sizeof(type)], tmp_reg, 0, 0, d.off + off or 0) else tmp_reg = e.vreg(e.tmpvar, 0, true, type) -- Reserve R0 for temporary relative offset dst_reg = e.vreg(var) -- Must rematerialize (if it was spilled by tmp var) e.emit(BPF.LD + BPF.IND + e.const_width[ffi.sizeof(type)], tmp_reg, dst_reg, 0, off or 0) end -- Finalize relative offset if mask then e.emit(BPF.ALU + BPF.AND + BPF.K, tmp_reg, 0, 0, mask) end if shift and shift ~= 0 then local op = BPF.LSH if shift < 0 then op = BPF.RSH shift = -shift end e.emit(BPF.ALU + op + BPF.K, tmp_reg, 0, 0, shift) end -- Add to base offset to turn it into effective address if dst_reg ~= tmp_reg then e.emit(BPF.ALU + BPF.ADD + BPF.X, dst_reg, tmp_reg, 0, 0) else e.emit(BPF.ALU + BPF.ADD + BPF.K, dst_reg, 0, 0, d.off) end -- Discard temporary allocations d.off = nil e.V[e.tmpvar].reg = nil end local function next_skip(e, var, off) local d = e.V[var].const if not d.off then local dst_reg = e.vreg(var) e.emit(BPF.ALU64 + BPF.ADD + BPF.K, dst_reg, 0, 0, off) else d.off = d.off + off end end local function skip_eth(e, dst) -- IP starts right after ETH header (fixed size) local d = e.V[dst].const d.off = d.off + ffi.sizeof('struct eth_t') end -- Export types M.type = function(typestr, t) t = t or {} t.__dissector=ffi.typeof(typestr) return t end M.skb = M.type('struct sk_buff', {source='ptr_to_ctx'}) M.pt_regs = M.type('struct pt_regs', {source='ptr_to_probe'}) M.pkt = M.type('struct eth_t', {off=0, source='ptr_to_pkt'}) -- skb needs special accessors -- M.eth = function (...) return dissector(ffi.typeof('struct eth_t'), ...) end M.dot1q = function (...) return dissector(ffi.typeof('struct dot1q_t'), ...) end M.arp = function (...) return dissector(ffi.typeof('struct arp_t'), ...) end M.icmp = function (...) return dissector(ffi.typeof('struct icmp_t'), ...) end M.ip = function (...) return dissector(ffi.typeof('struct ip_t'), ...) end M.icmp6 = function (...) return dissector(ffi.typeof('struct icmp6_t'), ...) end M.ip6 = function (...) return dissector(ffi.typeof('struct ip6_t'), ...) end M.ip6_opt = function (...) return dissector(ffi.typeof('struct ip6_opt_t'), ...) end M.udp = function (...) return dissector(ffi.typeof('struct udp_t'), ...) end M.tcp = function (...) return dissector(ffi.typeof('struct tcp_t'), ...) end M.vxlan = function (...) return dissector(ffi.typeof('struct vxlan_t'), ...) end M.data = function (...) return dissector(ffi.typeof('uint8_t'), ...) end M.net_off = function (...) return dissector(ffi.typeof('struct net_off_t'), ...) end -- Metatables ffi.metatype(ffi.typeof('struct eth_t'), { __index = { ip = skip_eth, ip6 = skip_eth, net_off = function (e, dst) next_skip(e, dst, BPF.NET_OFF) end, } }) ffi.metatype(ffi.typeof('struct net_off_t'), { __index = { ip = function () end, ip6 = function () end, } }) ffi.metatype(ffi.typeof('struct ip_t'), { __index = { -- Skip IP header length (stored as number of words) -- e.g. hlen = 5, Header Length = 5 x sizeof(u32) = 20 octets -- Mask first nibble and shift by 2 (multiplication by 4) icmp = function(e, dst) next_offset(e, dst, ffi.typeof('uint8_t'), 0, 0x0f, 2) end, udp = function(e, dst) next_offset(e, dst, ffi.typeof('uint8_t'), 0, 0x0f, 2) end, tcp = function(e, dst) next_offset(e, dst, ffi.typeof('uint8_t'), 0, 0x0f, 2) end, } }) ffi.metatype(ffi.typeof('struct ip6_t'), { __index = { -- Skip fixed IPv6 header length (40 bytes) -- The caller must check the value of `next_header` to skip any extension headers icmp6 = function(e, dst) next_skip(e, dst, ffi.sizeof('struct ip6_t'), 0) end, udp = function(e, dst) next_skip(e, dst, ffi.sizeof('struct ip6_t'), 0) end, tcp = function(e, dst) next_skip(e, dst, ffi.sizeof('struct ip6_t'), 0) end, ip6_opt = function(e, dst) next_skip(e, dst, ffi.sizeof('struct ip6_t'), 0) end, } }) local ip6_opt_ext_len_off = ffi.offsetof('struct ip6_opt_t', 'ext_len') ffi.metatype(ffi.typeof('struct ip6_opt_t'), { __index = { -- Skip IPv6 extension header length (field `ext_len`) icmp6 = function(e, dst) next_offset(e, dst, ffi.typeof('uint8_t'), ip6_opt_ext_len_off) end, udp = function(e, dst) next_offset(e, dst, ffi.typeof('uint8_t'), ip6_opt_ext_len_off) end, tcp = function(e, dst) next_offset(e, dst, ffi.typeof('uint8_t'), ip6_opt_ext_len_off) end, ip6_opt = function(e, dst) next_offset(e, dst, ffi.typeof('uint8_t'), ip6_opt_ext_len_off) end, } }) ffi.metatype(ffi.typeof('struct tcp_t'), { __index = { -- Skip TCP header length (stored as number of words) -- e.g. hlen = 5, Header Length = 5 x sizeof(u32) = 20 octets data = function(e, dst) next_offset(e, dst, ffi.typeof('uint8_t'), ffi.offsetof('struct tcp_t', 'offset'), 0xf0, -2) end, } }) ffi.metatype(ffi.typeof('struct udp_t'), { __index = { -- Skip UDP header length (8 octets) data = function(e, dst) next_skip(e, dst, ffi.sizeof('struct udp_t')) end, } }) -- Constants M.c = { eth = { -- Constants http://standards.ieee.org/regauth/ethertype ip = 0x0800, -- IP (v4) protocol ip6 = 0x86dd, -- IP (v6) protocol arp = 0x0806, -- Address resolution protocol revarp = 0x8035, -- Reverse addr resolution protocol vlan = 0x8100, -- IEEE 802.1Q VLAN tagging }, ip = { -- Reserved Addresses addr_any = 0x00000000, -- 0.0.0.0 addr_broadcast = 0xffffffff, -- 255.255.255.255 addr_loopback = 0x7f000001, -- 127.0.0.1 addr_mcast_all = 0xe0000001, -- 224.0.0.1 addr_mcast_local = 0xe00000ff, -- 224.0.0.255 -- Type of service (ip_tos), RFC 1349 ("obsoleted by RFC 2474") tos_default = 0x00, -- default tos_lowdelay = 0x10, -- low delay tos_throughput = 0x08, -- high throughput tos_reliability = 0x04, -- high reliability tos_lowcost = 0x02, -- low monetary cost - XXX tos_ect = 0x02, -- ECN-capable transport tos_ce = 0x01, -- congestion experienced -- Fragmentation flags (ip_off) rf = 0x8000, -- reserved df = 0x4000, -- don't fragment mf = 0x2000, -- more fragments (not last frag) offmask = 0x1fff, -- mask for fragment offset -- Time-to-live (ip_ttl), seconds ttl_default = 64, -- default ttl, RFC 1122, RFC 1340 ttl_max = 255, -- maximum ttl -- Protocol (ip_p) - http://www.iana.org/assignments/protocol-numbers proto_ip = 0, -- dummy for IP proto_hopopts = 0, -- IPv6 hop-by-hop options proto_icmp = 1, -- ICMP proto_igmp = 2, -- IGMP proto_ggp = 3, -- gateway-gateway protocol proto_ipip = 4, -- IP in IP proto_st = 5, -- ST datagram mode proto_tcp = 6, -- TCP proto_cbt = 7, -- CBT proto_egp = 8, -- exterior gateway protocol proto_igp = 9, -- interior gateway protocol proto_bbnrcc = 10, -- BBN RCC monitoring proto_nvp = 11, -- Network Voice Protocol proto_pup = 12, -- PARC universal packet proto_argus = 13, -- ARGUS proto_emcon = 14, -- EMCON proto_xnet = 15, -- Cross Net Debugger proto_chaos = 16, -- Chaos proto_udp = 17, -- UDP proto_mux = 18, -- multiplexing proto_dcnmeas = 19, -- DCN measurement proto_hmp = 20, -- Host Monitoring Protocol proto_prm = 21, -- Packet Radio Measurement proto_idp = 22, -- Xerox NS IDP proto_trunk1 = 23, -- Trunk-1 proto_trunk2 = 24, -- Trunk-2 proto_leaf1 = 25, -- Leaf-1 proto_leaf2 = 26, -- Leaf-2 proto_rdp = 27, -- "Reliable Datagram" proto proto_irtp = 28, -- Inet Reliable Transaction proto_tp = 29, -- ISO TP class 4 proto_netblt = 30, -- Bulk Data Transfer proto_mfpnsp = 31, -- MFE Network Services proto_meritinp= 32, -- Merit Internodal Protocol proto_sep = 33, -- Sequential Exchange proto proto_3pc = 34, -- Third Party Connect proto proto_idpr = 35, -- Interdomain Policy Route proto_xtp = 36, -- Xpress Transfer Protocol proto_ddp = 37, -- Datagram Delivery Proto proto_cmtp = 38, -- IDPR Ctrl Message Trans proto_tppp = 39, -- TP++ Transport Protocol proto_il = 40, -- IL Transport Protocol proto_ip6 = 41, -- IPv6 proto_sdrp = 42, -- Source Demand Routing proto_routing = 43, -- IPv6 routing header proto_fragment= 44, -- IPv6 fragmentation header proto_rsvp = 46, -- Reservation protocol proto_gre = 47, -- General Routing Encap proto_mhrp = 48, -- Mobile Host Routing proto_ena = 49, -- ENA proto_esp = 50, -- Encap Security Payload proto_ah = 51, -- Authentication Header proto_inlsp = 52, -- Integated Net Layer Sec proto_swipe = 53, -- SWIPE proto_narp = 54, -- NBMA Address Resolution proto_mobile = 55, -- Mobile IP, RFC 2004 proto_tlsp = 56, -- Transport Layer Security proto_skip = 57, -- SKIP proto_icmp6 = 58, -- ICMP for IPv6 proto_none = 59, -- IPv6 no next header proto_dstopts = 60, -- IPv6 destination options proto_anyhost = 61, -- any host internal proto proto_cftp = 62, -- CFTP proto_anynet = 63, -- any local network proto_expak = 64, -- SATNET and Backroom EXPAK proto_kryptolan = 65, -- Kryptolan proto_rvd = 66, -- MIT Remote Virtual Disk proto_ippc = 67, -- Inet Pluribus Packet Core proto_distfs = 68, -- any distributed fs proto_satmon = 69, -- SATNET Monitoring proto_visa = 70, -- VISA Protocol proto_ipcv = 71, -- Inet Packet Core Utility proto_cpnx = 72, -- Comp Proto Net Executive proto_cphb = 73, -- Comp Protocol Heart Beat proto_wsn = 74, -- Wang Span Network proto_pvp = 75, -- Packet Video Protocol proto_brsatmon= 76, -- Backroom SATNET Monitor proto_sunnd = 77, -- SUN ND Protocol proto_wbmon = 78, -- WIDEBAND Monitoring proto_wbexpak = 79, -- WIDEBAND EXPAK proto_eon = 80, -- ISO CNLP proto_vmtp = 81, -- Versatile Msg Transport proto_svmtp = 82, -- Secure VMTP proto_vines = 83, -- VINES proto_ttp = 84, -- TTP proto_nsfigp = 85, -- NSFNET-IGP proto_dgp = 86, -- Dissimilar Gateway Proto proto_tcf = 87, -- TCF proto_eigrp = 88, -- EIGRP proto_ospf = 89, -- Open Shortest Path First proto_spriterpc= 90, -- Sprite RPC Protocol proto_larp = 91, -- Locus Address Resolution proto_mtp = 92, -- Multicast Transport Proto proto_ax25 = 93, -- AX.25 Frames proto_ipipencap= 94, -- yet-another IP encap proto_micp = 95, -- Mobile Internet Ctrl proto_sccsp = 96, -- Semaphore Comm Sec Proto proto_etherip = 97, -- Ethernet in IPv4 proto_encap = 98, -- encapsulation header proto_anyenc = 99, -- private encryption scheme proto_gmtp = 100, -- GMTP proto_ifmp = 101, -- Ipsilon Flow Mgmt Proto proto_pnni = 102, -- PNNI over IP proto_pim = 103, -- Protocol Indep Multicast proto_aris = 104, -- ARIS proto_scps = 105, -- SCPS proto_qnx = 106, -- QNX proto_an = 107, -- Active Networks proto_ipcomp = 108, -- IP Payload Compression proto_snp = 109, -- Sitara Networks Protocol proto_compaqpeer= 110, -- Compaq Peer Protocol proto_ipxip = 111, -- IPX in IP proto_vrrp = 112, -- Virtual Router Redundancy proto_pgm = 113, -- PGM Reliable Transport proto_any0hop = 114, -- 0-hop protocol proto_l2tp = 115, -- Layer 2 Tunneling Proto proto_ddx = 116, -- D-II Data Exchange (DDX) proto_iatp = 117, -- Interactive Agent Xfer proto_stp = 118, -- Schedule Transfer Proto proto_srp = 119, -- SpectraLink Radio Proto proto_uti = 120, -- UTI proto_smp = 121, -- Simple Message Protocol proto_sm = 122, -- SM proto_ptp = 123, -- Performance Transparency proto_isis = 124, -- ISIS over IPv4 proto_fire = 125, -- FIRE proto_crtp = 126, -- Combat Radio Transport proto_crudp = 127, -- Combat Radio UDP proto_sscopmce= 128, -- SSCOPMCE proto_iplt = 129, -- IPLT proto_sps = 130, -- Secure Packet Shield proto_pipe = 131, -- Private IP Encap in IP proto_sctp = 132, -- Stream Ctrl Transmission proto_fc = 133, -- Fibre Channel proto_rsvpign = 134, -- RSVP-E2E-IGNORE proto_raw = 255, -- Raw IP packets proto_reserved= 255, -- Reserved }, } return Mbpfcc-0.12.0/src/lua/bpf/spec/000077500000000000000000000000001357404205000157205ustar00rootroot00000000000000bpfcc-0.12.0/src/lua/bpf/spec/README.md000066400000000000000000000002121357404205000171720ustar00rootroot00000000000000# Unit test specs This directory contains spec files for Lua BPF in [Busted] unit test format. [Busted]: http://olivinelabs.com/busted/ bpfcc-0.12.0/src/lua/bpf/spec/codegen_spec.lua000066400000000000000000000570421357404205000210510ustar00rootroot00000000000000local ffi = require('ffi') local S = require('syscall') -- Normalize whitespace and remove empty lines local function normalize_code(c) local res = {} for line in string.gmatch(c,'[^\r\n]+') do local op, d, s, t = line:match('(%S+)%s+(%S+)%s+(%S+)%s*([^-]*)') if op then t = t and t:match('^%s*(.-)%s*$') table.insert(res, string.format('%s\t%s %s %s', op, d, s, t)) end end return table.concat(res, '\n') end -- Compile code and check result local function compile(t) local bpf = require('bpf') -- require('jit.bc').dump(t.input) local code, err = bpf(t.input) assert.truthy(code) assert.falsy(err) if code then if t.expect then local got = normalize_code(bpf.dump_string(code, 1, true)) -- if normalize_code(t.expect) ~= got then print(bpf.dump_string(code, 1)) end assert.same(normalize_code(t.expect), got) end end end -- Make a mock map variable local function makemap(type, max_entries, key_ctype, val_ctype) if not key_ctype then key_ctype = ffi.typeof('uint32_t') end if not val_ctype then val_ctype = ffi.typeof('uint32_t') end if not max_entries then max_entries = 4096 end return { __map = true, max_entries = max_entries, key = ffi.new(ffi.typeof('$ [1]', key_ctype)), val = ffi.new(ffi.typeof('$ [1]', val_ctype)), map_type = S.c.BPF_MAP[type], key_type = key_ctype, val_type = val_ctype, fd = 42, } end describe('codegen', function() -- luacheck: ignore 113 211 212 311 511 describe('constants', function() it('remove dead constant store', function() compile { input = function () local proto = 5 end, expect = [[ MOV R0 #0 EXIT R0 #0 ]] } end) it('materialize constant', function() compile { input = function () return 5 end, expect = [[ MOV R0 #5 EXIT R0 #0 ]] } end) it('materialize constant longer than i32', function() compile { input = function () return 4294967295 end, expect = [[ LDDW R0 #4294967295 EXIT R0 #0 ]] } end) it('materialize cdata constant', function() compile { input = function () return 5ULL end, expect = [[ LDDW R0 #5 -- composed instruction EXIT R0 #0 ]] } end) it('materialize signed cdata constant', function() compile { input = function () return 5LL end, expect = [[ LDDW R0 #5 -- composed instruction EXIT R0 #0 ]] } end) it('materialize coercible numeric cdata constant', function() compile { input = function () return 0x00005 end, expect = [[ MOV R0 #5 EXIT R0 #0 ]] } end) it('materialize constant through variable', function() compile { input = function () local proto = 5 return proto end, expect = [[ MOV R0 #5 EXIT R0 #0 ]] } end) it('eliminate constant expressions', function() compile { input = function () return 2 + 3 - 0 end, expect = [[ MOV R0 #5 EXIT R0 #0 ]] } end) it('eliminate constant expressions (if block)', function() compile { input = function () local proto = 5 if proto == 5 then proto = 1 end return proto end, expect = [[ MOV R0 #1 EXIT R0 #0 ]] } end) it('eliminate negative constant expressions (if block) NYI', function() -- always negative condition is not fully eliminated compile { input = function () local proto = 5 if false then proto = 1 end return proto end, expect = [[ MOV R7 #5 STXDW [R10-8] R7 MOV R7 #0 JEQ R7 #0 => 0005 LDXDW R0 [R10-8] EXIT R0 #0 ]] } end) end) describe('variables', function() it('classic packet access (fold constant offset)', function() compile { input = function (skb) return eth.ip.tos -- constant expression will fold end, expect = [[ LDB R0 skb[15] EXIT R0 #0 ]] } end) it('classic packet access (load non-constant offset)', function() compile { input = function (skb) return eth.ip.udp.src_port -- need to skip variable-length header end, expect = [[ LDB R0 skb[14] AND R0 #15 LSH R0 #2 ADD R0 #14 STXDW [R10-16] R0 -- NYI: erase dead store LDH R0 skb[R0+0] END R0 R0 EXIT R0 #0 ]] } end) it('classic packet access (manipulate dissector offset)', function() compile { input = function (skb) local ptr = eth.ip.udp.data + 1 return ptr[0] -- dereference dissector pointer end, expect = [[ LDB R0 skb[14] AND R0 #15 LSH R0 #2 ADD R0 #14 -- NYI: fuse commutative operations in second pass ADD R0 #8 ADD R0 #1 STXDW [R10-16] R0 LDB R0 skb[R0+0] EXIT R0 #0 ]] } end) it('classic packet access (multi-byte load)', function() compile { input = function (skb) local ptr = eth.ip.udp.data return ptr(1, 5) -- load 4 bytes end, expect = [[ LDB R0 skb[14] AND R0 #15 LSH R0 #2 ADD R0 #14 ADD R0 #8 MOV R7 R0 STXDW [R10-16] R0 -- NYI: erase dead store LDW R0 skb[R7+1] END R0 R0 EXIT R0 #0 ]] } end) it('direct skb field access', function() compile { input = function (skb) return skb.len end, expect = [[ LDXW R7 [R6+0] MOV R0 R7 EXIT R0 #0 ]] } end) it('direct skb data access (manipulate offset)', function() compile { input = function (skb) local ptr = skb.data + 5 return ptr[0] end, expect = [[ LDXW R7 [R6+76] ADD R7 #5 LDXB R8 [R7+0] -- NYI: transform LD + ADD to LD + offset addressing MOV R0 R8 EXIT R0 #0 ]] } end) it('direct skb data access (offset boundary check)', function() compile { input = function (skb) local ptr = skb.data + 5 if ptr < skb.data_end then return ptr[0] end end, expect = [[ LDXW R7 [R6+76] ADD R7 #5 LDXW R8 [R6+80] JGE R7 R8 => 0008 LDXB R8 [R7+0] MOV R0 R8 EXIT R0 #0 MOV R0 #0 EXIT R0 #0 ]] } end) it('access stack memory (array, const load, const store)', function() compile { input = function (skb) local mem = ffi.new('uint8_t [16]') mem[0] = 5 end, expect = [[ MOV R0 #0 STXDW [R10-40] R0 STXDW [R10-48] R0 -- NYI: erase zero-fill on allocation when it's loaded later STB [R10-48] #5 MOV R0 #0 EXIT R0 #0 ]] } end) it('access stack memory (array, const load, packet store)', function() compile { input = function (skb) local mem = ffi.new('uint8_t [7]') mem[0] = eth.ip.tos end, expect = [[ MOV R0 #0 STXDW [R10-40] R0 -- NYI: erase zero-fill on allocation when it's loaded later LDB R0 skb[15] STXB [R10-40] R0 MOV R0 #0 EXIT R0 #0 ]] } end) it('access stack memory (array, packet load, const store)', function() compile { input = function (skb) local mem = ffi.new('uint8_t [1]') mem[eth.ip.tos] = 5 end, expect = [[ MOV R0 #0 STXDW [R10-48] R0 -- NYI: erase zero-fill on allocation when it's loaded later LDB R0 skb[15] MOV R7 R0 ADD R7 R10 STB [R7-48] #5 MOV R0 #0 EXIT R0 #0 ]] } end) it('access stack memory (array, packet load, packet store)', function() compile { input = function (skb) local mem = ffi.new('uint8_t [7]') local v = eth.ip.tos mem[v] = v end, expect = [[ MOV R0 #0 STXDW [R10-40] R0 -- NYI: erase zero-fill on allocation when it's loaded later LDB R0 skb[15] MOV R7 R0 ADD R7 R10 STXB [R7-40] R0 MOV R0 #0 EXIT R0 #0 ]] } end) it('access stack memory (struct, const/packet store)', function() local kv_t = 'struct { uint64_t a; uint64_t b; }' compile { input = function (skb) local mem = ffi.new(kv_t) mem.a = 5 mem.b = eth.ip.tos end, expect = [[ MOV R0 #0 STXDW [R10-40] R0 STXDW [R10-48] R0 -- NYI: erase zero-fill on allocation when it's loaded later MOV R7 #5 STXDW [R10-48] R7 LDB R0 skb[15] STXDW [R10-40] R0 MOV R0 #0 EXIT R0 #0 ]] } end) it('access stack memory (struct, const/stack store)', function() local kv_t = 'struct { uint64_t a; uint64_t b; }' compile { input = function (skb) local m1 = ffi.new(kv_t) local m2 = ffi.new(kv_t) m1.a = 5 m2.b = m1.a end, expect = [[ MOV R0 #0 STXDW [R10-48] R0 STXDW [R10-56] R0 -- NYI: erase zero-fill on allocation when it's loaded later MOV R0 #0 STXDW [R10-64] R0 STXDW [R10-72] R0 -- NYI: erase zero-fill on allocation when it's loaded later MOV R7 #5 STXDW [R10-56] R7 LDXDW R7 [R10-56] STXDW [R10-64] R7 MOV R0 #0 EXIT R0 #0 ]] } end) it('array map (u32, const key load)', function() local array_map = makemap('array', 256) compile { input = function (skb) return array_map[0] end, expect = [[ LDDW R1 #42 STW [R10-28] #0 MOV R2 R10 ADD R2 #4294967268 CALL R0 #1 ; map_lookup_elem JEQ R0 #0 => 0009 LDXW R0 [R0+0] EXIT R0 #0 ]] } end) it('array map (u32, packet key load)', function() local array_map = makemap('array', 256) compile { input = function (skb) return array_map[eth.ip.tos] end, expect = [[ LDB R0 skb[15] LDDW R1 #42 STXW [R10-36] R0 MOV R2 R10 ADD R2 #4294967260 STXDW [R10-24] R0 -- NYI: erase dead store CALL R0 #1 ; map_lookup_elem JEQ R0 #0 => 0011 LDXW R0 [R0+0] EXIT R0 #0 ]] } end) it('array map (u32, const key store, const value)', function() local array_map = makemap('array', 256) compile { input = function (skb) array_map[0] = 5 end, expect = [[ LDDW R1 #42 STW [R10-36] #0 MOV R2 R10 ADD R2 #4294967260 MOV R4 #0 STW [R10-40] #5 MOV R3 R10 ADD R3 #4294967256 CALL R0 #2 ; map_update_elem MOV R0 #0 EXIT R0 #0 ]] } end) it('array map (u32, const key store, packet value)', function() local array_map = makemap('array', 256) compile { input = function (skb) array_map[0] = eth.ip.tos end, expect = [[ LDB R0 skb[15] STXDW [R10-24] R0 LDDW R1 #42 STW [R10-36] #0 MOV R2 R10 ADD R2 #4294967260 MOV R4 #0 MOV R3 R10 ADD R3 #4294967272 CALL R0 #2 ; map_update_elem MOV R0 #0 EXIT R0 #0 ]] } end) it('array map (u32, const key store, map value)', function() local array_map = makemap('array', 256) compile { input = function (skb) array_map[0] = array_map[1] end, expect = [[ LDDW R1 #42 STW [R10-36] #1 MOV R2 R10 ADD R2 #4294967260 CALL R0 #1 ; map_lookup_elem STXDW [R10-24] R0 LDDW R1 #42 STW [R10-36] #0 MOV R2 R10 ADD R2 #4294967260 MOV R4 #0 LDXDW R3 [R10-24] JEQ R3 #0 => 0017 LDXW R3 [R3+0] STXW [R10-40] R3 MOV R3 R10 ADD R3 #4294967256 CALL R0 #2 ; map_update_elem MOV R0 #0 EXIT R0 #0 ]] } end) it('array map (u32, const key replace, const value)', function() local array_map = makemap('array', 256) compile { input = function (skb) local val = array_map[0] if val then val[0] = val[0] + 1 else array_map[0] = 5 end end, expect = [[ LDDW R1 #42 STW [R10-44] #0 MOV R2 R10 ADD R2 #4294967252 CALL R0 #1 ; map_lookup_elem JEQ R0 #0 => 0013 -- if (map_value ~= NULL) LDXW R7 [R0+0] ADD R7 #1 STXW [R0+0] R7 MOV R7 #0 JEQ R7 #0 => 0025 -- skip false branch STXDW [R10-16] R0 LDDW R1 #42 STW [R10-44] #0 MOV R2 R10 ADD R2 #4294967252 MOV R4 #0 STW [R10-48] #5 MOV R3 R10 ADD R3 #4294967248 CALL R0 #2 ; map_update_elem LDXDW R0 [R10-16] MOV R0 #0 EXIT R0 #0 ]] } end) it('array map (u32, const key replace xadd, const value)', function() local array_map = makemap('array', 256) compile { input = function (skb) local val = array_map[0] if val then xadd(val, 1) else array_map[0] = 5 end end, expect = [[ LDDW R1 #42 STW [R10-52] #0 MOV R2 R10 ADD R2 #4294967244 CALL R0 #1 ; map_lookup_elem JEQ R0 #0 => 0014 -- if (map_value ~= NULL) MOV R7 #1 MOV R8 R0 STXDW [R10-16] R0 XADDW [R8+0] R7 MOV R7 #0 JEQ R7 #0 => 0025 -- skip false branch STXDW [R10-16] R0 LDDW R1 #42 STW [R10-52] #0 MOV R2 R10 ADD R2 #4294967244 MOV R4 #0 STW [R10-56] #5 MOV R3 R10 ADD R3 #4294967240 CALL R0 #2 ; map_update_elem MOV R0 #0 EXIT R0 #0 ]] } end) it('array map (u32, const key replace xadd, const value) inverse nil check', function() local array_map = makemap('array', 256) compile { input = function (skb) local val = array_map[0] if not val then array_map[0] = 5 else xadd(val, 1) end end, expect = [[ LDDW R1 #42 STW [R10-52] #0 MOV R2 R10 ADD R2 #4294967244 CALL R0 #1 ; map_lookup_elem JNE R0 #0 => 0021 STXDW [R10-16] R0 LDDW R1 #42 STW [R10-52] #0 MOV R2 R10 ADD R2 #4294967244 MOV R4 #0 STW [R10-56] #5 MOV R3 R10 ADD R3 #4294967240 CALL R0 #2 ; map_update_elem MOV R7 #0 JEQ R7 #0 => 0025 MOV R7 #1 MOV R8 R0 STXDW [R10-16] R0 XADDW [R8+0] R7 MOV R0 #0 EXIT R0 #0 ]] } end) it('array map (struct, stack key load)', function() local kv_t = 'struct { uint64_t a; uint64_t b; }' local array_map = makemap('array', 256, ffi.typeof(kv_t), ffi.typeof(kv_t)) compile { input = function (skb) local key = ffi.new(kv_t) key.a = 2 key.b = 3 local val = array_map[key] -- Use composite key from stack memory if val then return val.a end end, expect = [[ MOV R0 #0 STXDW [R10-48] R0 STXDW [R10-56] R0 -- NYI: erase zero-fill on allocation when it's loaded later MOV R7 #2 STXDW [R10-56] R7 MOV R7 #3 STXDW [R10-48] R7 LDDW R1 #42 MOV R2 R10 ADD R2 #4294967240 CALL R0 #1 ; map_lookup_elem JEQ R0 #0 => 0017 LDXDW R7 [R0+0] MOV R0 R7 EXIT R0 #0 MOV R0 #0 EXIT R0 #0 ]] } end) it('array map (struct, stack key store)', function() local kv_t = 'struct { uint64_t a; uint64_t b; }' local array_map = makemap('array', 256, ffi.typeof(kv_t), ffi.typeof(kv_t)) compile { input = function (skb) local key = ffi.new(kv_t) key.a = 2 key.b = 3 array_map[key] = key -- Use composite key from stack memory end, expect = [[ MOV R0 #0 STXDW [R10-40] R0 STXDW [R10-48] R0 -- NYI: erase zero-fill on allocation when it's loaded later MOV R7 #2 STXDW [R10-48] R7 MOV R7 #3 STXDW [R10-40] R7 LDDW R1 #42 MOV R2 R10 ADD R2 #4294967248 MOV R4 #0 MOV R3 R10 ADD R3 #4294967248 CALL R0 #2 ; map_update_elem MOV R0 #0 EXIT R0 #0 ]] } end) it('array map (struct, stack/packet key update, const value)', function() local kv_t = 'struct { uint64_t a; uint64_t b; }' local array_map = makemap('array', 256, ffi.typeof(kv_t), ffi.typeof(kv_t)) compile { input = function (skb) local key = ffi.new(kv_t) key.a = eth.ip.tos -- Load key part from dissector local val = array_map[key] if val then val.a = 5 end end, expect = [[ MOV R0 #0 STXDW [R10-48] R0 STXDW [R10-56] R0 -- NYI: erase zero-fill on allocation when it's loaded later LDB R0 skb[15] STXDW [R10-56] R0 LDDW R1 #42 MOV R2 R10 ADD R2 #4294967240 CALL R0 #1 ; map_lookup_elem JEQ R0 #0 => 0014 MOV R7 #5 STXDW [R0+0] R7 MOV R0 #0 EXIT R0 #0 ]] } end) it('array map (struct, stack/packet key update, map value)', function() local kv_t = 'struct { uint64_t a; uint64_t b; }' local array_map = makemap('array', 256, ffi.typeof(kv_t), ffi.typeof(kv_t)) compile { input = function (skb) local key = ffi.new(kv_t) key.a = eth.ip.tos -- Load key part from dissector local val = array_map[key] if val then val.a = val.b end end, expect = [[ MOV R0 #0 STXDW [R10-48] R0 STXDW [R10-56] R0 -- NYI: erase zero-fill on allocation when it's loaded later LDB R0 skb[15] STXDW [R10-56] R0 LDDW R1 #42 MOV R2 R10 ADD R2 #4294967240 CALL R0 #1 ; map_lookup_elem JEQ R0 #0 => 0014 LDXDW R7 [R0+8] STXDW [R0+0] R7 MOV R0 #0 EXIT R0 #0 ]] } end) it('array map (struct, stack/packet key update, stack value)', function() local kv_t = 'struct { uint64_t a; uint64_t b; }' local array_map = makemap('array', 256, ffi.typeof(kv_t), ffi.typeof(kv_t)) compile { input = function (skb) local key = ffi.new(kv_t) key.a = eth.ip.tos -- Load key part from dissector local val = array_map[key] if val then val.a = key.b end end, expect = [[ MOV R0 #0 STXDW [R10-48] R0 STXDW [R10-56] R0 -- NYI: erase zero-fill on allocation when it's loaded later LDB R0 skb[15] STXDW [R10-56] R0 LDDW R1 #42 MOV R2 R10 ADD R2 #4294967240 CALL R0 #1 ; map_lookup_elem JEQ R0 #0 => 0014 LDXDW R7 [R10-48] STXDW [R0+0] R7 MOV R0 #0 EXIT R0 #0 ]] } end) it('array map (struct, stack/packet key replace, stack value)', function() local kv_t = 'struct { uint64_t a; uint64_t b; }' local array_map = makemap('array', 256, ffi.typeof(kv_t), ffi.typeof(kv_t)) compile { input = function (skb) local key = ffi.new(kv_t) key.a = eth.ip.tos -- Load key part from dissector local val = array_map[key] if val then val.a = key.b else array_map[key] = key end end, expect = [[ MOV R0 #0 STXDW [R10-48] R0 STXDW [R10-56] R0 LDB R0 skb[15] STXDW [R10-56] R0 LDDW R1 #42 MOV R2 R10 ADD R2 #4294967240 CALL R0 #1 ; map_lookup_elem JEQ R0 #0 => 0016 -- if (map_value ~= NULL) LDXDW R7 [R10-48] STXDW [R0+0] R7 MOV R7 #0 JEQ R7 #0 => 0026 -- jump over false branch STXDW [R10-24] R0 LDDW R1 #42 MOV R2 R10 ADD R2 #4294967240 MOV R4 #0 MOV R3 R10 ADD R3 #4294967240 CALL R0 #2 ; map_update_elem LDXDW R0 [R10-24] MOV R0 #0 EXIT R0 #0 ]] } end) end) describe('control flow', function() it('condition with constant return', function() compile { input = function (skb) local v = eth.ip.tos if v then return 1 else return 0 end end, expect = [[ LDB R0 skb[15] JEQ R0 #0 => 0005 MOV R0 #1 EXIT R0 #0 MOV R0 #0 -- 0005 jump target EXIT R0 #0 ]] } end) it('condition with cdata constant return', function() local cdata = 2ULL compile { input = function (skb) local v = eth.ip.tos if v then return cdata + 1 else return 0 end end, expect = [[ LDB R0 skb[15] JEQ R0 #0 => 0006 LDDW R0 #3 EXIT R0 #0 MOV R0 #0 -- 0006 jump target EXIT R0 #0 ]] } end) it('condition with constant return (inversed)', function() compile { input = function (skb) local v = eth.ip.tos if not v then return 1 else return 0 end end, expect = [[ LDB R0 skb[15] JNE R0 #0 => 0005 MOV R0 #1 EXIT R0 #0 MOV R0 #0 -- 0005 jump target EXIT R0 #0 ]] } end) it('condition with variable mutation', function() compile { input = function (skb) local v = 0 if eth.ip.tos then v = 1 end return v end, expect = [[ LDB R0 skb[15] MOV R1 #0 STXDW [R10-16] R1 JEQ R0 #0 => 0007 MOV R7 #1 STXDW [R10-16] R7 LDXDW R0 [R10-16] EXIT R0 #0 ]] } end) it('condition with nil variable mutation', function() compile { input = function (skb) local v -- nil, will be elided if eth.ip.tos then v = 1 else v = 0 end return v end, expect = [[ LDB R0 skb[15] JEQ R0 #0 => 0007 MOV R7 #1 STXDW [R10-16] R7 MOV R7 #0 JEQ R7 #0 => 0009 MOV R7 #0 STXDW [R10-16] R7 LDXDW R0 [R10-16] EXIT R0 #0 ]] } end) it('nested condition with variable mutation', function() compile { input = function (skb) local v = 0 local tos = eth.ip.tos if tos then if tos > 5 then v = 5 else v = 1 end end return v end, expect = [[ LDB R0 skb[15] MOV R1 #0 STXDW [R10-16] R1 -- materialize v = 0 JEQ R0 #0 => 0013 -- if not tos MOV R7 #5 JGE R7 R0 => 0011 -- if 5 > tos MOV R7 #5 STXDW [R10-16] R7 -- materialize v = 5 MOV R7 #0 JEQ R7 #0 => 0013 MOV R7 #1 -- 0011 jump target STXDW [R10-16] R7 -- materialize v = 1 LDXDW R0 [R10-16] EXIT R0 #0 ]] } end) it('nested condition with variable shadowing', function() compile { input = function (skb) local v = 0 local tos = eth.ip.tos if tos then local v = 0 -- luacheck: ignore 231 if tos > 5 then v = 5 -- changing shadowing variable end else v = 1 end return v end, expect = [[ LDB R0 skb[15] MOV R1 #0 STXDW [R10-16] R1 -- materialize v = 0 JEQ R0 #0 => 0011 -- if not tos MOV R7 #5 MOV R1 #0 STXDW [R10-32] R1 -- materialize shadowing variable JGE R7 R0 => 0013 -- if 5 > tos MOV R7 #0 -- erased 'v = 5' dead store JEQ R7 #0 => 0013 MOV R7 #1 -- 0011 jump target STXDW [R10-16] R7 -- materialize v = 1 LDXDW R0 [R10-16] -- 0013 jump target EXIT R0 #0 ]] } end) it('condition materializes shadowing variable at the end of BB', function() compile { input = function (skb) local v = time() local v1 = 0 -- luacheck: ignore 231 if eth.ip.tos then v1 = v end end, expect = [[ CALL R0 #5 ; ktime_get_ns STXDW [R10-16] R0 LDB R0 skb[15] MOV R1 #0 STXDW [R10-24] R1 -- materialize v1 = 0 JEQ R0 #0 => 0009 LDXDW R7 [R10-16] STXDW [R10-24] R7 -- v1 = v0 MOV R0 #0 EXIT R0 #0 ]] } end) end) end) bpfcc-0.12.0/src/lua/bpf/spec/compile_spec.lua000066400000000000000000000010361357404205000210650ustar00rootroot00000000000000describe('compile', function() local ffi = require('ffi') local bpf = require('bpf') it('can compile socket filter', function() -- Create mock BPF map local mock_map = { max_entries = 16, key_type = ffi.typeof('uint64_t [1]'), val_type = ffi.typeof('uint64_t [1]'), fd = 1, __map = true, } -- Compile small code example local code = bpf(function () local proto = pkt.ip.proto xadd(mock_map[proto], 1) end) assert.truthy(code) assert.same(type(code), 'table') assert.same(code.pc, 15) end) end) bpfcc-0.12.0/src/lua/bpf/spec/decoder_spec.lua000066400000000000000000000016161357404205000210460ustar00rootroot00000000000000describe('decoder', function() -- Decode simple function local bytecode = require('bpf.ljbytecode') local f = function (x) return x + 1 end it('should decode functions', function() -- Make sure it calls LJ decoder local bc = bytecode.decoder(f) assert.truthy(bc) -- Decode bytecode bytecode to instructions local jutil = require("jit.util") spy.on(jutil, 'funcbc') local pc, op = bc() -- Check bytecode for sanity (starts with ADDVN(x, 1)) assert.equal(pc, 1) assert.equal(op, 'ADDVN') for pc, op in bc do assert.truthy(pc and op) end assert.spy(jutil.funcbc).was.called() end) it('should fail on bad input', function() assert.has_error(function() bytecode.decoder(nil)() end) assert.has_error(function() bytecode.decoder(5)() end) assert.has_error(function() bytecode.decoder('test')() end) end) it('should dump bytecode', function() bytecode.dump(f) end) end) bpfcc-0.12.0/src/lua/bpf/spec/elf_spec.lua000066400000000000000000000011761357404205000202100ustar00rootroot00000000000000describe('elf reader', function() local ok, elf = pcall(require, 'bpf.elf') if not ok then return end it('should handle C library', function() -- Open libc local sh = elf.open('/bin/sh') assert.truthy(sh) -- Find load address local base = sh:loadaddr() assert.truthy(base) -- Find something from ISO C local malloc_addr = sh:resolve('malloc') assert.truthy(malloc_addr) -- Find something that doesn't exist local bad_addr = sh:resolve('thisnotexists') assert.falsy(bad_addr) end) it('should fail on bad input', function() assert.falsy(elf.open(nil)) assert.falsy(elf.open('/tmp'):loadaddr()) end) end) bpfcc-0.12.0/src/lua/bpf/spec/helper.lua000066400000000000000000000016431357404205000177060ustar00rootroot00000000000000local ffi = require('ffi') -- Define basic ctypes ffi.cdef [[ struct bpf_insn { uint8_t code; /* opcode */ uint8_t dst_reg:4; /* dest register */ uint8_t src_reg:4; /* source register */ uint16_t off; /* signed offset */ uint32_t imm; /* signed immediate constant */ }; ]] -- Inject mock ljsyscall for tests package.loaded['syscall'] = { bpf = function() error('mock') end, c = { BPF_MAP = {}, BPF_PROG = {} }, abi = { arch = 'x64' }, } package.loaded['syscall.helpers'] = { strflag = function (tab) local function flag(cache, str) if type(str) ~= "string" then return str end if #str == 0 then return 0 end local s = str:upper() if #s == 0 then return 0 end local val = rawget(tab, s) if not val then return nil end cache[str] = val return val end return setmetatable(tab, {__index = setmetatable({}, {__index = flag}), __call = function(t, a) return t[a] end}) end }bpfcc-0.12.0/src/lua/squishy000066400000000000000000000014701357404205000156510ustar00rootroot00000000000000Module "bcc.vendor.argparse" "bcc/vendor/argparse.lua" Module "bcc.vendor.posix" "bcc/vendor/posix.lua" Module "bcc.vendor.middleclass" "bcc/vendor/middleclass.lua" Module "bcc.vendor.json" "bcc/vendor/json.lua" Module "bcc.vendor.helpers" "bcc/vendor/helpers.lua" Module "bcc.init" "bcc/init.lua" Module "bcc.run" "bcc/run.lua" Module "bcc.bpf" "bcc/bpf.lua" Module "bcc.sym" "bcc/sym.lua" Module "bcc.libbcc" "bcc/libbcc.lua" Module "bcc.tracerpipe" "bcc/tracerpipe.lua" Module "bcc.table" "bcc/table.lua" Module "bcc.usdt" "bcc/usdt.lua" Module "bpf" "bpf/init.lua" Module "bpf.bpf" "bpf/bpf.lua" Module "bpf.builtins" "bpf/builtins.lua" Module "bpf.cdef" "bpf/cdef.lua" Module "bpf.elf" "bpf/elf.lua" Module "bpf.ljbytecode" "bpf/ljbytecode.lua" Module "bpf.proto" "bpf/proto.lua" Main "bcc/run.lua" Output "bcc.lua" bpfcc-0.12.0/src/lua/src/000077500000000000000000000000001357404205000150065ustar00rootroot00000000000000bpfcc-0.12.0/src/lua/src/main.c000066400000000000000000000111421357404205000160750ustar00rootroot00000000000000/* * Copyright 2016 GitHub, Inc * * Based on lua.c, the Lua C Interpreter * Copyright (C) 1994-2012 Lua.org, PUC-Rio. All rights reserved. * * 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. */ #include #include #include #include #include #include "lauxlib.h" #include "lua.h" #include "lualib.h" static lua_State *globalL = NULL; static const char *progname = NULL; static void lstop(lua_State *L, lua_Debug *ar) { (void)ar; /* unused arg. */ lua_sethook(L, NULL, 0, 0); luaL_error(L, "interrupted!"); } static void laction(int i) { signal(i, SIG_DFL); lua_sethook(globalL, lstop, LUA_MASKCALL | LUA_MASKRET | LUA_MASKCOUNT, 1); } static void l_message(const char *pname, const char *msg) { if (pname) fprintf(stderr, "%s: ", pname); fprintf(stderr, "%s\n", msg); fflush(stderr); } static int report(lua_State *L, int status) { if (status && !lua_isnil(L, -1)) { const char *msg = lua_tostring(L, -1); if (msg == NULL) msg = "(error object is not a string)"; l_message(progname, msg); lua_pop(L, 1); } return status; } static int traceback(lua_State *L) { if (!lua_isstring(L, 1)) /* 'message' not a string? */ return 1; /* keep it intact */ lua_getglobal(L, "debug"); if (!lua_istable(L, -1)) { lua_pop(L, 1); return 1; } lua_getfield(L, -1, "traceback"); if (!lua_isfunction(L, -1)) { lua_pop(L, 2); return 1; } lua_pushvalue(L, 1); /* pass error message */ lua_pushinteger(L, 2); /* skip this function and traceback */ lua_call(L, 2, 1); /* call debug.traceback */ return 1; } static int docall(lua_State *L, int narg, int clear) { int status; int base = lua_gettop(L) - narg; /* function index */ lua_pushcfunction(L, traceback); /* push traceback function */ lua_insert(L, base); /* put it under chunk and args */ signal(SIGINT, laction); status = lua_pcall(L, narg, (clear ? 0 : LUA_MULTRET), base); signal(SIGINT, SIG_DFL); lua_remove(L, base); /* remove traceback function */ /* force a complete garbage collection in case of errors */ if (status != 0) lua_gc(L, LUA_GCCOLLECT, 0); return status; } static int dolibrary(lua_State *L, const char *name, int clear) { lua_getglobal(L, "require"); lua_pushstring(L, name); return report(L, docall(L, 1, clear)); } struct Smain { int argc; char **argv; int status; }; static void pushargv(lua_State *L, char **argv, int argc, int offset) { int i, j; lua_createtable(L, argc, 0); for (i = offset, j = 1; i < argc; i++, j++) { lua_pushstring(L, argv[i]); lua_rawseti(L, -2, j); } } static int pmain(lua_State *L) { struct Smain *s = (struct Smain *)lua_touserdata(L, 1); globalL = L; lua_gc(L, LUA_GCSTOP, 0); luaL_openlibs(L); lua_gc(L, LUA_GCRESTART, 0); s->status = dolibrary(L, "bcc", 0); if (s->status) return 0; lua_pushstring(L, progname); lua_setglobal(L, "BCC_STANDALONE"); pushargv(L, s->argv, s->argc, 1); lua_setglobal(L, "arg"); s->status = report(L, docall(L, 0, 1)); return 0; } int main(int argc, char **argv) { int status; struct Smain s; lua_State *L = lua_open(); /* create state */ if (L == NULL) { l_message(argv[0], "cannot create state: not enough memory"); return EXIT_FAILURE; } if (geteuid() != 0) { l_message(argv[0], "bcc-lua must be ran as root"); return EXIT_FAILURE; } progname = argv[0]; s.argc = argc; s.argv = argv; s.status = 0; status = lua_cpcall(L, &pmain, &s); report(L, status); lua_close(L); return (status || s.status) ? EXIT_FAILURE : EXIT_SUCCESS; } bpfcc-0.12.0/src/lua/src/squish.lua000077500000000000000000000175351357404205000170430ustar00rootroot00000000000000#!/usr/bin/env lua local short_opts = { v = "verbose", vv = "very_verbose", o = "output", q = "quiet", qq = "very_quiet", g = "debug" } local opts = { use_http = false }; for _, opt in ipairs(arg) do if opt:match("^%-") then local name = opt:match("^%-%-?([^%s=]+)()") name = (short_opts[name] or name):gsub("%-+", "_"); if name:match("^no_") then name = name:sub(4, -1); opts[name] = false; else opts[name] = opt:match("=(.*)$") or true; end else base_path = opt; end end if opts.very_verbose then opts.verbose = true; end if opts.very_quiet then opts.quiet = true; end local noprint = function () end local print_err, print_info, print_verbose, print_debug = noprint, noprint, noprint, noprint; if not opts.very_quiet then print_err = print; end if not opts.quiet then print_info = print; end if opts.verbose or opts.very_verbose then print_verbose = print; end if opts.very_verbose then print_debug = print; end print = print_verbose; local modules, main_files, resources = {}, {}, {}; -- Functions to be called from squishy file -- function Module(name) if modules[name] then print_verbose("Ignoring duplicate module definition for "..name); return function () end end local i = #modules+1; modules[i] = { name = name, url = ___fetch_url }; modules[name] = modules[i]; return function (path) modules[i].path = path; end end function Resource(name, path) local i = #resources+1; resources[i] = { name = name, path = path or name }; return function (path) resources[i].path = path; end end function AutoFetchURL(url) ___fetch_url = url; end function Main(fn) table.insert(main_files, fn); end function Output(fn) if opts.output == nil then out_fn = fn; end end function Option(name) name = name:gsub("%-", "_"); if opts[name] == nil then opts[name] = true; return function (value) opts[name] = value; end else return function () end; end end function GetOption(name) return opts[name:gsub('%-', '_')]; end function Message(message) if not opts.quiet then print_info(message); end end function Error(message) if not opts.very_quiet then print_err(message); end end function Exit() os.exit(1); end -- -- -- -- -- -- -- --- -- -- -- -- -- -- -- -- base_path = (base_path or "."):gsub("/$", "").."/" squishy_file = base_path .. "squishy"; out_fn = opts.output; local ok, err = pcall(dofile, squishy_file); if not ok then print_err("Couldn't read squishy file: "..err); os.exit(1); end if not out_fn then print_err("No output file specified by user or squishy file"); os.exit(1); elseif #main_files == 0 and #modules == 0 and #resources == 0 then print_err("No files, modules or resources. Not going to generate an empty file."); os.exit(1); end local fetch = {}; function fetch.filesystem(path) local f, err = io.open(path); if not f then return false, err; end local data = f:read("*a"); f:close(); return data; end if opts.use_http then function fetch.http(url) local http = require "socket.http"; local body, status = http.request(url); if status == 200 then return body; end return false, "HTTP status code: "..tostring(status); end else function fetch.http(url) return false, "Module not found. Re-squish with --use-http option to fetch it from "..url; end end print_info("Writing "..out_fn.."..."); local f, err = io.open(out_fn, "w+"); if not f then print_err("Couldn't open output file: "..tostring(err)); os.exit(1); end if opts.executable then if opts.executable == true then f:write("#!/usr/bin/env lua\n"); else f:write(opts.executable, "\n"); end end if opts.debug then f:write(require_resource("squish.debug")); end print_verbose("Resolving modules..."); do local LUA_DIRSEP = package.config:sub(1,1); local LUA_PATH_MARK = package.config:sub(5,5); local package_path = package.path:gsub("[^;]+", function (path) if not path:match("^%"..LUA_DIRSEP) then return base_path..path; end end):gsub("/%./", "/"); local package_cpath = package.cpath:gsub("[^;]+", function (path) if not path:match("^%"..LUA_DIRSEP) then return base_path..path; end end):gsub("/%./", "/"); function resolve_module(name, path) name = name:gsub("%.", LUA_DIRSEP); for c in path:gmatch("[^;]+") do c = c:gsub("%"..LUA_PATH_MARK, name); print_debug("Looking for "..c) local f = io.open(c); if f then print_debug("Found!"); f:close(); return c; end end return nil; -- not found end for i, module in ipairs(modules) do if not module.path then module.path = resolve_module(module.name, package_path); if not module.path then print_err("Couldn't resolve module: "..module.name); else -- Strip base_path from resolved path module.path = module.path:gsub("^"..base_path:gsub("%p", "%%%1"), ""); end end end end print_verbose("Packing modules..."); for _, module in ipairs(modules) do local modulename, path = module.name, module.path; if module.path:sub(1,1) ~= "/" then path = base_path..module.path; end print_debug("Packing "..modulename.." ("..path..")..."); local data, err = fetch.filesystem(path); if (not data) and module.url then print_debug("Fetching: ".. module.url:gsub("%?", module.path)) data, err = fetch.http(module.url:gsub("%?", module.path)); end if data then f:write("package.preload['", modulename, "'] = (function (...)\n"); f:write(data); f:write(" end)\n"); if opts.debug then f:write(string.format("package.preload[%q] = ___adjust_chunk(package.preload[%q], %q);\n\n", modulename, modulename, "@"..path)); end else print_err("Couldn't pack module '"..modulename.."': "..(err or "unknown error... path to module file correct?")); os.exit(1); end end if #resources > 0 then print_verbose("Packing resources...") f:write("do local resources = {};\n"); for _, resource in ipairs(resources) do local name, path = resource.name, resource.path; local res_file, err = io.open(base_path..path, "rb"); if not res_file then print_err("Couldn't load resource: "..tostring(err)); os.exit(1); end local data = res_file:read("*a"); local maxequals = 0; data:gsub("(=+)", function (equals_string) maxequals = math.max(maxequals, #equals_string); end); f:write(("resources[%q] = %q"):format(name, data)); --[[ f:write(("resources[%q] = ["):format(name), string.rep("=", maxequals+1), "["); f:write(data); f:write("]", string.rep("=", maxequals+1), "];"); ]] end if opts.virtual_io then local vio = require_resource("vio"); if not vio then print_err("Virtual IO requested but is not enabled in this build of squish"); else -- Insert vio library f:write(vio, "\n") -- Override standard functions to use vio if opening a resource f:write[[local io_open, io_lines = io.open, io.lines; function io.open(fn, mode) if not resources[fn] then return io_open(fn, mode); else return vio.open(resources[fn]); end end function io.lines(fn) if not resources[fn] then return io_lines(fn); else return vio.open(resources[fn]):lines() end end local _dofile = dofile; function dofile(fn) if not resources[fn] then return _dofile(fn); else return assert(loadstring(resources[fn]))(); end end local _loadfile = loadfile; function loadfile(fn) if not resources[fn] then return _loadfile(fn); else return loadstring(resources[fn], "@"..fn); end end ]] end end f:write[[function require_resource(name) return resources[name] or error("resource '"..tostring(name).."' not found"); end end ]] end print_debug("Finalising...") for _, fn in pairs(main_files) do local fin, err = io.open(base_path..fn); if not fin then print_err("Failed to open "..fn..": "..err); os.exit(1); else f:write((fin:read("*a"):gsub("^#.-\n", ""))); fin:close(); end end f:close(); print_info("OK!"); bpfcc-0.12.0/src/python/000077500000000000000000000000001357404205000147575ustar00rootroot00000000000000bpfcc-0.12.0/src/python/CMakeLists.txt000066400000000000000000000027511357404205000175240ustar00rootroot00000000000000# Copyright (c) PLUMgrid, Inc. # Licensed under the Apache License, Version 2.0 (the "License") if(NOT PYTHON_CMD) set(PYTHON_CMD "python") endif() if(EXISTS "/etc/debian_version") set(PYTHON_FLAGS "${PYTHON_FLAGS} --install-layout deb") endif() file(GLOB_RECURSE PYTHON_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/*.py) file(GLOB_RECURSE PYTHON_INCLUDES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/*.py.in) foreach(PY_CMD ${PYTHON_CMD}) string(REPLACE "/" "-" PY_CMD_ESCAPED ${PY_CMD}) set(PY_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bcc-${PY_CMD_ESCAPED}) foreach(PY_SRC ${PYTHON_SOURCES}) configure_file(${PY_SRC} ${PY_DIRECTORY}/${PY_SRC} COPYONLY) endforeach() foreach(PY_INC ${PYTHON_INCLUDES}) string(REPLACE ".py.in" ".py" PY_INC_REPLACED ${PY_INC}) configure_file(${PY_INC} ${PY_DIRECTORY}/${PY_INC_REPLACED} @ONLY) endforeach() set(PIP_INSTALLABLE "${PY_DIRECTORY}/dist/bcc-${REVISION}.tar.gz") add_custom_command( OUTPUT ${PIP_INSTALLABLE} COMMAND ${PY_CMD} setup.py sdist WORKING_DIRECTORY ${PY_DIRECTORY} DEPENDS ${PYTHON_SOURCES} ${PYTHON_INCLUDES} COMMENT "Building sdist for ${PY_CMD}" ) add_custom_target(bcc_py_${PY_CMD_ESCAPED} ALL DEPENDS ${PIP_INSTALLABLE}) install( CODE " execute_process( COMMAND ${PY_CMD} setup.py install -f ${PYTHON_FLAGS} --prefix=${CMAKE_INSTALL_PREFIX} WORKING_DIRECTORY ${PY_DIRECTORY})" COMPONENT python) endforeach() bpfcc-0.12.0/src/python/bcc/000077500000000000000000000000001357404205000155065ustar00rootroot00000000000000bpfcc-0.12.0/src/python/bcc/__init__.py000066400000000000000000001457031357404205000176310ustar00rootroot00000000000000# Copyright 2015 PLUMgrid # # 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. from __future__ import print_function import atexit import ctypes as ct import fcntl import json import os import re import struct import errno import sys from .libbcc import lib, bcc_symbol, bcc_symbol_option, bcc_stacktrace_build_id, _SYM_CB_TYPE from .table import Table, PerfEventArray from .perf import Perf from .utils import get_online_cpus, printb, _assert_is_bytes, ArgString from .version import __version__ from .disassembler import disassemble_prog, decode_map try: basestring except NameError: # Python 3 basestring = str _probe_limit = 1000 _num_open_probes = 0 # for tests def _get_num_open_probes(): global _num_open_probes return _num_open_probes TRACEFS = "/sys/kernel/debug/tracing" # Debug flags # Debug output compiled LLVM IR. DEBUG_LLVM_IR = 0x1 # Debug output loaded BPF bytecode and register state on branches. DEBUG_BPF = 0x2 # Debug output pre-processor result. DEBUG_PREPROCESSOR = 0x4 # Debug output ASM instructions embedded with source. DEBUG_SOURCE = 0x8 # Debug output register state on all instructions in addition to DEBUG_BPF. DEBUG_BPF_REGISTER_STATE = 0x10 # Debug BTF. DEBUG_BTF = 0x20 class SymbolCache(object): def __init__(self, pid): self.cache = lib.bcc_symcache_new( pid, ct.cast(None, ct.POINTER(bcc_symbol_option))) def resolve(self, addr, demangle): """ Return a tuple of the symbol (function), its offset from the beginning of the function, and the module in which it lies. For example: ("start_thread", 0x202, "/usr/lib/.../libpthread-2.24.so") If the symbol cannot be found but we know which module it is in, return the module name and the offset from the beginning of the module. If we don't even know the module, return the absolute address as the offset. """ sym = bcc_symbol() if demangle: res = lib.bcc_symcache_resolve(self.cache, addr, ct.byref(sym)) else: res = lib.bcc_symcache_resolve_no_demangle(self.cache, addr, ct.byref(sym)) if res < 0: if sym.module and sym.offset: return (None, sym.offset, ct.cast(sym.module, ct.c_char_p).value) return (None, addr, None) if demangle: name_res = sym.demangle_name lib.bcc_symbol_free_demangle_name(ct.byref(sym)) else: name_res = sym.name return (name_res, sym.offset, ct.cast(sym.module, ct.c_char_p).value) def resolve_name(self, module, name): module = _assert_is_bytes(module) name = _assert_is_bytes(name) addr = ct.c_ulonglong() if lib.bcc_symcache_resolve_name(self.cache, module, name, ct.byref(addr)) < 0: return -1 return addr.value class PerfType: # From perf_type_id in uapi/linux/perf_event.h HARDWARE = 0 SOFTWARE = 1 class PerfHWConfig: # From perf_hw_id in uapi/linux/perf_event.h CPU_CYCLES = 0 INSTRUCTIONS = 1 CACHE_REFERENCES = 2 CACHE_MISSES = 3 BRANCH_INSTRUCTIONS = 4 BRANCH_MISSES = 5 BUS_CYCLES = 6 STALLED_CYCLES_FRONTEND = 7 STALLED_CYCLES_BACKEND = 8 REF_CPU_CYCLES = 9 class PerfSWConfig: # From perf_sw_id in uapi/linux/perf_event.h CPU_CLOCK = 0 TASK_CLOCK = 1 PAGE_FAULTS = 2 CONTEXT_SWITCHES = 3 CPU_MIGRATIONS = 4 PAGE_FAULTS_MIN = 5 PAGE_FAULTS_MAJ = 6 ALIGNMENT_FAULTS = 7 EMULATION_FAULTS = 8 DUMMY = 9 BPF_OUTPUT = 10 class BPF(object): # From bpf_prog_type in uapi/linux/bpf.h SOCKET_FILTER = 1 KPROBE = 2 SCHED_CLS = 3 SCHED_ACT = 4 TRACEPOINT = 5 XDP = 6 PERF_EVENT = 7 CGROUP_SKB = 8 CGROUP_SOCK = 9 LWT_IN = 10 LWT_OUT = 11 LWT_XMIT = 12 SOCK_OPS = 13 SK_SKB = 14 CGROUP_DEVICE = 15 SK_MSG = 16 RAW_TRACEPOINT = 17 CGROUP_SOCK_ADDR = 18 # from xdp_action uapi/linux/bpf.h XDP_ABORTED = 0 XDP_DROP = 1 XDP_PASS = 2 XDP_TX = 3 XDP_REDIRECT = 4 _probe_repl = re.compile(b"[^a-zA-Z0-9_]") _sym_caches = {} _bsymcache = lib.bcc_buildsymcache_new() _auto_includes = { "linux/time.h": ["time"], "linux/fs.h": ["fs", "file"], "linux/blkdev.h": ["bio", "request"], "linux/slab.h": ["alloc"], "linux/netdevice.h": ["sk_buff", "net_device"] } _syscall_prefixes = [ b"sys_", b"__x64_sys_", b"__x32_compat_sys_", b"__ia32_compat_sys_", b"__arm64_sys_", ] # BPF timestamps come from the monotonic clock. To be able to filter # and compare them from Python, we need to invoke clock_gettime. # Adapted from http://stackoverflow.com/a/1205762 CLOCK_MONOTONIC = 1 # see class timespec(ct.Structure): _fields_ = [('tv_sec', ct.c_long), ('tv_nsec', ct.c_long)] _librt = ct.CDLL('librt.so.1', use_errno=True) _clock_gettime = _librt.clock_gettime _clock_gettime.argtypes = [ct.c_int, ct.POINTER(timespec)] @classmethod def monotonic_time(cls): """monotonic_time() Returns the system monotonic time from clock_gettime, using the CLOCK_MONOTONIC constant. The time returned is in nanoseconds. """ t = cls.timespec() if cls._clock_gettime(cls.CLOCK_MONOTONIC, ct.byref(t)) != 0: errno = ct.get_errno() raise OSError(errno, os.strerror(errno)) return t.tv_sec * 1e9 + t.tv_nsec @classmethod def generate_auto_includes(cls, program_words): """ Generates #include statements automatically based on a set of recognized types such as sk_buff and bio. The input is all the words that appear in the BPF program, and the output is a (possibly empty) string of #include statements, such as "#include ". """ headers = "" for header, keywords in cls._auto_includes.items(): for keyword in keywords: for word in program_words: if keyword in word and header not in headers: headers += "#include <%s>\n" % header return headers # defined for compatibility reasons, to be removed Table = Table class Function(object): def __init__(self, bpf, name, fd): self.bpf = bpf self.name = name self.fd = fd @staticmethod def _find_file(filename): """ If filename is invalid, search in ./ of argv[0] """ if filename: if not os.path.isfile(filename): argv0 = ArgString(sys.argv[0]) t = b"/".join([os.path.abspath(os.path.dirname(argv0.__str__())), filename]) if os.path.isfile(t): filename = t else: raise Exception("Could not find file %s" % filename) return filename @staticmethod def find_exe(bin_path): """ find_exe(bin_path) Traverses the PATH environment variable, looking for the first directory that contains an executable file named bin_path, and returns the full path to that file, or None if no such file can be found. This is meant to replace invocations of the "which" shell utility, which doesn't have portable semantics for skipping aliases. """ # Source: http://stackoverflow.com/a/377028 def is_exe(fpath): return os.path.isfile(fpath) and \ os.access(fpath, os.X_OK) fpath, fname = os.path.split(bin_path) if fpath: if is_exe(bin_path): return bin_path else: for path in os.environ["PATH"].split(os.pathsep): path = path.strip('"') exe_file = os.path.join(path, bin_path) if is_exe(exe_file): return exe_file return None def __init__(self, src_file=b"", hdr_file=b"", text=None, debug=0, cflags=[], usdt_contexts=[], allow_rlimit=True, device=None): """Create a new BPF module with the given source code. Note: All fields are marked as optional, but either `src_file` or `text` must be supplied, and not both. Args: src_file (Optional[str]): Path to a source file for the module hdr_file (Optional[str]): Path to a helper header file for the `src_file` text (Optional[str]): Contents of a source file for the module debug (Optional[int]): Flags used for debug prints, can be |'d together See "Debug flags" for explanation """ src_file = _assert_is_bytes(src_file) hdr_file = _assert_is_bytes(hdr_file) text = _assert_is_bytes(text) assert not (text and src_file) self.kprobe_fds = {} self.uprobe_fds = {} self.tracepoint_fds = {} self.raw_tracepoint_fds = {} self.perf_buffers = {} self.open_perf_events = {} self.tracefile = None atexit.register(self.cleanup) self.debug = debug self.funcs = {} self.tables = {} self.module = None cflags_array = (ct.c_char_p * len(cflags))() for i, s in enumerate(cflags): cflags_array[i] = bytes(ArgString(s)) if src_file: src_file = BPF._find_file(src_file) hdr_file = BPF._find_file(hdr_file) # files that end in ".b" are treated as B files. Everything else is a (BPF-)C file if src_file.endswith(b".b"): self.module = lib.bpf_module_create_b(src_file, hdr_file, self.debug, device) else: if src_file: # Read the BPF C source file into the text variable. This ensures, # that files and inline text are treated equally. with open(src_file, mode="rb") as file: text = file.read() ctx_array = (ct.c_void_p * len(usdt_contexts))() for i, usdt in enumerate(usdt_contexts): ctx_array[i] = ct.c_void_p(usdt.get_context()) usdt_text = lib.bcc_usdt_genargs(ctx_array, len(usdt_contexts)) if usdt_text is None: raise Exception("can't generate USDT probe arguments; " + "possible cause is missing pid when a " + "probe in a shared object has multiple " + "locations") text = usdt_text + text self.module = lib.bpf_module_create_c_from_string(text, self.debug, cflags_array, len(cflags_array), allow_rlimit, device) if not self.module: raise Exception("Failed to compile BPF module %s" % (src_file or "")) for usdt_context in usdt_contexts: usdt_context.attach_uprobes(self) # If any "kprobe__" or "tracepoint__" or "raw_tracepoint__" # prefixed functions were defined, # they will be loaded and attached here. self._trace_autoload() def load_funcs(self, prog_type=KPROBE): """load_funcs(prog_type=KPROBE) Load all functions in this BPF module with the given type. Returns a list of the function handles.""" fns = [] for i in range(0, lib.bpf_num_functions(self.module)): func_name = lib.bpf_function_name(self.module, i) fns.append(self.load_func(func_name, prog_type)) return fns def load_func(self, func_name, prog_type, device = None): func_name = _assert_is_bytes(func_name) if func_name in self.funcs: return self.funcs[func_name] if not lib.bpf_function_start(self.module, func_name): raise Exception("Unknown program %s" % func_name) log_level = 0 if (self.debug & DEBUG_BPF_REGISTER_STATE): log_level = 2 elif (self.debug & DEBUG_BPF): log_level = 1 fd = lib.bcc_func_load(self.module, prog_type, func_name, lib.bpf_function_start(self.module, func_name), lib.bpf_function_size(self.module, func_name), lib.bpf_module_license(self.module), lib.bpf_module_kern_version(self.module), log_level, None, 0, device); if fd < 0: atexit.register(self.donothing) if ct.get_errno() == errno.EPERM: raise Exception("Need super-user privileges to run") errstr = os.strerror(ct.get_errno()) raise Exception("Failed to load BPF program %s: %s" % (func_name, errstr)) fn = BPF.Function(self, func_name, fd) self.funcs[func_name] = fn return fn def dump_func(self, func_name): """ Return the eBPF bytecodes for the specified function as a string """ func_name = _assert_is_bytes(func_name) if not lib.bpf_function_start(self.module, func_name): raise Exception("Unknown program %s" % func_name) start, = lib.bpf_function_start(self.module, func_name), size, = lib.bpf_function_size(self.module, func_name), return ct.string_at(start, size) def disassemble_func(self, func_name): bpfstr = self.dump_func(func_name) return disassemble_prog(func_name, bpfstr) def decode_table(self, table_name, sizeinfo=False): table_obj = self[table_name] table_type = lib.bpf_table_type_id(self.module, table_obj.map_id) return decode_map(table_name, table_obj, table_type, sizeinfo=sizeinfo) str2ctype = { u"_Bool": ct.c_bool, u"char": ct.c_char, u"wchar_t": ct.c_wchar, u"unsigned char": ct.c_ubyte, u"short": ct.c_short, u"unsigned short": ct.c_ushort, u"int": ct.c_int, u"unsigned int": ct.c_uint, u"long": ct.c_long, u"unsigned long": ct.c_ulong, u"long long": ct.c_longlong, u"unsigned long long": ct.c_ulonglong, u"float": ct.c_float, u"double": ct.c_double, u"long double": ct.c_longdouble, u"__int128": ct.c_int64 * 2, u"unsigned __int128": ct.c_uint64 * 2, } @staticmethod def _decode_table_type(desc): if isinstance(desc, basestring): return BPF.str2ctype[desc] anon = [] fields = [] for t in desc[1]: if len(t) == 2: fields.append((t[0], BPF._decode_table_type(t[1]))) elif len(t) == 3: if isinstance(t[2], list): fields.append((t[0], BPF._decode_table_type(t[1]) * t[2][0])) elif isinstance(t[2], int): fields.append((t[0], BPF._decode_table_type(t[1]), t[2])) elif isinstance(t[2], basestring) and ( t[2] == u"union" or t[2] == u"struct" or t[2] == u"struct_packed"): name = t[0] if name == "": name = "__anon%d" % len(anon) anon.append(name) fields.append((name, BPF._decode_table_type(t))) else: raise Exception("Failed to decode type %s" % str(t)) else: raise Exception("Failed to decode type %s" % str(t)) base = ct.Structure is_packed = False if len(desc) > 2: if desc[2] == u"union": base = ct.Union elif desc[2] == u"struct": base = ct.Structure elif desc[2] == u"struct_packed": base = ct.Structure is_packed = True if is_packed: cls = type(str(desc[0]), (base,), dict(_anonymous_=anon, _pack_=1, _fields_=fields)) else: cls = type(str(desc[0]), (base,), dict(_anonymous_=anon, _fields_=fields)) return cls def get_table(self, name, keytype=None, leaftype=None, reducer=None): name = _assert_is_bytes(name) map_id = lib.bpf_table_id(self.module, name) map_fd = lib.bpf_table_fd(self.module, name) if map_fd < 0: raise KeyError if not keytype: key_desc = lib.bpf_table_key_desc(self.module, name).decode("utf-8") if not key_desc: raise Exception("Failed to load BPF Table %s key desc" % name) keytype = BPF._decode_table_type(json.loads(key_desc)) if not leaftype: leaf_desc = lib.bpf_table_leaf_desc(self.module, name).decode("utf-8") if not leaf_desc: raise Exception("Failed to load BPF Table %s leaf desc" % name) leaftype = BPF._decode_table_type(json.loads(leaf_desc)) return Table(self, map_id, map_fd, keytype, leaftype, name, reducer=reducer) def __getitem__(self, key): if key not in self.tables: self.tables[key] = self.get_table(key) return self.tables[key] def __setitem__(self, key, leaf): self.tables[key] = leaf def __len__(self): return len(self.tables) def __delitem__(self, key): del self.tables[key] def __iter__(self): return self.tables.__iter__() @staticmethod def attach_raw_socket(fn, dev): dev = _assert_is_bytes(dev) if not isinstance(fn, BPF.Function): raise Exception("arg 1 must be of type BPF.Function") sock = lib.bpf_open_raw_sock(dev) if sock < 0: errstr = os.strerror(ct.get_errno()) raise Exception("Failed to open raw device %s: %s" % (dev, errstr)) res = lib.bpf_attach_socket(sock, fn.fd) if res < 0: errstr = os.strerror(ct.get_errno()) raise Exception("Failed to attach BPF to device %s: %s" % (dev, errstr)) fn.sock = sock @staticmethod def get_kprobe_functions(event_re): with open("%s/../kprobes/blacklist" % TRACEFS, "rb") as blacklist_f: blacklist = set([line.rstrip().split()[1] for line in blacklist_f]) fns = [] in_init_section = 0 in_irq_section = 0 with open("/proc/kallsyms", "rb") as avail_file: for line in avail_file: (t, fn) = line.rstrip().split()[1:3] # Skip all functions defined between __init_begin and # __init_end if in_init_section == 0: if fn == b'__init_begin': in_init_section = 1 continue elif in_init_section == 1: if fn == b'__init_end': in_init_section = 2 continue # Skip all functions defined between __irqentry_text_start and # __irqentry_text_end if in_irq_section == 0: if fn == b'__irqentry_text_start': in_irq_section = 1 continue elif in_irq_section == 1: if fn == b'__irqentry_text_end': in_irq_section = 2 continue # All functions defined as NOKPROBE_SYMBOL() start with the # prefix _kbl_addr_*, blacklisting them by looking at the name # allows to catch also those symbols that are defined in kernel # modules. if fn.startswith(b'_kbl_addr_'): continue # Explicitly blacklist perf-related functions, they are all # non-attachable. elif fn.startswith(b'__perf') or fn.startswith(b'perf_'): continue # Exclude all gcc 8's extra .cold functions elif re.match(b'^.*\.cold(\.\d+)?$', fn): continue if (t.lower() in [b't', b'w']) and re.match(event_re, fn) \ and fn not in blacklist: fns.append(fn) return set(fns) # Some functions may appear more than once def _check_probe_quota(self, num_new_probes): global _num_open_probes if _num_open_probes + num_new_probes > _probe_limit: raise Exception("Number of open probes would exceed global quota") def _add_kprobe_fd(self, name, fd): global _num_open_probes self.kprobe_fds[name] = fd _num_open_probes += 1 def _del_kprobe_fd(self, name): global _num_open_probes del self.kprobe_fds[name] _num_open_probes -= 1 def _add_uprobe_fd(self, name, fd): global _num_open_probes self.uprobe_fds[name] = fd _num_open_probes += 1 def _del_uprobe_fd(self, name): global _num_open_probes del self.uprobe_fds[name] _num_open_probes -= 1 # Find current system's syscall prefix by testing on the BPF syscall. # If no valid value found, will return the first possible value which # would probably lead to error in later API calls. def get_syscall_prefix(self): for prefix in self._syscall_prefixes: if self.ksymname(b"%sbpf" % prefix) != -1: return prefix return self._syscall_prefixes[0] # Given a syscall's name, return the full Kernel function name with current # system's syscall prefix. For example, given "clone" the helper would # return "sys_clone" or "__x64_sys_clone". def get_syscall_fnname(self, name): name = _assert_is_bytes(name) return self.get_syscall_prefix() + name # Given a Kernel function name that represents a syscall but already has a # prefix included, transform it to current system's prefix. For example, # if "sys_clone" provided, the helper may translate it to "__x64_sys_clone". def fix_syscall_fnname(self, name): name = _assert_is_bytes(name) for prefix in self._syscall_prefixes: if name.startswith(prefix): return self.get_syscall_fnname(name[len(prefix):]) return name def attach_kprobe(self, event=b"", event_off=0, fn_name=b"", event_re=b""): event = _assert_is_bytes(event) fn_name = _assert_is_bytes(fn_name) event_re = _assert_is_bytes(event_re) # allow the caller to glob multiple functions together if event_re: matches = BPF.get_kprobe_functions(event_re) self._check_probe_quota(len(matches)) for line in matches: try: self.attach_kprobe(event=line, fn_name=fn_name) except: pass return self._check_probe_quota(1) fn = self.load_func(fn_name, BPF.KPROBE) ev_name = b"p_" + event.replace(b"+", b"_").replace(b".", b"_") fd = lib.bpf_attach_kprobe(fn.fd, 0, ev_name, event, event_off, 0) if fd < 0: raise Exception("Failed to attach BPF program %s to kprobe %s" % (fn_name, event)) self._add_kprobe_fd(ev_name, fd) return self def attach_kretprobe(self, event=b"", fn_name=b"", event_re=b"", maxactive=0): event = _assert_is_bytes(event) fn_name = _assert_is_bytes(fn_name) event_re = _assert_is_bytes(event_re) # allow the caller to glob multiple functions together if event_re: for line in BPF.get_kprobe_functions(event_re): try: self.attach_kretprobe(event=line, fn_name=fn_name, maxactive=maxactive) except: pass return self._check_probe_quota(1) fn = self.load_func(fn_name, BPF.KPROBE) ev_name = b"r_" + event.replace(b"+", b"_").replace(b".", b"_") fd = lib.bpf_attach_kprobe(fn.fd, 1, ev_name, event, 0, maxactive) if fd < 0: raise Exception("Failed to attach BPF program %s to kretprobe %s" % (fn_name, event)) self._add_kprobe_fd(ev_name, fd) return self def detach_kprobe_event(self, ev_name): if ev_name not in self.kprobe_fds: raise Exception("Kprobe %s is not attached" % ev_name) res = lib.bpf_close_perf_event_fd(self.kprobe_fds[ev_name]) if res < 0: raise Exception("Failed to close kprobe FD") res = lib.bpf_detach_kprobe(ev_name) if res < 0: raise Exception("Failed to detach BPF from kprobe") self._del_kprobe_fd(ev_name) def detach_kprobe(self, event): event = _assert_is_bytes(event) ev_name = b"p_" + event.replace(b"+", b"_").replace(b".", b"_") self.detach_kprobe_event(ev_name) def detach_kretprobe(self, event): event = _assert_is_bytes(event) ev_name = b"r_" + event.replace(b"+", b"_").replace(b".", b"_") self.detach_kprobe_event(ev_name) @staticmethod def attach_xdp(dev, fn, flags=0): ''' This function attaches a BPF function to a device on the device driver level (XDP) ''' dev = _assert_is_bytes(dev) if not isinstance(fn, BPF.Function): raise Exception("arg 1 must be of type BPF.Function") res = lib.bpf_attach_xdp(dev, fn.fd, flags) if res < 0: err_no = ct.get_errno() if err_no == errno.EBADMSG: raise Exception("Internal error while attaching BPF to device,"+ " try increasing the debug level!") else: errstr = os.strerror(err_no) raise Exception("Failed to attach BPF to device %s: %s" % (dev, errstr)) @staticmethod def remove_xdp(dev, flags=0): ''' This function removes any BPF function from a device on the device driver level (XDP) ''' dev = _assert_is_bytes(dev) res = lib.bpf_attach_xdp(dev, -1, flags) if res < 0: errstr = os.strerror(ct.get_errno()) raise Exception("Failed to detach BPF from device %s: %s" % (dev, errstr)) @classmethod def _check_path_symbol(cls, module, symname, addr, pid, sym_off=0): module = _assert_is_bytes(module) symname = _assert_is_bytes(symname) sym = bcc_symbol() c_pid = 0 if pid == -1 else pid if lib.bcc_resolve_symname( module, symname, addr or 0x0, c_pid, ct.cast(None, ct.POINTER(bcc_symbol_option)), ct.byref(sym), ) < 0: raise Exception("could not determine address of symbol %s" % symname) new_addr = sym.offset + sym_off module_path = ct.cast(sym.module, ct.c_char_p).value lib.bcc_procutils_free(sym.module) return module_path, new_addr @staticmethod def find_library(libname): libname = _assert_is_bytes(libname) res = lib.bcc_procutils_which_so(libname, 0) if not res: return None libpath = ct.cast(res, ct.c_char_p).value lib.bcc_procutils_free(res) return libpath @staticmethod def get_tracepoints(tp_re): results = [] events_dir = os.path.join(TRACEFS, "events") for category in os.listdir(events_dir): cat_dir = os.path.join(events_dir, category) if not os.path.isdir(cat_dir): continue for event in os.listdir(cat_dir): evt_dir = os.path.join(cat_dir, event) if os.path.isdir(evt_dir): tp = ("%s:%s" % (category, event)) if re.match(tp_re.decode(), tp): results.append(tp) return results @staticmethod def tracepoint_exists(category, event): evt_dir = os.path.join(TRACEFS, "events", category, event) return os.path.isdir(evt_dir) def attach_tracepoint(self, tp=b"", tp_re=b"", fn_name=b""): """attach_tracepoint(tp="", tp_re="", fn_name="") Run the bpf function denoted by fn_name every time the kernel tracepoint specified by 'tp' is hit. The optional parameters pid, cpu, and group_fd can be used to filter the probe. The tracepoint specification is simply the tracepoint category and the tracepoint name, separated by a colon. For example: sched:sched_switch, syscalls:sys_enter_bind, etc. Instead of a tracepoint name, a regular expression can be provided in tp_re. The program will then attach to tracepoints that match the provided regular expression. To obtain a list of kernel tracepoints, use the tplist tool or cat the file /sys/kernel/debug/tracing/available_events. Examples: BPF(text).attach_tracepoint(tp="sched:sched_switch", fn_name="on_switch") BPF(text).attach_tracepoint(tp_re="sched:.*", fn_name="on_switch") """ tp = _assert_is_bytes(tp) tp_re = _assert_is_bytes(tp_re) fn_name = _assert_is_bytes(fn_name) if tp_re: for tp in BPF.get_tracepoints(tp_re): self.attach_tracepoint(tp=tp, fn_name=fn_name) return fn = self.load_func(fn_name, BPF.TRACEPOINT) (tp_category, tp_name) = tp.split(b':') fd = lib.bpf_attach_tracepoint(fn.fd, tp_category, tp_name) if fd < 0: raise Exception("Failed to attach BPF program %s to tracepoint %s" % (fn_name, tp)) self.tracepoint_fds[tp] = fd return self def attach_raw_tracepoint(self, tp=b"", fn_name=b""): """attach_raw_tracepoint(self, tp=b"", fn_name=b"") Run the bpf function denoted by fn_name every time the kernel tracepoint specified by 'tp' is hit. The bpf function should be loaded as a RAW_TRACEPOINT type. The fn_name is the kernel tracepoint name, e.g., sched_switch, sys_enter_bind, etc. Examples: BPF(text).attach_raw_tracepoint(tp="sched_switch", fn_name="on_switch") """ tp = _assert_is_bytes(tp) if tp in self.raw_tracepoint_fds: raise Exception("Raw tracepoint %s has been attached" % tp) fn_name = _assert_is_bytes(fn_name) fn = self.load_func(fn_name, BPF.RAW_TRACEPOINT) fd = lib.bpf_attach_raw_tracepoint(fn.fd, tp) if fd < 0: raise Exception("Failed to attach BPF to raw tracepoint") self.raw_tracepoint_fds[tp] = fd; return self def detach_raw_tracepoint(self, tp=b""): """detach_raw_tracepoint(tp="") Stop running the bpf function that is attached to the kernel tracepoint specified by 'tp'. Example: bpf.detach_raw_tracepoint("sched_switch") """ tp = _assert_is_bytes(tp) if tp not in self.raw_tracepoint_fds: raise Exception("Raw tracepoint %s is not attached" % tp) os.close(self.raw_tracepoint_fds[tp]) del self.raw_tracepoint_fds[tp] @staticmethod def support_raw_tracepoint(): # kernel symbol "bpf_find_raw_tracepoint" indicates raw_tracepoint support if BPF.ksymname("bpf_find_raw_tracepoint") != -1 or \ BPF.ksymname("bpf_get_raw_tracepoint") != -1: return True return False def detach_tracepoint(self, tp=b""): """detach_tracepoint(tp="") Stop running a bpf function that is attached to the kernel tracepoint specified by 'tp'. Example: bpf.detach_tracepoint("sched:sched_switch") """ tp = _assert_is_bytes(tp) if tp not in self.tracepoint_fds: raise Exception("Tracepoint %s is not attached" % tp) res = lib.bpf_close_perf_event_fd(self.tracepoint_fds[tp]) if res < 0: raise Exception("Failed to detach BPF from tracepoint") (tp_category, tp_name) = tp.split(b':') res = lib.bpf_detach_tracepoint(tp_category, tp_name) if res < 0: raise Exception("Failed to detach BPF from tracepoint") del self.tracepoint_fds[tp] def _attach_perf_event(self, progfd, ev_type, ev_config, sample_period, sample_freq, pid, cpu, group_fd): res = lib.bpf_attach_perf_event(progfd, ev_type, ev_config, sample_period, sample_freq, pid, cpu, group_fd) if res < 0: raise Exception("Failed to attach BPF to perf event") return res def attach_perf_event(self, ev_type=-1, ev_config=-1, fn_name=b"", sample_period=0, sample_freq=0, pid=-1, cpu=-1, group_fd=-1): fn_name = _assert_is_bytes(fn_name) fn = self.load_func(fn_name, BPF.PERF_EVENT) res = {} if cpu >= 0: res[cpu] = self._attach_perf_event(fn.fd, ev_type, ev_config, sample_period, sample_freq, pid, cpu, group_fd) else: for i in get_online_cpus(): res[i] = self._attach_perf_event(fn.fd, ev_type, ev_config, sample_period, sample_freq, pid, i, group_fd) self.open_perf_events[(ev_type, ev_config)] = res def detach_perf_event(self, ev_type=-1, ev_config=-1): try: fds = self.open_perf_events[(ev_type, ev_config)] except KeyError: raise Exception("Perf event type {} config {} not attached".format( ev_type, ev_config)) res = 0 for fd in fds.values(): res = lib.bpf_close_perf_event_fd(fd) or res if res != 0: raise Exception("Failed to detach BPF from perf event") del self.open_perf_events[(ev_type, ev_config)] @staticmethod def get_user_functions(name, sym_re): return set([name for (name, _) in BPF.get_user_functions_and_addresses(name, sym_re)]) @staticmethod def get_user_addresses(name, sym_re): """ We are returning addresses here instead of symbol names because it turns out that the same name may appear multiple times with different addresses, and the same address may appear multiple times with the same name. We can't attach a uprobe to the same address more than once, so it makes sense to return the unique set of addresses that are mapped to a symbol that matches the provided regular expression. """ return set([address for (_, address) in BPF.get_user_functions_and_addresses(name, sym_re)]) @staticmethod def get_user_functions_and_addresses(name, sym_re): name = _assert_is_bytes(name) sym_re = _assert_is_bytes(sym_re) addresses = [] def sym_cb(sym_name, addr): dname = sym_name if re.match(sym_re, dname): addresses.append((dname, addr)) return 0 res = lib.bcc_foreach_function_symbol(name, _SYM_CB_TYPE(sym_cb)) if res < 0: raise Exception("Error %d enumerating symbols in %s" % (res, name)) return addresses def _get_uprobe_evname(self, prefix, path, addr, pid): if pid == -1: return b"%s_%s_0x%x" % (prefix, self._probe_repl.sub(b"_", path), addr) else: # if pid is valid, put pid in the name, so different pid # can have different event names return b"%s_%s_0x%x_%d" % (prefix, self._probe_repl.sub(b"_", path), addr, pid) def attach_uprobe(self, name=b"", sym=b"", sym_re=b"", addr=None, fn_name=b"", pid=-1, sym_off=0): """attach_uprobe(name="", sym="", sym_re="", addr=None, fn_name="" pid=-1, sym_off=0) Run the bpf function denoted by fn_name every time the symbol sym in the library or binary 'name' is encountered. Optional parameters pid, cpu, and group_fd can be used to filter the probe. If sym_off is given, attach uprobe to offset within the symbol. The real address addr may be supplied in place of sym, in which case sym must be set to its default value. If the file is a non-PIE executable, addr must be a virtual address, otherwise it must be an offset relative to the file load address. Instead of a symbol name, a regular expression can be provided in sym_re. The uprobe will then attach to symbols that match the provided regular expression. Libraries can be given in the name argument without the lib prefix, or with the full path (/usr/lib/...). Binaries can be given only with the full path (/bin/sh). If a PID is given, the uprobe will attach to the version of the library used by the process. Example: BPF(text).attach_uprobe("c", "malloc") BPF(text).attach_uprobe("/usr/bin/python", "main") """ assert sym_off >= 0 if addr is not None: assert sym_off == 0, "offset with addr is not supported" name = _assert_is_bytes(name) sym = _assert_is_bytes(sym) sym_re = _assert_is_bytes(sym_re) fn_name = _assert_is_bytes(fn_name) if sym_re: addresses = BPF.get_user_addresses(name, sym_re) self._check_probe_quota(len(addresses)) for sym_addr in addresses: self.attach_uprobe(name=name, addr=sym_addr, fn_name=fn_name, pid=pid) return (path, addr) = BPF._check_path_symbol(name, sym, addr, pid, sym_off) self._check_probe_quota(1) fn = self.load_func(fn_name, BPF.KPROBE) ev_name = self._get_uprobe_evname(b"p", path, addr, pid) fd = lib.bpf_attach_uprobe(fn.fd, 0, ev_name, path, addr, pid) if fd < 0: raise Exception("Failed to attach BPF to uprobe") self._add_uprobe_fd(ev_name, fd) return self def attach_uretprobe(self, name=b"", sym=b"", sym_re=b"", addr=None, fn_name=b"", pid=-1): """attach_uretprobe(name="", sym="", sym_re="", addr=None, fn_name="" pid=-1) Run the bpf function denoted by fn_name every time the symbol sym in the library or binary 'name' finishes execution. See attach_uprobe for meaning of additional parameters. """ name = _assert_is_bytes(name) sym = _assert_is_bytes(sym) sym_re = _assert_is_bytes(sym_re) fn_name = _assert_is_bytes(fn_name) if sym_re: for sym_addr in BPF.get_user_addresses(name, sym_re): self.attach_uretprobe(name=name, addr=sym_addr, fn_name=fn_name, pid=pid) return (path, addr) = BPF._check_path_symbol(name, sym, addr, pid) self._check_probe_quota(1) fn = self.load_func(fn_name, BPF.KPROBE) ev_name = self._get_uprobe_evname(b"r", path, addr, pid) fd = lib.bpf_attach_uprobe(fn.fd, 1, ev_name, path, addr, pid) if fd < 0: raise Exception("Failed to attach BPF to uretprobe") self._add_uprobe_fd(ev_name, fd) return self def detach_uprobe_event(self, ev_name): if ev_name not in self.uprobe_fds: raise Exception("Uprobe %s is not attached" % ev_name) res = lib.bpf_close_perf_event_fd(self.uprobe_fds[ev_name]) if res < 0: raise Exception("Failed to detach BPF from uprobe") res = lib.bpf_detach_uprobe(ev_name) if res < 0: raise Exception("Failed to detach BPF from uprobe") self._del_uprobe_fd(ev_name) def detach_uprobe(self, name=b"", sym=b"", addr=None, pid=-1, sym_off=0): """detach_uprobe(name="", sym="", addr=None, pid=-1) Stop running a bpf function that is attached to symbol 'sym' in library or binary 'name'. """ name = _assert_is_bytes(name) sym = _assert_is_bytes(sym) (path, addr) = BPF._check_path_symbol(name, sym, addr, pid, sym_off) ev_name = self._get_uprobe_evname(b"p", path, addr, pid) self.detach_uprobe_event(ev_name) def detach_uretprobe(self, name=b"", sym=b"", addr=None, pid=-1): """detach_uretprobe(name="", sym="", addr=None, pid=-1) Stop running a bpf function that is attached to symbol 'sym' in library or binary 'name'. """ name = _assert_is_bytes(name) sym = _assert_is_bytes(sym) (path, addr) = BPF._check_path_symbol(name, sym, addr, pid) ev_name = self._get_uprobe_evname(b"r", path, addr, pid) self.detach_uprobe_event(ev_name) def _trace_autoload(self): for i in range(0, lib.bpf_num_functions(self.module)): func_name = lib.bpf_function_name(self.module, i) if func_name.startswith(b"kprobe__"): fn = self.load_func(func_name, BPF.KPROBE) self.attach_kprobe( event=self.fix_syscall_fnname(func_name[8:]), fn_name=fn.name) elif func_name.startswith(b"kretprobe__"): fn = self.load_func(func_name, BPF.KPROBE) self.attach_kretprobe( event=self.fix_syscall_fnname(func_name[11:]), fn_name=fn.name) elif func_name.startswith(b"tracepoint__"): fn = self.load_func(func_name, BPF.TRACEPOINT) tp = fn.name[len(b"tracepoint__"):].replace(b"__", b":") self.attach_tracepoint(tp=tp, fn_name=fn.name) elif func_name.startswith(b"raw_tracepoint__"): fn = self.load_func(func_name, BPF.RAW_TRACEPOINT) tp = fn.name[len(b"raw_tracepoint__"):] self.attach_raw_tracepoint(tp=tp, fn_name=fn.name) def trace_open(self, nonblocking=False): """trace_open(nonblocking=False) Open the trace_pipe if not already open """ if not self.tracefile: self.tracefile = open("%s/trace_pipe" % TRACEFS, "rb") if nonblocking: fd = self.tracefile.fileno() fl = fcntl.fcntl(fd, fcntl.F_GETFL) fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK) return self.tracefile def trace_fields(self, nonblocking=False): """trace_fields(nonblocking=False) Read from the kernel debug trace pipe and return a tuple of the fields (task, pid, cpu, flags, timestamp, msg) or None if no line was read (nonblocking=True) """ while True: line = self.trace_readline(nonblocking) if not line and nonblocking: return (None,) * 6 # don't print messages related to lost events if line.startswith(b"CPU:"): continue task = line[:16].lstrip() line = line[17:] ts_end = line.find(b":") pid, cpu, flags, ts = line[:ts_end].split() cpu = cpu[1:-1] # line[ts_end:] will have ": [sym_or_addr]: msgs" # For trace_pipe debug output, the addr typically # is invalid (e.g., 0x1). For kernel 4.12 or earlier, # if address is not able to match a kernel symbol, # nothing will be printed out. For kernel 4.13 and later, # however, the illegal address will be printed out. # Hence, both cases are handled here. line = line[ts_end + 1:] sym_end = line.find(b":") msg = line[sym_end + 2:] return (task, int(pid), int(cpu), flags, float(ts), msg) def trace_readline(self, nonblocking=False): """trace_readline(nonblocking=False) Read from the kernel debug trace pipe and return one line If nonblocking is False, this will block until ctrl-C is pressed. """ trace = self.trace_open(nonblocking) line = None try: line = trace.readline(1024).rstrip() except IOError: pass return line def trace_print(self, fmt=None): """trace_print(self, fmt=None) Read from the kernel debug trace pipe and print on stdout. If fmt is specified, apply as a format string to the output. See trace_fields for the members of the tuple example: trace_print(fmt="pid {1}, msg = {5}") """ while True: if fmt: fields = self.trace_fields(nonblocking=False) if not fields: continue line = fmt.format(*fields) else: line = self.trace_readline(nonblocking=False) print(line) sys.stdout.flush() @staticmethod def _sym_cache(pid): """_sym_cache(pid) Returns a symbol cache for the specified PID. The kernel symbol cache is accessed by providing any PID less than zero. """ if pid < 0 and pid != -1: pid = -1 if not pid in BPF._sym_caches: BPF._sym_caches[pid] = SymbolCache(pid) return BPF._sym_caches[pid] @staticmethod def sym(addr, pid, show_module=False, show_offset=False, demangle=True): """sym(addr, pid, show_module=False, show_offset=False) Translate a memory address into a function name for a pid, which is returned. When show_module is True, the module name is also included. When show_offset is True, the instruction offset as a hexadecimal number is also included in the string. A pid of less than zero will access the kernel symbol cache. Example output when both show_module and show_offset are True: "start_thread+0x202 [libpthread-2.24.so]" Example output when both show_module and show_offset are False: "start_thread" """ #addr is of type stacktrace_build_id #so invoke the bsym address resolver typeofaddr = str(type(addr)) if typeofaddr.find('bpf_stack_build_id') != -1: sym = bcc_symbol() b = bcc_stacktrace_build_id() b.status = addr.status b.build_id = addr.build_id b.u.offset = addr.offset; res = lib.bcc_buildsymcache_resolve(BPF._bsymcache, ct.byref(b), ct.byref(sym)) if res < 0: if sym.module and sym.offset: name,offset,module = (None, sym.offset, ct.cast(sym.module, ct.c_char_p).value) else: name, offset, module = (None, addr, None) else: name, offset, module = (sym.name, sym.offset, ct.cast(sym.module, ct.c_char_p).value) else: name, offset, module = BPF._sym_cache(pid).resolve(addr, demangle) offset = b"+0x%x" % offset if show_offset and name is not None else b"" name = name or b"[unknown]" name = name + offset module = b" [%s]" % os.path.basename(module) \ if show_module and module is not None else b"" return name + module @staticmethod def ksym(addr, show_module=False, show_offset=False): """ksym(addr) Translate a kernel memory address into a kernel function name, which is returned. When show_module is True, the module name ("kernel") is also included. When show_offset is true, the instruction offset as a hexadecimal number is also included in the string. Example output when both show_module and show_offset are True: "default_idle+0x0 [kernel]" """ return BPF.sym(addr, -1, show_module, show_offset, False) @staticmethod def ksymname(name): """ksymname(name) Translate a kernel name into an address. This is the reverse of ksym. Returns -1 when the function name is unknown.""" return BPF._sym_cache(-1).resolve_name(None, name) def num_open_kprobes(self): """num_open_kprobes() Get the number of open K[ret]probes. Can be useful for scenarios where event_re is used while attaching and detaching probes. """ return len(self.kprobe_fds) def num_open_uprobes(self): """num_open_uprobes() Get the number of open U[ret]probes. """ return len(self.uprobe_fds) def num_open_tracepoints(self): """num_open_tracepoints() Get the number of open tracepoints. """ return len(self.tracepoint_fds) def perf_buffer_poll(self, timeout = -1): """perf_buffer_poll(self) Poll from all open perf ring buffers, calling the callback that was provided when calling open_perf_buffer for each entry. """ readers = (ct.c_void_p * len(self.perf_buffers))() for i, v in enumerate(self.perf_buffers.values()): readers[i] = v lib.perf_reader_poll(len(readers), readers, timeout) def kprobe_poll(self, timeout = -1): """kprobe_poll(self) Deprecated. Use perf_buffer_poll instead. """ self.perf_buffer_poll(timeout) def free_bcc_memory(self): return lib.bcc_free_memory() @staticmethod def add_module(modname): """add_module(modname) Add a library or exe to buildsym cache """ try: lib.bcc_buildsymcache_add_module(BPF._bsymcache, modname.encode()) except Exception as e: print("Error adding module to build sym cache"+str(e)) def donothing(self): """the do nothing exit handler""" def cleanup(self): # Clean up opened probes for k, v in list(self.kprobe_fds.items()): self.detach_kprobe_event(k) for k, v in list(self.uprobe_fds.items()): self.detach_uprobe_event(k) for k, v in list(self.tracepoint_fds.items()): self.detach_tracepoint(k) for k, v in list(self.raw_tracepoint_fds.items()): self.detach_raw_tracepoint(k) # Clean up opened perf ring buffer and perf events table_keys = list(self.tables.keys()) for key in table_keys: if isinstance(self.tables[key], PerfEventArray): del self.tables[key] for (ev_type, ev_config) in list(self.open_perf_events.keys()): self.detach_perf_event(ev_type, ev_config) if self.tracefile: self.tracefile.close() self.tracefile = None for name, fn in list(self.funcs.items()): os.close(fn.fd) if self.module: lib.bpf_module_destroy(self.module) self.module = None def __enter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb): self.cleanup() from .usdt import USDT, USDTException bpfcc-0.12.0/src/python/bcc/disassembler.py000066400000000000000000000506621357404205000205460ustar00rootroot00000000000000# Copyright 2019 Clevernet # # 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. from os import linesep import ctypes as ct from .table import get_table_type_name from .libbcc import lib class OffsetUnion(ct.Union): _fields_ = [('offsetu', ct.c_uint16), ('offset', ct.c_int16)] class ImmUnion(ct.Union): _fields_ = [('immu', ct.c_uint32), ('imm', ct.c_int32)] class BPFInstrFields(ct.Structure): _pack_ = 1 _anonymous_ = ('o', 'i') _fields_ = [('opcode', ct.c_uint8), ('dst', ct.c_uint8, 4), ('src', ct.c_uint8, 4), ('o', OffsetUnion), ('i', ImmUnion)] class BPFInstr(ct.Union): _pack_ = 1 _anonymous_ = ('s') _fields_ = [('s', BPFInstrFields), ('instr', ct.c_uint64)] class BPFDecoder(): BPF_PSEUDO_CALL = 1 bpf_helpers = ['unspec', 'map_lookup_elem', 'map_update_elem', 'map_delete_elem', 'probe_read', 'ktime_get_ns', 'trace_printk', 'get_prandom_u32', 'get_smp_processor_id', 'skb_store_bytes', 'l3_csum_replace', 'l4_csum_replace', 'tail_call', 'clone_redirect', 'get_current_pid_tgid', 'get_current_uid_gid', 'get_current_comm', 'get_cgroup_classid', 'skb_vlan_push', 'skb_vlan_pop', 'skb_get_tunnel_key', 'skb_set_tunnel_key', 'perf_event_read', 'redirect', 'get_route_realm', 'perf_event_output', 'skb_load_bytes', 'get_stackid', 'csum_diff', 'skb_get_tunnel_opt', 'skb_set_tunnel_opt', 'skb_change_proto', 'skb_change_type', 'skb_under_cgroup', 'get_hash_recalc', 'get_current_task', 'probe_write_user', 'current_task_under_cgroup', 'skb_change_tail', 'skb_pull_data', 'csum_update', 'set_hash_invalid', 'get_numa_node_id', 'skb_change_head', 'xdp_adjust_head', 'probe_read_str', 'get_socket_cookie', 'get_socket_uid', 'set_hash', 'setsockopt', 'skb_adjust_room', 'redirect_map', 'sk_redirect_map', 'sock_map_update', 'xdp_adjust_meta', 'perf_event_read_value', 'perf_prog_read_value', 'getsockopt', 'override_return', 'sock_ops_cb_flags_set', 'msg_redirect_map', 'msg_apply_bytes', 'msg_cork_bytes', 'msg_pull_data', 'bind', 'xdp_adjust_tail', 'skb_get_xfrm_state', 'get_stack', 'skb_load_bytes_relative', 'fib_lookup', 'sock_hash_update', 'msg_redirect_hash', 'sk_redirect_hash', 'lwt_push_encap', 'lwt_seg6_store_bytes', 'lwt_seg6_adjust_srh', 'lwt_seg6_action', 'rc_repeat', 'rc_keydown', 'skb_cgroup_id', 'get_current_cgroup_id', 'get_local_storage', 'sk_select_reuseport', 'skb_ancestor_cgroup_id', 'sk_lookup_tcp', 'sk_lookup_udp', 'sk_release', 'map_push_elem', 'map_pop_elem', 'map_peek_elem', 'msg_push_data', 'msg_pop_data', 'rc_pointer_rel'] opcodes = {0x04: ('add32', 'dstimm', '+=', 32), 0x05: ('ja', 'joff', None, 64), 0x07: ('add', 'dstimm', '+=', 64), 0x0c: ('add32', 'dstsrc', '+=', 32), 0x0f: ('add', 'dstsrc', '+=', 64), 0x14: ('sub32', 'dstimm', '-=', 32), 0x15: ('jeq', 'jdstimmoff', '==', 64), 0x17: ('sub', 'dstimm', '-=', 64), 0x18: ('lddw', 'lddw', None, 64), 0x1c: ('sub32', 'dstsrc', '-=', 32), 0x1d: ('jeq', 'jdstsrcoff', '==', 64), 0x1f: ('sub', 'dstsrc', '-=', 64), 0x20: ('ldabsw', 'ldabs', None, 32), 0x24: ('mul32', 'dstimm', '*=', 32), 0x25: ('jgt', 'jdstimmoff', '>', 64), 0x27: ('mul', 'dstimm', '*=', 64), 0x28: ('ldabsh', 'ldabs', None, 16), 0x2c: ('mul32', 'dstsrc', '*=', 32), 0x2d: ('jgt', 'jdstsrcoff', '>', 64), 0x2f: ('mul', 'dstsrc', '*=', 64), 0x30: ('ldabsb', 'ldabs', None, 8), 0x34: ('div32', 'dstimm', '/=', 32), 0x35: ('jge', 'jdstimmoff', '>=', 64), 0x37: ('div', 'dstimm', '/=', 64), 0x38: ('ldabsdw', 'ldabs', None, 64), 0x3c: ('div32', 'dstsrc', '/=', 32), 0x3d: ('jge', 'jdstsrcoff', '>=', 64), 0x3f: ('div', 'dstsrc', '/=', 64), 0x40: ('ldindw', 'ldind', None, 32), 0x44: ('or32', 'dstimm_bw', '|=', 32), 0x45: ('jset', 'jdstimmoff', '&', 64), 0x47: ('or', 'dstimm_bw', '|=', 64), 0x48: ('ldindh', 'ldind', None, 16), 0x4c: ('or32', 'dstsrc', '|=', 32), 0x4d: ('jset', 'jdstsrcoff', '&', 64), 0x4f: ('or', 'dstsrc', '|=', 64), 0x50: ('ldindb', 'ldind', None, 8), 0x54: ('and32', 'dstimm_bw', '&=', 32), 0x55: ('jne', 'jdstimmoff', '!=', 64), 0x57: ('and', 'dstimm_bw', '&=', 64), 0x58: ('ldinddw', 'ldind', None, 64), 0x5c: ('and32', 'dstsrc', '&=', 32), 0x5d: ('jne', 'jdstsrcoff', '!=', 64), 0x5f: ('and', 'dstsrc', '&=', 64), 0x61: ('ldxw', 'ldstsrcoff', None, 32), 0x62: ('stw', 'sdstoffimm', None, 32), 0x63: ('stxw', 'sdstoffsrc', None, 32), 0x64: ('lsh32', 'dstimm', '<<=', 32), 0x65: ('jsgt', 'jdstimmoff', 's>', 64), 0x67: ('lsh', 'dstimm', '<<=', 64), 0x69: ('ldxh', 'ldstsrcoff', None, 16), 0x6a: ('sth', 'sdstoffimm', None, 16), 0x6b: ('stxh', 'sdstoffsrc', None, 16), 0x6c: ('lsh32', 'dstsrc', '<<=', 32), 0x6d: ('jsgt', 'jdstsrcoff', 's>', 64), 0x6f: ('lsh', 'dstsrc', '<<=', 64), 0x71: ('ldxb', 'ldstsrcoff', None, 8), 0x72: ('stb', 'sdstoffimm', None, 8), 0x73: ('stxb', 'sdstoffsrc', None, 8), 0x74: ('rsh32', 'dstimm', '>>=', 32), 0x75: ('jsge', 'jdstimmoff', 's>=', 64), 0x77: ('rsh', 'dstimm', '>>=', 64), 0x79: ('ldxdw', 'ldstsrcoff', None, 64), 0x7a: ('stdw', 'sdstoffimm', None, 64), 0x7b: ('stxdw', 'sdstoffsrc', None, 64), 0x7c: ('rsh32', 'dstsrc', '>>=', 32), 0x7d: ('jsge', 'jdstsrcoff', 's>=', 64), 0x7f: ('rsh', 'dstsrc', '>>=', 64), 0x84: ('neg32', 'dst', '~', 32), 0x85: ('call', 'call', None, 64), 0x87: ('neg', 'dst', '~', 64), 0x94: ('mod32', 'dstimm', '%=', 32), 0x95: ('exit', 'exit', None, 64), 0x97: ('mod', 'dstimm', '%=', 64), 0x9c: ('mod32', 'dstsrc', '%=', 32), 0x9f: ('mod', 'dstsrc', '%=', 64), 0xa4: ('xor32', 'dstimm_bw', '^=', 32), 0xa5: ('jlt', 'jdstimmoff', '<', 64), 0xa7: ('xor', 'dstimm_bw', '^=', 64), 0xac: ('xor32', 'dstsrc', '^=', 32), 0xad: ('jlt', 'jdstsrcoff', '<', 64), 0xaf: ('xor', 'dstsrc', '^=', 64), 0xb4: ('mov32', 'dstimm', '=', 32), 0xb5: ('jle', 'jdstimmoff', '<=', 64), 0xb7: ('mov', 'dstimm', '=', 64), 0xbc: ('mov32', 'dstsrc', '=', 32), 0xbd: ('jle', 'jdstsrcoff', '<=', 64), 0xbf: ('mov', 'dstsrc', '=', 64), 0xc4: ('arsh32', 'dstimm', 's>>=', 32), 0xc5: ('jslt', 'jdstimmoff', 's<', 64), 0xc7: ('arsh', 'dstimm', 's>>=', 64), 0xcc: ('arsh32', 'dstsrc', 's>>=', 32), 0xcd: ('jslt', 'jdstsrcoff', 's<', 64), 0xcf: ('arsh', 'dstsrc', 's>>=', 64), 0xd5: ('jsle', 'jdstimmoff', 's<=', 64), 0xdc: ('endian32', 'dstsrc', 'endian', 32), 0xdd: ('jsle', 'jdstimmoff', 's<=', 64),} @classmethod def decode(cls, i, w, w1): try: name, opclass, op, bits = cls.opcodes[w.opcode] if opclass == 'dstimm': return 'r%d %s %d' % (w.dst, op, w.imm), 0 elif opclass == 'dstimm_bw': return 'r%d %s 0x%x' % (w.dst, op, w.immu), 0 elif opclass == 'joff': return 'goto %s <%d>' % ('%+d' % (w.offset), i + w.offset + 1), 0 elif opclass == 'dstsrc': return 'r%d %s r%d' % (w.dst, op, w.src), 0 elif opclass == 'jdstimmoff': return 'if r%d %s %d goto pc%s <%d>' % (w.dst, op, w.imm, '%+d' % (w.offset), i + w.offset + 1), 0 elif opclass == 'jdstsrcoff': return 'if r%d %s r%d goto pc%s <%d>' % (w.dst, op, w.src, '%+d' % (w.offset), i + w.offset + 1), 0 elif opclass == 'lddw': # imm contains the file descriptor (FD) of the map being loaded; # the kernel will translate this into the proper address if w1 is None: raise Exception("lddw requires two instructions to be disassembled") if w1.imm == 0: return 'r%d = ' % (w.dst, w.imm), 1 imm = (w1.imm << 32) | w.imm return 'r%d = 0x%x' % (w.dst, imm), 1 elif opclass == 'ldabs': return 'r0 = *(u%s*)skb[%s]' % (bits, w.imm), 0 elif opclass == 'ldind': return 'r0 = *(u%d*)skb[r%d %s]' % (bits, w.src, '%+d' % (w.imm)), 0 elif opclass == 'ldstsrcoff': return 'r%d = *(u%d*)(r%d %s)' % (w.dst, bits, w.src, '%+d' % (w.offset)), 0 elif opclass == 'sdstoffimm': return '*(u%d*)(r%d %s) = %d' % (bits, w.dst, '%+d' % (w.offset), w.imm), 0 elif opclass == 'sdstoffsrc': return '*(u%d*)(r%d %s) = r%d' % (bits, w.dst, '%+d' % (w.offset), w.src), 0 elif opclass == 'dst': return 'r%d = %s (u%s)r%d' % (w.dst, op, bits, w.dst), 0 elif opclass == 'call': if w.src != cls.BPF_PSEUDO_CALL: try: return '%s bpf_%s#%d' % (name, cls.bpf_helpers[w.immu], w.immu), 0 except IndexError: return '%s ' % (op, w.immu), 0 return '%s %s' % (name, '%+d' % (w.imm)), 0 elif opclass == 'exit': return name, 0 else: raise Exception('unknown opcode class') except KeyError: return 'unknown <0x%x>' % (w.opcode) def disassemble_instruction(i, w0, w1=None): instr, skip = BPFDecoder.decode(i, w0, w1) return "%4d: (%02x) %s" % (i, w0.opcode, instr), skip def disassemble_str(bpfstr): ptr = ct.cast(ct.c_char_p(bpfstr), ct.POINTER(BPFInstr)) numinstr = int(len(bpfstr) / 8) w0 = ptr[0] skip = 0 instr_list = [] for i in range(1, numinstr): w1 = ptr[i] if skip: skip -= 1 instr_str = "%4d: (64-bit upper word)" % (i) else: instr_str, skip = disassemble_instruction(i - 1, w0, w1) instr_list.append(instr_str) w0 = w1 instr_str, skip = disassemble_instruction(numinstr - 1, w0, None) instr_list.append(instr_str) return instr_list def disassemble_prog(func_name, bpfstr): instr_list = ["Disassemble of BPF program %s:" % (func_name)] instr_list += disassemble_str(bpfstr) return linesep.join(instr_list) class MapDecoder (): ctype2str = {ct.c_bool: u"_Bool", ct.c_char: u"char", ct.c_wchar: u"wchar_t", ct.c_ubyte: u"unsigned char", ct.c_short: u"short", ct.c_ushort: u"unsigned short", ct.c_int: u"int", ct.c_uint: u"unsigned int", ct.c_long: u"long", ct.c_ulong: u"unsigned long", ct.c_longlong: u"long long", ct.c_ulonglong: u"unsigned long long", ct.c_float: u"float", ct.c_double: u"double", ct.c_longdouble: u"long double", ct.c_int64 * 2: u"__int128", ct.c_uint64 * 2: u"unsigned __int128",} @classmethod def get_ct_name(cls, t): try: if issubclass(t, ct.Structure): field_type_name = "struct" elif issubclass(t, ct.Union): field_type_name = "union" elif issubclass(t, ct.Array): field_type_name = cls.ctype2str[t._type_] + "[" + str(t._length_) + "]" else: field_type_name = cls.ctype2str[t] except KeyError: field_type_name = str(t) return field_type_name @classmethod def format_size_info(cls, offset, size, enabled=False, bitoffset=None): if not enabled: return "" if bitoffset is not None: return "[%d,%d +%d bit]" % (offset, bitoffset, size) return "[%d +%d] " % (offset, size) @classmethod def print_ct_map(cls, t, indent="", offset=0, sizeinfo=False): map_lines = [] try: for field_name, field_type in t._fields_: is_structured = (issubclass(field_type, ct.Structure) or issubclass(field_type, ct.Union)) field_type_name = cls.get_ct_name(field_type) field_offset = getattr(t, field_name).offset field_size = ct.sizeof(field_type) sizedesc = cls.format_size_info(offset + field_offset, field_size, sizeinfo) if is_structured: map_lines.append("%s%s%s {" % (indent, sizedesc, field_type_name)) map_lines += cls.print_ct_map(field_type, indent + " ", offset + field_offset) map_lines.append("%s} %s;" % (indent, field_name)) else: map_lines.append("%s%s%s %s;" % (indent, sizedesc, field_type_name, field_name)) except ValueError: # is a bit field offset_bits = 0 for field in t._fields_: if len(field) == 3: field_name, field_type, field_bits = field field_type_name = cls.get_ct_name(field_type) sizedesc = cls.format_size_info(offset, offset_bits, sizeinfo, field_bits) map_lines.append("%s%s%s %s:%d;" % (indent, sizedesc, field_type_name, field_name, field_bits)) else: # end of previous bit field field_name, field_type = field field_type_name = cls.get_ct_name(field_type) field_offset = getattr(t, field_name).offset field_size = ct.sizeof(field_type) field_bits = 0 offset_bits = 0 sizedesc = cls.format_size_info(offset + field_offset, field_size, sizeinfo) map_lines.append("%s%s%s %s;" % (indent, sizedesc, field_type_name, field_name)) offset += field_offset offset_bits += field_bits return map_lines @classmethod def print_map_ctype(cls, t, field_name, sizeinfo): is_structured = (issubclass(t, ct.Structure) or issubclass(t, ct.Union)) type_name = cls.get_ct_name(t); if is_structured: map_lines = [" %s {" % (type_name)] map_lines += cls.print_ct_map(t, " ", sizeinfo=sizeinfo) map_lines.append(" } %s;" % (field_name)) else: map_lines = [" %s %s;" % (type_name, field_name)] return map_lines @classmethod def decode_map(cls, map_name, map_obj, map_type, sizeinfo=False): map_lines = ['Layout of BPF map %s (type %s, FD %d, ID %d):' % (map_name, map_type, map_obj.map_fd, map_obj.map_id)] map_lines += cls.print_map_ctype(map_obj.Key, 'key', sizeinfo=sizeinfo) map_lines += cls.print_map_ctype(map_obj.Leaf, 'value', sizeinfo=sizeinfo) return linesep.join(map_lines) def decode_map(map_name, map_obj, map_type, sizeinfo=False): map_type_name = get_table_type_name(map_type) return MapDecoder.decode_map(map_name, map_obj, map_type_name, sizeinfo=sizeinfo) bpfcc-0.12.0/src/python/bcc/libbcc.py000066400000000000000000000267201357404205000173050ustar00rootroot00000000000000# Copyright 2015 PLUMgrid # # 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. import ctypes as ct lib = ct.CDLL("libbcc.so.0", use_errno=True) # keep in sync with bcc_common.h lib.bpf_module_create_b.restype = ct.c_void_p lib.bpf_module_create_b.argtypes = [ct.c_char_p, ct.c_char_p, ct.c_uint, ct.c_char_p] lib.bpf_module_create_c.restype = ct.c_void_p lib.bpf_module_create_c.argtypes = [ct.c_char_p, ct.c_uint, ct.POINTER(ct.c_char_p), ct.c_int, ct.c_bool, ct.c_char_p] lib.bpf_module_create_c_from_string.restype = ct.c_void_p lib.bpf_module_create_c_from_string.argtypes = [ct.c_char_p, ct.c_uint, ct.POINTER(ct.c_char_p), ct.c_int, ct.c_bool, ct.c_char_p] lib.bpf_module_destroy.restype = None lib.bpf_module_destroy.argtypes = [ct.c_void_p] lib.bpf_module_license.restype = ct.c_char_p lib.bpf_module_license.argtypes = [ct.c_void_p] lib.bpf_module_kern_version.restype = ct.c_uint lib.bpf_module_kern_version.argtypes = [ct.c_void_p] lib.bpf_num_functions.restype = ct.c_ulonglong lib.bpf_num_functions.argtypes = [ct.c_void_p] lib.bpf_function_name.restype = ct.c_char_p lib.bpf_function_name.argtypes = [ct.c_void_p, ct.c_ulonglong] lib.bpf_function_start.restype = ct.c_void_p lib.bpf_function_start.argtypes = [ct.c_void_p, ct.c_char_p] lib.bpf_function_size.restype = ct.c_size_t lib.bpf_function_size.argtypes = [ct.c_void_p, ct.c_char_p] lib.bpf_table_id.restype = ct.c_ulonglong lib.bpf_table_id.argtypes = [ct.c_void_p, ct.c_char_p] lib.bpf_table_fd.restype = ct.c_int lib.bpf_table_fd.argtypes = [ct.c_void_p, ct.c_char_p] lib.bpf_table_type_id.restype = ct.c_int lib.bpf_table_type_id.argtypes = [ct.c_void_p, ct.c_ulonglong] lib.bpf_table_max_entries_id.restype = ct.c_ulonglong lib.bpf_table_max_entries_id.argtypes = [ct.c_void_p, ct.c_ulonglong] lib.bpf_table_flags_id.restype = ct.c_int lib.bpf_table_flags_id.argtypes = [ct.c_void_p, ct.c_ulonglong] lib.bpf_table_key_desc.restype = ct.c_char_p lib.bpf_table_key_desc.argtypes = [ct.c_void_p, ct.c_char_p] lib.bpf_table_leaf_desc.restype = ct.c_char_p lib.bpf_table_leaf_desc.argtypes = [ct.c_void_p, ct.c_char_p] lib.bpf_table_key_snprintf.restype = ct.c_int lib.bpf_table_key_snprintf.argtypes = [ct.c_void_p, ct.c_ulonglong, ct.c_char_p, ct.c_ulonglong, ct.c_void_p] lib.bpf_table_leaf_snprintf.restype = ct.c_int lib.bpf_table_leaf_snprintf.argtypes = [ct.c_void_p, ct.c_ulonglong, ct.c_char_p, ct.c_ulonglong, ct.c_void_p] lib.bpf_table_key_sscanf.restype = ct.c_int lib.bpf_table_key_sscanf.argtypes = [ct.c_void_p, ct.c_ulonglong, ct.c_char_p, ct.c_void_p] lib.bpf_table_leaf_sscanf.restype = ct.c_int lib.bpf_table_leaf_sscanf.argtypes = [ct.c_void_p, ct.c_ulonglong, ct.c_char_p, ct.c_void_p] lib.bpf_perf_event_fields.restype = ct.c_ulonglong lib.bpf_perf_event_fields.argtypes = [ct.c_void_p, ct.c_char_p] lib.bpf_perf_event_field.restype = ct.c_char_p lib.bpf_perf_event_field.argtypes = [ct.c_void_p, ct.c_char_p, ct.c_ulonglong] # keep in sync with libbpf.h lib.bpf_get_next_key.restype = ct.c_int lib.bpf_get_next_key.argtypes = [ct.c_int, ct.c_void_p, ct.c_void_p] lib.bpf_get_first_key.restype = ct.c_int lib.bpf_get_first_key.argtypes = [ct.c_int, ct.c_void_p, ct.c_uint] lib.bpf_lookup_elem.restype = ct.c_int lib.bpf_lookup_elem.argtypes = [ct.c_int, ct.c_void_p, ct.c_void_p] lib.bpf_update_elem.restype = ct.c_int lib.bpf_update_elem.argtypes = [ct.c_int, ct.c_void_p, ct.c_void_p, ct.c_ulonglong] lib.bpf_delete_elem.restype = ct.c_int lib.bpf_delete_elem.argtypes = [ct.c_int, ct.c_void_p] lib.bpf_open_raw_sock.restype = ct.c_int lib.bpf_open_raw_sock.argtypes = [ct.c_char_p] lib.bpf_attach_socket.restype = ct.c_int lib.bpf_attach_socket.argtypes = [ct.c_int, ct.c_int] lib.bcc_func_load.restype = ct.c_int lib.bcc_func_load.argtypes = [ct.c_void_p, ct.c_int, ct.c_char_p, ct.c_void_p, ct.c_size_t, ct.c_char_p, ct.c_uint, ct.c_int, ct.c_char_p, ct.c_uint, ct.c_char_p] _RAW_CB_TYPE = ct.CFUNCTYPE(None, ct.py_object, ct.c_void_p, ct.c_int) _LOST_CB_TYPE = ct.CFUNCTYPE(None, ct.py_object, ct.c_ulonglong) lib.bpf_attach_kprobe.restype = ct.c_int lib.bpf_attach_kprobe.argtypes = [ct.c_int, ct.c_int, ct.c_char_p, ct.c_char_p, ct.c_ulonglong, ct.c_int] lib.bpf_detach_kprobe.restype = ct.c_int lib.bpf_detach_kprobe.argtypes = [ct.c_char_p] lib.bpf_attach_uprobe.restype = ct.c_int lib.bpf_attach_uprobe.argtypes = [ct.c_int, ct.c_int, ct.c_char_p, ct.c_char_p, ct.c_ulonglong, ct.c_int] lib.bpf_detach_uprobe.restype = ct.c_int lib.bpf_detach_uprobe.argtypes = [ct.c_char_p] lib.bpf_attach_tracepoint.restype = ct.c_int lib.bpf_attach_tracepoint.argtypes = [ct.c_int, ct.c_char_p, ct.c_char_p] lib.bpf_detach_tracepoint.restype = ct.c_int lib.bpf_detach_tracepoint.argtypes = [ct.c_char_p, ct.c_char_p] lib.bpf_attach_raw_tracepoint.restype = ct.c_int lib.bpf_attach_raw_tracepoint.argtypes = [ct.c_int, ct.c_char_p] lib.bpf_open_perf_buffer.restype = ct.c_void_p lib.bpf_open_perf_buffer.argtypes = [_RAW_CB_TYPE, _LOST_CB_TYPE, ct.py_object, ct.c_int, ct.c_int, ct.c_int] lib.bpf_open_perf_event.restype = ct.c_int lib.bpf_open_perf_event.argtypes = [ct.c_uint, ct.c_ulonglong, ct.c_int, ct.c_int] lib.perf_reader_poll.restype = ct.c_int lib.perf_reader_poll.argtypes = [ct.c_int, ct.POINTER(ct.c_void_p), ct.c_int] lib.perf_reader_free.restype = None lib.perf_reader_free.argtypes = [ct.c_void_p] lib.perf_reader_fd.restype = int lib.perf_reader_fd.argtypes = [ct.c_void_p] lib.bpf_attach_xdp.restype = ct.c_int lib.bpf_attach_xdp.argtypes = [ct.c_char_p, ct.c_int, ct.c_uint] lib.bpf_attach_perf_event.restype = ct.c_int lib.bpf_attach_perf_event.argtype = [ct.c_int, ct.c_uint, ct.c_uint, ct.c_ulonglong, ct.c_ulonglong, ct.c_int, ct.c_int, ct.c_int] lib.bpf_close_perf_event_fd.restype = ct.c_int lib.bpf_close_perf_event_fd.argtype = [ct.c_int] # bcc symbol helpers class bcc_symbol(ct.Structure): _fields_ = [ ('name', ct.c_char_p), ('demangle_name', ct.c_char_p), ('module', ct.POINTER(ct.c_char)), ('offset', ct.c_ulonglong), ] class bcc_ip_offset_union(ct.Union): _fields_ = [ ('offset', ct.c_uint64), ('ip', ct.c_uint64) ] class bcc_stacktrace_build_id(ct.Structure): _fields_ = [ ('status', ct.c_uint32), ('build_id',ct.c_ubyte*20), ('u',bcc_ip_offset_union) ] class bcc_symbol_option(ct.Structure): _fields_ = [ ('use_debug_file', ct.c_int), ('check_debug_file_crc', ct.c_int), ('use_symbol_type', ct.c_uint), ] lib.bcc_procutils_which_so.restype = ct.POINTER(ct.c_char) lib.bcc_procutils_which_so.argtypes = [ct.c_char_p, ct.c_int] lib.bcc_procutils_free.restype = None lib.bcc_procutils_free.argtypes = [ct.c_void_p] lib.bcc_procutils_language.restype = ct.POINTER(ct.c_char) lib.bcc_procutils_language.argtypes = [ct.c_int] lib.bcc_resolve_symname.restype = ct.c_int lib.bcc_resolve_symname.argtypes = [ ct.c_char_p, ct.c_char_p, ct.c_ulonglong, ct.c_int, ct.POINTER(bcc_symbol_option), ct.POINTER(bcc_symbol)] _SYM_CB_TYPE = ct.CFUNCTYPE(ct.c_int, ct.c_char_p, ct.c_ulonglong) lib.bcc_foreach_function_symbol.restype = ct.c_int lib.bcc_foreach_function_symbol.argtypes = [ct.c_char_p, _SYM_CB_TYPE] lib.bcc_symcache_new.restype = ct.c_void_p lib.bcc_symcache_new.argtypes = [ct.c_int, ct.POINTER(bcc_symbol_option)] lib.bcc_free_symcache.restype = ct.c_void_p lib.bcc_free_symcache.argtypes = [ct.c_void_p, ct.c_int] lib.bcc_buildsymcache_new.restype = ct.c_void_p lib.bcc_buildsymcache_new.argtypes = None lib.bcc_free_buildsymcache.restype = None lib.bcc_free_buildsymcache.argtypes = [ct.c_void_p] lib.bcc_buildsymcache_add_module.restype = ct.c_int lib.bcc_buildsymcache_add_module.argtypes = [ct.c_void_p, ct.c_char_p] lib.bcc_buildsymcache_resolve.restype = ct.c_int lib.bcc_buildsymcache_resolve.argtypes = [ct.c_void_p, ct.POINTER(bcc_stacktrace_build_id), ct.POINTER(bcc_symbol)] lib.bcc_symbol_free_demangle_name.restype = ct.c_void_p lib.bcc_symbol_free_demangle_name.argtypes = [ct.POINTER(bcc_symbol)] lib.bcc_symcache_resolve.restype = ct.c_int lib.bcc_symcache_resolve.argtypes = [ct.c_void_p, ct.c_ulonglong, ct.POINTER(bcc_symbol)] lib.bcc_symcache_resolve_no_demangle.restype = ct.c_int lib.bcc_symcache_resolve_no_demangle.argtypes = [ct.c_void_p, ct.c_ulonglong, ct.POINTER(bcc_symbol)] lib.bcc_symcache_resolve_name.restype = ct.c_int lib.bcc_symcache_resolve_name.argtypes = [ ct.c_void_p, ct.c_char_p, ct.c_char_p, ct.POINTER(ct.c_ulonglong)] lib.bcc_symcache_refresh.restype = None lib.bcc_symcache_refresh.argtypes = [ct.c_void_p] lib.bcc_free_memory.restype = ct.c_int lib.bcc_free_memory.argtypes = None lib.bcc_usdt_new_frompid.restype = ct.c_void_p lib.bcc_usdt_new_frompid.argtypes = [ct.c_int, ct.c_char_p] lib.bcc_usdt_new_frompath.restype = ct.c_void_p lib.bcc_usdt_new_frompath.argtypes = [ct.c_char_p] lib.bcc_usdt_close.restype = None lib.bcc_usdt_close.argtypes = [ct.c_void_p] lib.bcc_usdt_enable_probe.restype = ct.c_int lib.bcc_usdt_enable_probe.argtypes = [ct.c_void_p, ct.c_char_p, ct.c_char_p] lib.bcc_usdt_genargs.restype = ct.c_char_p lib.bcc_usdt_genargs.argtypes = [ct.POINTER(ct.c_void_p), ct.c_int] lib.bcc_usdt_get_probe_argctype.restype = ct.c_char_p lib.bcc_usdt_get_probe_argctype.argtypes = [ct.c_void_p, ct.c_char_p, ct.c_int] class bcc_usdt(ct.Structure): _fields_ = [ ('provider', ct.c_char_p), ('name', ct.c_char_p), ('bin_path', ct.c_char_p), ('semaphore', ct.c_ulonglong), ('num_locations', ct.c_int), ('num_arguments', ct.c_int), ] class bcc_usdt_location(ct.Structure): _fields_ = [ ('address', ct.c_ulonglong), ('bin_path', ct.c_char_p), ] class BCC_USDT_ARGUMENT_FLAGS(object): NONE = 0x0 CONSTANT = 0x1 DEREF_OFFSET = 0x2 DEREF_IDENT = 0x4 BASE_REGISTER_NAME = 0x8 INDEX_REGISTER_NAME = 0x10 SCALE = 0x20 class bcc_usdt_argument(ct.Structure): _fields_ = [ ('size', ct.c_int), ('valid', ct.c_int), ('constant', ct.c_int), ('deref_offset', ct.c_int), ('deref_ident', ct.c_char_p), ('base_register_name', ct.c_char_p), ('index_register_name', ct.c_char_p), ('scale', ct.c_int) ] _USDT_CB = ct.CFUNCTYPE(None, ct.POINTER(bcc_usdt)) lib.bcc_usdt_foreach.restype = None lib.bcc_usdt_foreach.argtypes = [ct.c_void_p, _USDT_CB] lib.bcc_usdt_get_location.restype = ct.c_int lib.bcc_usdt_get_location.argtypes = [ct.c_void_p, ct.c_char_p, ct.c_char_p, ct.c_int, ct.POINTER(bcc_usdt_location)] lib.bcc_usdt_get_argument.restype = ct.c_int lib.bcc_usdt_get_argument.argtypes = [ct.c_void_p, ct.c_char_p, ct.c_char_p, ct.c_int, ct.c_int, ct.POINTER(bcc_usdt_argument)] _USDT_PROBE_CB = ct.CFUNCTYPE(None, ct.c_char_p, ct.c_char_p, ct.c_ulonglong, ct.c_int) lib.bcc_usdt_foreach_uprobe.restype = None lib.bcc_usdt_foreach_uprobe.argtypes = [ct.c_void_p, _USDT_PROBE_CB] bpfcc-0.12.0/src/python/bcc/perf.py000066400000000000000000000102651357404205000170200ustar00rootroot00000000000000# Copyright 2016 Sasha Goldshtein # # 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. import ctypes as ct import os from .utils import get_online_cpus class Perf(object): class perf_event_attr(ct.Structure): _fields_ = [ ('type', ct.c_uint), ('size', ct.c_uint), ('config', ct.c_ulong), ('sample_period', ct.c_ulong), ('sample_type', ct.c_ulong), ('read_format', ct.c_ulong), ('flags', ct.c_ulong), ('wakeup_events', ct.c_uint), ('IGNORE3', ct.c_uint), ('IGNORE4', ct.c_ulong), ('IGNORE5', ct.c_ulong), ('IGNORE6', ct.c_ulong), ('IGNORE7', ct.c_uint), ('IGNORE8', ct.c_int), ('IGNORE9', ct.c_ulong), ('IGNORE10', ct.c_uint), ('IGNORE11', ct.c_uint) ] # x86 specific, from arch/x86/include/generated/uapi/asm/unistd_64.h NR_PERF_EVENT_OPEN = 298 # # Selected constants from include/uapi/linux/perf_event.h. # Values copied during Linux 4.7 series. # # perf_type_id PERF_TYPE_HARDWARE = 0 PERF_TYPE_SOFTWARE = 1 PERF_TYPE_TRACEPOINT = 2 PERF_TYPE_HW_CACHE = 3 # perf_event_sample_format PERF_SAMPLE_RAW = 1024 # it's a u32; could also try zero args # perf_event_attr PERF_ATTR_FLAG_FREQ = 1024 # perf_event.h PERF_FLAG_FD_CLOEXEC = 8 PERF_EVENT_IOC_SET_FILTER = 1074275334 PERF_EVENT_IOC_ENABLE = 9216 # fetch syscall routines libc = ct.CDLL('libc.so.6', use_errno=True) syscall = libc.syscall # not declaring vararg types ioctl = libc.ioctl # not declaring vararg types @staticmethod def _open_for_cpu(cpu, attr): pfd = Perf.syscall(Perf.NR_PERF_EVENT_OPEN, ct.byref(attr), attr.pid, cpu, -1, Perf.PERF_FLAG_FD_CLOEXEC) if pfd < 0: errno_ = ct.get_errno() raise OSError(errno_, os.strerror(errno_)) if attr.type == Perf.PERF_TYPE_TRACEPOINT: if Perf.ioctl(pfd, Perf.PERF_EVENT_IOC_SET_FILTER, "common_pid == -17") < 0: errno_ = ct.get_errno() raise OSError(errno_, os.strerror(errno_)) # we don't setup the perf ring buffers, as we won't read them if Perf.ioctl(pfd, Perf.PERF_EVENT_IOC_ENABLE, 0) < 0: errno_ = ct.get_errno() raise OSError(errno_, os.strerror(errno_)) @staticmethod def perf_event_open(tpoint_id, pid=-1, ptype=PERF_TYPE_TRACEPOINT, freq=0): attr = Perf.perf_event_attr() attr.config = tpoint_id attr.pid = pid attr.type = ptype attr.sample_type = Perf.PERF_SAMPLE_RAW if freq > 0: # setup sampling attr.flags = Perf.PERF_ATTR_FLAG_FREQ # no mmap or comm attr.sample_period = freq else: attr.sample_period = 1 attr.wakeup_events = 9999999 # don't wake up for cpu in get_online_cpus(): Perf._open_for_cpu(cpu, attr) bpfcc-0.12.0/src/python/bcc/syscall.py000066400000000000000000000226701357404205000175410ustar00rootroot00000000000000# Copyright 2017 Sasha Goldshtein # Copyright 2018 Red Hat, Inc. # # 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. """syscall.py contains functions useful for mapping between syscall names and numbers""" import subprocess import platform # # Syscall table for Linux x86_64, not very recent. # Automatically generated from strace/linux/x86_64/syscallent.h using the # following command: # # cat syscallent.h | awk -F, '{ gsub(/[ \t"}]/, "", $4); # gsub(/[\[\] \t{]/, "", $1); split($1, a, "="); # print " "a[1]": b\""$4"\","; } # BEGIN { print "syscalls = {" } # END { print "}" '} syscalls = { 0: b"read", 1: b"write", 2: b"open", 3: b"close", 4: b"stat", 5: b"fstat", 6: b"lstat", 7: b"poll", 8: b"lseek", 9: b"mmap", 10: b"mprotect", 11: b"munmap", 12: b"brk", 13: b"rt_sigaction", 14: b"rt_sigprocmask", 15: b"rt_sigreturn", 16: b"ioctl", 17: b"pread64", 18: b"pwrite64", 19: b"readv", 20: b"writev", 21: b"access", 22: b"pipe", 23: b"select", 24: b"sched_yield", 25: b"mremap", 26: b"msync", 27: b"mincore", 28: b"madvise", 29: b"shmget", 30: b"shmat", 31: b"shmctl", 32: b"dup", 33: b"dup2", 34: b"pause", 35: b"nanosleep", 36: b"getitimer", 37: b"alarm", 38: b"setitimer", 39: b"getpid", 40: b"sendfile", 41: b"socket", 42: b"connect", 43: b"accept", 44: b"sendto", 45: b"recvfrom", 46: b"sendmsg", 47: b"recvmsg", 48: b"shutdown", 49: b"bind", 50: b"listen", 51: b"getsockname", 52: b"getpeername", 53: b"socketpair", 54: b"setsockopt", 55: b"getsockopt", 56: b"clone", 57: b"fork", 58: b"vfork", 59: b"execve", 60: b"exit", 61: b"wait4", 62: b"kill", 63: b"uname", 64: b"semget", 65: b"semop", 66: b"semctl", 67: b"shmdt", 68: b"msgget", 69: b"msgsnd", 70: b"msgrcv", 71: b"msgctl", 72: b"fcntl", 73: b"flock", 74: b"fsync", 75: b"fdatasync", 76: b"truncate", 77: b"ftruncate", 78: b"getdents", 79: b"getcwd", 80: b"chdir", 81: b"fchdir", 82: b"rename", 83: b"mkdir", 84: b"rmdir", 85: b"creat", 86: b"link", 87: b"unlink", 88: b"symlink", 89: b"readlink", 90: b"chmod", 91: b"fchmod", 92: b"chown", 93: b"fchown", 94: b"lchown", 95: b"umask", 96: b"gettimeofday", 97: b"getrlimit", 98: b"getrusage", 99: b"sysinfo", 100: b"times", 101: b"ptrace", 102: b"getuid", 103: b"syslog", 104: b"getgid", 105: b"setuid", 106: b"setgid", 107: b"geteuid", 108: b"getegid", 109: b"setpgid", 110: b"getppid", 111: b"getpgrp", 112: b"setsid", 113: b"setreuid", 114: b"setregid", 115: b"getgroups", 116: b"setgroups", 117: b"setresuid", 118: b"getresuid", 119: b"setresgid", 120: b"getresgid", 121: b"getpgid", 122: b"setfsuid", 123: b"setfsgid", 124: b"getsid", 125: b"capget", 126: b"capset", 127: b"rt_sigpending", 128: b"rt_sigtimedwait", 129: b"rt_sigqueueinfo", 130: b"rt_sigsuspend", 131: b"sigaltstack", 132: b"utime", 133: b"mknod", 134: b"uselib", 135: b"personality", 136: b"ustat", 137: b"statfs", 138: b"fstatfs", 139: b"sysfs", 140: b"getpriority", 141: b"setpriority", 142: b"sched_setparam", 143: b"sched_getparam", 144: b"sched_setscheduler", 145: b"sched_getscheduler", 146: b"sched_get_priority_max", 147: b"sched_get_priority_min", 148: b"sched_rr_get_interval", 149: b"mlock", 150: b"munlock", 151: b"mlockall", 152: b"munlockall", 153: b"vhangup", 154: b"modify_ldt", 155: b"pivot_root", 156: b"_sysctl", 157: b"prctl", 158: b"arch_prctl", 159: b"adjtimex", 160: b"setrlimit", 161: b"chroot", 162: b"sync", 163: b"acct", 164: b"settimeofday", 165: b"mount", 166: b"umount2", 167: b"swapon", 168: b"swapoff", 169: b"reboot", 170: b"sethostname", 171: b"setdomainname", 172: b"iopl", 173: b"ioperm", 174: b"create_module", 175: b"init_module", 176: b"delete_module", 177: b"get_kernel_syms", 178: b"query_module", 179: b"quotactl", 180: b"nfsservctl", 181: b"getpmsg", 182: b"putpmsg", 183: b"afs_syscall", 184: b"tuxcall", 185: b"security", 186: b"gettid", 187: b"readahead", 188: b"setxattr", 189: b"lsetxattr", 190: b"fsetxattr", 191: b"getxattr", 192: b"lgetxattr", 193: b"fgetxattr", 194: b"listxattr", 195: b"llistxattr", 196: b"flistxattr", 197: b"removexattr", 198: b"lremovexattr", 199: b"fremovexattr", 200: b"tkill", 201: b"time", 202: b"futex", 203: b"sched_setaffinity", 204: b"sched_getaffinity", 205: b"set_thread_area", 206: b"io_setup", 207: b"io_destroy", 208: b"io_getevents", 209: b"io_submit", 210: b"io_cancel", 211: b"get_thread_area", 212: b"lookup_dcookie", 213: b"epoll_create", 214: b"epoll_ctl_old", 215: b"epoll_wait_old", 216: b"remap_file_pages", 217: b"getdents64", 218: b"set_tid_address", 219: b"restart_syscall", 220: b"semtimedop", 221: b"fadvise64", 222: b"timer_create", 223: b"timer_settime", 224: b"timer_gettime", 225: b"timer_getoverrun", 226: b"timer_delete", 227: b"clock_settime", 228: b"clock_gettime", 229: b"clock_getres", 230: b"clock_nanosleep", 231: b"exit_group", 232: b"epoll_wait", 233: b"epoll_ctl", 234: b"tgkill", 235: b"utimes", 236: b"vserver", 237: b"mbind", 238: b"set_mempolicy", 239: b"get_mempolicy", 240: b"mq_open", 241: b"mq_unlink", 242: b"mq_timedsend", 243: b"mq_timedreceive", 244: b"mq_notify", 245: b"mq_getsetattr", 246: b"kexec_load", 247: b"waitid", 248: b"add_key", 249: b"request_key", 250: b"keyctl", 251: b"ioprio_set", 252: b"ioprio_get", 253: b"inotify_init", 254: b"inotify_add_watch", 255: b"inotify_rm_watch", 256: b"migrate_pages", 257: b"openat", 258: b"mkdirat", 259: b"mknodat", 260: b"fchownat", 261: b"futimesat", 262: b"newfstatat", 263: b"unlinkat", 264: b"renameat", 265: b"linkat", 266: b"symlinkat", 267: b"readlinkat", 268: b"fchmodat", 269: b"faccessat", 270: b"pselect6", 271: b"ppoll", 272: b"unshare", 273: b"set_robust_list", 274: b"get_robust_list", 275: b"splice", 276: b"tee", 277: b"sync_file_range", 278: b"vmsplice", 279: b"move_pages", 280: b"utimensat", 281: b"epoll_pwait", 282: b"signalfd", 283: b"timerfd_create", 284: b"eventfd", 285: b"fallocate", 286: b"timerfd_settime", 287: b"timerfd_gettime", 288: b"accept4", 289: b"signalfd4", 290: b"eventfd2", 291: b"epoll_create1", 292: b"dup3", 293: b"pipe2", 294: b"inotify_init1", 295: b"preadv", 296: b"pwritev", 297: b"rt_tgsigqueueinfo", 298: b"perf_event_open", 299: b"recvmmsg", 300: b"fanotify_init", 301: b"fanotify_mark", 302: b"prlimit64", 303: b"name_to_handle_at", 304: b"open_by_handle_at", 305: b"clock_adjtime", 306: b"syncfs", 307: b"sendmmsg", 308: b"setns", 309: b"getcpu", 310: b"process_vm_readv", 311: b"process_vm_writev", 312: b"kcmp", 313: b"finit_module", 314: b"sched_setattr", 315: b"sched_getattr", 316: b"renameat2", 317: b"seccomp", 318: b"getrandom", 319: b"memfd_create", 320: b"kexec_file_load", 321: b"bpf", 322: b"execveat", 323: b"userfaultfd", 324: b"membarrier", 325: b"mlock2", 326: b"copy_file_range", 327: b"preadv2", 328: b"pwritev2", 329: b"pkey_mprotect", 330: b"pkey_alloc", 331: b"pkey_free", 332: b"statx", 333: b"io_pgetevents", 334: b"rseq", } # Try to use ausyscall if it is available, because it can give us an up-to-date # list of syscalls for various architectures, rather than the x86-64 hardcoded # list above. def _parse_syscall(line): parts = line.split() return (int(parts[0]), parts[1].strip()) try: # Skip the first line, which is a header. The rest of the lines are simply # SYSCALL_NUM\tSYSCALL_NAME pairs. out = subprocess.check_output(['ausyscall', '--dump'], stderr=subprocess.STDOUT) # remove the first line of expected output out = out.split(b'\n',1)[1] syscalls = dict(map(_parse_syscall, out.strip().split(b'\n'))) except Exception as e: if platform.machine() == "x86_64": pass else: raise Exception("ausyscall: command not found") def syscall_name(syscall_num): """Return the syscall name for the particular syscall number.""" return syscalls.get(syscall_num, b"[unknown: %d]" % syscall_num) bpfcc-0.12.0/src/python/bcc/table.py000066400000000000000000001007471357404205000171600ustar00rootroot00000000000000# Copyright 2015 PLUMgrid # # 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. from collections import MutableMapping import ctypes as ct from functools import reduce import multiprocessing import os import errno import re from .libbcc import lib, _RAW_CB_TYPE, _LOST_CB_TYPE from .perf import Perf from .utils import get_online_cpus from .utils import get_possible_cpus from subprocess import check_output BPF_MAP_TYPE_HASH = 1 BPF_MAP_TYPE_ARRAY = 2 BPF_MAP_TYPE_PROG_ARRAY = 3 BPF_MAP_TYPE_PERF_EVENT_ARRAY = 4 BPF_MAP_TYPE_PERCPU_HASH = 5 BPF_MAP_TYPE_PERCPU_ARRAY = 6 BPF_MAP_TYPE_STACK_TRACE = 7 BPF_MAP_TYPE_CGROUP_ARRAY = 8 BPF_MAP_TYPE_LRU_HASH = 9 BPF_MAP_TYPE_LRU_PERCPU_HASH = 10 BPF_MAP_TYPE_LPM_TRIE = 11 BPF_MAP_TYPE_ARRAY_OF_MAPS = 12 BPF_MAP_TYPE_HASH_OF_MAPS = 13 BPF_MAP_TYPE_DEVMAP = 14 BPF_MAP_TYPE_SOCKMAP = 15 BPF_MAP_TYPE_CPUMAP = 16 BPF_MAP_TYPE_XSKMAP = 17 BPF_MAP_TYPE_SOCKHASH = 18 map_type_name = {BPF_MAP_TYPE_HASH: "HASH", BPF_MAP_TYPE_ARRAY: "ARRAY", BPF_MAP_TYPE_PROG_ARRAY: "PROG_ARRAY", BPF_MAP_TYPE_PERF_EVENT_ARRAY: "PERF_EVENT_ARRAY", BPF_MAP_TYPE_PERCPU_HASH: "PERCPU_HASH", BPF_MAP_TYPE_PERCPU_ARRAY: "PERCPU_ARRAY", BPF_MAP_TYPE_STACK_TRACE: "STACK_TRACE", BPF_MAP_TYPE_CGROUP_ARRAY: "CGROUP_ARRAY", BPF_MAP_TYPE_LRU_HASH: "LRU_HASH", BPF_MAP_TYPE_LRU_PERCPU_HASH: "LRU_PERCPU_HASH", BPF_MAP_TYPE_LPM_TRIE: "LPM_TRIE", BPF_MAP_TYPE_ARRAY_OF_MAPS: "ARRAY_OF_MAPS", BPF_MAP_TYPE_HASH_OF_MAPS: "HASH_OF_MAPS", BPF_MAP_TYPE_DEVMAP: "DEVMAP", BPF_MAP_TYPE_SOCKMAP: "SOCKMAP", BPF_MAP_TYPE_CPUMAP: "CPUMAP", BPF_MAP_TYPE_XSKMAP: "XSKMAP", BPF_MAP_TYPE_SOCKHASH: "SOCKHASH",} stars_max = 40 log2_index_max = 65 linear_index_max = 1025 # helper functions, consider moving these to a utils module def _stars(val, val_max, width): i = 0 text = "" while (1): if (i > (width * val / val_max) - 1) or (i > width - 1): break text += "*" i += 1 if val > val_max: text = text[:-1] + "+" return text def _print_log2_hist(vals, val_type, strip_leading_zero): global stars_max log2_dist_max = 64 idx_max = -1 val_max = 0 for i, v in enumerate(vals): if v > 0: idx_max = i if v > val_max: val_max = v if idx_max <= 32: header = " %-19s : count distribution" body = "%10d -> %-10d : %-8d |%-*s|" stars = stars_max else: header = " %-29s : count distribution" body = "%20d -> %-20d : %-8d |%-*s|" stars = int(stars_max / 2) if idx_max > 0: print(header % val_type) for i in range(1, idx_max + 1): low = (1 << i) >> 1 high = (1 << i) - 1 if (low == high): low -= 1 val = vals[i] if strip_leading_zero: if val: print(body % (low, high, val, stars, _stars(val, val_max, stars))) strip_leading_zero = False else: print(body % (low, high, val, stars, _stars(val, val_max, stars))) def _print_linear_hist(vals, val_type): global stars_max log2_dist_max = 64 idx_max = -1 val_max = 0 for i, v in enumerate(vals): if v > 0: idx_max = i if v > val_max: val_max = v header = " %-13s : count distribution" body = " %-10d : %-8d |%-*s|" stars = stars_max if idx_max >= 0: print(header % val_type); for i in range(0, idx_max + 1): val = vals[i] print(body % (i, val, stars, _stars(val, val_max, stars))) def get_table_type_name(ttype): try: return map_type_name[ttype] except KeyError: return "" def Table(bpf, map_id, map_fd, keytype, leaftype, name, **kwargs): """Table(bpf, map_id, map_fd, keytype, leaftype, **kwargs) Create a python object out of a reference to a bpf table handle""" ttype = lib.bpf_table_type_id(bpf.module, map_id) t = None if ttype == BPF_MAP_TYPE_HASH: t = HashTable(bpf, map_id, map_fd, keytype, leaftype) elif ttype == BPF_MAP_TYPE_ARRAY: t = Array(bpf, map_id, map_fd, keytype, leaftype) elif ttype == BPF_MAP_TYPE_PROG_ARRAY: t = ProgArray(bpf, map_id, map_fd, keytype, leaftype) elif ttype == BPF_MAP_TYPE_PERF_EVENT_ARRAY: t = PerfEventArray(bpf, map_id, map_fd, keytype, leaftype, name) elif ttype == BPF_MAP_TYPE_PERCPU_HASH: t = PerCpuHash(bpf, map_id, map_fd, keytype, leaftype, **kwargs) elif ttype == BPF_MAP_TYPE_PERCPU_ARRAY: t = PerCpuArray(bpf, map_id, map_fd, keytype, leaftype, **kwargs) elif ttype == BPF_MAP_TYPE_LPM_TRIE: t = LpmTrie(bpf, map_id, map_fd, keytype, leaftype) elif ttype == BPF_MAP_TYPE_STACK_TRACE: t = StackTrace(bpf, map_id, map_fd, keytype, leaftype) elif ttype == BPF_MAP_TYPE_LRU_HASH: t = LruHash(bpf, map_id, map_fd, keytype, leaftype) elif ttype == BPF_MAP_TYPE_LRU_PERCPU_HASH: t = LruPerCpuHash(bpf, map_id, map_fd, keytype, leaftype) elif ttype == BPF_MAP_TYPE_CGROUP_ARRAY: t = CgroupArray(bpf, map_id, map_fd, keytype, leaftype) elif ttype == BPF_MAP_TYPE_DEVMAP: t = DevMap(bpf, map_id, map_fd, keytype, leaftype) elif ttype == BPF_MAP_TYPE_CPUMAP: t = CpuMap(bpf, map_id, map_fd, keytype, leaftype) elif ttype == BPF_MAP_TYPE_ARRAY_OF_MAPS: t = MapInMapArray(bpf, map_id, map_fd, keytype, leaftype) elif ttype == BPF_MAP_TYPE_HASH_OF_MAPS: t = MapInMapHash(bpf, map_id, map_fd, keytype, leaftype) if t == None: raise Exception("Unknown table type %d" % ttype) return t class TableBase(MutableMapping): def __init__(self, bpf, map_id, map_fd, keytype, leaftype, name=None): self.bpf = bpf self.map_id = map_id self.map_fd = map_fd self.Key = keytype self.Leaf = leaftype self.ttype = lib.bpf_table_type_id(self.bpf.module, self.map_id) self.flags = lib.bpf_table_flags_id(self.bpf.module, self.map_id) self._cbs = {} self._name = name def get_fd(self): return self.map_fd def key_sprintf(self, key): buf = ct.create_string_buffer(ct.sizeof(self.Key) * 8) res = lib.bpf_table_key_snprintf(self.bpf.module, self.map_id, buf, len(buf), ct.byref(key)) if res < 0: raise Exception("Could not printf key") return buf.value def leaf_sprintf(self, leaf): buf = ct.create_string_buffer(ct.sizeof(self.Leaf) * 8) res = lib.bpf_table_leaf_snprintf(self.bpf.module, self.map_id, buf, len(buf), ct.byref(leaf)) if res < 0: raise Exception("Could not printf leaf") return buf.value def key_scanf(self, key_str): key = self.Key() res = lib.bpf_table_key_sscanf(self.bpf.module, self.map_id, key_str, ct.byref(key)) if res < 0: raise Exception("Could not scanf key") return key def leaf_scanf(self, leaf_str): leaf = self.Leaf() res = lib.bpf_table_leaf_sscanf(self.bpf.module, self.map_id, leaf_str, ct.byref(leaf)) if res < 0: raise Exception("Could not scanf leaf") return leaf def __getitem__(self, key): leaf = self.Leaf() res = lib.bpf_lookup_elem(self.map_fd, ct.byref(key), ct.byref(leaf)) if res < 0: raise KeyError return leaf def __setitem__(self, key, leaf): res = lib.bpf_update_elem(self.map_fd, ct.byref(key), ct.byref(leaf), 0) if res < 0: errstr = os.strerror(ct.get_errno()) raise Exception("Could not update table: %s" % errstr) def __delitem__(self, key): res = lib.bpf_delete_elem(self.map_fd, ct.byref(key)) if res < 0: raise KeyError # override the MutableMapping's implementation of these since they # don't handle KeyError nicely def itervalues(self): for key in self: # a map entry may be deleted in between discovering the key and # fetching the value, suppress such errors try: yield self[key] except KeyError: pass def iteritems(self): for key in self: try: yield (key, self[key]) except KeyError: pass def items(self): return [item for item in self.iteritems()] def values(self): return [value for value in self.itervalues()] def clear(self): # default clear uses popitem, which can race with the bpf prog for k in self.keys(): self.__delitem__(k) def zero(self): # Even though this is not very efficient, we grab the entire list of # keys before enumerating it. This helps avoid a potential race where # the leaf assignment changes a hash table bucket that is being # enumerated by the same loop, and may lead to a hang. for k in list(self.keys()): self[k] = self.Leaf() def __iter__(self): return TableBase.Iter(self) def iter(self): return self.__iter__() def keys(self): return self.__iter__() class Iter(object): def __init__(self, table): self.table = table self.key = None def __iter__(self): return self def __next__(self): return self.next() def next(self): self.key = self.table.next(self.key) return self.key def next(self, key): next_key = self.Key() if key is None: res = lib.bpf_get_first_key(self.map_fd, ct.byref(next_key), ct.sizeof(self.Key)) else: res = lib.bpf_get_next_key(self.map_fd, ct.byref(key), ct.byref(next_key)) if res < 0: raise StopIteration() return next_key def print_log2_hist(self, val_type="value", section_header="Bucket ptr", section_print_fn=None, bucket_fn=None, strip_leading_zero=None, bucket_sort_fn=None): """print_log2_hist(val_type="value", section_header="Bucket ptr", section_print_fn=None, bucket_fn=None, strip_leading_zero=None, bucket_sort_fn=None): Prints a table as a log2 histogram. The table must be stored as log2. The val_type argument is optional, and is a column header. If the histogram has a secondary key, multiple tables will print and section_header can be used as a header description for each. If section_print_fn is not None, it will be passed the bucket value to format into a string as it sees fit. If bucket_fn is not None, it will be used to produce a bucket value for the histogram keys. If the value of strip_leading_zero is not False, prints a histogram that is omitted leading zeros from the beginning. If bucket_sort_fn is not None, it will be used to sort the buckets before iterating them, and it is useful when there are multiple fields in the secondary key. The maximum index allowed is log2_index_max (65), which will accommodate any 64-bit integer in the histogram. """ if isinstance(self.Key(), ct.Structure): tmp = {} f1 = self.Key._fields_[0][0] f2 = self.Key._fields_[1][0] # The above code assumes that self.Key._fields_[1][0] holds the # slot. But a padding member may have been inserted here, which # breaks the assumption and leads to chaos. # TODO: this is a quick fix. Fixing/working around in the BCC # internal library is the right thing to do. if f2 == '__pad_1' and len(self.Key._fields_) == 3: f2 = self.Key._fields_[2][0] for k, v in self.items(): bucket = getattr(k, f1) if bucket_fn: bucket = bucket_fn(bucket) vals = tmp[bucket] = tmp.get(bucket, [0] * log2_index_max) slot = getattr(k, f2) vals[slot] = v.value buckets = list(tmp.keys()) if bucket_sort_fn: buckets = bucket_sort_fn(buckets) for bucket in buckets: vals = tmp[bucket] if section_print_fn: print("\n%s = %s" % (section_header, section_print_fn(bucket))) else: print("\n%s = %r" % (section_header, bucket)) _print_log2_hist(vals, val_type, strip_leading_zero) else: vals = [0] * log2_index_max for k, v in self.items(): vals[k.value] = v.value _print_log2_hist(vals, val_type, strip_leading_zero) def print_linear_hist(self, val_type="value", section_header="Bucket ptr", section_print_fn=None, bucket_fn=None, bucket_sort_fn=None): """print_linear_hist(val_type="value", section_header="Bucket ptr", section_print_fn=None, bucket_fn=None, bucket_sort_fn=None) Prints a table as a linear histogram. This is intended to span integer ranges, eg, from 0 to 100. The val_type argument is optional, and is a column header. If the histogram has a secondary key, multiple tables will print and section_header can be used as a header description for each. If section_print_fn is not None, it will be passed the bucket value to format into a string as it sees fit. If bucket_fn is not None, it will be used to produce a bucket value for the histogram keys. If bucket_sort_fn is not None, it will be used to sort the buckets before iterating them, and it is useful when there are multiple fields in the secondary key. The maximum index allowed is linear_index_max (1025), which is hoped to be sufficient for integer ranges spanned. """ if isinstance(self.Key(), ct.Structure): tmp = {} f1 = self.Key._fields_[0][0] f2 = self.Key._fields_[1][0] for k, v in self.items(): bucket = getattr(k, f1) if bucket_fn: bucket = bucket_fn(bucket) vals = tmp[bucket] = tmp.get(bucket, [0] * linear_index_max) slot = getattr(k, f2) vals[slot] = v.value buckets = tmp.keys() if bucket_sort_fn: buckets = bucket_sort_fn(buckets) for bucket in buckets: vals = tmp[bucket] if section_print_fn: print("\n%s = %s" % (section_header, section_print_fn(bucket))) else: print("\n%s = %r" % (section_header, bucket)) _print_linear_hist(vals, val_type) else: vals = [0] * linear_index_max for k, v in self.items(): try: vals[k.value] = v.value except IndexError: # Improve error text. If the limit proves a nusiance, this # function be rewritten to avoid having one. raise IndexError(("Index in print_linear_hist() of %d " + "exceeds max of %d.") % (k.value, linear_index_max)) _print_linear_hist(vals, val_type) class HashTable(TableBase): def __init__(self, *args, **kwargs): super(HashTable, self).__init__(*args, **kwargs) def __len__(self): i = 0 for k in self: i += 1 return i class LruHash(HashTable): def __init__(self, *args, **kwargs): super(LruHash, self).__init__(*args, **kwargs) class ArrayBase(TableBase): def __init__(self, *args, **kwargs): super(ArrayBase, self).__init__(*args, **kwargs) self.max_entries = int(lib.bpf_table_max_entries_id(self.bpf.module, self.map_id)) def _normalize_key(self, key): if isinstance(key, int): if key < 0: key = len(self) + key key = self.Key(key) if not isinstance(key, ct._SimpleCData): raise IndexError("Array index must be an integer type") if key.value >= len(self): raise IndexError("Array index out of range") return key def __len__(self): return self.max_entries def __getitem__(self, key): key = self._normalize_key(key) return super(ArrayBase, self).__getitem__(key) def __setitem__(self, key, leaf): key = self._normalize_key(key) super(ArrayBase, self).__setitem__(key, leaf) def __delitem__(self, key): key = self._normalize_key(key) super(ArrayBase, self).__delitem__(key) def clearitem(self, key): key = self._normalize_key(key) leaf = self.Leaf() res = lib.bpf_update_elem(self.map_fd, ct.byref(key), ct.byref(leaf), 0) if res < 0: raise Exception("Could not clear item") def __iter__(self): return ArrayBase.Iter(self, self.Key) class Iter(object): def __init__(self, table, keytype): self.Key = keytype self.table = table self.i = -1 def __iter__(self): return self def __next__(self): return self.next() def next(self): self.i += 1 if self.i == len(self.table): raise StopIteration() return self.Key(self.i) class Array(ArrayBase): def __init__(self, *args, **kwargs): super(Array, self).__init__(*args, **kwargs) def __delitem__(self, key): # Delete in Array type does not have an effect, so zero out instead self.clearitem(key) class ProgArray(ArrayBase): def __init__(self, *args, **kwargs): super(ProgArray, self).__init__(*args, **kwargs) def __setitem__(self, key, leaf): if isinstance(leaf, int): leaf = self.Leaf(leaf) if isinstance(leaf, self.bpf.Function): leaf = self.Leaf(leaf.fd) super(ProgArray, self).__setitem__(key, leaf) class FileDesc: def __init__(self, fd): if (fd is None) or (fd < 0): raise Exception("Invalid file descriptor") self.fd = fd def clean_up(self): if (self.fd is not None) and (self.fd >= 0): os.close(self.fd) self.fd = None def __del__(self): self.clean_up() def __enter__(self, *args, **kwargs): return self def __exit__(self, *args, **kwargs): self.clean_up() class CgroupArray(ArrayBase): def __init__(self, *args, **kwargs): super(CgroupArray, self).__init__(*args, **kwargs) def __setitem__(self, key, leaf): if isinstance(leaf, int): super(CgroupArray, self).__setitem__(key, self.Leaf(leaf)) elif isinstance(leaf, str): # TODO: Add os.O_CLOEXEC once we move to Python version >3.3 with FileDesc(os.open(leaf, os.O_RDONLY)) as f: super(CgroupArray, self).__setitem__(key, self.Leaf(f.fd)) else: raise Exception("Cgroup array key must be either FD or cgroup path") class PerfEventArray(ArrayBase): def __init__(self, *args, **kwargs): super(PerfEventArray, self).__init__(*args, **kwargs) self._open_key_fds = {} self._event_class = None def __del__(self): keys = list(self._open_key_fds.keys()) for key in keys: del self[key] def __delitem__(self, key): if key not in self._open_key_fds: return # Delete entry from the array super(PerfEventArray, self).__delitem__(key) key_id = (id(self), key) if key_id in self.bpf.perf_buffers: # The key is opened for perf ring buffer lib.perf_reader_free(self.bpf.perf_buffers[key_id]) del self.bpf.perf_buffers[key_id] del self._cbs[key] else: # The key is opened for perf event read lib.bpf_close_perf_event_fd(self._open_key_fds[key]) del self._open_key_fds[key] def _get_event_class(self): ct_mapping = { 'char' : ct.c_char, 's8' : ct.c_char, 'unsigned char' : ct.c_ubyte, 'u8' : ct.c_ubyte, 'u8 *' : ct.c_char_p, 'char *' : ct.c_char_p, 'short' : ct.c_short, 's16' : ct.c_short, 'unsigned short' : ct.c_ushort, 'u16' : ct.c_ushort, 'int' : ct.c_int, 's32' : ct.c_int, 'enum' : ct.c_int, 'unsigned int' : ct.c_uint, 'u32' : ct.c_uint, 'long' : ct.c_long, 'unsigned long' : ct.c_ulong, 'long long' : ct.c_longlong, 's64' : ct.c_longlong, 'unsigned long long': ct.c_ulonglong, 'u64' : ct.c_ulonglong, '__int128' : (ct.c_longlong * 2), 'unsigned __int128' : (ct.c_ulonglong * 2), 'void *' : ct.c_void_p } # handle array types e.g. "int [16] foo" array_type = re.compile(r"(.+) \[([0-9]+)\]$") fields = [] num_fields = lib.bpf_perf_event_fields(self.bpf.module, self._name) i = 0 while i < num_fields: field = lib.bpf_perf_event_field(self.bpf.module, self._name, i).decode() m = re.match(r"(.*)#(.*)", field) field_name = m.group(1) field_type = m.group(2) if re.match(r"enum .*", field_type): field_type = "enum" m = array_type.match(field_type) try: if m: fields.append((field_name, ct_mapping[m.group(1)] * int(m.group(2)))) else: fields.append((field_name, ct_mapping[field_type])) except KeyError: print("Type: '%s' not recognized. Please define the data with ctypes manually." % field_type) exit() i += 1 return type('', (ct.Structure,), {'_fields_': fields}) def event(self, data): """event(data) When ring buffers are opened to receive custom perf event, the underlying event data struct which is defined in C in the BPF program can be deduced via this function. This avoids redundant definitions in Python. """ if self._event_class == None: self._event_class = self._get_event_class() return ct.cast(data, ct.POINTER(self._event_class)).contents def open_perf_buffer(self, callback, page_cnt=8, lost_cb=None): """open_perf_buffers(callback) Opens a set of per-cpu ring buffer to receive custom perf event data from the bpf program. The callback will be invoked for each event submitted from the kernel, up to millions per second. Use page_cnt to change the size of the per-cpu ring buffer. The value must be a power of two and defaults to 8. """ if page_cnt & (page_cnt - 1) != 0: raise Exception("Perf buffer page_cnt must be a power of two") for i in get_online_cpus(): self._open_perf_buffer(i, callback, page_cnt, lost_cb) def _open_perf_buffer(self, cpu, callback, page_cnt, lost_cb): def raw_cb_(_, data, size): try: callback(cpu, data, size) except IOError as e: if e.errno == errno.EPIPE: exit() else: raise e def lost_cb_(_, lost): try: lost_cb(lost) except IOError as e: if e.errno == errno.EPIPE: exit() else: raise e fn = _RAW_CB_TYPE(raw_cb_) lost_fn = _LOST_CB_TYPE(lost_cb_) if lost_cb else ct.cast(None, _LOST_CB_TYPE) reader = lib.bpf_open_perf_buffer(fn, lost_fn, None, -1, cpu, page_cnt) if not reader: raise Exception("Could not open perf buffer") fd = lib.perf_reader_fd(reader) self[self.Key(cpu)] = self.Leaf(fd) self.bpf.perf_buffers[(id(self), cpu)] = reader # keep a refcnt self._cbs[cpu] = (fn, lost_fn) # The actual fd is held by the perf reader, add to track opened keys self._open_key_fds[cpu] = -1 def _open_perf_event(self, cpu, typ, config): fd = lib.bpf_open_perf_event(typ, config, -1, cpu) if fd < 0: raise Exception("bpf_open_perf_event failed") self[self.Key(cpu)] = self.Leaf(fd) self._open_key_fds[cpu] = fd def open_perf_event(self, typ, config): """open_perf_event(typ, config) Configures the table such that calls from the bpf program to table.perf_read(CUR_CPU_IDENTIFIER) will return the hardware counter denoted by event ev on the local cpu. """ for i in get_online_cpus(): self._open_perf_event(i, typ, config) class PerCpuHash(HashTable): def __init__(self, *args, **kwargs): self.reducer = kwargs.pop("reducer", None) super(PerCpuHash, self).__init__(*args, **kwargs) self.sLeaf = self.Leaf self.total_cpu = len(get_possible_cpus()) # This needs to be 8 as hard coded into the linux kernel. self.alignment = ct.sizeof(self.sLeaf) % 8 if self.alignment == 0: self.Leaf = self.sLeaf * self.total_cpu else: # Currently Float, Char, un-aligned structs are not supported if self.sLeaf == ct.c_uint: self.Leaf = ct.c_uint64 * self.total_cpu elif self.sLeaf == ct.c_int: self.Leaf = ct.c_int64 * self.total_cpu else: raise IndexError("Leaf must be aligned to 8 bytes") def getvalue(self, key): result = super(PerCpuHash, self).__getitem__(key) if self.alignment == 0: ret = result else: ret = (self.sLeaf * self.total_cpu)() for i in range(0, self.total_cpu): ret[i] = result[i] return ret def __getitem__(self, key): if self.reducer: return reduce(self.reducer, self.getvalue(key)) else: return self.getvalue(key) def __setitem__(self, key, leaf): super(PerCpuHash, self).__setitem__(key, leaf) def sum(self, key): if isinstance(self.Leaf(), ct.Structure): raise IndexError("Leaf must be an integer type for default sum functions") return self.sLeaf(sum(self.getvalue(key))) def max(self, key): if isinstance(self.Leaf(), ct.Structure): raise IndexError("Leaf must be an integer type for default max functions") return self.sLeaf(max(self.getvalue(key))) def average(self, key): result = self.sum(key) return result.value / self.total_cpu class LruPerCpuHash(PerCpuHash): def __init__(self, *args, **kwargs): super(LruPerCpuHash, self).__init__(*args, **kwargs) class PerCpuArray(ArrayBase): def __init__(self, *args, **kwargs): self.reducer = kwargs.pop("reducer", None) super(PerCpuArray, self).__init__(*args, **kwargs) self.sLeaf = self.Leaf self.total_cpu = len(get_possible_cpus()) # This needs to be 8 as hard coded into the linux kernel. self.alignment = ct.sizeof(self.sLeaf) % 8 if self.alignment == 0: self.Leaf = self.sLeaf * self.total_cpu else: # Currently Float, Char, un-aligned structs are not supported if self.sLeaf == ct.c_uint: self.Leaf = ct.c_uint64 * self.total_cpu elif self.sLeaf == ct.c_int: self.Leaf = ct.c_int64 * self.total_cpu else: raise IndexError("Leaf must be aligned to 8 bytes") def getvalue(self, key): result = super(PerCpuArray, self).__getitem__(key) if self.alignment == 0: ret = result else: ret = (self.sLeaf * self.total_cpu)() for i in range(0, self.total_cpu): ret[i] = result[i] return ret def __getitem__(self, key): if (self.reducer): return reduce(self.reducer, self.getvalue(key)) else: return self.getvalue(key) def __setitem__(self, key, leaf): super(PerCpuArray, self).__setitem__(key, leaf) def __delitem__(self, key): # Delete in this type does not have an effect, so zero out instead self.clearitem(key) def sum(self, key): if isinstance(self.Leaf(), ct.Structure): raise IndexError("Leaf must be an integer type for default sum functions") return self.sLeaf(sum(self.getvalue(key))) def max(self, key): if isinstance(self.Leaf(), ct.Structure): raise IndexError("Leaf must be an integer type for default max functions") return self.sLeaf(max(self.getvalue(key))) def average(self, key): result = self.sum(key) return result.value / self.total_cpu class LpmTrie(TableBase): def __init__(self, *args, **kwargs): super(LpmTrie, self).__init__(*args, **kwargs) def __len__(self): raise NotImplementedError class StackTrace(TableBase): MAX_DEPTH = 127 BPF_F_STACK_BUILD_ID = (1<<5) BPF_STACK_BUILD_ID_EMPTY = 0 #can't get stacktrace BPF_STACK_BUILD_ID_VALID = 1 #valid build-id,ip BPF_STACK_BUILD_ID_IP = 2 #fallback to ip def __init__(self, *args, **kwargs): super(StackTrace, self).__init__(*args, **kwargs) class StackWalker(object): def __init__(self, stack, flags, resolve=None): self.stack = stack self.n = -1 self.resolve = resolve self.flags = flags def __iter__(self): return self def __next__(self): return self.next() def next(self): self.n += 1 if self.n == StackTrace.MAX_DEPTH: raise StopIteration() if self.flags & StackTrace.BPF_F_STACK_BUILD_ID: addr = self.stack.trace[self.n] if addr.status == StackTrace.BPF_STACK_BUILD_ID_IP or \ addr.status == StackTrace.BPF_STACK_BUILD_ID_EMPTY: raise StopIteration() else: addr = self.stack.ip[self.n] if addr == 0 : raise StopIteration() return self.resolve(addr) if self.resolve else addr def walk(self, stack_id, resolve=None): return StackTrace.StackWalker(self[self.Key(stack_id)], self.flags, resolve) def __len__(self): i = 0 for k in self: i += 1 return i def clear(self): pass class DevMap(ArrayBase): def __init__(self, *args, **kwargs): super(DevMap, self).__init__(*args, **kwargs) class CpuMap(ArrayBase): def __init__(self, *args, **kwargs): super(CpuMap, self).__init__(*args, **kwargs) class MapInMapArray(ArrayBase): def __init__(self, *args, **kwargs): super(MapInMapArray, self).__init__(*args, **kwargs) class MapInMapHash(HashTable): def __init__(self, *args, **kwargs): super(MapInMapHash, self).__init__(*args, **kwargs) bpfcc-0.12.0/src/python/bcc/tcp.py000066400000000000000000000031041357404205000166440ustar00rootroot00000000000000# Copyright 2018 Netflix, Inc. # # 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. # from include/net/tcp_states.h: tcpstate = {} tcpstate[1] = 'ESTABLISHED' tcpstate[2] = 'SYN_SENT' tcpstate[3] = 'SYN_RECV' tcpstate[4] = 'FIN_WAIT1' tcpstate[5] = 'FIN_WAIT2' tcpstate[6] = 'TIME_WAIT' tcpstate[7] = 'CLOSE' tcpstate[8] = 'CLOSE_WAIT' tcpstate[9] = 'LAST_ACK' tcpstate[10] = 'LISTEN' tcpstate[11] = 'CLOSING' tcpstate[12] = 'NEW_SYN_RECV' # from include/net/tcp.h: TCPHDR_FIN = 0x01; TCPHDR_SYN = 0x02; TCPHDR_RST = 0x04; TCPHDR_PSH = 0x08; TCPHDR_ACK = 0x10; TCPHDR_URG = 0x20; TCPHDR_ECE = 0x40; TCPHDR_CWR = 0x80; def flags2str(flags): arr = []; if flags & TCPHDR_FIN: arr.append("FIN"); if flags & TCPHDR_SYN: arr.append("SYN"); if flags & TCPHDR_RST: arr.append("RST"); if flags & TCPHDR_PSH: arr.append("PSH"); if flags & TCPHDR_ACK: arr.append("ACK"); if flags & TCPHDR_URG: arr.append("URG"); if flags & TCPHDR_ECE: arr.append("ECE"); if flags & TCPHDR_CWR: arr.append("CWR"); return "|".join(arr); bpfcc-0.12.0/src/python/bcc/usdt.py000066400000000000000000000205671357404205000170510ustar00rootroot00000000000000# Copyright 2016 Sasha Goldshtein # # 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. import ctypes as ct import os, sys from .libbcc import lib, _USDT_CB, _USDT_PROBE_CB, \ bcc_usdt_location, bcc_usdt_argument, \ BCC_USDT_ARGUMENT_FLAGS class USDTException(Exception): pass class USDTProbeArgument(object): def __init__(self, argument): self.signed = argument.size < 0 self.size = abs(argument.size) self.valid = argument.valid if self.valid & BCC_USDT_ARGUMENT_FLAGS.CONSTANT != 0: self.constant = argument.constant if self.valid & BCC_USDT_ARGUMENT_FLAGS.DEREF_OFFSET != 0: self.deref_offset = argument.deref_offset if self.valid & BCC_USDT_ARGUMENT_FLAGS.DEREF_IDENT != 0: self.deref_ident = argument.deref_ident if self.valid & BCC_USDT_ARGUMENT_FLAGS.BASE_REGISTER_NAME != 0: self.base_register_name = argument.base_register_name if self.valid & BCC_USDT_ARGUMENT_FLAGS.INDEX_REGISTER_NAME != 0: self.index_register_name = argument.index_register_name if self.valid & BCC_USDT_ARGUMENT_FLAGS.SCALE != 0: self.scale = argument.scale def _size_prefix(self): return "%d %s bytes" % \ (self.size, "signed " if self.signed else "unsigned") def _format(self): # This mimics the logic in cc/usdt_args.cc that gives meaning to the # various argument settings. A change there will require a change here. if self.valid & BCC_USDT_ARGUMENT_FLAGS.CONSTANT != 0: return "%d" % self.constant if self.valid & BCC_USDT_ARGUMENT_FLAGS.DEREF_OFFSET == 0: return "%s" % self.base_register_name if self.valid & BCC_USDT_ARGUMENT_FLAGS.DEREF_OFFSET != 0 and \ self.valid & BCC_USDT_ARGUMENT_FLAGS.DEREF_IDENT == 0: if self.valid & BCC_USDT_ARGUMENT_FLAGS.INDEX_REGISTER_NAME != 0: index_offset = " + %s" % self.index_register_name if self.valid & BCC_USDT_ARGUMENT_FLAGS.SCALE != 0: index_offset += " * %d" % self.scale else: index_offset = "" sign = '+' if self.deref_offset >= 0 else '-' return "*(%s %s %d%s)" % (self.base_register_name, sign, abs(self.deref_offset), index_offset) if self.valid & BCC_USDT_ARGUMENT_FLAGS.DEREF_OFFSET != 0 and \ self.valid & BCC_USDT_ARGUMENT_FLAGS.DEREF_IDENT != 0 and \ self.valid & BCC_USDT_ARGUMENT_FLAGS.BASE_REGISTER_NAME != 0 and \ self.base_register_name == "ip": sign = '+' if self.deref_offset >= 0 else '-' return "*(&%s %s %d)" % (self.deref_ident, sign, abs(self.deref_offset)) # If we got here, this is an unrecognized case. Doesn't mean it's # necessarily bad, so just provide the raw data. It just means that # other tools won't be able to work with this argument. return "unrecognized argument format, flags %d" % self.valid def __str__(self): return "%s @ %s" % (self._size_prefix(), self._format()) class USDTProbeLocation(object): def __init__(self, probe, index, location): self.probe = probe self.index = index self.num_arguments = probe.num_arguments self.address = location.address self.bin_path = location.bin_path def __str__(self): return "%s 0x%x" % (self.bin_path, self.address) def get_argument(self, index): arg = bcc_usdt_argument() res = lib.bcc_usdt_get_argument(self.probe.context, self.probe.provider, self.probe.name, self.index, index, ct.byref(arg)) if res != 0: raise USDTException( "error retrieving probe argument %d location %d" % (index, self.index)) return USDTProbeArgument(arg) class USDTProbe(object): def __init__(self, context, probe): self.context = context self.provider = probe.provider self.name = probe.name self.bin_path = probe.bin_path self.semaphore = probe.semaphore self.num_locations = probe.num_locations self.num_arguments = probe.num_arguments def __str__(self): return "%s:%s [sema 0x%x]" % \ (self.provider, self.name, self.semaphore) def short_name(self): return "%s:%s" % (self.provider, self.name) def get_location(self, index): loc = bcc_usdt_location() res = lib.bcc_usdt_get_location(self.context, self.provider, self.name, index, ct.byref(loc)) if res != 0: raise USDTException("error retrieving probe location %d" % index) return USDTProbeLocation(self, index, loc) class USDT(object): def __init__(self, pid=None, path=None): if pid and pid != -1: self.pid = pid if path: self.context = lib.bcc_usdt_new_frompid(pid, path.encode('ascii')) else: self.context = lib.bcc_usdt_new_frompid(pid, ct.c_char_p(0)) if self.context == None: raise USDTException("USDT failed to instrument PID %d" % pid) elif path: self.path = path self.context = lib.bcc_usdt_new_frompath(path.encode('ascii')) if self.context == None: raise USDTException("USDT failed to instrument path %s" % path) else: raise USDTException( "either a pid or a binary path must be specified") def __del__(self): lib.bcc_usdt_close(self.context) def enable_probe(self, probe, fn_name): if lib.bcc_usdt_enable_probe(self.context, probe.encode('ascii'), fn_name.encode('ascii')) != 0: raise USDTException( ("failed to enable probe '%s'; a possible cause " + "can be that the probe requires a pid to enable") % probe ) def enable_probe_or_bail(self, probe, fn_name): if lib.bcc_usdt_enable_probe(self.context, probe.encode('ascii'), fn_name.encode('ascii')) != 0: print( """Error attaching USDT probes: the specified pid might not contain the given language's runtime, or the runtime was not built with the required USDT probes. Look for a configure flag similar to --with-dtrace or --enable-dtrace. To check which probes are present in the process, use the tplist tool.""") sys.exit(1) def get_context(self): return self.context def get_text(self): ctx_array = (ct.c_void_p * 1)() ctx_array[0] = ct.c_void_p(self.context) return lib.bcc_usdt_genargs(ctx_array, 1).decode() def get_probe_arg_ctype(self, probe_name, arg_index): return lib.bcc_usdt_get_probe_argctype( self.context, probe_name.encode('ascii'), arg_index).decode() def enumerate_probes(self): probes = [] def _add_probe(probe): probes.append(USDTProbe(self.context, probe.contents)) lib.bcc_usdt_foreach(self.context, _USDT_CB(_add_probe)) return probes # This is called by the BPF module's __init__ when it realizes that there # is a USDT context and probes need to be attached. def attach_uprobes(self, bpf): probes = self.enumerate_active_probes() for (binpath, fn_name, addr, pid) in probes: bpf.attach_uprobe(name=binpath.decode(), fn_name=fn_name.decode(), addr=addr, pid=pid) def enumerate_active_probes(self): probes = [] def _add_probe(binpath, fn_name, addr, pid): probes.append((binpath, fn_name, addr, pid)) lib.bcc_usdt_foreach_uprobe(self.context, _USDT_PROBE_CB(_add_probe)) return probes bpfcc-0.12.0/src/python/bcc/utils.py000066400000000000000000000057701357404205000172310ustar00rootroot00000000000000# Copyright 2016 Catalysts GmbH # # 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. import ctypes as ct import sys import traceback import warnings from .libbcc import lib def _read_cpu_range(path): cpus = [] with open(path, 'r') as f: cpus_range_str = f.read() for cpu_range in cpus_range_str.split(','): rangeop = cpu_range.find('-') if rangeop == -1: cpus.append(int(cpu_range)) else: start = int(cpu_range[:rangeop]) end = int(cpu_range[rangeop+1:]) cpus.extend(range(start, end+1)) return cpus def get_online_cpus(): return _read_cpu_range('/sys/devices/system/cpu/online') def get_possible_cpus(): return _read_cpu_range('/sys/devices/system/cpu/possible') def detect_language(candidates, pid): res = lib.bcc_procutils_language(pid) language = ct.cast(res, ct.c_char_p).value.decode() return language if language in candidates else None FILESYSTEMENCODING = sys.getfilesystemencoding() def printb(s, file=sys.stdout, nl=1): """ printb(s) print a bytes object to stdout and flush """ buf = file.buffer if hasattr(file, "buffer") else file buf.write(s) if nl: buf.write(b"\n") file.flush() class ArgString(object): """ ArgString(arg) encapsulate a system argument that can be easily coerced to a bytes() object, which is better for comparing to kernel or probe data (which should never be en/decode()'ed). """ def __init__(self, arg): if sys.version_info[0] >= 3: self.s = arg else: self.s = arg.decode(FILESYSTEMENCODING) def __bytes__(self): return self.s.encode(FILESYSTEMENCODING) def __str__(self): return self.__bytes__() def warn_with_traceback(message, category, filename, lineno, file=None, line=None): log = file if hasattr(file, "write") else sys.stderr traceback.print_stack(f=sys._getframe(2), file=log) log.write(warnings.formatwarning(message, category, filename, lineno, line)) # uncomment to get full tracebacks for invalid uses of python3+str in arguments #warnings.showwarning = warn_with_traceback _strict_bytes = False def _assert_is_bytes(arg): if arg is None: return arg if _strict_bytes: assert type(arg) is bytes, "not a bytes object: %r" % arg elif type(arg) is not bytes: warnings.warn("not a bytes object: %r" % arg, DeprecationWarning, 2) return ArgString(arg).__bytes__() return arg bpfcc-0.12.0/src/python/bcc/version.py.in000066400000000000000000000000331357404205000201460ustar00rootroot00000000000000__version__ = '@REVISION@' bpfcc-0.12.0/src/python/setup.py.in000066400000000000000000000007471357404205000171060ustar00rootroot00000000000000# Copyright (c) PLUMgrid, Inc. # Licensed under the Apache License, Version 2.0 (the "License") from distutils.core import setup import os import sys if os.environ.get('DESTDIR'): sys.argv += ['--root', os.environ['DESTDIR']] setup(name='bcc', version='@REVISION@', description='BPF Loader Library', author='Brenden Blanco', author_email='bblanco@plumgrid.com', url='https://github.com/iovisor/bcc', packages=['bcc'], platforms=['Linux']) bpfcc-0.12.0/tests/000077500000000000000000000000001357404205000140115ustar00rootroot00000000000000bpfcc-0.12.0/tests/CMakeLists.txt000066400000000000000000000007441357404205000165560ustar00rootroot00000000000000# Copyright (c) PLUMgrid, Inc. # Licensed under the Apache License, Version 2.0 (the "License") configure_file(wrapper.sh.in "${CMAKE_CURRENT_BINARY_DIR}/wrapper.sh" @ONLY) set(TEST_WRAPPER ${CMAKE_CURRENT_BINARY_DIR}/wrapper.sh) add_test(NAME style-check COMMAND ${CMAKE_SOURCE_DIR}/scripts/c-style-check.sh) set_tests_properties(style-check PROPERTIES PASS_REGULAR_EXPRESSION ".*") if(ENABLE_CLANG_JIT) add_subdirectory(cc) add_subdirectory(python) add_subdirectory(lua) endif() bpfcc-0.12.0/tests/cc/000077500000000000000000000000001357404205000143765ustar00rootroot00000000000000bpfcc-0.12.0/tests/cc/CMakeLists.txt000066400000000000000000000041371357404205000171430ustar00rootroot00000000000000# Copyright (c) PLUMgrid, Inc. # Licensed under the Apache License, Version 2.0 (the "License") include_directories(${CMAKE_SOURCE_DIR}/src/cc) include_directories(${CMAKE_SOURCE_DIR}/src/cc/api) include_directories(${CMAKE_SOURCE_DIR}/src/cc/libbpf/include/uapi) include_directories(${CMAKE_SOURCE_DIR}/tests/python/include) add_executable(test_static test_static.c) target_link_libraries(test_static bcc-static) add_test(NAME c_test_static COMMAND ${TEST_WRAPPER} c_test_static sudo ${CMAKE_CURRENT_BINARY_DIR}/test_static) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-result") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-result") if(ENABLE_USDT) set(TEST_LIBBCC_SOURCES test_libbcc.cc test_c_api.cc test_array_table.cc test_bpf_table.cc test_hash_table.cc test_map_in_map.cc test_perf_event.cc test_pinned_table.cc test_prog_table.cc test_shared_table.cc test_usdt_args.cc test_usdt_probes.cc utils.cc) add_executable(test_libbcc ${TEST_LIBBCC_SOURCES}) file(COPY dummy_proc_map.txt DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) add_library(usdt_test_lib SHARED usdt_test_lib.c) add_dependencies(test_libbcc bcc-shared) target_link_libraries(test_libbcc ${PROJECT_BINARY_DIR}/src/cc/libbcc.so dl usdt_test_lib) set_target_properties(test_libbcc PROPERTIES INSTALL_RPATH ${PROJECT_BINARY_DIR}/src/cc) target_compile_definitions(test_libbcc PRIVATE -DLIBBCC_NAME=\"libbcc.so\") add_test(NAME test_libbcc COMMAND ${TEST_WRAPPER} c_test_all sudo ${CMAKE_CURRENT_BINARY_DIR}/test_libbcc) if(LIBBPF_FOUND) add_executable(test_libbcc_no_libbpf ${TEST_LIBBCC_SOURCES}) add_dependencies(test_libbcc_no_libbpf bcc-shared-no-libbpf) target_link_libraries(test_libbcc_no_libbpf ${PROJECT_BINARY_DIR}/src/cc/libbcc-no-libbpf.so dl usdt_test_lib bpf) set_target_properties(test_libbcc_no_libbpf PROPERTIES INSTALL_RPATH ${PROJECT_BINARY_DIR}/src/cc) target_compile_definitions(test_libbcc_no_libbpf PRIVATE -DLIBBCC_NAME=\"libbcc-no-libbpf.so\") add_test(NAME test_libbcc_no_libbpf COMMAND ${TEST_WRAPPER} c_test_all_no_libbpf sudo ${CMAKE_CURRENT_BINARY_DIR}/test_libbcc_no_libbpf) endif() endif(ENABLE_USDT) bpfcc-0.12.0/tests/cc/catch.hpp000066400000000000000000013401201357404205000161720ustar00rootroot00000000000000/* * Catch v1.4.0 * Generated: 2016-03-15 07:23:12.623111 * ---------------------------------------------------------- * This file has been merged from multiple headers. Please don't edit it directly * Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved. * * Distributed under the Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */ #ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED #define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED #define TWOBLUECUBES_CATCH_HPP_INCLUDED #ifdef __clang__ # pragma clang system_header #elif defined __GNUC__ # pragma GCC system_header #endif // #included from: internal/catch_suppress_warnings.h #ifdef __clang__ # ifdef __ICC // icpc defines the __clang__ macro # pragma warning(push) # pragma warning(disable: 161 1682) # else // __ICC # pragma clang diagnostic ignored "-Wglobal-constructors" # pragma clang diagnostic ignored "-Wvariadic-macros" # pragma clang diagnostic ignored "-Wc99-extensions" # pragma clang diagnostic ignored "-Wunused-variable" # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wpadded" # pragma clang diagnostic ignored "-Wc++98-compat" # pragma clang diagnostic ignored "-Wc++98-compat-pedantic" # pragma clang diagnostic ignored "-Wswitch-enum" # pragma clang diagnostic ignored "-Wcovered-switch-default" # endif #elif defined __GNUC__ # pragma GCC diagnostic ignored "-Wvariadic-macros" # pragma GCC diagnostic ignored "-Wunused-variable" # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wpadded" #endif #if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER) # define CATCH_IMPL #endif #ifdef CATCH_IMPL # ifndef CLARA_CONFIG_MAIN # define CLARA_CONFIG_MAIN_NOT_DEFINED # define CLARA_CONFIG_MAIN # endif #endif // #included from: internal/catch_notimplemented_exception.h #define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_H_INCLUDED // #included from: catch_common.h #define TWOBLUECUBES_CATCH_COMMON_H_INCLUDED #define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line #define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) #ifdef CATCH_CONFIG_COUNTER # define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ ) #else # define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) #endif #define INTERNAL_CATCH_STRINGIFY2( expr ) #expr #define INTERNAL_CATCH_STRINGIFY( expr ) INTERNAL_CATCH_STRINGIFY2( expr ) #include #include #include // #included from: catch_compiler_capabilities.h #define TWOBLUECUBES_CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED // Detect a number of compiler features - mostly C++11/14 conformance - by compiler // The following features are defined: // // CATCH_CONFIG_CPP11_NULLPTR : is nullptr supported? // CATCH_CONFIG_CPP11_NOEXCEPT : is noexcept supported? // CATCH_CONFIG_CPP11_GENERATED_METHODS : The delete and default keywords for compiler generated methods // CATCH_CONFIG_CPP11_IS_ENUM : std::is_enum is supported? // CATCH_CONFIG_CPP11_TUPLE : std::tuple is supported // CATCH_CONFIG_CPP11_LONG_LONG : is long long supported? // CATCH_CONFIG_CPP11_OVERRIDE : is override supported? // CATCH_CONFIG_CPP11_UNIQUE_PTR : is unique_ptr supported (otherwise use auto_ptr) // CATCH_CONFIG_CPP11_OR_GREATER : Is C++11 supported? // CATCH_CONFIG_VARIADIC_MACROS : are variadic macros supported? // CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported? // **************** // Note to maintainers: if new toggles are added please document them // in configuration.md, too // **************** // In general each macro has a _NO_ form // (e.g. CATCH_CONFIG_CPP11_NO_NULLPTR) which disables the feature. // Many features, at point of detection, define an _INTERNAL_ macro, so they // can be combined, en-mass, with the _NO_ forms later. // All the C++11 features can be disabled with CATCH_CONFIG_NO_CPP11 #if defined(__cplusplus) && __cplusplus >= 201103L # define CATCH_CPP11_OR_GREATER #endif #ifdef __clang__ # if __has_feature(cxx_nullptr) # define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR # endif # if __has_feature(cxx_noexcept) # define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT # endif # if defined(CATCH_CPP11_OR_GREATER) # define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) # endif #endif // __clang__ //////////////////////////////////////////////////////////////////////////////// // Borland #ifdef __BORLANDC__ #endif // __BORLANDC__ //////////////////////////////////////////////////////////////////////////////// // EDG #ifdef __EDG_VERSION__ #endif // __EDG_VERSION__ //////////////////////////////////////////////////////////////////////////////// // Digital Mars #ifdef __DMC__ #endif // __DMC__ //////////////////////////////////////////////////////////////////////////////// // GCC #ifdef __GNUC__ # if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__) # define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR # endif # if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) && defined(CATCH_CPP11_OR_GREATER) # define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS _Pragma( "GCC diagnostic ignored \"-Wparentheses\"" ) # endif // - otherwise more recent versions define __cplusplus >= 201103L // and will get picked up below #endif // __GNUC__ //////////////////////////////////////////////////////////////////////////////// // Visual C++ #ifdef _MSC_VER #if (_MSC_VER >= 1600) # define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR # define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR #endif #if (_MSC_VER >= 1900 ) // (VC++ 13 (VS2015)) #define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT #define CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS #endif #endif // _MSC_VER //////////////////////////////////////////////////////////////////////////////// // Use variadic macros if the compiler supports them #if ( defined _MSC_VER && _MSC_VER > 1400 && !defined __EDGE__) || \ ( defined __WAVE__ && __WAVE_HAS_VARIADICS ) || \ ( defined __GNUC__ && __GNUC__ >= 3 ) || \ ( !defined __cplusplus && __STDC_VERSION__ >= 199901L || __cplusplus >= 201103L ) #define CATCH_INTERNAL_CONFIG_VARIADIC_MACROS #endif // Use __COUNTER__ if the compiler supports it #if ( defined _MSC_VER && _MSC_VER >= 1300 ) || \ ( defined __GNUC__ && __GNUC__ >= 4 && __GNUC_MINOR__ >= 3 ) || \ ( defined __clang__ && __clang_major__ >= 3 ) #define CATCH_INTERNAL_CONFIG_COUNTER #endif //////////////////////////////////////////////////////////////////////////////// // C++ language feature support // catch all support for C++11 #if defined(CATCH_CPP11_OR_GREATER) # if !defined(CATCH_INTERNAL_CONFIG_CPP11_NULLPTR) # define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR # endif # ifndef CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT # define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT # endif # ifndef CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS # define CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS # endif # ifndef CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM # define CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM # endif # ifndef CATCH_INTERNAL_CONFIG_CPP11_TUPLE # define CATCH_INTERNAL_CONFIG_CPP11_TUPLE # endif # ifndef CATCH_INTERNAL_CONFIG_VARIADIC_MACROS # define CATCH_INTERNAL_CONFIG_VARIADIC_MACROS # endif # if !defined(CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG) # define CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG # endif # if !defined(CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE) # define CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE # endif # if !defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) # define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR # endif #endif // __cplusplus >= 201103L // Now set the actual defines based on the above + anything the user has configured #if defined(CATCH_INTERNAL_CONFIG_CPP11_NULLPTR) && !defined(CATCH_CONFIG_CPP11_NO_NULLPTR) && !defined(CATCH_CONFIG_CPP11_NULLPTR) && !defined(CATCH_CONFIG_NO_CPP11) # define CATCH_CONFIG_CPP11_NULLPTR #endif #if defined(CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_CONFIG_CPP11_NO_NOEXCEPT) && !defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_CONFIG_NO_CPP11) # define CATCH_CONFIG_CPP11_NOEXCEPT #endif #if defined(CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS) && !defined(CATCH_CONFIG_CPP11_NO_GENERATED_METHODS) && !defined(CATCH_CONFIG_CPP11_GENERATED_METHODS) && !defined(CATCH_CONFIG_NO_CPP11) # define CATCH_CONFIG_CPP11_GENERATED_METHODS #endif #if defined(CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM) && !defined(CATCH_CONFIG_CPP11_NO_IS_ENUM) && !defined(CATCH_CONFIG_CPP11_IS_ENUM) && !defined(CATCH_CONFIG_NO_CPP11) # define CATCH_CONFIG_CPP11_IS_ENUM #endif #if defined(CATCH_INTERNAL_CONFIG_CPP11_TUPLE) && !defined(CATCH_CONFIG_CPP11_NO_TUPLE) && !defined(CATCH_CONFIG_CPP11_TUPLE) && !defined(CATCH_CONFIG_NO_CPP11) # define CATCH_CONFIG_CPP11_TUPLE #endif #if defined(CATCH_INTERNAL_CONFIG_VARIADIC_MACROS) && !defined(CATCH_CONFIG_NO_VARIADIC_MACROS) && !defined(CATCH_CONFIG_VARIADIC_MACROS) # define CATCH_CONFIG_VARIADIC_MACROS #endif #if defined(CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_NO_LONG_LONG) && !defined(CATCH_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_NO_CPP11) # define CATCH_CONFIG_CPP11_LONG_LONG #endif #if defined(CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_NO_OVERRIDE) && !defined(CATCH_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_NO_CPP11) # define CATCH_CONFIG_CPP11_OVERRIDE #endif #if defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_NO_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_NO_CPP11) # define CATCH_CONFIG_CPP11_UNIQUE_PTR #endif #if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) # define CATCH_CONFIG_COUNTER #endif #if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS #endif // noexcept support: #if defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_NOEXCEPT) # define CATCH_NOEXCEPT noexcept # define CATCH_NOEXCEPT_IS(x) noexcept(x) #else # define CATCH_NOEXCEPT throw() # define CATCH_NOEXCEPT_IS(x) #endif // nullptr support #ifdef CATCH_CONFIG_CPP11_NULLPTR # define CATCH_NULL nullptr #else # define CATCH_NULL NULL #endif // override support #ifdef CATCH_CONFIG_CPP11_OVERRIDE # define CATCH_OVERRIDE override #else # define CATCH_OVERRIDE #endif // unique_ptr support #ifdef CATCH_CONFIG_CPP11_UNIQUE_PTR # define CATCH_AUTO_PTR( T ) std::unique_ptr #else # define CATCH_AUTO_PTR( T ) std::auto_ptr #endif namespace Catch { struct IConfig; struct CaseSensitive { enum Choice { Yes, No }; }; class NonCopyable { #ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS NonCopyable( NonCopyable const& ) = delete; NonCopyable( NonCopyable && ) = delete; NonCopyable& operator = ( NonCopyable const& ) = delete; NonCopyable& operator = ( NonCopyable && ) = delete; #else NonCopyable( NonCopyable const& info ); NonCopyable& operator = ( NonCopyable const& ); #endif protected: NonCopyable() {} virtual ~NonCopyable(); }; class SafeBool { public: typedef void (SafeBool::*type)() const; static type makeSafe( bool value ) { return value ? &SafeBool::trueValue : 0; } private: void trueValue() const {} }; template inline void deleteAll( ContainerT& container ) { typename ContainerT::const_iterator it = container.begin(); typename ContainerT::const_iterator itEnd = container.end(); for(; it != itEnd; ++it ) delete *it; } template inline void deleteAllValues( AssociativeContainerT& container ) { typename AssociativeContainerT::const_iterator it = container.begin(); typename AssociativeContainerT::const_iterator itEnd = container.end(); for(; it != itEnd; ++it ) delete it->second; } bool startsWith( std::string const& s, std::string const& prefix ); bool endsWith( std::string const& s, std::string const& suffix ); bool contains( std::string const& s, std::string const& infix ); void toLowerInPlace( std::string& s ); std::string toLower( std::string const& s ); std::string trim( std::string const& str ); bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ); struct pluralise { pluralise( std::size_t count, std::string const& label ); friend std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ); std::size_t m_count; std::string m_label; }; struct SourceLineInfo { SourceLineInfo(); SourceLineInfo( char const* _file, std::size_t _line ); SourceLineInfo( SourceLineInfo const& other ); # ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS SourceLineInfo( SourceLineInfo && ) = default; SourceLineInfo& operator = ( SourceLineInfo const& ) = default; SourceLineInfo& operator = ( SourceLineInfo && ) = default; # endif bool empty() const; bool operator == ( SourceLineInfo const& other ) const; bool operator < ( SourceLineInfo const& other ) const; std::string file; std::size_t line; }; std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ); // This is just here to avoid compiler warnings with macro constants and boolean literals inline bool isTrue( bool value ){ return value; } inline bool alwaysTrue() { return true; } inline bool alwaysFalse() { return false; } void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo ); void seedRng( IConfig const& config ); unsigned int rngSeed(); // Use this in variadic streaming macros to allow // >> +StreamEndStop // as well as // >> stuff +StreamEndStop struct StreamEndStop { std::string operator+() { return std::string(); } }; template T const& operator + ( T const& value, StreamEndStop ) { return value; } } #define CATCH_INTERNAL_LINEINFO ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) #define CATCH_INTERNAL_ERROR( msg ) ::Catch::throwLogicError( msg, CATCH_INTERNAL_LINEINFO ); #include namespace Catch { class NotImplementedException : public std::exception { public: NotImplementedException( SourceLineInfo const& lineInfo ); NotImplementedException( NotImplementedException const& ) {} virtual ~NotImplementedException() CATCH_NOEXCEPT {} virtual const char* what() const CATCH_NOEXCEPT; private: std::string m_what; SourceLineInfo m_lineInfo; }; } // end namespace Catch /////////////////////////////////////////////////////////////////////////////// #define CATCH_NOT_IMPLEMENTED throw Catch::NotImplementedException( CATCH_INTERNAL_LINEINFO ) // #included from: internal/catch_context.h #define TWOBLUECUBES_CATCH_CONTEXT_H_INCLUDED // #included from: catch_interfaces_generators.h #define TWOBLUECUBES_CATCH_INTERFACES_GENERATORS_H_INCLUDED #include namespace Catch { struct IGeneratorInfo { virtual ~IGeneratorInfo(); virtual bool moveNext() = 0; virtual std::size_t getCurrentIndex() const = 0; }; struct IGeneratorsForTest { virtual ~IGeneratorsForTest(); virtual IGeneratorInfo& getGeneratorInfo( std::string const& fileInfo, std::size_t size ) = 0; virtual bool moveNext() = 0; }; IGeneratorsForTest* createGeneratorsForTest(); } // end namespace Catch // #included from: catch_ptr.hpp #define TWOBLUECUBES_CATCH_PTR_HPP_INCLUDED #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wpadded" #endif namespace Catch { // An intrusive reference counting smart pointer. // T must implement addRef() and release() methods // typically implementing the IShared interface template class Ptr { public: Ptr() : m_p( CATCH_NULL ){} Ptr( T* p ) : m_p( p ){ if( m_p ) m_p->addRef(); } Ptr( Ptr const& other ) : m_p( other.m_p ){ if( m_p ) m_p->addRef(); } ~Ptr(){ if( m_p ) m_p->release(); } void reset() { if( m_p ) m_p->release(); m_p = CATCH_NULL; } Ptr& operator = ( T* p ){ Ptr temp( p ); swap( temp ); return *this; } Ptr& operator = ( Ptr const& other ){ Ptr temp( other ); swap( temp ); return *this; } void swap( Ptr& other ) { std::swap( m_p, other.m_p ); } T* get() const{ return m_p; } T& operator*() const { return *m_p; } T* operator->() const { return m_p; } bool operator !() const { return m_p == CATCH_NULL; } operator SafeBool::type() const { return SafeBool::makeSafe( m_p != CATCH_NULL ); } private: T* m_p; }; struct IShared : NonCopyable { virtual ~IShared(); virtual void addRef() const = 0; virtual void release() const = 0; }; template struct SharedImpl : T { SharedImpl() : m_rc( 0 ){} virtual void addRef() const { ++m_rc; } virtual void release() const { if( --m_rc == 0 ) delete this; } mutable unsigned int m_rc; }; } // end namespace Catch #ifdef __clang__ #pragma clang diagnostic pop #endif #include #include #include namespace Catch { class TestCase; class Stream; struct IResultCapture; struct IRunner; struct IGeneratorsForTest; struct IConfig; struct IContext { virtual ~IContext(); virtual IResultCapture* getResultCapture() = 0; virtual IRunner* getRunner() = 0; virtual size_t getGeneratorIndex( std::string const& fileInfo, size_t totalSize ) = 0; virtual bool advanceGeneratorsForCurrentTest() = 0; virtual Ptr getConfig() const = 0; }; struct IMutableContext : IContext { virtual ~IMutableContext(); virtual void setResultCapture( IResultCapture* resultCapture ) = 0; virtual void setRunner( IRunner* runner ) = 0; virtual void setConfig( Ptr const& config ) = 0; }; IContext& getCurrentContext(); IMutableContext& getCurrentMutableContext(); void cleanUpContext(); Stream createStream( std::string const& streamName ); } // #included from: internal/catch_test_registry.hpp #define TWOBLUECUBES_CATCH_TEST_REGISTRY_HPP_INCLUDED // #included from: catch_interfaces_testcase.h #define TWOBLUECUBES_CATCH_INTERFACES_TESTCASE_H_INCLUDED #include namespace Catch { class TestSpec; struct ITestCase : IShared { virtual void invoke () const = 0; protected: virtual ~ITestCase(); }; class TestCase; struct IConfig; struct ITestCaseRegistry { virtual ~ITestCaseRegistry(); virtual std::vector const& getAllTests() const = 0; virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0; }; bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); std::vector const& getAllTestCasesSorted( IConfig const& config ); } namespace Catch { template class MethodTestCase : public SharedImpl { public: MethodTestCase( void (C::*method)() ) : m_method( method ) {} virtual void invoke() const { C obj; (obj.*m_method)(); } private: virtual ~MethodTestCase() {} void (C::*m_method)(); }; typedef void(*TestFunction)(); struct NameAndDesc { NameAndDesc( const char* _name = "", const char* _description= "" ) : name( _name ), description( _description ) {} const char* name; const char* description; }; void registerTestCase ( ITestCase* testCase, char const* className, NameAndDesc const& nameAndDesc, SourceLineInfo const& lineInfo ); struct AutoReg { AutoReg ( TestFunction function, SourceLineInfo const& lineInfo, NameAndDesc const& nameAndDesc ); template AutoReg ( void (C::*method)(), char const* className, NameAndDesc const& nameAndDesc, SourceLineInfo const& lineInfo ) { registerTestCase ( new MethodTestCase( method ), className, nameAndDesc, lineInfo ); } ~AutoReg(); private: AutoReg( AutoReg const& ); void operator= ( AutoReg const& ); }; void registerTestCaseFunction ( TestFunction function, SourceLineInfo const& lineInfo, NameAndDesc const& nameAndDesc ); } // end namespace Catch #ifdef CATCH_CONFIG_VARIADIC_MACROS /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \ static void TestName(); \ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); }\ static void TestName() #define INTERNAL_CATCH_TESTCASE( ... ) \ INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), __VA_ARGS__ ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); } /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\ namespace{ \ struct TestName : ClassName{ \ void test(); \ }; \ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &TestName::test, #ClassName, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); \ } \ void TestName::test() #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \ INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, __VA_ARGS__ ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \ Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); #else /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TESTCASE2( TestName, Name, Desc ) \ static void TestName(); \ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); }\ static void TestName() #define INTERNAL_CATCH_TESTCASE( Name, Desc ) \ INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), Name, Desc ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, Name, Desc ) \ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( Name, Desc ), CATCH_INTERNAL_LINEINFO ); } /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestCaseName, ClassName, TestName, Desc )\ namespace{ \ struct TestCaseName : ClassName{ \ void test(); \ }; \ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &TestCaseName::test, #ClassName, Catch::NameAndDesc( TestName, Desc ), CATCH_INTERNAL_LINEINFO ); \ } \ void TestCaseName::test() #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, TestName, Desc )\ INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, TestName, Desc ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, Name, Desc ) \ Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); #endif // #included from: internal/catch_capture.hpp #define TWOBLUECUBES_CATCH_CAPTURE_HPP_INCLUDED // #included from: catch_result_builder.h #define TWOBLUECUBES_CATCH_RESULT_BUILDER_H_INCLUDED // #included from: catch_result_type.h #define TWOBLUECUBES_CATCH_RESULT_TYPE_H_INCLUDED namespace Catch { // ResultWas::OfType enum struct ResultWas { enum OfType { Unknown = -1, Ok = 0, Info = 1, Warning = 2, FailureBit = 0x10, ExpressionFailed = FailureBit | 1, ExplicitFailure = FailureBit | 2, Exception = 0x100 | FailureBit, ThrewException = Exception | 1, DidntThrowException = Exception | 2, FatalErrorCondition = 0x200 | FailureBit }; }; inline bool isOk( ResultWas::OfType resultType ) { return ( resultType & ResultWas::FailureBit ) == 0; } inline bool isJustInfo( int flags ) { return flags == ResultWas::Info; } // ResultDisposition::Flags enum struct ResultDisposition { enum Flags { Normal = 0x01, ContinueOnFailure = 0x02, // Failures fail test, but execution continues FalseTest = 0x04, // Prefix expression with ! SuppressFail = 0x08 // Failures are reported but do not fail the test }; }; inline ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ) { return static_cast( static_cast( lhs ) | static_cast( rhs ) ); } inline bool shouldContinueOnFailure( int flags ) { return ( flags & ResultDisposition::ContinueOnFailure ) != 0; } inline bool isFalseTest( int flags ) { return ( flags & ResultDisposition::FalseTest ) != 0; } inline bool shouldSuppressFailure( int flags ) { return ( flags & ResultDisposition::SuppressFail ) != 0; } } // end namespace Catch // #included from: catch_assertionresult.h #define TWOBLUECUBES_CATCH_ASSERTIONRESULT_H_INCLUDED #include namespace Catch { struct AssertionInfo { AssertionInfo() {} AssertionInfo( std::string const& _macroName, SourceLineInfo const& _lineInfo, std::string const& _capturedExpression, ResultDisposition::Flags _resultDisposition ); std::string macroName; SourceLineInfo lineInfo; std::string capturedExpression; ResultDisposition::Flags resultDisposition; }; struct AssertionResultData { AssertionResultData() : resultType( ResultWas::Unknown ) {} std::string reconstructedExpression; std::string message; ResultWas::OfType resultType; }; class AssertionResult { public: AssertionResult(); AssertionResult( AssertionInfo const& info, AssertionResultData const& data ); ~AssertionResult(); # ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS AssertionResult( AssertionResult const& ) = default; AssertionResult( AssertionResult && ) = default; AssertionResult& operator = ( AssertionResult const& ) = default; AssertionResult& operator = ( AssertionResult && ) = default; # endif bool isOk() const; bool succeeded() const; ResultWas::OfType getResultType() const; bool hasExpression() const; bool hasMessage() const; std::string getExpression() const; std::string getExpressionInMacro() const; bool hasExpandedExpression() const; std::string getExpandedExpression() const; std::string getMessage() const; SourceLineInfo getSourceInfo() const; std::string getTestMacroName() const; protected: AssertionInfo m_info; AssertionResultData m_resultData; }; } // end namespace Catch // #included from: catch_matchers.hpp #define TWOBLUECUBES_CATCH_MATCHERS_HPP_INCLUDED namespace Catch { namespace Matchers { namespace Impl { namespace Generic { template class AllOf; template class AnyOf; template class Not; } template struct Matcher : SharedImpl { typedef ExpressionT ExpressionType; virtual ~Matcher() {} virtual Ptr clone() const = 0; virtual bool match( ExpressionT const& expr ) const = 0; virtual std::string toString() const = 0; Generic::AllOf operator && ( Matcher const& other ) const; Generic::AnyOf operator || ( Matcher const& other ) const; Generic::Not operator ! () const; }; template struct MatcherImpl : Matcher { virtual Ptr > clone() const { return Ptr >( new DerivedT( static_cast( *this ) ) ); } }; namespace Generic { template class Not : public MatcherImpl, ExpressionT> { public: explicit Not( Matcher const& matcher ) : m_matcher(matcher.clone()) {} Not( Not const& other ) : m_matcher( other.m_matcher ) {} virtual bool match( ExpressionT const& expr ) const CATCH_OVERRIDE { return !m_matcher->match( expr ); } virtual std::string toString() const CATCH_OVERRIDE { return "not " + m_matcher->toString(); } private: Ptr< Matcher > m_matcher; }; template class AllOf : public MatcherImpl, ExpressionT> { public: AllOf() {} AllOf( AllOf const& other ) : m_matchers( other.m_matchers ) {} AllOf& add( Matcher const& matcher ) { m_matchers.push_back( matcher.clone() ); return *this; } virtual bool match( ExpressionT const& expr ) const { for( std::size_t i = 0; i < m_matchers.size(); ++i ) if( !m_matchers[i]->match( expr ) ) return false; return true; } virtual std::string toString() const { std::ostringstream oss; oss << "( "; for( std::size_t i = 0; i < m_matchers.size(); ++i ) { if( i != 0 ) oss << " and "; oss << m_matchers[i]->toString(); } oss << " )"; return oss.str(); } AllOf operator && ( Matcher const& other ) const { AllOf allOfExpr( *this ); allOfExpr.add( other ); return allOfExpr; } private: std::vector > > m_matchers; }; template class AnyOf : public MatcherImpl, ExpressionT> { public: AnyOf() {} AnyOf( AnyOf const& other ) : m_matchers( other.m_matchers ) {} AnyOf& add( Matcher const& matcher ) { m_matchers.push_back( matcher.clone() ); return *this; } virtual bool match( ExpressionT const& expr ) const { for( std::size_t i = 0; i < m_matchers.size(); ++i ) if( m_matchers[i]->match( expr ) ) return true; return false; } virtual std::string toString() const { std::ostringstream oss; oss << "( "; for( std::size_t i = 0; i < m_matchers.size(); ++i ) { if( i != 0 ) oss << " or "; oss << m_matchers[i]->toString(); } oss << " )"; return oss.str(); } AnyOf operator || ( Matcher const& other ) const { AnyOf anyOfExpr( *this ); anyOfExpr.add( other ); return anyOfExpr; } private: std::vector > > m_matchers; }; } // namespace Generic template Generic::AllOf Matcher::operator && ( Matcher const& other ) const { Generic::AllOf allOfExpr; allOfExpr.add( *this ); allOfExpr.add( other ); return allOfExpr; } template Generic::AnyOf Matcher::operator || ( Matcher const& other ) const { Generic::AnyOf anyOfExpr; anyOfExpr.add( *this ); anyOfExpr.add( other ); return anyOfExpr; } template Generic::Not Matcher::operator ! () const { return Generic::Not( *this ); } namespace StdString { inline std::string makeString( std::string const& str ) { return str; } inline std::string makeString( const char* str ) { return str ? std::string( str ) : std::string(); } struct CasedString { CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity ) : m_caseSensitivity( caseSensitivity ), m_str( adjustString( str ) ) {} std::string adjustString( std::string const& str ) const { return m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str; } std::string toStringSuffix() const { return m_caseSensitivity == CaseSensitive::No ? " (case insensitive)" : ""; } CaseSensitive::Choice m_caseSensitivity; std::string m_str; }; struct Equals : MatcherImpl { Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) : m_data( str, caseSensitivity ) {} Equals( Equals const& other ) : m_data( other.m_data ){} virtual ~Equals(); virtual bool match( std::string const& expr ) const { return m_data.m_str == m_data.adjustString( expr );; } virtual std::string toString() const { return "equals: \"" + m_data.m_str + "\"" + m_data.toStringSuffix(); } CasedString m_data; }; struct Contains : MatcherImpl { Contains( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) : m_data( substr, caseSensitivity ){} Contains( Contains const& other ) : m_data( other.m_data ){} virtual ~Contains(); virtual bool match( std::string const& expr ) const { return m_data.adjustString( expr ).find( m_data.m_str ) != std::string::npos; } virtual std::string toString() const { return "contains: \"" + m_data.m_str + "\"" + m_data.toStringSuffix(); } CasedString m_data; }; struct StartsWith : MatcherImpl { StartsWith( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) : m_data( substr, caseSensitivity ){} StartsWith( StartsWith const& other ) : m_data( other.m_data ){} virtual ~StartsWith(); virtual bool match( std::string const& expr ) const { return startsWith( m_data.adjustString( expr ), m_data.m_str ); } virtual std::string toString() const { return "starts with: \"" + m_data.m_str + "\"" + m_data.toStringSuffix(); } CasedString m_data; }; struct EndsWith : MatcherImpl { EndsWith( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) : m_data( substr, caseSensitivity ){} EndsWith( EndsWith const& other ) : m_data( other.m_data ){} virtual ~EndsWith(); virtual bool match( std::string const& expr ) const { return endsWith( m_data.adjustString( expr ), m_data.m_str ); } virtual std::string toString() const { return "ends with: \"" + m_data.m_str + "\"" + m_data.toStringSuffix(); } CasedString m_data; }; } // namespace StdString } // namespace Impl // The following functions create the actual matcher objects. // This allows the types to be inferred template inline Impl::Generic::Not Not( Impl::Matcher const& m ) { return Impl::Generic::Not( m ); } template inline Impl::Generic::AllOf AllOf( Impl::Matcher const& m1, Impl::Matcher const& m2 ) { return Impl::Generic::AllOf().add( m1 ).add( m2 ); } template inline Impl::Generic::AllOf AllOf( Impl::Matcher const& m1, Impl::Matcher const& m2, Impl::Matcher const& m3 ) { return Impl::Generic::AllOf().add( m1 ).add( m2 ).add( m3 ); } template inline Impl::Generic::AnyOf AnyOf( Impl::Matcher const& m1, Impl::Matcher const& m2 ) { return Impl::Generic::AnyOf().add( m1 ).add( m2 ); } template inline Impl::Generic::AnyOf AnyOf( Impl::Matcher const& m1, Impl::Matcher const& m2, Impl::Matcher const& m3 ) { return Impl::Generic::AnyOf().add( m1 ).add( m2 ).add( m3 ); } inline Impl::StdString::Equals Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) { return Impl::StdString::Equals( str, caseSensitivity ); } inline Impl::StdString::Equals Equals( const char* str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) { return Impl::StdString::Equals( Impl::StdString::makeString( str ), caseSensitivity ); } inline Impl::StdString::Contains Contains( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) { return Impl::StdString::Contains( substr, caseSensitivity ); } inline Impl::StdString::Contains Contains( const char* substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) { return Impl::StdString::Contains( Impl::StdString::makeString( substr ), caseSensitivity ); } inline Impl::StdString::StartsWith StartsWith( std::string const& substr ) { return Impl::StdString::StartsWith( substr ); } inline Impl::StdString::StartsWith StartsWith( const char* substr ) { return Impl::StdString::StartsWith( Impl::StdString::makeString( substr ) ); } inline Impl::StdString::EndsWith EndsWith( std::string const& substr ) { return Impl::StdString::EndsWith( substr ); } inline Impl::StdString::EndsWith EndsWith( const char* substr ) { return Impl::StdString::EndsWith( Impl::StdString::makeString( substr ) ); } } // namespace Matchers using namespace Matchers; } // namespace Catch namespace Catch { struct TestFailureException{}; template class ExpressionLhs; struct STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison; struct CopyableStream { CopyableStream() {} CopyableStream( CopyableStream const& other ) { oss << other.oss.str(); } CopyableStream& operator=( CopyableStream const& other ) { oss.str(""); oss << other.oss.str(); return *this; } std::ostringstream oss; }; class ResultBuilder { public: ResultBuilder( char const* macroName, SourceLineInfo const& lineInfo, char const* capturedExpression, ResultDisposition::Flags resultDisposition, char const* secondArg = "" ); template ExpressionLhs operator <= ( T const& operand ); ExpressionLhs operator <= ( bool value ); template ResultBuilder& operator << ( T const& value ) { m_stream.oss << value; return *this; } template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( RhsT const& ); template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( RhsT const& ); ResultBuilder& setResultType( ResultWas::OfType result ); ResultBuilder& setResultType( bool result ); ResultBuilder& setLhs( std::string const& lhs ); ResultBuilder& setRhs( std::string const& rhs ); ResultBuilder& setOp( std::string const& op ); void endExpression(); std::string reconstructExpression() const; AssertionResult build() const; void useActiveException( ResultDisposition::Flags resultDisposition = ResultDisposition::Normal ); void captureResult( ResultWas::OfType resultType ); void captureExpression(); void captureExpectedException( std::string const& expectedMessage ); void captureExpectedException( Matchers::Impl::Matcher const& matcher ); void handleResult( AssertionResult const& result ); void react(); bool shouldDebugBreak() const; bool allowThrows() const; private: AssertionInfo m_assertionInfo; AssertionResultData m_data; struct ExprComponents { ExprComponents() : testFalse( false ) {} bool testFalse; std::string lhs, rhs, op; } m_exprComponents; CopyableStream m_stream; bool m_shouldDebugBreak; bool m_shouldThrow; }; } // namespace Catch // Include after due to circular dependency: // #included from: catch_expression_lhs.hpp #define TWOBLUECUBES_CATCH_EXPRESSION_LHS_HPP_INCLUDED // #included from: catch_evaluate.hpp #define TWOBLUECUBES_CATCH_EVALUATE_HPP_INCLUDED #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable:4389) // '==' : signed/unsigned mismatch #endif #include namespace Catch { namespace Internal { enum Operator { IsEqualTo, IsNotEqualTo, IsLessThan, IsGreaterThan, IsLessThanOrEqualTo, IsGreaterThanOrEqualTo }; template struct OperatorTraits { static const char* getName(){ return "*error*"; } }; template<> struct OperatorTraits { static const char* getName(){ return "=="; } }; template<> struct OperatorTraits { static const char* getName(){ return "!="; } }; template<> struct OperatorTraits { static const char* getName(){ return "<"; } }; template<> struct OperatorTraits { static const char* getName(){ return ">"; } }; template<> struct OperatorTraits { static const char* getName(){ return "<="; } }; template<> struct OperatorTraits{ static const char* getName(){ return ">="; } }; template inline T& opCast(T const& t) { return const_cast(t); } // nullptr_t support based on pull request #154 from Konstantin Baumann #ifdef CATCH_CONFIG_CPP11_NULLPTR inline std::nullptr_t opCast(std::nullptr_t) { return nullptr; } #endif // CATCH_CONFIG_CPP11_NULLPTR // So the compare overloads can be operator agnostic we convey the operator as a template // enum, which is used to specialise an Evaluator for doing the comparison. template class Evaluator{}; template struct Evaluator { static bool evaluate( T1 const& lhs, T2 const& rhs) { return bool( opCast( lhs ) == opCast( rhs ) ); } }; template struct Evaluator { static bool evaluate( T1 const& lhs, T2 const& rhs ) { return bool( opCast( lhs ) != opCast( rhs ) ); } }; template struct Evaluator { static bool evaluate( T1 const& lhs, T2 const& rhs ) { return bool( opCast( lhs ) < opCast( rhs ) ); } }; template struct Evaluator { static bool evaluate( T1 const& lhs, T2 const& rhs ) { return bool( opCast( lhs ) > opCast( rhs ) ); } }; template struct Evaluator { static bool evaluate( T1 const& lhs, T2 const& rhs ) { return bool( opCast( lhs ) >= opCast( rhs ) ); } }; template struct Evaluator { static bool evaluate( T1 const& lhs, T2 const& rhs ) { return bool( opCast( lhs ) <= opCast( rhs ) ); } }; template bool applyEvaluator( T1 const& lhs, T2 const& rhs ) { return Evaluator::evaluate( lhs, rhs ); } // This level of indirection allows us to specialise for integer types // to avoid signed/ unsigned warnings // "base" overload template bool compare( T1 const& lhs, T2 const& rhs ) { return Evaluator::evaluate( lhs, rhs ); } // unsigned X to int template bool compare( unsigned int lhs, int rhs ) { return applyEvaluator( lhs, static_cast( rhs ) ); } template bool compare( unsigned long lhs, int rhs ) { return applyEvaluator( lhs, static_cast( rhs ) ); } template bool compare( unsigned char lhs, int rhs ) { return applyEvaluator( lhs, static_cast( rhs ) ); } // unsigned X to long template bool compare( unsigned int lhs, long rhs ) { return applyEvaluator( lhs, static_cast( rhs ) ); } template bool compare( unsigned long lhs, long rhs ) { return applyEvaluator( lhs, static_cast( rhs ) ); } template bool compare( unsigned char lhs, long rhs ) { return applyEvaluator( lhs, static_cast( rhs ) ); } // int to unsigned X template bool compare( int lhs, unsigned int rhs ) { return applyEvaluator( static_cast( lhs ), rhs ); } template bool compare( int lhs, unsigned long rhs ) { return applyEvaluator( static_cast( lhs ), rhs ); } template bool compare( int lhs, unsigned char rhs ) { return applyEvaluator( static_cast( lhs ), rhs ); } // long to unsigned X template bool compare( long lhs, unsigned int rhs ) { return applyEvaluator( static_cast( lhs ), rhs ); } template bool compare( long lhs, unsigned long rhs ) { return applyEvaluator( static_cast( lhs ), rhs ); } template bool compare( long lhs, unsigned char rhs ) { return applyEvaluator( static_cast( lhs ), rhs ); } // pointer to long (when comparing against NULL) template bool compare( long lhs, T* rhs ) { return Evaluator::evaluate( reinterpret_cast( lhs ), rhs ); } template bool compare( T* lhs, long rhs ) { return Evaluator::evaluate( lhs, reinterpret_cast( rhs ) ); } // pointer to int (when comparing against NULL) template bool compare( int lhs, T* rhs ) { return Evaluator::evaluate( reinterpret_cast( lhs ), rhs ); } template bool compare( T* lhs, int rhs ) { return Evaluator::evaluate( lhs, reinterpret_cast( rhs ) ); } #ifdef CATCH_CONFIG_CPP11_LONG_LONG // long long to unsigned X template bool compare( long long lhs, unsigned int rhs ) { return applyEvaluator( static_cast( lhs ), rhs ); } template bool compare( long long lhs, unsigned long rhs ) { return applyEvaluator( static_cast( lhs ), rhs ); } template bool compare( long long lhs, unsigned long long rhs ) { return applyEvaluator( static_cast( lhs ), rhs ); } template bool compare( long long lhs, unsigned char rhs ) { return applyEvaluator( static_cast( lhs ), rhs ); } // unsigned long long to X template bool compare( unsigned long long lhs, int rhs ) { return applyEvaluator( static_cast( lhs ), rhs ); } template bool compare( unsigned long long lhs, long rhs ) { return applyEvaluator( static_cast( lhs ), rhs ); } template bool compare( unsigned long long lhs, long long rhs ) { return applyEvaluator( static_cast( lhs ), rhs ); } template bool compare( unsigned long long lhs, char rhs ) { return applyEvaluator( static_cast( lhs ), rhs ); } // pointer to long long (when comparing against NULL) template bool compare( long long lhs, T* rhs ) { return Evaluator::evaluate( reinterpret_cast( lhs ), rhs ); } template bool compare( T* lhs, long long rhs ) { return Evaluator::evaluate( lhs, reinterpret_cast( rhs ) ); } #endif // CATCH_CONFIG_CPP11_LONG_LONG #ifdef CATCH_CONFIG_CPP11_NULLPTR // pointer to nullptr_t (when comparing against nullptr) template bool compare( std::nullptr_t, T* rhs ) { return Evaluator::evaluate( nullptr, rhs ); } template bool compare( T* lhs, std::nullptr_t ) { return Evaluator::evaluate( lhs, nullptr ); } #endif // CATCH_CONFIG_CPP11_NULLPTR } // end of namespace Internal } // end of namespace Catch #ifdef _MSC_VER #pragma warning(pop) #endif // #included from: catch_tostring.h #define TWOBLUECUBES_CATCH_TOSTRING_H_INCLUDED #include #include #include #include #include #ifdef __OBJC__ // #included from: catch_objc_arc.hpp #define TWOBLUECUBES_CATCH_OBJC_ARC_HPP_INCLUDED #import #ifdef __has_feature #define CATCH_ARC_ENABLED __has_feature(objc_arc) #else #define CATCH_ARC_ENABLED 0 #endif void arcSafeRelease( NSObject* obj ); id performOptionalSelector( id obj, SEL sel ); #if !CATCH_ARC_ENABLED inline void arcSafeRelease( NSObject* obj ) { [obj release]; } inline id performOptionalSelector( id obj, SEL sel ) { if( [obj respondsToSelector: sel] ) return [obj performSelector: sel]; return nil; } #define CATCH_UNSAFE_UNRETAINED #define CATCH_ARC_STRONG #else inline void arcSafeRelease( NSObject* ){} inline id performOptionalSelector( id obj, SEL sel ) { #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-performSelector-leaks" #endif if( [obj respondsToSelector: sel] ) return [obj performSelector: sel]; #ifdef __clang__ #pragma clang diagnostic pop #endif return nil; } #define CATCH_UNSAFE_UNRETAINED __unsafe_unretained #define CATCH_ARC_STRONG __strong #endif #endif #ifdef CATCH_CONFIG_CPP11_TUPLE #include #endif #ifdef CATCH_CONFIG_CPP11_IS_ENUM #include #endif namespace Catch { // Why we're here. template std::string toString( T const& value ); // Built in overloads std::string toString( std::string const& value ); std::string toString( std::wstring const& value ); std::string toString( const char* const value ); std::string toString( char* const value ); std::string toString( const wchar_t* const value ); std::string toString( wchar_t* const value ); std::string toString( int value ); std::string toString( unsigned long value ); std::string toString( unsigned int value ); std::string toString( const double value ); std::string toString( const float value ); std::string toString( bool value ); std::string toString( char value ); std::string toString( signed char value ); std::string toString( unsigned char value ); #ifdef CATCH_CONFIG_CPP11_LONG_LONG std::string toString( long long value ); std::string toString( unsigned long long value ); #endif #ifdef CATCH_CONFIG_CPP11_NULLPTR std::string toString( std::nullptr_t ); #endif #ifdef __OBJC__ std::string toString( NSString const * const& nsstring ); std::string toString( NSString * CATCH_ARC_STRONG const& nsstring ); std::string toString( NSObject* const& nsObject ); #endif namespace Detail { extern const std::string unprintableString; struct BorgType { template BorgType( T const& ); }; struct TrueType { char sizer[1]; }; struct FalseType { char sizer[2]; }; TrueType& testStreamable( std::ostream& ); FalseType testStreamable( FalseType ); FalseType operator<<( std::ostream const&, BorgType const& ); template struct IsStreamInsertable { static std::ostream &s; static T const&t; enum { value = sizeof( testStreamable(s << t) ) == sizeof( TrueType ) }; }; #if defined(CATCH_CONFIG_CPP11_IS_ENUM) template::value > struct EnumStringMaker { static std::string convert( T const& ) { return unprintableString; } }; template struct EnumStringMaker { static std::string convert( T const& v ) { return ::Catch::toString( static_cast::type>(v) ); } }; #endif template struct StringMakerBase { #if defined(CATCH_CONFIG_CPP11_IS_ENUM) template static std::string convert( T const& v ) { return EnumStringMaker::convert( v ); } #else template static std::string convert( T const& ) { return unprintableString; } #endif }; template<> struct StringMakerBase { template static std::string convert( T const& _value ) { std::ostringstream oss; oss << _value; return oss.str(); } }; std::string rawMemoryToString( const void *object, std::size_t size ); template inline std::string rawMemoryToString( const T& object ) { return rawMemoryToString( &object, sizeof(object) ); } } // end namespace Detail template struct StringMaker : Detail::StringMakerBase::value> {}; template struct StringMaker { template static std::string convert( U* p ) { if( !p ) return "NULL"; else return Detail::rawMemoryToString( p ); } }; template struct StringMaker { static std::string convert( R C::* p ) { if( !p ) return "NULL"; else return Detail::rawMemoryToString( p ); } }; namespace Detail { template std::string rangeToString( InputIterator first, InputIterator last ); } //template //struct StringMaker > { // static std::string convert( std::vector const& v ) { // return Detail::rangeToString( v.begin(), v.end() ); // } //}; template std::string toString( std::vector const& v ) { return Detail::rangeToString( v.begin(), v.end() ); } #ifdef CATCH_CONFIG_CPP11_TUPLE // toString for tuples namespace TupleDetail { template< typename Tuple, std::size_t N = 0, bool = (N < std::tuple_size::value) > struct ElementPrinter { static void print( const Tuple& tuple, std::ostream& os ) { os << ( N ? ", " : " " ) << Catch::toString(std::get(tuple)); ElementPrinter::print(tuple,os); } }; template< typename Tuple, std::size_t N > struct ElementPrinter { static void print( const Tuple&, std::ostream& ) {} }; } template struct StringMaker> { static std::string convert( const std::tuple& tuple ) { std::ostringstream os; os << '{'; TupleDetail::ElementPrinter>::print( tuple, os ); os << " }"; return os.str(); } }; #endif // CATCH_CONFIG_CPP11_TUPLE namespace Detail { template std::string makeString( T const& value ) { return StringMaker::convert( value ); } } // end namespace Detail /// \brief converts any type to a string /// /// The default template forwards on to ostringstream - except when an /// ostringstream overload does not exist - in which case it attempts to detect /// that and writes {?}. /// Overload (not specialise) this template for custom typs that you don't want /// to provide an ostream overload for. template std::string toString( T const& value ) { return StringMaker::convert( value ); } namespace Detail { template std::string rangeToString( InputIterator first, InputIterator last ) { std::ostringstream oss; oss << "{ "; if( first != last ) { oss << Catch::toString( *first ); for( ++first ; first != last ; ++first ) oss << ", " << Catch::toString( *first ); } oss << " }"; return oss.str(); } } } // end namespace Catch namespace Catch { // Wraps the LHS of an expression and captures the operator and RHS (if any) - // wrapping them all in a ResultBuilder object template class ExpressionLhs { ExpressionLhs& operator = ( ExpressionLhs const& ); # ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS ExpressionLhs& operator = ( ExpressionLhs && ) = delete; # endif public: ExpressionLhs( ResultBuilder& rb, T lhs ) : m_rb( rb ), m_lhs( lhs ) {} # ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS ExpressionLhs( ExpressionLhs const& ) = default; ExpressionLhs( ExpressionLhs && ) = default; # endif template ResultBuilder& operator == ( RhsT const& rhs ) { return captureExpression( rhs ); } template ResultBuilder& operator != ( RhsT const& rhs ) { return captureExpression( rhs ); } template ResultBuilder& operator < ( RhsT const& rhs ) { return captureExpression( rhs ); } template ResultBuilder& operator > ( RhsT const& rhs ) { return captureExpression( rhs ); } template ResultBuilder& operator <= ( RhsT const& rhs ) { return captureExpression( rhs ); } template ResultBuilder& operator >= ( RhsT const& rhs ) { return captureExpression( rhs ); } ResultBuilder& operator == ( bool rhs ) { return captureExpression( rhs ); } ResultBuilder& operator != ( bool rhs ) { return captureExpression( rhs ); } void endExpression() { bool value = m_lhs ? true : false; m_rb .setLhs( Catch::toString( value ) ) .setResultType( value ) .endExpression(); } // Only simple binary expressions are allowed on the LHS. // If more complex compositions are required then place the sub expression in parentheses template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator + ( RhsT const& ); template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator - ( RhsT const& ); template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator / ( RhsT const& ); template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator * ( RhsT const& ); template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( RhsT const& ); template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( RhsT const& ); private: template ResultBuilder& captureExpression( RhsT const& rhs ) { return m_rb .setResultType( Internal::compare( m_lhs, rhs ) ) .setLhs( Catch::toString( m_lhs ) ) .setRhs( Catch::toString( rhs ) ) .setOp( Internal::OperatorTraits::getName() ); } private: ResultBuilder& m_rb; T m_lhs; }; } // end namespace Catch namespace Catch { template inline ExpressionLhs ResultBuilder::operator <= ( T const& operand ) { return ExpressionLhs( *this, operand ); } inline ExpressionLhs ResultBuilder::operator <= ( bool value ) { return ExpressionLhs( *this, value ); } } // namespace Catch // #included from: catch_message.h #define TWOBLUECUBES_CATCH_MESSAGE_H_INCLUDED #include namespace Catch { struct MessageInfo { MessageInfo( std::string const& _macroName, SourceLineInfo const& _lineInfo, ResultWas::OfType _type ); std::string macroName; SourceLineInfo lineInfo; ResultWas::OfType type; std::string message; unsigned int sequence; bool operator == ( MessageInfo const& other ) const { return sequence == other.sequence; } bool operator < ( MessageInfo const& other ) const { return sequence < other.sequence; } private: static unsigned int globalCount; }; struct MessageBuilder { MessageBuilder( std::string const& macroName, SourceLineInfo const& lineInfo, ResultWas::OfType type ) : m_info( macroName, lineInfo, type ) {} template MessageBuilder& operator << ( T const& value ) { m_stream << value; return *this; } MessageInfo m_info; std::ostringstream m_stream; }; class ScopedMessage { public: ScopedMessage( MessageBuilder const& builder ); ScopedMessage( ScopedMessage const& other ); ~ScopedMessage(); MessageInfo m_info; }; } // end namespace Catch // #included from: catch_interfaces_capture.h #define TWOBLUECUBES_CATCH_INTERFACES_CAPTURE_H_INCLUDED #include namespace Catch { class TestCase; class AssertionResult; struct AssertionInfo; struct SectionInfo; struct SectionEndInfo; struct MessageInfo; class ScopedMessageBuilder; struct Counts; struct IResultCapture { virtual ~IResultCapture(); virtual void assertionEnded( AssertionResult const& result ) = 0; virtual bool sectionStarted( SectionInfo const& sectionInfo, Counts& assertions ) = 0; virtual void sectionEnded( SectionEndInfo const& endInfo ) = 0; virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) = 0; virtual void pushScopedMessage( MessageInfo const& message ) = 0; virtual void popScopedMessage( MessageInfo const& message ) = 0; virtual std::string getCurrentTestName() const = 0; virtual const AssertionResult* getLastResult() const = 0; virtual void handleFatalErrorCondition( std::string const& message ) = 0; }; IResultCapture& getResultCapture(); } // #included from: catch_debugger.h #define TWOBLUECUBES_CATCH_DEBUGGER_H_INCLUDED // #included from: catch_platform.h #define TWOBLUECUBES_CATCH_PLATFORM_H_INCLUDED #if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) #define CATCH_PLATFORM_MAC #elif defined(__IPHONE_OS_VERSION_MIN_REQUIRED) #define CATCH_PLATFORM_IPHONE #elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) #define CATCH_PLATFORM_WINDOWS #endif #include namespace Catch{ bool isDebuggerActive(); void writeToDebugConsole( std::string const& text ); } #ifdef CATCH_PLATFORM_MAC // The following code snippet based on: // http://cocoawithlove.com/2008/03/break-into-debugger.html #ifdef DEBUG #if defined(__ppc64__) || defined(__ppc__) #define CATCH_BREAK_INTO_DEBUGGER() \ if( Catch::isDebuggerActive() ) { \ __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n" \ : : : "memory","r0","r3","r4" ); \ } #else #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) {__asm__("int $3\n" : : );} #endif #endif #elif defined(_MSC_VER) #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { __debugbreak(); } #elif defined(__MINGW32__) extern "C" __declspec(dllimport) void __stdcall DebugBreak(); #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { DebugBreak(); } #endif #ifndef CATCH_BREAK_INTO_DEBUGGER #define CATCH_BREAK_INTO_DEBUGGER() Catch::alwaysTrue(); #endif // #included from: catch_interfaces_runner.h #define TWOBLUECUBES_CATCH_INTERFACES_RUNNER_H_INCLUDED namespace Catch { class TestCase; struct IRunner { virtual ~IRunner(); virtual bool aborting() const = 0; }; } /////////////////////////////////////////////////////////////////////////////// // In the event of a failure works out if the debugger needs to be invoked // and/or an exception thrown and takes appropriate action. // This needs to be done as a macro so the debugger will stop in the user // source code rather than in Catch library code #define INTERNAL_CATCH_REACT( resultBuilder ) \ if( resultBuilder.shouldDebugBreak() ) CATCH_BREAK_INTO_DEBUGGER(); \ resultBuilder.react(); /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ) \ do { \ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ try { \ CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ ( __catchResult <= expr ).endExpression(); \ } \ catch( ... ) { \ __catchResult.useActiveException( Catch::ResultDisposition::Normal ); \ } \ INTERNAL_CATCH_REACT( __catchResult ) \ } while( Catch::isTrue( false && static_cast(expr) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_IF( expr, resultDisposition, macroName ) \ INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ); \ if( Catch::getResultCapture().getLastResult()->succeeded() ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_ELSE( expr, resultDisposition, macroName ) \ INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ); \ if( !Catch::getResultCapture().getLastResult()->succeeded() ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_NO_THROW( expr, resultDisposition, macroName ) \ do { \ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ try { \ expr; \ __catchResult.captureResult( Catch::ResultWas::Ok ); \ } \ catch( ... ) { \ __catchResult.useActiveException( resultDisposition ); \ } \ INTERNAL_CATCH_REACT( __catchResult ) \ } while( Catch::alwaysFalse() ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_THROWS( expr, resultDisposition, matcher, macroName ) \ do { \ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition, #matcher ); \ if( __catchResult.allowThrows() ) \ try { \ expr; \ __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \ } \ catch( ... ) { \ __catchResult.captureExpectedException( matcher ); \ } \ else \ __catchResult.captureResult( Catch::ResultWas::Ok ); \ INTERNAL_CATCH_REACT( __catchResult ) \ } while( Catch::alwaysFalse() ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_THROWS_AS( expr, exceptionType, resultDisposition, macroName ) \ do { \ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ if( __catchResult.allowThrows() ) \ try { \ expr; \ __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \ } \ catch( exceptionType ) { \ __catchResult.captureResult( Catch::ResultWas::Ok ); \ } \ catch( ... ) { \ __catchResult.useActiveException( resultDisposition ); \ } \ else \ __catchResult.captureResult( Catch::ResultWas::Ok ); \ INTERNAL_CATCH_REACT( __catchResult ) \ } while( Catch::alwaysFalse() ) /////////////////////////////////////////////////////////////////////////////// #ifdef CATCH_CONFIG_VARIADIC_MACROS #define INTERNAL_CATCH_MSG( messageType, resultDisposition, macroName, ... ) \ do { \ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \ __catchResult << __VA_ARGS__ + ::Catch::StreamEndStop(); \ __catchResult.captureResult( messageType ); \ INTERNAL_CATCH_REACT( __catchResult ) \ } while( Catch::alwaysFalse() ) #else #define INTERNAL_CATCH_MSG( messageType, resultDisposition, macroName, log ) \ do { \ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \ __catchResult << log + ::Catch::StreamEndStop(); \ __catchResult.captureResult( messageType ); \ INTERNAL_CATCH_REACT( __catchResult ) \ } while( Catch::alwaysFalse() ) #endif /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_INFO( log, macroName ) \ Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage ) = Catch::MessageBuilder( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log; /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CHECK_THAT( arg, matcher, resultDisposition, macroName ) \ do { \ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #arg ", " #matcher, resultDisposition ); \ try { \ std::string matcherAsString = (matcher).toString(); \ __catchResult \ .setLhs( Catch::toString( arg ) ) \ .setRhs( matcherAsString == Catch::Detail::unprintableString ? #matcher : matcherAsString ) \ .setOp( "matches" ) \ .setResultType( (matcher).match( arg ) ); \ __catchResult.captureExpression(); \ } catch( ... ) { \ __catchResult.useActiveException( resultDisposition | Catch::ResultDisposition::ContinueOnFailure ); \ } \ INTERNAL_CATCH_REACT( __catchResult ) \ } while( Catch::alwaysFalse() ) // #included from: internal/catch_section.h #define TWOBLUECUBES_CATCH_SECTION_H_INCLUDED // #included from: catch_section_info.h #define TWOBLUECUBES_CATCH_SECTION_INFO_H_INCLUDED // #included from: catch_totals.hpp #define TWOBLUECUBES_CATCH_TOTALS_HPP_INCLUDED #include namespace Catch { struct Counts { Counts() : passed( 0 ), failed( 0 ), failedButOk( 0 ) {} Counts operator - ( Counts const& other ) const { Counts diff; diff.passed = passed - other.passed; diff.failed = failed - other.failed; diff.failedButOk = failedButOk - other.failedButOk; return diff; } Counts& operator += ( Counts const& other ) { passed += other.passed; failed += other.failed; failedButOk += other.failedButOk; return *this; } std::size_t total() const { return passed + failed + failedButOk; } bool allPassed() const { return failed == 0 && failedButOk == 0; } bool allOk() const { return failed == 0; } std::size_t passed; std::size_t failed; std::size_t failedButOk; }; struct Totals { Totals operator - ( Totals const& other ) const { Totals diff; diff.assertions = assertions - other.assertions; diff.testCases = testCases - other.testCases; return diff; } Totals delta( Totals const& prevTotals ) const { Totals diff = *this - prevTotals; if( diff.assertions.failed > 0 ) ++diff.testCases.failed; else if( diff.assertions.failedButOk > 0 ) ++diff.testCases.failedButOk; else ++diff.testCases.passed; return diff; } Totals& operator += ( Totals const& other ) { assertions += other.assertions; testCases += other.testCases; return *this; } Counts assertions; Counts testCases; }; } namespace Catch { struct SectionInfo { SectionInfo ( SourceLineInfo const& _lineInfo, std::string const& _name, std::string const& _description = std::string() ); std::string name; std::string description; SourceLineInfo lineInfo; }; struct SectionEndInfo { SectionEndInfo( SectionInfo const& _sectionInfo, Counts const& _prevAssertions, double _durationInSeconds ) : sectionInfo( _sectionInfo ), prevAssertions( _prevAssertions ), durationInSeconds( _durationInSeconds ) {} SectionInfo sectionInfo; Counts prevAssertions; double durationInSeconds; }; } // end namespace Catch // #included from: catch_timer.h #define TWOBLUECUBES_CATCH_TIMER_H_INCLUDED #ifdef CATCH_PLATFORM_WINDOWS typedef unsigned long long uint64_t; #else #include #endif namespace Catch { class Timer { public: Timer() : m_ticks( 0 ) {} void start(); unsigned int getElapsedMicroseconds() const; unsigned int getElapsedMilliseconds() const; double getElapsedSeconds() const; private: uint64_t m_ticks; }; } // namespace Catch #include namespace Catch { class Section : NonCopyable { public: Section( SectionInfo const& info ); ~Section(); // This indicates whether the section should be executed or not operator bool() const; private: SectionInfo m_info; std::string m_name; Counts m_assertions; bool m_sectionIncluded; Timer m_timer; }; } // end namespace Catch #ifdef CATCH_CONFIG_VARIADIC_MACROS #define INTERNAL_CATCH_SECTION( ... ) \ if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) #else #define INTERNAL_CATCH_SECTION( name, desc ) \ if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, name, desc ) ) #endif // #included from: internal/catch_generators.hpp #define TWOBLUECUBES_CATCH_GENERATORS_HPP_INCLUDED #include #include #include #include namespace Catch { template struct IGenerator { virtual ~IGenerator() {} virtual T getValue( std::size_t index ) const = 0; virtual std::size_t size () const = 0; }; template class BetweenGenerator : public IGenerator { public: BetweenGenerator( T from, T to ) : m_from( from ), m_to( to ){} virtual T getValue( std::size_t index ) const { return m_from+static_cast( index ); } virtual std::size_t size() const { return static_cast( 1+m_to-m_from ); } private: T m_from; T m_to; }; template class ValuesGenerator : public IGenerator { public: ValuesGenerator(){} void add( T value ) { m_values.push_back( value ); } virtual T getValue( std::size_t index ) const { return m_values[index]; } virtual std::size_t size() const { return m_values.size(); } private: std::vector m_values; }; template class CompositeGenerator { public: CompositeGenerator() : m_totalSize( 0 ) {} // *** Move semantics, similar to auto_ptr *** CompositeGenerator( CompositeGenerator& other ) : m_fileInfo( other.m_fileInfo ), m_totalSize( 0 ) { move( other ); } CompositeGenerator& setFileInfo( const char* fileInfo ) { m_fileInfo = fileInfo; return *this; } ~CompositeGenerator() { deleteAll( m_composed ); } operator T () const { size_t overallIndex = getCurrentContext().getGeneratorIndex( m_fileInfo, m_totalSize ); typename std::vector*>::const_iterator it = m_composed.begin(); typename std::vector*>::const_iterator itEnd = m_composed.end(); for( size_t index = 0; it != itEnd; ++it ) { const IGenerator* generator = *it; if( overallIndex >= index && overallIndex < index + generator->size() ) { return generator->getValue( overallIndex-index ); } index += generator->size(); } CATCH_INTERNAL_ERROR( "Indexed past end of generated range" ); return T(); // Suppress spurious "not all control paths return a value" warning in Visual Studio - if you know how to fix this please do so } void add( const IGenerator* generator ) { m_totalSize += generator->size(); m_composed.push_back( generator ); } CompositeGenerator& then( CompositeGenerator& other ) { move( other ); return *this; } CompositeGenerator& then( T value ) { ValuesGenerator* valuesGen = new ValuesGenerator(); valuesGen->add( value ); add( valuesGen ); return *this; } private: void move( CompositeGenerator& other ) { std::copy( other.m_composed.begin(), other.m_composed.end(), std::back_inserter( m_composed ) ); m_totalSize += other.m_totalSize; other.m_composed.clear(); } std::vector*> m_composed; std::string m_fileInfo; size_t m_totalSize; }; namespace Generators { template CompositeGenerator between( T from, T to ) { CompositeGenerator generators; generators.add( new BetweenGenerator( from, to ) ); return generators; } template CompositeGenerator values( T val1, T val2 ) { CompositeGenerator generators; ValuesGenerator* valuesGen = new ValuesGenerator(); valuesGen->add( val1 ); valuesGen->add( val2 ); generators.add( valuesGen ); return generators; } template CompositeGenerator values( T val1, T val2, T val3 ){ CompositeGenerator generators; ValuesGenerator* valuesGen = new ValuesGenerator(); valuesGen->add( val1 ); valuesGen->add( val2 ); valuesGen->add( val3 ); generators.add( valuesGen ); return generators; } template CompositeGenerator values( T val1, T val2, T val3, T val4 ) { CompositeGenerator generators; ValuesGenerator* valuesGen = new ValuesGenerator(); valuesGen->add( val1 ); valuesGen->add( val2 ); valuesGen->add( val3 ); valuesGen->add( val4 ); generators.add( valuesGen ); return generators; } } // end namespace Generators using namespace Generators; } // end namespace Catch #define INTERNAL_CATCH_LINESTR2( line ) #line #define INTERNAL_CATCH_LINESTR( line ) INTERNAL_CATCH_LINESTR2( line ) #define INTERNAL_CATCH_GENERATE( expr ) expr.setFileInfo( __FILE__ "(" INTERNAL_CATCH_LINESTR( __LINE__ ) ")" ) // #included from: internal/catch_interfaces_exception.h #define TWOBLUECUBES_CATCH_INTERFACES_EXCEPTION_H_INCLUDED #include #include // #included from: catch_interfaces_registry_hub.h #define TWOBLUECUBES_CATCH_INTERFACES_REGISTRY_HUB_H_INCLUDED #include namespace Catch { class TestCase; struct ITestCaseRegistry; struct IExceptionTranslatorRegistry; struct IExceptionTranslator; struct IReporterRegistry; struct IReporterFactory; struct IRegistryHub { virtual ~IRegistryHub(); virtual IReporterRegistry const& getReporterRegistry() const = 0; virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0; virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() = 0; }; struct IMutableRegistryHub { virtual ~IMutableRegistryHub(); virtual void registerReporter( std::string const& name, Ptr const& factory ) = 0; virtual void registerListener( Ptr const& factory ) = 0; virtual void registerTest( TestCase const& testInfo ) = 0; virtual void registerTranslator( const IExceptionTranslator* translator ) = 0; }; IRegistryHub& getRegistryHub(); IMutableRegistryHub& getMutableRegistryHub(); void cleanUp(); std::string translateActiveException(); } namespace Catch { typedef std::string(*exceptionTranslateFunction)(); struct IExceptionTranslator; typedef std::vector ExceptionTranslators; struct IExceptionTranslator { virtual ~IExceptionTranslator(); virtual std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const = 0; }; struct IExceptionTranslatorRegistry { virtual ~IExceptionTranslatorRegistry(); virtual std::string translateActiveException() const = 0; }; class ExceptionTranslatorRegistrar { template class ExceptionTranslator : public IExceptionTranslator { public: ExceptionTranslator( std::string(*translateFunction)( T& ) ) : m_translateFunction( translateFunction ) {} virtual std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const CATCH_OVERRIDE { try { if( it == itEnd ) throw; else return (*it)->translate( it+1, itEnd ); } catch( T& ex ) { return m_translateFunction( ex ); } } protected: std::string(*m_translateFunction)( T& ); }; public: template ExceptionTranslatorRegistrar( std::string(*translateFunction)( T& ) ) { getMutableRegistryHub().registerTranslator ( new ExceptionTranslator( translateFunction ) ); } }; } /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TRANSLATE_EXCEPTION2( translatorName, signature ) \ static std::string translatorName( signature ); \ namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &translatorName ); }\ static std::string translatorName( signature ) #define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION2( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature ) // #included from: internal/catch_approx.hpp #define TWOBLUECUBES_CATCH_APPROX_HPP_INCLUDED #include #include namespace Catch { namespace Detail { class Approx { public: explicit Approx ( double value ) : m_epsilon( std::numeric_limits::epsilon()*100 ), m_scale( 1.0 ), m_value( value ) {} Approx( Approx const& other ) : m_epsilon( other.m_epsilon ), m_scale( other.m_scale ), m_value( other.m_value ) {} static Approx custom() { return Approx( 0 ); } Approx operator()( double value ) { Approx approx( value ); approx.epsilon( m_epsilon ); approx.scale( m_scale ); return approx; } friend bool operator == ( double lhs, Approx const& rhs ) { // Thanks to Richard Harris for his help refining this formula return fabs( lhs - rhs.m_value ) < rhs.m_epsilon * (rhs.m_scale + (std::max)( fabs(lhs), fabs(rhs.m_value) ) ); } friend bool operator == ( Approx const& lhs, double rhs ) { return operator==( rhs, lhs ); } friend bool operator != ( double lhs, Approx const& rhs ) { return !operator==( lhs, rhs ); } friend bool operator != ( Approx const& lhs, double rhs ) { return !operator==( rhs, lhs ); } Approx& epsilon( double newEpsilon ) { m_epsilon = newEpsilon; return *this; } Approx& scale( double newScale ) { m_scale = newScale; return *this; } std::string toString() const { std::ostringstream oss; oss << "Approx( " << Catch::toString( m_value ) << " )"; return oss.str(); } private: double m_epsilon; double m_scale; double m_value; }; } template<> inline std::string toString( Detail::Approx const& value ) { return value.toString(); } } // end namespace Catch // #included from: internal/catch_interfaces_tag_alias_registry.h #define TWOBLUECUBES_CATCH_INTERFACES_TAG_ALIAS_REGISTRY_H_INCLUDED // #included from: catch_tag_alias.h #define TWOBLUECUBES_CATCH_TAG_ALIAS_H_INCLUDED #include namespace Catch { struct TagAlias { TagAlias( std::string _tag, SourceLineInfo _lineInfo ) : tag( _tag ), lineInfo( _lineInfo ) {} std::string tag; SourceLineInfo lineInfo; }; struct RegistrarForTagAliases { RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); }; } // end namespace Catch #define CATCH_REGISTER_TAG_ALIAS( alias, spec ) namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } // #included from: catch_option.hpp #define TWOBLUECUBES_CATCH_OPTION_HPP_INCLUDED namespace Catch { // An optional type template class Option { public: Option() : nullableValue( CATCH_NULL ) {} Option( T const& _value ) : nullableValue( new( storage ) T( _value ) ) {} Option( Option const& _other ) : nullableValue( _other ? new( storage ) T( *_other ) : CATCH_NULL ) {} ~Option() { reset(); } Option& operator= ( Option const& _other ) { if( &_other != this ) { reset(); if( _other ) nullableValue = new( storage ) T( *_other ); } return *this; } Option& operator = ( T const& _value ) { reset(); nullableValue = new( storage ) T( _value ); return *this; } void reset() { if( nullableValue ) nullableValue->~T(); nullableValue = CATCH_NULL; } T& operator*() { return *nullableValue; } T const& operator*() const { return *nullableValue; } T* operator->() { return nullableValue; } const T* operator->() const { return nullableValue; } T valueOr( T const& defaultValue ) const { return nullableValue ? *nullableValue : defaultValue; } bool some() const { return nullableValue != CATCH_NULL; } bool none() const { return nullableValue == CATCH_NULL; } bool operator !() const { return nullableValue == CATCH_NULL; } operator SafeBool::type() const { return SafeBool::makeSafe( some() ); } private: T* nullableValue; char storage[sizeof(T)]; }; } // end namespace Catch namespace Catch { struct ITagAliasRegistry { virtual ~ITagAliasRegistry(); virtual Option find( std::string const& alias ) const = 0; virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const = 0; static ITagAliasRegistry const& get(); }; } // end namespace Catch // These files are included here so the single_include script doesn't put them // in the conditionally compiled sections // #included from: internal/catch_test_case_info.h #define TWOBLUECUBES_CATCH_TEST_CASE_INFO_H_INCLUDED #include #include #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wpadded" #endif namespace Catch { struct ITestCase; struct TestCaseInfo { enum SpecialProperties{ None = 0, IsHidden = 1 << 1, ShouldFail = 1 << 2, MayFail = 1 << 3, Throws = 1 << 4 }; TestCaseInfo( std::string const& _name, std::string const& _className, std::string const& _description, std::set const& _tags, SourceLineInfo const& _lineInfo ); TestCaseInfo( TestCaseInfo const& other ); friend void setTags( TestCaseInfo& testCaseInfo, std::set const& tags ); bool isHidden() const; bool throws() const; bool okToFail() const; bool expectedToFail() const; std::string name; std::string className; std::string description; std::set tags; std::set lcaseTags; std::string tagsAsString; SourceLineInfo lineInfo; SpecialProperties properties; }; class TestCase : public TestCaseInfo { public: TestCase( ITestCase* testCase, TestCaseInfo const& info ); TestCase( TestCase const& other ); TestCase withName( std::string const& _newName ) const; void invoke() const; TestCaseInfo const& getTestCaseInfo() const; void swap( TestCase& other ); bool operator == ( TestCase const& other ) const; bool operator < ( TestCase const& other ) const; TestCase& operator = ( TestCase const& other ); private: Ptr test; }; TestCase makeTestCase( ITestCase* testCase, std::string const& className, std::string const& name, std::string const& description, SourceLineInfo const& lineInfo ); } #ifdef __clang__ #pragma clang diagnostic pop #endif #ifdef __OBJC__ // #included from: internal/catch_objc.hpp #define TWOBLUECUBES_CATCH_OBJC_HPP_INCLUDED #import #include // NB. Any general catch headers included here must be included // in catch.hpp first to make sure they are included by the single // header for non obj-usage /////////////////////////////////////////////////////////////////////////////// // This protocol is really only here for (self) documenting purposes, since // all its methods are optional. @protocol OcFixture @optional -(void) setUp; -(void) tearDown; @end namespace Catch { class OcMethod : public SharedImpl { public: OcMethod( Class cls, SEL sel ) : m_cls( cls ), m_sel( sel ) {} virtual void invoke() const { id obj = [[m_cls alloc] init]; performOptionalSelector( obj, @selector(setUp) ); performOptionalSelector( obj, m_sel ); performOptionalSelector( obj, @selector(tearDown) ); arcSafeRelease( obj ); } private: virtual ~OcMethod() {} Class m_cls; SEL m_sel; }; namespace Detail{ inline std::string getAnnotation( Class cls, std::string const& annotationName, std::string const& testCaseName ) { NSString* selStr = [[NSString alloc] initWithFormat:@"Catch_%s_%s", annotationName.c_str(), testCaseName.c_str()]; SEL sel = NSSelectorFromString( selStr ); arcSafeRelease( selStr ); id value = performOptionalSelector( cls, sel ); if( value ) return [(NSString*)value UTF8String]; return ""; } } inline size_t registerTestMethods() { size_t noTestMethods = 0; int noClasses = objc_getClassList( CATCH_NULL, 0 ); Class* classes = (CATCH_UNSAFE_UNRETAINED Class *)malloc( sizeof(Class) * noClasses); objc_getClassList( classes, noClasses ); for( int c = 0; c < noClasses; c++ ) { Class cls = classes[c]; { u_int count; Method* methods = class_copyMethodList( cls, &count ); for( u_int m = 0; m < count ; m++ ) { SEL selector = method_getName(methods[m]); std::string methodName = sel_getName(selector); if( startsWith( methodName, "Catch_TestCase_" ) ) { std::string testCaseName = methodName.substr( 15 ); std::string name = Detail::getAnnotation( cls, "Name", testCaseName ); std::string desc = Detail::getAnnotation( cls, "Description", testCaseName ); const char* className = class_getName( cls ); getMutableRegistryHub().registerTest( makeTestCase( new OcMethod( cls, selector ), className, name.c_str(), desc.c_str(), SourceLineInfo() ) ); noTestMethods++; } } free(methods); } } return noTestMethods; } namespace Matchers { namespace Impl { namespace NSStringMatchers { template struct StringHolder : MatcherImpl{ StringHolder( NSString* substr ) : m_substr( [substr copy] ){} StringHolder( StringHolder const& other ) : m_substr( [other.m_substr copy] ){} StringHolder() { arcSafeRelease( m_substr ); } NSString* m_substr; }; struct Equals : StringHolder { Equals( NSString* substr ) : StringHolder( substr ){} virtual bool match( ExpressionType const& str ) const { return (str != nil || m_substr == nil ) && [str isEqualToString:m_substr]; } virtual std::string toString() const { return "equals string: " + Catch::toString( m_substr ); } }; struct Contains : StringHolder { Contains( NSString* substr ) : StringHolder( substr ){} virtual bool match( ExpressionType const& str ) const { return (str != nil || m_substr == nil ) && [str rangeOfString:m_substr].location != NSNotFound; } virtual std::string toString() const { return "contains string: " + Catch::toString( m_substr ); } }; struct StartsWith : StringHolder { StartsWith( NSString* substr ) : StringHolder( substr ){} virtual bool match( ExpressionType const& str ) const { return (str != nil || m_substr == nil ) && [str rangeOfString:m_substr].location == 0; } virtual std::string toString() const { return "starts with: " + Catch::toString( m_substr ); } }; struct EndsWith : StringHolder { EndsWith( NSString* substr ) : StringHolder( substr ){} virtual bool match( ExpressionType const& str ) const { return (str != nil || m_substr == nil ) && [str rangeOfString:m_substr].location == [str length] - [m_substr length]; } virtual std::string toString() const { return "ends with: " + Catch::toString( m_substr ); } }; } // namespace NSStringMatchers } // namespace Impl inline Impl::NSStringMatchers::Equals Equals( NSString* substr ){ return Impl::NSStringMatchers::Equals( substr ); } inline Impl::NSStringMatchers::Contains Contains( NSString* substr ){ return Impl::NSStringMatchers::Contains( substr ); } inline Impl::NSStringMatchers::StartsWith StartsWith( NSString* substr ){ return Impl::NSStringMatchers::StartsWith( substr ); } inline Impl::NSStringMatchers::EndsWith EndsWith( NSString* substr ){ return Impl::NSStringMatchers::EndsWith( substr ); } } // namespace Matchers using namespace Matchers; } // namespace Catch /////////////////////////////////////////////////////////////////////////////// #define OC_TEST_CASE( name, desc )\ +(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Name_test ) \ {\ return @ name; \ }\ +(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Description_test ) \ { \ return @ desc; \ } \ -(void) INTERNAL_CATCH_UNIQUE_NAME( Catch_TestCase_test ) #endif #ifdef CATCH_IMPL // #included from: internal/catch_impl.hpp #define TWOBLUECUBES_CATCH_IMPL_HPP_INCLUDED // Collect all the implementation files together here // These are the equivalent of what would usually be cpp files #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wweak-vtables" #endif // #included from: ../catch_session.hpp #define TWOBLUECUBES_CATCH_RUNNER_HPP_INCLUDED // #included from: internal/catch_commandline.hpp #define TWOBLUECUBES_CATCH_COMMANDLINE_HPP_INCLUDED // #included from: catch_config.hpp #define TWOBLUECUBES_CATCH_CONFIG_HPP_INCLUDED // #included from: catch_test_spec_parser.hpp #define TWOBLUECUBES_CATCH_TEST_SPEC_PARSER_HPP_INCLUDED #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wpadded" #endif // #included from: catch_test_spec.hpp #define TWOBLUECUBES_CATCH_TEST_SPEC_HPP_INCLUDED #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wpadded" #endif // #included from: catch_wildcard_pattern.hpp #define TWOBLUECUBES_CATCH_WILDCARD_PATTERN_HPP_INCLUDED namespace Catch { class WildcardPattern { enum WildcardPosition { NoWildcard = 0, WildcardAtStart = 1, WildcardAtEnd = 2, WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd }; public: WildcardPattern( std::string const& pattern, CaseSensitive::Choice caseSensitivity ) : m_caseSensitivity( caseSensitivity ), m_wildcard( NoWildcard ), m_pattern( adjustCase( pattern ) ) { if( startsWith( m_pattern, "*" ) ) { m_pattern = m_pattern.substr( 1 ); m_wildcard = WildcardAtStart; } if( endsWith( m_pattern, "*" ) ) { m_pattern = m_pattern.substr( 0, m_pattern.size()-1 ); m_wildcard = static_cast( m_wildcard | WildcardAtEnd ); } } virtual ~WildcardPattern(); virtual bool matches( std::string const& str ) const { switch( m_wildcard ) { case NoWildcard: return m_pattern == adjustCase( str ); case WildcardAtStart: return endsWith( adjustCase( str ), m_pattern ); case WildcardAtEnd: return startsWith( adjustCase( str ), m_pattern ); case WildcardAtBothEnds: return contains( adjustCase( str ), m_pattern ); } #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunreachable-code" #endif throw std::logic_error( "Unknown enum" ); #ifdef __clang__ #pragma clang diagnostic pop #endif } private: std::string adjustCase( std::string const& str ) const { return m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str; } CaseSensitive::Choice m_caseSensitivity; WildcardPosition m_wildcard; std::string m_pattern; }; } #include #include namespace Catch { class TestSpec { struct Pattern : SharedImpl<> { virtual ~Pattern(); virtual bool matches( TestCaseInfo const& testCase ) const = 0; }; class NamePattern : public Pattern { public: NamePattern( std::string const& name ) : m_wildcardPattern( toLower( name ), CaseSensitive::No ) {} virtual ~NamePattern(); virtual bool matches( TestCaseInfo const& testCase ) const { return m_wildcardPattern.matches( toLower( testCase.name ) ); } private: WildcardPattern m_wildcardPattern; }; class TagPattern : public Pattern { public: TagPattern( std::string const& tag ) : m_tag( toLower( tag ) ) {} virtual ~TagPattern(); virtual bool matches( TestCaseInfo const& testCase ) const { return testCase.lcaseTags.find( m_tag ) != testCase.lcaseTags.end(); } private: std::string m_tag; }; class ExcludedPattern : public Pattern { public: ExcludedPattern( Ptr const& underlyingPattern ) : m_underlyingPattern( underlyingPattern ) {} virtual ~ExcludedPattern(); virtual bool matches( TestCaseInfo const& testCase ) const { return !m_underlyingPattern->matches( testCase ); } private: Ptr m_underlyingPattern; }; struct Filter { std::vector > m_patterns; bool matches( TestCaseInfo const& testCase ) const { // All patterns in a filter must match for the filter to be a match for( std::vector >::const_iterator it = m_patterns.begin(), itEnd = m_patterns.end(); it != itEnd; ++it ) if( !(*it)->matches( testCase ) ) return false; return true; } }; public: bool hasFilters() const { return !m_filters.empty(); } bool matches( TestCaseInfo const& testCase ) const { // A TestSpec matches if any filter matches for( std::vector::const_iterator it = m_filters.begin(), itEnd = m_filters.end(); it != itEnd; ++it ) if( it->matches( testCase ) ) return true; return false; } private: std::vector m_filters; friend class TestSpecParser; }; } #ifdef __clang__ #pragma clang diagnostic pop #endif namespace Catch { class TestSpecParser { enum Mode{ None, Name, QuotedName, Tag }; Mode m_mode; bool m_exclusion; std::size_t m_start, m_pos; std::string m_arg; TestSpec::Filter m_currentFilter; TestSpec m_testSpec; ITagAliasRegistry const* m_tagAliases; public: TestSpecParser( ITagAliasRegistry const& tagAliases ) : m_tagAliases( &tagAliases ) {} TestSpecParser& parse( std::string const& arg ) { m_mode = None; m_exclusion = false; m_start = std::string::npos; m_arg = m_tagAliases->expandAliases( arg ); for( m_pos = 0; m_pos < m_arg.size(); ++m_pos ) visitChar( m_arg[m_pos] ); if( m_mode == Name ) addPattern(); return *this; } TestSpec testSpec() { addFilter(); return m_testSpec; } private: void visitChar( char c ) { if( m_mode == None ) { switch( c ) { case ' ': return; case '~': m_exclusion = true; return; case '[': return startNewMode( Tag, ++m_pos ); case '"': return startNewMode( QuotedName, ++m_pos ); default: startNewMode( Name, m_pos ); break; } } if( m_mode == Name ) { if( c == ',' ) { addPattern(); addFilter(); } else if( c == '[' ) { if( subString() == "exclude:" ) m_exclusion = true; else addPattern(); startNewMode( Tag, ++m_pos ); } } else if( m_mode == QuotedName && c == '"' ) addPattern(); else if( m_mode == Tag && c == ']' ) addPattern(); } void startNewMode( Mode mode, std::size_t start ) { m_mode = mode; m_start = start; } std::string subString() const { return m_arg.substr( m_start, m_pos - m_start ); } template void addPattern() { std::string token = subString(); if( startsWith( token, "exclude:" ) ) { m_exclusion = true; token = token.substr( 8 ); } if( !token.empty() ) { Ptr pattern = new T( token ); if( m_exclusion ) pattern = new TestSpec::ExcludedPattern( pattern ); m_currentFilter.m_patterns.push_back( pattern ); } m_exclusion = false; m_mode = None; } void addFilter() { if( !m_currentFilter.m_patterns.empty() ) { m_testSpec.m_filters.push_back( m_currentFilter ); m_currentFilter = TestSpec::Filter(); } } }; inline TestSpec parseTestSpec( std::string const& arg ) { return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec(); } } // namespace Catch #ifdef __clang__ #pragma clang diagnostic pop #endif // #included from: catch_interfaces_config.h #define TWOBLUECUBES_CATCH_INTERFACES_CONFIG_H_INCLUDED #include #include #include namespace Catch { struct Verbosity { enum Level { NoOutput = 0, Quiet, Normal }; }; struct WarnAbout { enum What { Nothing = 0x00, NoAssertions = 0x01 }; }; struct ShowDurations { enum OrNot { DefaultForReporter, Always, Never }; }; struct RunTests { enum InWhatOrder { InDeclarationOrder, InLexicographicalOrder, InRandomOrder }; }; struct UseColour { enum YesOrNo { Auto, Yes, No }; }; class TestSpec; struct IConfig : IShared { virtual ~IConfig(); virtual bool allowThrows() const = 0; virtual std::ostream& stream() const = 0; virtual std::string name() const = 0; virtual bool includeSuccessfulResults() const = 0; virtual bool shouldDebugBreak() const = 0; virtual bool warnAboutMissingAssertions() const = 0; virtual int abortAfter() const = 0; virtual bool showInvisibles() const = 0; virtual ShowDurations::OrNot showDurations() const = 0; virtual TestSpec const& testSpec() const = 0; virtual RunTests::InWhatOrder runOrder() const = 0; virtual unsigned int rngSeed() const = 0; virtual UseColour::YesOrNo useColour() const = 0; }; } // #included from: catch_stream.h #define TWOBLUECUBES_CATCH_STREAM_H_INCLUDED // #included from: catch_streambuf.h #define TWOBLUECUBES_CATCH_STREAMBUF_H_INCLUDED #include namespace Catch { class StreamBufBase : public std::streambuf { public: virtual ~StreamBufBase() CATCH_NOEXCEPT; }; } #include #include #include namespace Catch { std::ostream& cout(); std::ostream& cerr(); struct IStream { virtual ~IStream() CATCH_NOEXCEPT; virtual std::ostream& stream() const = 0; }; class FileStream : public IStream { mutable std::ofstream m_ofs; public: FileStream( std::string const& filename ); virtual ~FileStream() CATCH_NOEXCEPT; public: // IStream virtual std::ostream& stream() const CATCH_OVERRIDE; }; class CoutStream : public IStream { mutable std::ostream m_os; public: CoutStream(); virtual ~CoutStream() CATCH_NOEXCEPT; public: // IStream virtual std::ostream& stream() const CATCH_OVERRIDE; }; class DebugOutStream : public IStream { std::auto_ptr m_streamBuf; mutable std::ostream m_os; public: DebugOutStream(); virtual ~DebugOutStream() CATCH_NOEXCEPT; public: // IStream virtual std::ostream& stream() const CATCH_OVERRIDE; }; } #include #include #include #include #include #ifndef CATCH_CONFIG_CONSOLE_WIDTH #define CATCH_CONFIG_CONSOLE_WIDTH 80 #endif namespace Catch { struct ConfigData { ConfigData() : listTests( false ), listTags( false ), listReporters( false ), listTestNamesOnly( false ), showSuccessfulTests( false ), shouldDebugBreak( false ), noThrow( false ), showHelp( false ), showInvisibles( false ), filenamesAsTags( false ), abortAfter( -1 ), rngSeed( 0 ), verbosity( Verbosity::Normal ), warnings( WarnAbout::Nothing ), showDurations( ShowDurations::DefaultForReporter ), runOrder( RunTests::InDeclarationOrder ), useColour( UseColour::Auto ) {} bool listTests; bool listTags; bool listReporters; bool listTestNamesOnly; bool showSuccessfulTests; bool shouldDebugBreak; bool noThrow; bool showHelp; bool showInvisibles; bool filenamesAsTags; int abortAfter; unsigned int rngSeed; Verbosity::Level verbosity; WarnAbout::What warnings; ShowDurations::OrNot showDurations; RunTests::InWhatOrder runOrder; UseColour::YesOrNo useColour; std::string outputFilename; std::string name; std::string processName; std::vector reporterNames; std::vector testsOrTags; }; class Config : public SharedImpl { private: Config( Config const& other ); Config& operator = ( Config const& other ); virtual void dummy(); public: Config() {} Config( ConfigData const& data ) : m_data( data ), m_stream( openStream() ) { if( !data.testsOrTags.empty() ) { TestSpecParser parser( ITagAliasRegistry::get() ); for( std::size_t i = 0; i < data.testsOrTags.size(); ++i ) parser.parse( data.testsOrTags[i] ); m_testSpec = parser.testSpec(); } } virtual ~Config() { } std::string const& getFilename() const { return m_data.outputFilename ; } bool listTests() const { return m_data.listTests; } bool listTestNamesOnly() const { return m_data.listTestNamesOnly; } bool listTags() const { return m_data.listTags; } bool listReporters() const { return m_data.listReporters; } std::string getProcessName() const { return m_data.processName; } bool shouldDebugBreak() const { return m_data.shouldDebugBreak; } std::vector getReporterNames() const { return m_data.reporterNames; } int abortAfter() const { return m_data.abortAfter; } TestSpec const& testSpec() const { return m_testSpec; } bool showHelp() const { return m_data.showHelp; } bool showInvisibles() const { return m_data.showInvisibles; } // IConfig interface virtual bool allowThrows() const { return !m_data.noThrow; } virtual std::ostream& stream() const { return m_stream->stream(); } virtual std::string name() const { return m_data.name.empty() ? m_data.processName : m_data.name; } virtual bool includeSuccessfulResults() const { return m_data.showSuccessfulTests; } virtual bool warnAboutMissingAssertions() const { return m_data.warnings & WarnAbout::NoAssertions; } virtual ShowDurations::OrNot showDurations() const { return m_data.showDurations; } virtual RunTests::InWhatOrder runOrder() const { return m_data.runOrder; } virtual unsigned int rngSeed() const { return m_data.rngSeed; } virtual UseColour::YesOrNo useColour() const { return m_data.useColour; } private: IStream const* openStream() { if( m_data.outputFilename.empty() ) return new CoutStream(); else if( m_data.outputFilename[0] == '%' ) { if( m_data.outputFilename == "%debug" ) return new DebugOutStream(); else throw std::domain_error( "Unrecognised stream: " + m_data.outputFilename ); } else return new FileStream( m_data.outputFilename ); } ConfigData m_data; std::auto_ptr m_stream; TestSpec m_testSpec; }; } // end namespace Catch // #included from: catch_clara.h #define TWOBLUECUBES_CATCH_CLARA_H_INCLUDED // Use Catch's value for console width (store Clara's off to the side, if present) #ifdef CLARA_CONFIG_CONSOLE_WIDTH #define CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH CLARA_CONFIG_CONSOLE_WIDTH #undef CLARA_CONFIG_CONSOLE_WIDTH #endif #define CLARA_CONFIG_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH // Declare Clara inside the Catch namespace #define STITCH_CLARA_OPEN_NAMESPACE namespace Catch { // #included from: ../external/clara.h // Version 0.0.1.1 // Only use header guard if we are not using an outer namespace #if !defined(TWOBLUECUBES_CLARA_H_INCLUDED) || defined(STITCH_CLARA_OPEN_NAMESPACE) #ifndef STITCH_CLARA_OPEN_NAMESPACE #define TWOBLUECUBES_CLARA_H_INCLUDED #define STITCH_CLARA_OPEN_NAMESPACE #define STITCH_CLARA_CLOSE_NAMESPACE #else #define STITCH_CLARA_CLOSE_NAMESPACE } #endif #define STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE STITCH_CLARA_OPEN_NAMESPACE // ----------- #included from tbc_text_format.h ----------- // Only use header guard if we are not using an outer namespace #if !defined(TBC_TEXT_FORMAT_H_INCLUDED) || defined(STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE) #ifndef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE #define TBC_TEXT_FORMAT_H_INCLUDED #endif #include #include #include #include // Use optional outer namespace #ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE namespace STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE { #endif namespace Tbc { #ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH; #else const unsigned int consoleWidth = 80; #endif struct TextAttributes { TextAttributes() : initialIndent( std::string::npos ), indent( 0 ), width( consoleWidth-1 ), tabChar( '\t' ) {} TextAttributes& setInitialIndent( std::size_t _value ) { initialIndent = _value; return *this; } TextAttributes& setIndent( std::size_t _value ) { indent = _value; return *this; } TextAttributes& setWidth( std::size_t _value ) { width = _value; return *this; } TextAttributes& setTabChar( char _value ) { tabChar = _value; return *this; } std::size_t initialIndent; // indent of first line, or npos std::size_t indent; // indent of subsequent lines, or all if initialIndent is npos std::size_t width; // maximum width of text, including indent. Longer text will wrap char tabChar; // If this char is seen the indent is changed to current pos }; class Text { public: Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() ) : attr( _attr ) { std::string wrappableChars = " [({.,/|\\-"; std::size_t indent = _attr.initialIndent != std::string::npos ? _attr.initialIndent : _attr.indent; std::string remainder = _str; while( !remainder.empty() ) { if( lines.size() >= 1000 ) { lines.push_back( "... message truncated due to excessive size" ); return; } std::size_t tabPos = std::string::npos; std::size_t width = (std::min)( remainder.size(), _attr.width - indent ); std::size_t pos = remainder.find_first_of( '\n' ); if( pos <= width ) { width = pos; } pos = remainder.find_last_of( _attr.tabChar, width ); if( pos != std::string::npos ) { tabPos = pos; if( remainder[width] == '\n' ) width--; remainder = remainder.substr( 0, tabPos ) + remainder.substr( tabPos+1 ); } if( width == remainder.size() ) { spliceLine( indent, remainder, width ); } else if( remainder[width] == '\n' ) { spliceLine( indent, remainder, width ); if( width <= 1 || remainder.size() != 1 ) remainder = remainder.substr( 1 ); indent = _attr.indent; } else { pos = remainder.find_last_of( wrappableChars, width ); if( pos != std::string::npos && pos > 0 ) { spliceLine( indent, remainder, pos ); if( remainder[0] == ' ' ) remainder = remainder.substr( 1 ); } else { spliceLine( indent, remainder, width-1 ); lines.back() += "-"; } if( lines.size() == 1 ) indent = _attr.indent; if( tabPos != std::string::npos ) indent += tabPos; } } } void spliceLine( std::size_t _indent, std::string& _remainder, std::size_t _pos ) { lines.push_back( std::string( _indent, ' ' ) + _remainder.substr( 0, _pos ) ); _remainder = _remainder.substr( _pos ); } typedef std::vector::const_iterator const_iterator; const_iterator begin() const { return lines.begin(); } const_iterator end() const { return lines.end(); } std::string const& last() const { return lines.back(); } std::size_t size() const { return lines.size(); } std::string const& operator[]( std::size_t _index ) const { return lines[_index]; } std::string toString() const { std::ostringstream oss; oss << *this; return oss.str(); } inline friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) { for( Text::const_iterator it = _text.begin(), itEnd = _text.end(); it != itEnd; ++it ) { if( it != _text.begin() ) _stream << "\n"; _stream << *it; } return _stream; } private: std::string str; TextAttributes attr; std::vector lines; }; } // end namespace Tbc #ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE } // end outer namespace #endif #endif // TBC_TEXT_FORMAT_H_INCLUDED // ----------- end of #include from tbc_text_format.h ----------- // ........... back in clara.h #undef STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE // ----------- #included from clara_compilers.h ----------- #ifndef TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED #define TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED // Detect a number of compiler features - mostly C++11/14 conformance - by compiler // The following features are defined: // // CLARA_CONFIG_CPP11_NULLPTR : is nullptr supported? // CLARA_CONFIG_CPP11_NOEXCEPT : is noexcept supported? // CLARA_CONFIG_CPP11_GENERATED_METHODS : The delete and default keywords for compiler generated methods // CLARA_CONFIG_CPP11_OVERRIDE : is override supported? // CLARA_CONFIG_CPP11_UNIQUE_PTR : is unique_ptr supported (otherwise use auto_ptr) // CLARA_CONFIG_CPP11_OR_GREATER : Is C++11 supported? // CLARA_CONFIG_VARIADIC_MACROS : are variadic macros supported? // In general each macro has a _NO_ form // (e.g. CLARA_CONFIG_CPP11_NO_NULLPTR) which disables the feature. // Many features, at point of detection, define an _INTERNAL_ macro, so they // can be combined, en-mass, with the _NO_ forms later. // All the C++11 features can be disabled with CLARA_CONFIG_NO_CPP11 #ifdef __clang__ #if __has_feature(cxx_nullptr) #define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR #endif #if __has_feature(cxx_noexcept) #define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT #endif #endif // __clang__ //////////////////////////////////////////////////////////////////////////////// // GCC #ifdef __GNUC__ #if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__) #define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR #endif // - otherwise more recent versions define __cplusplus >= 201103L // and will get picked up below #endif // __GNUC__ //////////////////////////////////////////////////////////////////////////////// // Visual C++ #ifdef _MSC_VER #if (_MSC_VER >= 1600) #define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR #define CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR #endif #if (_MSC_VER >= 1900 ) // (VC++ 13 (VS2015)) #define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT #define CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS #endif #endif // _MSC_VER //////////////////////////////////////////////////////////////////////////////// // C++ language feature support // catch all support for C++11 #if defined(__cplusplus) && __cplusplus >= 201103L #define CLARA_CPP11_OR_GREATER #if !defined(CLARA_INTERNAL_CONFIG_CPP11_NULLPTR) #define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR #endif #ifndef CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT #define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT #endif #ifndef CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS #define CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS #endif #if !defined(CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE) #define CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE #endif #if !defined(CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) #define CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR #endif #endif // __cplusplus >= 201103L // Now set the actual defines based on the above + anything the user has configured #if defined(CLARA_INTERNAL_CONFIG_CPP11_NULLPTR) && !defined(CLARA_CONFIG_CPP11_NO_NULLPTR) && !defined(CLARA_CONFIG_CPP11_NULLPTR) && !defined(CLARA_CONFIG_NO_CPP11) #define CLARA_CONFIG_CPP11_NULLPTR #endif #if defined(CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_CONFIG_CPP11_NO_NOEXCEPT) && !defined(CLARA_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_CONFIG_NO_CPP11) #define CLARA_CONFIG_CPP11_NOEXCEPT #endif #if defined(CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS) && !defined(CLARA_CONFIG_CPP11_NO_GENERATED_METHODS) && !defined(CLARA_CONFIG_CPP11_GENERATED_METHODS) && !defined(CLARA_CONFIG_NO_CPP11) #define CLARA_CONFIG_CPP11_GENERATED_METHODS #endif #if defined(CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE) && !defined(CLARA_CONFIG_NO_OVERRIDE) && !defined(CLARA_CONFIG_CPP11_OVERRIDE) && !defined(CLARA_CONFIG_NO_CPP11) #define CLARA_CONFIG_CPP11_OVERRIDE #endif #if defined(CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) && !defined(CLARA_CONFIG_NO_UNIQUE_PTR) && !defined(CLARA_CONFIG_CPP11_UNIQUE_PTR) && !defined(CLARA_CONFIG_NO_CPP11) #define CLARA_CONFIG_CPP11_UNIQUE_PTR #endif // noexcept support: #if defined(CLARA_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_NOEXCEPT) #define CLARA_NOEXCEPT noexcept # define CLARA_NOEXCEPT_IS(x) noexcept(x) #else #define CLARA_NOEXCEPT throw() # define CLARA_NOEXCEPT_IS(x) #endif // nullptr support #ifdef CLARA_CONFIG_CPP11_NULLPTR #define CLARA_NULL nullptr #else #define CLARA_NULL NULL #endif // override support #ifdef CLARA_CONFIG_CPP11_OVERRIDE #define CLARA_OVERRIDE override #else #define CLARA_OVERRIDE #endif // unique_ptr support #ifdef CLARA_CONFIG_CPP11_UNIQUE_PTR # define CLARA_AUTO_PTR( T ) std::unique_ptr #else # define CLARA_AUTO_PTR( T ) std::auto_ptr #endif #endif // TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED // ----------- end of #include from clara_compilers.h ----------- // ........... back in clara.h #include #include #include // Use optional outer namespace #ifdef STITCH_CLARA_OPEN_NAMESPACE STITCH_CLARA_OPEN_NAMESPACE #endif namespace Clara { struct UnpositionalTag {}; extern UnpositionalTag _; #ifdef CLARA_CONFIG_MAIN UnpositionalTag _; #endif namespace Detail { #ifdef CLARA_CONSOLE_WIDTH const unsigned int consoleWidth = CLARA_CONFIG_CONSOLE_WIDTH; #else const unsigned int consoleWidth = 80; #endif // Use this to try and stop compiler from warning about unreachable code inline bool isTrue( bool value ) { return value; } using namespace Tbc; inline bool startsWith( std::string const& str, std::string const& prefix ) { return str.size() >= prefix.size() && str.substr( 0, prefix.size() ) == prefix; } template struct RemoveConstRef{ typedef T type; }; template struct RemoveConstRef{ typedef T type; }; template struct RemoveConstRef{ typedef T type; }; template struct RemoveConstRef{ typedef T type; }; template struct IsBool { static const bool value = false; }; template<> struct IsBool { static const bool value = true; }; template void convertInto( std::string const& _source, T& _dest ) { std::stringstream ss; ss << _source; ss >> _dest; if( ss.fail() ) throw std::runtime_error( "Unable to convert " + _source + " to destination type" ); } inline void convertInto( std::string const& _source, std::string& _dest ) { _dest = _source; } inline void convertInto( std::string const& _source, bool& _dest ) { std::string sourceLC = _source; std::transform( sourceLC.begin(), sourceLC.end(), sourceLC.begin(), ::tolower ); if( sourceLC == "y" || sourceLC == "1" || sourceLC == "true" || sourceLC == "yes" || sourceLC == "on" ) _dest = true; else if( sourceLC == "n" || sourceLC == "0" || sourceLC == "false" || sourceLC == "no" || sourceLC == "off" ) _dest = false; else throw std::runtime_error( "Expected a boolean value but did not recognise:\n '" + _source + "'" ); } inline void convertInto( bool _source, bool& _dest ) { _dest = _source; } template inline void convertInto( bool, T& ) { if( isTrue( true ) ) throw std::runtime_error( "Invalid conversion" ); } template struct IArgFunction { virtual ~IArgFunction() {} #ifdef CLARA_CONFIG_CPP11_GENERATED_METHODS IArgFunction() = default; IArgFunction( IArgFunction const& ) = default; #endif virtual void set( ConfigT& config, std::string const& value ) const = 0; virtual void setFlag( ConfigT& config ) const = 0; virtual bool takesArg() const = 0; virtual IArgFunction* clone() const = 0; }; template class BoundArgFunction { public: BoundArgFunction() : functionObj( CLARA_NULL ) {} BoundArgFunction( IArgFunction* _functionObj ) : functionObj( _functionObj ) {} BoundArgFunction( BoundArgFunction const& other ) : functionObj( other.functionObj ? other.functionObj->clone() : CLARA_NULL ) {} BoundArgFunction& operator = ( BoundArgFunction const& other ) { IArgFunction* newFunctionObj = other.functionObj ? other.functionObj->clone() : CLARA_NULL; delete functionObj; functionObj = newFunctionObj; return *this; } ~BoundArgFunction() { delete functionObj; } void set( ConfigT& config, std::string const& value ) const { functionObj->set( config, value ); } void setFlag( ConfigT& config ) const { functionObj->setFlag( config ); } bool takesArg() const { return functionObj->takesArg(); } bool isSet() const { return functionObj != CLARA_NULL; } private: IArgFunction* functionObj; }; template struct NullBinder : IArgFunction{ virtual void set( C&, std::string const& ) const {} virtual void setFlag( C& ) const {} virtual bool takesArg() const { return true; } virtual IArgFunction* clone() const { return new NullBinder( *this ); } }; template struct BoundDataMember : IArgFunction{ BoundDataMember( M C::* _member ) : member( _member ) {} virtual void set( C& p, std::string const& stringValue ) const { convertInto( stringValue, p.*member ); } virtual void setFlag( C& p ) const { convertInto( true, p.*member ); } virtual bool takesArg() const { return !IsBool::value; } virtual IArgFunction* clone() const { return new BoundDataMember( *this ); } M C::* member; }; template struct BoundUnaryMethod : IArgFunction{ BoundUnaryMethod( void (C::*_member)( M ) ) : member( _member ) {} virtual void set( C& p, std::string const& stringValue ) const { typename RemoveConstRef::type value; convertInto( stringValue, value ); (p.*member)( value ); } virtual void setFlag( C& p ) const { typename RemoveConstRef::type value; convertInto( true, value ); (p.*member)( value ); } virtual bool takesArg() const { return !IsBool::value; } virtual IArgFunction* clone() const { return new BoundUnaryMethod( *this ); } void (C::*member)( M ); }; template struct BoundNullaryMethod : IArgFunction{ BoundNullaryMethod( void (C::*_member)() ) : member( _member ) {} virtual void set( C& p, std::string const& stringValue ) const { bool value; convertInto( stringValue, value ); if( value ) (p.*member)(); } virtual void setFlag( C& p ) const { (p.*member)(); } virtual bool takesArg() const { return false; } virtual IArgFunction* clone() const { return new BoundNullaryMethod( *this ); } void (C::*member)(); }; template struct BoundUnaryFunction : IArgFunction{ BoundUnaryFunction( void (*_function)( C& ) ) : function( _function ) {} virtual void set( C& obj, std::string const& stringValue ) const { bool value; convertInto( stringValue, value ); if( value ) function( obj ); } virtual void setFlag( C& p ) const { function( p ); } virtual bool takesArg() const { return false; } virtual IArgFunction* clone() const { return new BoundUnaryFunction( *this ); } void (*function)( C& ); }; template struct BoundBinaryFunction : IArgFunction{ BoundBinaryFunction( void (*_function)( C&, T ) ) : function( _function ) {} virtual void set( C& obj, std::string const& stringValue ) const { typename RemoveConstRef::type value; convertInto( stringValue, value ); function( obj, value ); } virtual void setFlag( C& obj ) const { typename RemoveConstRef::type value; convertInto( true, value ); function( obj, value ); } virtual bool takesArg() const { return !IsBool::value; } virtual IArgFunction* clone() const { return new BoundBinaryFunction( *this ); } void (*function)( C&, T ); }; } // namespace Detail struct Parser { Parser() : separators( " \t=:" ) {} struct Token { enum Type { Positional, ShortOpt, LongOpt }; Token( Type _type, std::string const& _data ) : type( _type ), data( _data ) {} Type type; std::string data; }; void parseIntoTokens( int argc, char const* const argv[], std::vector& tokens ) const { const std::string doubleDash = "--"; for( int i = 1; i < argc && argv[i] != doubleDash; ++i ) parseIntoTokens( argv[i] , tokens); } void parseIntoTokens( std::string arg, std::vector& tokens ) const { while( !arg.empty() ) { Parser::Token token( Parser::Token::Positional, arg ); arg = ""; if( token.data[0] == '-' ) { if( token.data.size() > 1 && token.data[1] == '-' ) { token = Parser::Token( Parser::Token::LongOpt, token.data.substr( 2 ) ); } else { token = Parser::Token( Parser::Token::ShortOpt, token.data.substr( 1 ) ); if( token.data.size() > 1 && separators.find( token.data[1] ) == std::string::npos ) { arg = "-" + token.data.substr( 1 ); token.data = token.data.substr( 0, 1 ); } } } if( token.type != Parser::Token::Positional ) { std::size_t pos = token.data.find_first_of( separators ); if( pos != std::string::npos ) { arg = token.data.substr( pos+1 ); token.data = token.data.substr( 0, pos ); } } tokens.push_back( token ); } } std::string separators; }; template struct CommonArgProperties { CommonArgProperties() {} CommonArgProperties( Detail::BoundArgFunction const& _boundField ) : boundField( _boundField ) {} Detail::BoundArgFunction boundField; std::string description; std::string detail; std::string placeholder; // Only value if boundField takes an arg bool takesArg() const { return !placeholder.empty(); } void validate() const { if( !boundField.isSet() ) throw std::logic_error( "option not bound" ); } }; struct OptionArgProperties { std::vector shortNames; std::string longName; bool hasShortName( std::string const& shortName ) const { return std::find( shortNames.begin(), shortNames.end(), shortName ) != shortNames.end(); } bool hasLongName( std::string const& _longName ) const { return _longName == longName; } }; struct PositionalArgProperties { PositionalArgProperties() : position( -1 ) {} int position; // -1 means non-positional (floating) bool isFixedPositional() const { return position != -1; } }; template class CommandLine { struct Arg : CommonArgProperties, OptionArgProperties, PositionalArgProperties { Arg() {} Arg( Detail::BoundArgFunction const& _boundField ) : CommonArgProperties( _boundField ) {} using CommonArgProperties::placeholder; // !TBD std::string dbgName() const { if( !longName.empty() ) return "--" + longName; if( !shortNames.empty() ) return "-" + shortNames[0]; return "positional args"; } std::string commands() const { std::ostringstream oss; bool first = true; std::vector::const_iterator it = shortNames.begin(), itEnd = shortNames.end(); for(; it != itEnd; ++it ) { if( first ) first = false; else oss << ", "; oss << "-" << *it; } if( !longName.empty() ) { if( !first ) oss << ", "; oss << "--" << longName; } if( !placeholder.empty() ) oss << " <" << placeholder << ">"; return oss.str(); } }; typedef CLARA_AUTO_PTR( Arg ) ArgAutoPtr; friend void addOptName( Arg& arg, std::string const& optName ) { if( optName.empty() ) return; if( Detail::startsWith( optName, "--" ) ) { if( !arg.longName.empty() ) throw std::logic_error( "Only one long opt may be specified. '" + arg.longName + "' already specified, now attempting to add '" + optName + "'" ); arg.longName = optName.substr( 2 ); } else if( Detail::startsWith( optName, "-" ) ) arg.shortNames.push_back( optName.substr( 1 ) ); else throw std::logic_error( "option must begin with - or --. Option was: '" + optName + "'" ); } friend void setPositionalArg( Arg& arg, int position ) { arg.position = position; } class ArgBuilder { public: ArgBuilder( Arg* arg ) : m_arg( arg ) {} // Bind a non-boolean data member (requires placeholder string) template void bind( M C::* field, std::string const& placeholder ) { m_arg->boundField = new Detail::BoundDataMember( field ); m_arg->placeholder = placeholder; } // Bind a boolean data member (no placeholder required) template void bind( bool C::* field ) { m_arg->boundField = new Detail::BoundDataMember( field ); } // Bind a method taking a single, non-boolean argument (requires a placeholder string) template void bind( void (C::* unaryMethod)( M ), std::string const& placeholder ) { m_arg->boundField = new Detail::BoundUnaryMethod( unaryMethod ); m_arg->placeholder = placeholder; } // Bind a method taking a single, boolean argument (no placeholder string required) template void bind( void (C::* unaryMethod)( bool ) ) { m_arg->boundField = new Detail::BoundUnaryMethod( unaryMethod ); } // Bind a method that takes no arguments (will be called if opt is present) template void bind( void (C::* nullaryMethod)() ) { m_arg->boundField = new Detail::BoundNullaryMethod( nullaryMethod ); } // Bind a free function taking a single argument - the object to operate on (no placeholder string required) template void bind( void (* unaryFunction)( C& ) ) { m_arg->boundField = new Detail::BoundUnaryFunction( unaryFunction ); } // Bind a free function taking a single argument - the object to operate on (requires a placeholder string) template void bind( void (* binaryFunction)( C&, T ), std::string const& placeholder ) { m_arg->boundField = new Detail::BoundBinaryFunction( binaryFunction ); m_arg->placeholder = placeholder; } ArgBuilder& describe( std::string const& description ) { m_arg->description = description; return *this; } ArgBuilder& detail( std::string const& detail ) { m_arg->detail = detail; return *this; } protected: Arg* m_arg; }; class OptBuilder : public ArgBuilder { public: OptBuilder( Arg* arg ) : ArgBuilder( arg ) {} OptBuilder( OptBuilder& other ) : ArgBuilder( other ) {} OptBuilder& operator[]( std::string const& optName ) { addOptName( *ArgBuilder::m_arg, optName ); return *this; } }; public: CommandLine() : m_boundProcessName( new Detail::NullBinder() ), m_highestSpecifiedArgPosition( 0 ), m_throwOnUnrecognisedTokens( false ) {} CommandLine( CommandLine const& other ) : m_boundProcessName( other.m_boundProcessName ), m_options ( other.m_options ), m_positionalArgs( other.m_positionalArgs ), m_highestSpecifiedArgPosition( other.m_highestSpecifiedArgPosition ), m_throwOnUnrecognisedTokens( other.m_throwOnUnrecognisedTokens ) { if( other.m_floatingArg.get() ) m_floatingArg.reset( new Arg( *other.m_floatingArg ) ); } CommandLine& setThrowOnUnrecognisedTokens( bool shouldThrow = true ) { m_throwOnUnrecognisedTokens = shouldThrow; return *this; } OptBuilder operator[]( std::string const& optName ) { m_options.push_back( Arg() ); addOptName( m_options.back(), optName ); OptBuilder builder( &m_options.back() ); return builder; } ArgBuilder operator[]( int position ) { m_positionalArgs.insert( std::make_pair( position, Arg() ) ); if( position > m_highestSpecifiedArgPosition ) m_highestSpecifiedArgPosition = position; setPositionalArg( m_positionalArgs[position], position ); ArgBuilder builder( &m_positionalArgs[position] ); return builder; } // Invoke this with the _ instance ArgBuilder operator[]( UnpositionalTag ) { if( m_floatingArg.get() ) throw std::logic_error( "Only one unpositional argument can be added" ); m_floatingArg.reset( new Arg() ); ArgBuilder builder( m_floatingArg.get() ); return builder; } template void bindProcessName( M C::* field ) { m_boundProcessName = new Detail::BoundDataMember( field ); } template void bindProcessName( void (C::*_unaryMethod)( M ) ) { m_boundProcessName = new Detail::BoundUnaryMethod( _unaryMethod ); } void optUsage( std::ostream& os, std::size_t indent = 0, std::size_t width = Detail::consoleWidth ) const { typename std::vector::const_iterator itBegin = m_options.begin(), itEnd = m_options.end(), it; std::size_t maxWidth = 0; for( it = itBegin; it != itEnd; ++it ) maxWidth = (std::max)( maxWidth, it->commands().size() ); for( it = itBegin; it != itEnd; ++it ) { Detail::Text usage( it->commands(), Detail::TextAttributes() .setWidth( maxWidth+indent ) .setIndent( indent ) ); Detail::Text desc( it->description, Detail::TextAttributes() .setWidth( width - maxWidth - 3 ) ); for( std::size_t i = 0; i < (std::max)( usage.size(), desc.size() ); ++i ) { std::string usageCol = i < usage.size() ? usage[i] : ""; os << usageCol; if( i < desc.size() && !desc[i].empty() ) os << std::string( indent + 2 + maxWidth - usageCol.size(), ' ' ) << desc[i]; os << "\n"; } } } std::string optUsage() const { std::ostringstream oss; optUsage( oss ); return oss.str(); } void argSynopsis( std::ostream& os ) const { for( int i = 1; i <= m_highestSpecifiedArgPosition; ++i ) { if( i > 1 ) os << " "; typename std::map::const_iterator it = m_positionalArgs.find( i ); if( it != m_positionalArgs.end() ) os << "<" << it->second.placeholder << ">"; else if( m_floatingArg.get() ) os << "<" << m_floatingArg->placeholder << ">"; else throw std::logic_error( "non consecutive positional arguments with no floating args" ); } // !TBD No indication of mandatory args if( m_floatingArg.get() ) { if( m_highestSpecifiedArgPosition > 1 ) os << " "; os << "[<" << m_floatingArg->placeholder << "> ...]"; } } std::string argSynopsis() const { std::ostringstream oss; argSynopsis( oss ); return oss.str(); } void usage( std::ostream& os, std::string const& procName ) const { validate(); os << "usage:\n " << procName << " "; argSynopsis( os ); if( !m_options.empty() ) { os << " [options]\n\nwhere options are: \n"; optUsage( os, 2 ); } os << "\n"; } std::string usage( std::string const& procName ) const { std::ostringstream oss; usage( oss, procName ); return oss.str(); } ConfigT parse( int argc, char const* const argv[] ) const { ConfigT config; parseInto( argc, argv, config ); return config; } std::vector parseInto( int argc, char const* argv[], ConfigT& config ) const { std::string processName = argv[0]; std::size_t lastSlash = processName.find_last_of( "/\\" ); if( lastSlash != std::string::npos ) processName = processName.substr( lastSlash+1 ); m_boundProcessName.set( config, processName ); std::vector tokens; Parser parser; parser.parseIntoTokens( argc, argv, tokens ); return populate( tokens, config ); } std::vector populate( std::vector const& tokens, ConfigT& config ) const { validate(); std::vector unusedTokens = populateOptions( tokens, config ); unusedTokens = populateFixedArgs( unusedTokens, config ); unusedTokens = populateFloatingArgs( unusedTokens, config ); return unusedTokens; } std::vector populateOptions( std::vector const& tokens, ConfigT& config ) const { std::vector unusedTokens; std::vector errors; for( std::size_t i = 0; i < tokens.size(); ++i ) { Parser::Token const& token = tokens[i]; typename std::vector::const_iterator it = m_options.begin(), itEnd = m_options.end(); for(; it != itEnd; ++it ) { Arg const& arg = *it; try { if( ( token.type == Parser::Token::ShortOpt && arg.hasShortName( token.data ) ) || ( token.type == Parser::Token::LongOpt && arg.hasLongName( token.data ) ) ) { if( arg.takesArg() ) { if( i == tokens.size()-1 || tokens[i+1].type != Parser::Token::Positional ) errors.push_back( "Expected argument to option: " + token.data ); else arg.boundField.set( config, tokens[++i].data ); } else { arg.boundField.setFlag( config ); } break; } } catch( std::exception& ex ) { errors.push_back( std::string( ex.what() ) + "\n- while parsing: (" + arg.commands() + ")" ); } } if( it == itEnd ) { if( token.type == Parser::Token::Positional || !m_throwOnUnrecognisedTokens ) unusedTokens.push_back( token ); else if( errors.empty() && m_throwOnUnrecognisedTokens ) errors.push_back( "unrecognised option: " + token.data ); } } if( !errors.empty() ) { std::ostringstream oss; for( std::vector::const_iterator it = errors.begin(), itEnd = errors.end(); it != itEnd; ++it ) { if( it != errors.begin() ) oss << "\n"; oss << *it; } throw std::runtime_error( oss.str() ); } return unusedTokens; } std::vector populateFixedArgs( std::vector const& tokens, ConfigT& config ) const { std::vector unusedTokens; int position = 1; for( std::size_t i = 0; i < tokens.size(); ++i ) { Parser::Token const& token = tokens[i]; typename std::map::const_iterator it = m_positionalArgs.find( position ); if( it != m_positionalArgs.end() ) it->second.boundField.set( config, token.data ); else unusedTokens.push_back( token ); if( token.type == Parser::Token::Positional ) position++; } return unusedTokens; } std::vector populateFloatingArgs( std::vector const& tokens, ConfigT& config ) const { if( !m_floatingArg.get() ) return tokens; std::vector unusedTokens; for( std::size_t i = 0; i < tokens.size(); ++i ) { Parser::Token const& token = tokens[i]; if( token.type == Parser::Token::Positional ) m_floatingArg->boundField.set( config, token.data ); else unusedTokens.push_back( token ); } return unusedTokens; } void validate() const { if( m_options.empty() && m_positionalArgs.empty() && !m_floatingArg.get() ) throw std::logic_error( "No options or arguments specified" ); for( typename std::vector::const_iterator it = m_options.begin(), itEnd = m_options.end(); it != itEnd; ++it ) it->validate(); } private: Detail::BoundArgFunction m_boundProcessName; std::vector m_options; std::map m_positionalArgs; ArgAutoPtr m_floatingArg; int m_highestSpecifiedArgPosition; bool m_throwOnUnrecognisedTokens; }; } // end namespace Clara STITCH_CLARA_CLOSE_NAMESPACE #undef STITCH_CLARA_OPEN_NAMESPACE #undef STITCH_CLARA_CLOSE_NAMESPACE #endif // TWOBLUECUBES_CLARA_H_INCLUDED #undef STITCH_CLARA_OPEN_NAMESPACE // Restore Clara's value for console width, if present #ifdef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH #define CLARA_CONFIG_CONSOLE_WIDTH CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH #undef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH #endif #include namespace Catch { inline void abortAfterFirst( ConfigData& config ) { config.abortAfter = 1; } inline void abortAfterX( ConfigData& config, int x ) { if( x < 1 ) throw std::runtime_error( "Value after -x or --abortAfter must be greater than zero" ); config.abortAfter = x; } inline void addTestOrTags( ConfigData& config, std::string const& _testSpec ) { config.testsOrTags.push_back( _testSpec ); } inline void addReporterName( ConfigData& config, std::string const& _reporterName ) { config.reporterNames.push_back( _reporterName ); } inline void addWarning( ConfigData& config, std::string const& _warning ) { if( _warning == "NoAssertions" ) config.warnings = static_cast( config.warnings | WarnAbout::NoAssertions ); else throw std::runtime_error( "Unrecognised warning: '" + _warning + "'" ); } inline void setOrder( ConfigData& config, std::string const& order ) { if( startsWith( "declared", order ) ) config.runOrder = RunTests::InDeclarationOrder; else if( startsWith( "lexical", order ) ) config.runOrder = RunTests::InLexicographicalOrder; else if( startsWith( "random", order ) ) config.runOrder = RunTests::InRandomOrder; else throw std::runtime_error( "Unrecognised ordering: '" + order + "'" ); } inline void setRngSeed( ConfigData& config, std::string const& seed ) { if( seed == "time" ) { config.rngSeed = static_cast( std::time(0) ); } else { std::stringstream ss; ss << seed; ss >> config.rngSeed; if( ss.fail() ) throw std::runtime_error( "Argment to --rng-seed should be the word 'time' or a number" ); } } inline void setVerbosity( ConfigData& config, int level ) { // !TBD: accept strings? config.verbosity = static_cast( level ); } inline void setShowDurations( ConfigData& config, bool _showDurations ) { config.showDurations = _showDurations ? ShowDurations::Always : ShowDurations::Never; } inline void setUseColour( ConfigData& config, std::string const& value ) { std::string mode = toLower( value ); if( mode == "yes" ) config.useColour = UseColour::Yes; else if( mode == "no" ) config.useColour = UseColour::No; else if( mode == "auto" ) config.useColour = UseColour::Auto; else throw std::runtime_error( "colour mode must be one of: auto, yes or no" ); } inline void forceColour( ConfigData& config ) { config.useColour = UseColour::Yes; } inline void loadTestNamesFromFile( ConfigData& config, std::string const& _filename ) { std::ifstream f( _filename.c_str() ); if( !f.is_open() ) throw std::domain_error( "Unable to load input file: " + _filename ); std::string line; while( std::getline( f, line ) ) { line = trim(line); if( !line.empty() && !startsWith( line, "#" ) ) addTestOrTags( config, "\"" + line + "\"," ); } } inline Clara::CommandLine makeCommandLineParser() { using namespace Clara; CommandLine cli; cli.bindProcessName( &ConfigData::processName ); cli["-?"]["-h"]["--help"] .describe( "display usage information" ) .bind( &ConfigData::showHelp ); cli["-l"]["--list-tests"] .describe( "list all/matching test cases" ) .bind( &ConfigData::listTests ); cli["-t"]["--list-tags"] .describe( "list all/matching tags" ) .bind( &ConfigData::listTags ); cli["-s"]["--success"] .describe( "include successful tests in output" ) .bind( &ConfigData::showSuccessfulTests ); cli["-b"]["--break"] .describe( "break into debugger on failure" ) .bind( &ConfigData::shouldDebugBreak ); cli["-e"]["--nothrow"] .describe( "skip exception tests" ) .bind( &ConfigData::noThrow ); cli["-i"]["--invisibles"] .describe( "show invisibles (tabs, newlines)" ) .bind( &ConfigData::showInvisibles ); cli["-o"]["--out"] .describe( "output filename" ) .bind( &ConfigData::outputFilename, "filename" ); cli["-r"]["--reporter"] // .placeholder( "name[:filename]" ) .describe( "reporter to use (defaults to console)" ) .bind( &addReporterName, "name" ); cli["-n"]["--name"] .describe( "suite name" ) .bind( &ConfigData::name, "name" ); cli["-a"]["--abort"] .describe( "abort at first failure" ) .bind( &abortAfterFirst ); cli["-x"]["--abortx"] .describe( "abort after x failures" ) .bind( &abortAfterX, "no. failures" ); cli["-w"]["--warn"] .describe( "enable warnings" ) .bind( &addWarning, "warning name" ); // - needs updating if reinstated // cli.into( &setVerbosity ) // .describe( "level of verbosity (0=no output)" ) // .shortOpt( "v") // .longOpt( "verbosity" ) // .placeholder( "level" ); cli[_] .describe( "which test or tests to use" ) .bind( &addTestOrTags, "test name, pattern or tags" ); cli["-d"]["--durations"] .describe( "show test durations" ) .bind( &setShowDurations, "yes|no" ); cli["-f"]["--input-file"] .describe( "load test names to run from a file" ) .bind( &loadTestNamesFromFile, "filename" ); cli["-#"]["--filenames-as-tags"] .describe( "adds a tag for the filename" ) .bind( &ConfigData::filenamesAsTags ); // Less common commands which don't have a short form cli["--list-test-names-only"] .describe( "list all/matching test cases names only" ) .bind( &ConfigData::listTestNamesOnly ); cli["--list-reporters"] .describe( "list all reporters" ) .bind( &ConfigData::listReporters ); cli["--order"] .describe( "test case order (defaults to decl)" ) .bind( &setOrder, "decl|lex|rand" ); cli["--rng-seed"] .describe( "set a specific seed for random numbers" ) .bind( &setRngSeed, "'time'|number" ); cli["--force-colour"] .describe( "force colourised output (deprecated)" ) .bind( &forceColour ); cli["--use-colour"] .describe( "should output be colourised" ) .bind( &setUseColour, "yes|no" ); return cli; } } // end namespace Catch // #included from: internal/catch_list.hpp #define TWOBLUECUBES_CATCH_LIST_HPP_INCLUDED // #included from: catch_text.h #define TWOBLUECUBES_CATCH_TEXT_H_INCLUDED #define TBC_TEXT_FORMAT_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH #define CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE Catch // #included from: ../external/tbc_text_format.h // Only use header guard if we are not using an outer namespace #ifndef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE # ifdef TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED # ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED # define TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED # endif # else # define TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED # endif #endif #ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED #include #include #include // Use optional outer namespace #ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE namespace CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE { #endif namespace Tbc { #ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH; #else const unsigned int consoleWidth = 80; #endif struct TextAttributes { TextAttributes() : initialIndent( std::string::npos ), indent( 0 ), width( consoleWidth-1 ), tabChar( '\t' ) {} TextAttributes& setInitialIndent( std::size_t _value ) { initialIndent = _value; return *this; } TextAttributes& setIndent( std::size_t _value ) { indent = _value; return *this; } TextAttributes& setWidth( std::size_t _value ) { width = _value; return *this; } TextAttributes& setTabChar( char _value ) { tabChar = _value; return *this; } std::size_t initialIndent; // indent of first line, or npos std::size_t indent; // indent of subsequent lines, or all if initialIndent is npos std::size_t width; // maximum width of text, including indent. Longer text will wrap char tabChar; // If this char is seen the indent is changed to current pos }; class Text { public: Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() ) : attr( _attr ) { std::string wrappableChars = " [({.,/|\\-"; std::size_t indent = _attr.initialIndent != std::string::npos ? _attr.initialIndent : _attr.indent; std::string remainder = _str; while( !remainder.empty() ) { if( lines.size() >= 1000 ) { lines.push_back( "... message truncated due to excessive size" ); return; } std::size_t tabPos = std::string::npos; std::size_t width = (std::min)( remainder.size(), _attr.width - indent ); std::size_t pos = remainder.find_first_of( '\n' ); if( pos <= width ) { width = pos; } pos = remainder.find_last_of( _attr.tabChar, width ); if( pos != std::string::npos ) { tabPos = pos; if( remainder[width] == '\n' ) width--; remainder = remainder.substr( 0, tabPos ) + remainder.substr( tabPos+1 ); } if( width == remainder.size() ) { spliceLine( indent, remainder, width ); } else if( remainder[width] == '\n' ) { spliceLine( indent, remainder, width ); if( width <= 1 || remainder.size() != 1 ) remainder = remainder.substr( 1 ); indent = _attr.indent; } else { pos = remainder.find_last_of( wrappableChars, width ); if( pos != std::string::npos && pos > 0 ) { spliceLine( indent, remainder, pos ); if( remainder[0] == ' ' ) remainder = remainder.substr( 1 ); } else { spliceLine( indent, remainder, width-1 ); lines.back() += "-"; } if( lines.size() == 1 ) indent = _attr.indent; if( tabPos != std::string::npos ) indent += tabPos; } } } void spliceLine( std::size_t _indent, std::string& _remainder, std::size_t _pos ) { lines.push_back( std::string( _indent, ' ' ) + _remainder.substr( 0, _pos ) ); _remainder = _remainder.substr( _pos ); } typedef std::vector::const_iterator const_iterator; const_iterator begin() const { return lines.begin(); } const_iterator end() const { return lines.end(); } std::string const& last() const { return lines.back(); } std::size_t size() const { return lines.size(); } std::string const& operator[]( std::size_t _index ) const { return lines[_index]; } std::string toString() const { std::ostringstream oss; oss << *this; return oss.str(); } inline friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) { for( Text::const_iterator it = _text.begin(), itEnd = _text.end(); it != itEnd; ++it ) { if( it != _text.begin() ) _stream << "\n"; _stream << *it; } return _stream; } private: std::string str; TextAttributes attr; std::vector lines; }; } // end namespace Tbc #ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE } // end outer namespace #endif #endif // TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED #undef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE namespace Catch { using Tbc::Text; using Tbc::TextAttributes; } // #included from: catch_console_colour.hpp #define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_HPP_INCLUDED namespace Catch { struct Colour { enum Code { None = 0, White, Red, Green, Blue, Cyan, Yellow, Grey, Bright = 0x10, BrightRed = Bright | Red, BrightGreen = Bright | Green, LightGrey = Bright | Grey, BrightWhite = Bright | White, // By intention FileName = LightGrey, Warning = Yellow, ResultError = BrightRed, ResultSuccess = BrightGreen, ResultExpectedFailure = Warning, Error = BrightRed, Success = Green, OriginalExpression = Cyan, ReconstructedExpression = Yellow, SecondaryText = LightGrey, Headers = White }; // Use constructed object for RAII guard Colour( Code _colourCode ); Colour( Colour const& other ); ~Colour(); // Use static method for one-shot changes static void use( Code _colourCode ); private: bool m_moved; }; inline std::ostream& operator << ( std::ostream& os, Colour const& ) { return os; } } // end namespace Catch // #included from: catch_interfaces_reporter.h #define TWOBLUECUBES_CATCH_INTERFACES_REPORTER_H_INCLUDED #include #include #include #include namespace Catch { struct ReporterConfig { explicit ReporterConfig( Ptr const& _fullConfig ) : m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {} ReporterConfig( Ptr const& _fullConfig, std::ostream& _stream ) : m_stream( &_stream ), m_fullConfig( _fullConfig ) {} std::ostream& stream() const { return *m_stream; } Ptr fullConfig() const { return m_fullConfig; } private: std::ostream* m_stream; Ptr m_fullConfig; }; struct ReporterPreferences { ReporterPreferences() : shouldRedirectStdOut( false ) {} bool shouldRedirectStdOut; }; template struct LazyStat : Option { LazyStat() : used( false ) {} LazyStat& operator=( T const& _value ) { Option::operator=( _value ); used = false; return *this; } void reset() { Option::reset(); used = false; } bool used; }; struct TestRunInfo { TestRunInfo( std::string const& _name ) : name( _name ) {} std::string name; }; struct GroupInfo { GroupInfo( std::string const& _name, std::size_t _groupIndex, std::size_t _groupsCount ) : name( _name ), groupIndex( _groupIndex ), groupsCounts( _groupsCount ) {} std::string name; std::size_t groupIndex; std::size_t groupsCounts; }; struct AssertionStats { AssertionStats( AssertionResult const& _assertionResult, std::vector const& _infoMessages, Totals const& _totals ) : assertionResult( _assertionResult ), infoMessages( _infoMessages ), totals( _totals ) { if( assertionResult.hasMessage() ) { // Copy message into messages list. // !TBD This should have been done earlier, somewhere MessageBuilder builder( assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType() ); builder << assertionResult.getMessage(); builder.m_info.message = builder.m_stream.str(); infoMessages.push_back( builder.m_info ); } } virtual ~AssertionStats(); # ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS AssertionStats( AssertionStats const& ) = default; AssertionStats( AssertionStats && ) = default; AssertionStats& operator = ( AssertionStats const& ) = default; AssertionStats& operator = ( AssertionStats && ) = default; # endif AssertionResult assertionResult; std::vector infoMessages; Totals totals; }; struct SectionStats { SectionStats( SectionInfo const& _sectionInfo, Counts const& _assertions, double _durationInSeconds, bool _missingAssertions ) : sectionInfo( _sectionInfo ), assertions( _assertions ), durationInSeconds( _durationInSeconds ), missingAssertions( _missingAssertions ) {} virtual ~SectionStats(); # ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS SectionStats( SectionStats const& ) = default; SectionStats( SectionStats && ) = default; SectionStats& operator = ( SectionStats const& ) = default; SectionStats& operator = ( SectionStats && ) = default; # endif SectionInfo sectionInfo; Counts assertions; double durationInSeconds; bool missingAssertions; }; struct TestCaseStats { TestCaseStats( TestCaseInfo const& _testInfo, Totals const& _totals, std::string const& _stdOut, std::string const& _stdErr, bool _aborting ) : testInfo( _testInfo ), totals( _totals ), stdOut( _stdOut ), stdErr( _stdErr ), aborting( _aborting ) {} virtual ~TestCaseStats(); # ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS TestCaseStats( TestCaseStats const& ) = default; TestCaseStats( TestCaseStats && ) = default; TestCaseStats& operator = ( TestCaseStats const& ) = default; TestCaseStats& operator = ( TestCaseStats && ) = default; # endif TestCaseInfo testInfo; Totals totals; std::string stdOut; std::string stdErr; bool aborting; }; struct TestGroupStats { TestGroupStats( GroupInfo const& _groupInfo, Totals const& _totals, bool _aborting ) : groupInfo( _groupInfo ), totals( _totals ), aborting( _aborting ) {} TestGroupStats( GroupInfo const& _groupInfo ) : groupInfo( _groupInfo ), aborting( false ) {} virtual ~TestGroupStats(); # ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS TestGroupStats( TestGroupStats const& ) = default; TestGroupStats( TestGroupStats && ) = default; TestGroupStats& operator = ( TestGroupStats const& ) = default; TestGroupStats& operator = ( TestGroupStats && ) = default; # endif GroupInfo groupInfo; Totals totals; bool aborting; }; struct TestRunStats { TestRunStats( TestRunInfo const& _runInfo, Totals const& _totals, bool _aborting ) : runInfo( _runInfo ), totals( _totals ), aborting( _aborting ) {} virtual ~TestRunStats(); # ifndef CATCH_CONFIG_CPP11_GENERATED_METHODS TestRunStats( TestRunStats const& _other ) : runInfo( _other.runInfo ), totals( _other.totals ), aborting( _other.aborting ) {} # else TestRunStats( TestRunStats const& ) = default; TestRunStats( TestRunStats && ) = default; TestRunStats& operator = ( TestRunStats const& ) = default; TestRunStats& operator = ( TestRunStats && ) = default; # endif TestRunInfo runInfo; Totals totals; bool aborting; }; struct IStreamingReporter : IShared { virtual ~IStreamingReporter(); // Implementing class must also provide the following static method: // static std::string getDescription(); virtual ReporterPreferences getPreferences() const = 0; virtual void noMatchingTestCases( std::string const& spec ) = 0; virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0; virtual void testGroupStarting( GroupInfo const& groupInfo ) = 0; virtual void testCaseStarting( TestCaseInfo const& testInfo ) = 0; virtual void sectionStarting( SectionInfo const& sectionInfo ) = 0; virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0; // The return value indicates if the messages buffer should be cleared: virtual bool assertionEnded( AssertionStats const& assertionStats ) = 0; virtual void sectionEnded( SectionStats const& sectionStats ) = 0; virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0; virtual void testGroupEnded( TestGroupStats const& testGroupStats ) = 0; virtual void testRunEnded( TestRunStats const& testRunStats ) = 0; virtual void skipTest( TestCaseInfo const& testInfo ) = 0; }; struct IReporterFactory : IShared { virtual ~IReporterFactory(); virtual IStreamingReporter* create( ReporterConfig const& config ) const = 0; virtual std::string getDescription() const = 0; }; struct IReporterRegistry { typedef std::map > FactoryMap; typedef std::vector > Listeners; virtual ~IReporterRegistry(); virtual IStreamingReporter* create( std::string const& name, Ptr const& config ) const = 0; virtual FactoryMap const& getFactories() const = 0; virtual Listeners const& getListeners() const = 0; }; Ptr addReporter( Ptr const& existingReporter, Ptr const& additionalReporter ); } #include #include namespace Catch { inline std::size_t listTests( Config const& config ) { TestSpec testSpec = config.testSpec(); if( config.testSpec().hasFilters() ) Catch::cout() << "Matching test cases:\n"; else { Catch::cout() << "All available test cases:\n"; testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); } std::size_t matchedTests = 0; TextAttributes nameAttr, tagsAttr; nameAttr.setInitialIndent( 2 ).setIndent( 4 ); tagsAttr.setIndent( 6 ); std::vector matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); for( std::vector::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); it != itEnd; ++it ) { matchedTests++; TestCaseInfo const& testCaseInfo = it->getTestCaseInfo(); Colour::Code colour = testCaseInfo.isHidden() ? Colour::SecondaryText : Colour::None; Colour colourGuard( colour ); Catch::cout() << Text( testCaseInfo.name, nameAttr ) << std::endl; if( !testCaseInfo.tags.empty() ) Catch::cout() << Text( testCaseInfo.tagsAsString, tagsAttr ) << std::endl; } if( !config.testSpec().hasFilters() ) Catch::cout() << pluralise( matchedTests, "test case" ) << "\n" << std::endl; else Catch::cout() << pluralise( matchedTests, "matching test case" ) << "\n" << std::endl; return matchedTests; } inline std::size_t listTestsNamesOnly( Config const& config ) { TestSpec testSpec = config.testSpec(); if( !config.testSpec().hasFilters() ) testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); std::size_t matchedTests = 0; std::vector matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); for( std::vector::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); it != itEnd; ++it ) { matchedTests++; TestCaseInfo const& testCaseInfo = it->getTestCaseInfo(); Catch::cout() << testCaseInfo.name << std::endl; } return matchedTests; } struct TagInfo { TagInfo() : count ( 0 ) {} void add( std::string const& spelling ) { ++count; spellings.insert( spelling ); } std::string all() const { std::string out; for( std::set::const_iterator it = spellings.begin(), itEnd = spellings.end(); it != itEnd; ++it ) out += "[" + *it + "]"; return out; } std::set spellings; std::size_t count; }; inline std::size_t listTags( Config const& config ) { TestSpec testSpec = config.testSpec(); if( config.testSpec().hasFilters() ) Catch::cout() << "Tags for matching test cases:\n"; else { Catch::cout() << "All available tags:\n"; testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); } std::map tagCounts; std::vector matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); for( std::vector::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); it != itEnd; ++it ) { for( std::set::const_iterator tagIt = it->getTestCaseInfo().tags.begin(), tagItEnd = it->getTestCaseInfo().tags.end(); tagIt != tagItEnd; ++tagIt ) { std::string tagName = *tagIt; std::string lcaseTagName = toLower( tagName ); std::map::iterator countIt = tagCounts.find( lcaseTagName ); if( countIt == tagCounts.end() ) countIt = tagCounts.insert( std::make_pair( lcaseTagName, TagInfo() ) ).first; countIt->second.add( tagName ); } } for( std::map::const_iterator countIt = tagCounts.begin(), countItEnd = tagCounts.end(); countIt != countItEnd; ++countIt ) { std::ostringstream oss; oss << " " << std::setw(2) << countIt->second.count << " "; Text wrapper( countIt->second.all(), TextAttributes() .setInitialIndent( 0 ) .setIndent( oss.str().size() ) .setWidth( CATCH_CONFIG_CONSOLE_WIDTH-10 ) ); Catch::cout() << oss.str() << wrapper << "\n"; } Catch::cout() << pluralise( tagCounts.size(), "tag" ) << "\n" << std::endl; return tagCounts.size(); } inline std::size_t listReporters( Config const& /*config*/ ) { Catch::cout() << "Available reporters:\n"; IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories(); IReporterRegistry::FactoryMap::const_iterator itBegin = factories.begin(), itEnd = factories.end(), it; std::size_t maxNameLen = 0; for(it = itBegin; it != itEnd; ++it ) maxNameLen = (std::max)( maxNameLen, it->first.size() ); for(it = itBegin; it != itEnd; ++it ) { Text wrapper( it->second->getDescription(), TextAttributes() .setInitialIndent( 0 ) .setIndent( 7+maxNameLen ) .setWidth( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen-8 ) ); Catch::cout() << " " << it->first << ":" << std::string( maxNameLen - it->first.size() + 2, ' ' ) << wrapper << "\n"; } Catch::cout() << std::endl; return factories.size(); } inline Option list( Config const& config ) { Option listedCount; if( config.listTests() ) listedCount = listedCount.valueOr(0) + listTests( config ); if( config.listTestNamesOnly() ) listedCount = listedCount.valueOr(0) + listTestsNamesOnly( config ); if( config.listTags() ) listedCount = listedCount.valueOr(0) + listTags( config ); if( config.listReporters() ) listedCount = listedCount.valueOr(0) + listReporters( config ); return listedCount; } } // end namespace Catch // #included from: internal/catch_run_context.hpp #define TWOBLUECUBES_CATCH_RUNNER_IMPL_HPP_INCLUDED // #included from: catch_test_case_tracker.hpp #define TWOBLUECUBES_CATCH_TEST_CASE_TRACKER_HPP_INCLUDED #include #include #include #include namespace Catch { namespace TestCaseTracking { struct ITracker : SharedImpl<> { virtual ~ITracker(); // static queries virtual std::string name() const = 0; // dynamic queries virtual bool isComplete() const = 0; // Successfully completed or failed virtual bool isSuccessfullyCompleted() const = 0; virtual bool isOpen() const = 0; // Started but not complete virtual bool hasChildren() const = 0; virtual ITracker& parent() = 0; // actions virtual void close() = 0; // Successfully complete virtual void fail() = 0; virtual void markAsNeedingAnotherRun() = 0; virtual void addChild( Ptr const& child ) = 0; virtual ITracker* findChild( std::string const& name ) = 0; virtual void openChild() = 0; }; class TrackerContext { enum RunState { NotStarted, Executing, CompletedCycle }; Ptr m_rootTracker; ITracker* m_currentTracker; RunState m_runState; public: static TrackerContext& instance() { static TrackerContext s_instance; return s_instance; } TrackerContext() : m_currentTracker( CATCH_NULL ), m_runState( NotStarted ) {} ITracker& startRun(); void endRun() { m_rootTracker.reset(); m_currentTracker = CATCH_NULL; m_runState = NotStarted; } void startCycle() { m_currentTracker = m_rootTracker.get(); m_runState = Executing; } void completeCycle() { m_runState = CompletedCycle; } bool completedCycle() const { return m_runState == CompletedCycle; } ITracker& currentTracker() { return *m_currentTracker; } void setCurrentTracker( ITracker* tracker ) { m_currentTracker = tracker; } }; class TrackerBase : public ITracker { protected: enum CycleState { NotStarted, Executing, ExecutingChildren, NeedsAnotherRun, CompletedSuccessfully, Failed }; class TrackerHasName { std::string m_name; public: TrackerHasName( std::string const& name ) : m_name( name ) {} bool operator ()( Ptr const& tracker ) { return tracker->name() == m_name; } }; typedef std::vector > Children; std::string m_name; TrackerContext& m_ctx; ITracker* m_parent; Children m_children; CycleState m_runState; public: TrackerBase( std::string const& name, TrackerContext& ctx, ITracker* parent ) : m_name( name ), m_ctx( ctx ), m_parent( parent ), m_runState( NotStarted ) {} virtual ~TrackerBase(); virtual std::string name() const CATCH_OVERRIDE { return m_name; } virtual bool isComplete() const CATCH_OVERRIDE { return m_runState == CompletedSuccessfully || m_runState == Failed; } virtual bool isSuccessfullyCompleted() const CATCH_OVERRIDE { return m_runState == CompletedSuccessfully; } virtual bool isOpen() const CATCH_OVERRIDE { return m_runState != NotStarted && !isComplete(); } virtual bool hasChildren() const CATCH_OVERRIDE { return !m_children.empty(); } virtual void addChild( Ptr const& child ) CATCH_OVERRIDE { m_children.push_back( child ); } virtual ITracker* findChild( std::string const& name ) CATCH_OVERRIDE { Children::const_iterator it = std::find_if( m_children.begin(), m_children.end(), TrackerHasName( name ) ); return( it != m_children.end() ) ? it->get() : CATCH_NULL; } virtual ITracker& parent() CATCH_OVERRIDE { assert( m_parent ); // Should always be non-null except for root return *m_parent; } virtual void openChild() CATCH_OVERRIDE { if( m_runState != ExecutingChildren ) { m_runState = ExecutingChildren; if( m_parent ) m_parent->openChild(); } } void open() { m_runState = Executing; moveToThis(); if( m_parent ) m_parent->openChild(); } virtual void close() CATCH_OVERRIDE { // Close any still open children (e.g. generators) while( &m_ctx.currentTracker() != this ) m_ctx.currentTracker().close(); switch( m_runState ) { case NotStarted: case CompletedSuccessfully: case Failed: throw std::logic_error( "Illogical state" ); case NeedsAnotherRun: break;; case Executing: m_runState = CompletedSuccessfully; break; case ExecutingChildren: if( m_children.empty() || m_children.back()->isComplete() ) m_runState = CompletedSuccessfully; break; default: throw std::logic_error( "Unexpected state" ); } moveToParent(); m_ctx.completeCycle(); } virtual void fail() CATCH_OVERRIDE { m_runState = Failed; if( m_parent ) m_parent->markAsNeedingAnotherRun(); moveToParent(); m_ctx.completeCycle(); } virtual void markAsNeedingAnotherRun() CATCH_OVERRIDE { m_runState = NeedsAnotherRun; } private: void moveToParent() { assert( m_parent ); m_ctx.setCurrentTracker( m_parent ); } void moveToThis() { m_ctx.setCurrentTracker( this ); } }; class SectionTracker : public TrackerBase { public: SectionTracker( std::string const& name, TrackerContext& ctx, ITracker* parent ) : TrackerBase( name, ctx, parent ) {} virtual ~SectionTracker(); static SectionTracker& acquire( TrackerContext& ctx, std::string const& name ) { SectionTracker* section = CATCH_NULL; ITracker& currentTracker = ctx.currentTracker(); if( ITracker* childTracker = currentTracker.findChild( name ) ) { section = dynamic_cast( childTracker ); assert( section ); } else { section = new SectionTracker( name, ctx, ¤tTracker ); currentTracker.addChild( section ); } if( !ctx.completedCycle() && !section->isComplete() ) { section->open(); } return *section; } }; class IndexTracker : public TrackerBase { int m_size; int m_index; public: IndexTracker( std::string const& name, TrackerContext& ctx, ITracker* parent, int size ) : TrackerBase( name, ctx, parent ), m_size( size ), m_index( -1 ) {} virtual ~IndexTracker(); static IndexTracker& acquire( TrackerContext& ctx, std::string const& name, int size ) { IndexTracker* tracker = CATCH_NULL; ITracker& currentTracker = ctx.currentTracker(); if( ITracker* childTracker = currentTracker.findChild( name ) ) { tracker = dynamic_cast( childTracker ); assert( tracker ); } else { tracker = new IndexTracker( name, ctx, ¤tTracker, size ); currentTracker.addChild( tracker ); } if( !ctx.completedCycle() && !tracker->isComplete() ) { if( tracker->m_runState != ExecutingChildren && tracker->m_runState != NeedsAnotherRun ) tracker->moveNext(); tracker->open(); } return *tracker; } int index() const { return m_index; } void moveNext() { m_index++; m_children.clear(); } virtual void close() CATCH_OVERRIDE { TrackerBase::close(); if( m_runState == CompletedSuccessfully && m_index < m_size-1 ) m_runState = Executing; } }; inline ITracker& TrackerContext::startRun() { m_rootTracker = new SectionTracker( "{root}", *this, CATCH_NULL ); m_currentTracker = CATCH_NULL; m_runState = Executing; return *m_rootTracker; } } // namespace TestCaseTracking using TestCaseTracking::ITracker; using TestCaseTracking::TrackerContext; using TestCaseTracking::SectionTracker; using TestCaseTracking::IndexTracker; } // namespace Catch // #included from: catch_fatal_condition.hpp #define TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED namespace Catch { // Report the error condition then exit the process inline void fatal( std::string const& message, int exitCode ) { IContext& context = Catch::getCurrentContext(); IResultCapture* resultCapture = context.getResultCapture(); resultCapture->handleFatalErrorCondition( message ); if( Catch::alwaysTrue() ) // avoids "no return" warnings exit( exitCode ); } } // namespace Catch #if defined ( CATCH_PLATFORM_WINDOWS ) ///////////////////////////////////////// namespace Catch { struct FatalConditionHandler { void reset() {} }; } // namespace Catch #else // Not Windows - assumed to be POSIX compatible ////////////////////////// #include namespace Catch { struct SignalDefs { int id; const char* name; }; extern SignalDefs signalDefs[]; SignalDefs signalDefs[] = { { SIGINT, "SIGINT - Terminal interrupt signal" }, { SIGILL, "SIGILL - Illegal instruction signal" }, { SIGFPE, "SIGFPE - Floating point error signal" }, { SIGSEGV, "SIGSEGV - Segmentation violation signal" }, { SIGTERM, "SIGTERM - Termination request signal" }, { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" } }; struct FatalConditionHandler { static void handleSignal( int sig ) { for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) if( sig == signalDefs[i].id ) fatal( signalDefs[i].name, -sig ); fatal( "", -sig ); } FatalConditionHandler() : m_isSet( true ) { for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) signal( signalDefs[i].id, handleSignal ); } ~FatalConditionHandler() { reset(); } void reset() { if( m_isSet ) { for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) signal( signalDefs[i].id, SIG_DFL ); m_isSet = false; } } bool m_isSet; }; } // namespace Catch #endif // not Windows #include #include namespace Catch { class StreamRedirect { public: StreamRedirect( std::ostream& stream, std::string& targetString ) : m_stream( stream ), m_prevBuf( stream.rdbuf() ), m_targetString( targetString ) { stream.rdbuf( m_oss.rdbuf() ); } ~StreamRedirect() { m_targetString += m_oss.str(); m_stream.rdbuf( m_prevBuf ); } private: std::ostream& m_stream; std::streambuf* m_prevBuf; std::ostringstream m_oss; std::string& m_targetString; }; /////////////////////////////////////////////////////////////////////////// class RunContext : public IResultCapture, public IRunner { RunContext( RunContext const& ); void operator =( RunContext const& ); public: explicit RunContext( Ptr const& _config, Ptr const& reporter ) : m_runInfo( _config->name() ), m_context( getCurrentMutableContext() ), m_activeTestCase( CATCH_NULL ), m_config( _config ), m_reporter( reporter ) { m_context.setRunner( this ); m_context.setConfig( m_config ); m_context.setResultCapture( this ); m_reporter->testRunStarting( m_runInfo ); } virtual ~RunContext() { m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, aborting() ) ); } void testGroupStarting( std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount ) { m_reporter->testGroupStarting( GroupInfo( testSpec, groupIndex, groupsCount ) ); } void testGroupEnded( std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount ) { m_reporter->testGroupEnded( TestGroupStats( GroupInfo( testSpec, groupIndex, groupsCount ), totals, aborting() ) ); } Totals runTest( TestCase const& testCase ) { Totals prevTotals = m_totals; std::string redirectedCout; std::string redirectedCerr; TestCaseInfo testInfo = testCase.getTestCaseInfo(); m_reporter->testCaseStarting( testInfo ); m_activeTestCase = &testCase; do { m_trackerContext.startRun(); do { m_trackerContext.startCycle(); m_testCaseTracker = &SectionTracker::acquire( m_trackerContext, testInfo.name ); runCurrentTest( redirectedCout, redirectedCerr ); } while( !m_testCaseTracker->isSuccessfullyCompleted() && !aborting() ); } // !TBD: deprecated - this will be replaced by indexed trackers while( getCurrentContext().advanceGeneratorsForCurrentTest() && !aborting() ); Totals deltaTotals = m_totals.delta( prevTotals ); if( testInfo.expectedToFail() && deltaTotals.testCases.passed > 0 ) { deltaTotals.assertions.failed++; deltaTotals.testCases.passed--; deltaTotals.testCases.failed++; } m_totals.testCases += deltaTotals.testCases; m_reporter->testCaseEnded( TestCaseStats( testInfo, deltaTotals, redirectedCout, redirectedCerr, aborting() ) ); m_activeTestCase = CATCH_NULL; m_testCaseTracker = CATCH_NULL; return deltaTotals; } Ptr config() const { return m_config; } private: // IResultCapture virtual void assertionEnded( AssertionResult const& result ) { if( result.getResultType() == ResultWas::Ok ) { m_totals.assertions.passed++; } else if( !result.isOk() ) { m_totals.assertions.failed++; } if( m_reporter->assertionEnded( AssertionStats( result, m_messages, m_totals ) ) ) m_messages.clear(); // Reset working state m_lastAssertionInfo = AssertionInfo( "", m_lastAssertionInfo.lineInfo, "{Unknown expression after the reported line}" , m_lastAssertionInfo.resultDisposition ); m_lastResult = result; } virtual bool sectionStarted ( SectionInfo const& sectionInfo, Counts& assertions ) { std::ostringstream oss; oss << sectionInfo.name << "@" << sectionInfo.lineInfo; ITracker& sectionTracker = SectionTracker::acquire( m_trackerContext, oss.str() ); if( !sectionTracker.isOpen() ) return false; m_activeSections.push_back( §ionTracker ); m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo; m_reporter->sectionStarting( sectionInfo ); assertions = m_totals.assertions; return true; } bool testForMissingAssertions( Counts& assertions ) { if( assertions.total() != 0 ) return false; if( !m_config->warnAboutMissingAssertions() ) return false; if( m_trackerContext.currentTracker().hasChildren() ) return false; m_totals.assertions.failed++; assertions.failed++; return true; } virtual void sectionEnded( SectionEndInfo const& endInfo ) { Counts assertions = m_totals.assertions - endInfo.prevAssertions; bool missingAssertions = testForMissingAssertions( assertions ); if( !m_activeSections.empty() ) { m_activeSections.back()->close(); m_activeSections.pop_back(); } m_reporter->sectionEnded( SectionStats( endInfo.sectionInfo, assertions, endInfo.durationInSeconds, missingAssertions ) ); m_messages.clear(); } virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) { if( m_unfinishedSections.empty() ) m_activeSections.back()->fail(); else m_activeSections.back()->close(); m_activeSections.pop_back(); m_unfinishedSections.push_back( endInfo ); } virtual void pushScopedMessage( MessageInfo const& message ) { m_messages.push_back( message ); } virtual void popScopedMessage( MessageInfo const& message ) { m_messages.erase( std::remove( m_messages.begin(), m_messages.end(), message ), m_messages.end() ); } virtual std::string getCurrentTestName() const { return m_activeTestCase ? m_activeTestCase->getTestCaseInfo().name : ""; } virtual const AssertionResult* getLastResult() const { return &m_lastResult; } virtual void handleFatalErrorCondition( std::string const& message ) { ResultBuilder resultBuilder = makeUnexpectedResultBuilder(); resultBuilder.setResultType( ResultWas::FatalErrorCondition ); resultBuilder << message; resultBuilder.captureExpression(); handleUnfinishedSections(); // Recreate section for test case (as we will lose the one that was in scope) TestCaseInfo const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); SectionInfo testCaseSection( testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description ); Counts assertions; assertions.failed = 1; SectionStats testCaseSectionStats( testCaseSection, assertions, 0, false ); m_reporter->sectionEnded( testCaseSectionStats ); TestCaseInfo testInfo = m_activeTestCase->getTestCaseInfo(); Totals deltaTotals; deltaTotals.testCases.failed = 1; m_reporter->testCaseEnded( TestCaseStats( testInfo, deltaTotals, "", "", false ) ); m_totals.testCases.failed++; testGroupEnded( "", m_totals, 1, 1 ); m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, false ) ); } public: // !TBD We need to do this another way! bool aborting() const { return m_totals.assertions.failed == static_cast( m_config->abortAfter() ); } private: void runCurrentTest( std::string& redirectedCout, std::string& redirectedCerr ) { TestCaseInfo const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); SectionInfo testCaseSection( testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description ); m_reporter->sectionStarting( testCaseSection ); Counts prevAssertions = m_totals.assertions; double duration = 0; try { m_lastAssertionInfo = AssertionInfo( "TEST_CASE", testCaseInfo.lineInfo, "", ResultDisposition::Normal ); seedRng( *m_config ); Timer timer; timer.start(); if( m_reporter->getPreferences().shouldRedirectStdOut ) { StreamRedirect coutRedir( Catch::cout(), redirectedCout ); StreamRedirect cerrRedir( Catch::cerr(), redirectedCerr ); invokeActiveTestCase(); } else { invokeActiveTestCase(); } duration = timer.getElapsedSeconds(); } catch( TestFailureException& ) { // This just means the test was aborted due to failure } catch(...) { makeUnexpectedResultBuilder().useActiveException(); } m_testCaseTracker->close(); handleUnfinishedSections(); m_messages.clear(); Counts assertions = m_totals.assertions - prevAssertions; bool missingAssertions = testForMissingAssertions( assertions ); if( testCaseInfo.okToFail() ) { std::swap( assertions.failedButOk, assertions.failed ); m_totals.assertions.failed -= assertions.failedButOk; m_totals.assertions.failedButOk += assertions.failedButOk; } SectionStats testCaseSectionStats( testCaseSection, assertions, duration, missingAssertions ); m_reporter->sectionEnded( testCaseSectionStats ); } void invokeActiveTestCase() { FatalConditionHandler fatalConditionHandler; // Handle signals m_activeTestCase->invoke(); fatalConditionHandler.reset(); } private: ResultBuilder makeUnexpectedResultBuilder() const { return ResultBuilder( m_lastAssertionInfo.macroName.c_str(), m_lastAssertionInfo.lineInfo, m_lastAssertionInfo.capturedExpression.c_str(), m_lastAssertionInfo.resultDisposition ); } void handleUnfinishedSections() { // If sections ended prematurely due to an exception we stored their // infos here so we can tear them down outside the unwind process. for( std::vector::const_reverse_iterator it = m_unfinishedSections.rbegin(), itEnd = m_unfinishedSections.rend(); it != itEnd; ++it ) sectionEnded( *it ); m_unfinishedSections.clear(); } TestRunInfo m_runInfo; IMutableContext& m_context; TestCase const* m_activeTestCase; ITracker* m_testCaseTracker; ITracker* m_currentSectionTracker; AssertionResult m_lastResult; Ptr m_config; Totals m_totals; Ptr m_reporter; std::vector m_messages; AssertionInfo m_lastAssertionInfo; std::vector m_unfinishedSections; std::vector m_activeSections; TrackerContext m_trackerContext; }; IResultCapture& getResultCapture() { if( IResultCapture* capture = getCurrentContext().getResultCapture() ) return *capture; else throw std::logic_error( "No result capture instance" ); } } // end namespace Catch // #included from: internal/catch_version.h #define TWOBLUECUBES_CATCH_VERSION_H_INCLUDED namespace Catch { // Versioning information struct Version { Version( unsigned int _majorVersion, unsigned int _minorVersion, unsigned int _patchNumber, std::string const& _branchName, unsigned int _buildNumber ); unsigned int const majorVersion; unsigned int const minorVersion; unsigned int const patchNumber; // buildNumber is only used if branchName is not null std::string const branchName; unsigned int const buildNumber; friend std::ostream& operator << ( std::ostream& os, Version const& version ); private: void operator=( Version const& ); }; extern Version libraryVersion; } #include #include #include namespace Catch { Ptr createReporter( std::string const& reporterName, Ptr const& config ) { Ptr reporter = getRegistryHub().getReporterRegistry().create( reporterName, config.get() ); if( !reporter ) { std::ostringstream oss; oss << "No reporter registered with name: '" << reporterName << "'"; throw std::domain_error( oss.str() ); } return reporter; } Ptr makeReporter( Ptr const& config ) { std::vector reporters = config->getReporterNames(); if( reporters.empty() ) reporters.push_back( "console" ); Ptr reporter; for( std::vector::const_iterator it = reporters.begin(), itEnd = reporters.end(); it != itEnd; ++it ) reporter = addReporter( reporter, createReporter( *it, config ) ); return reporter; } Ptr addListeners( Ptr const& config, Ptr reporters ) { IReporterRegistry::Listeners listeners = getRegistryHub().getReporterRegistry().getListeners(); for( IReporterRegistry::Listeners::const_iterator it = listeners.begin(), itEnd = listeners.end(); it != itEnd; ++it ) reporters = addReporter(reporters, (*it)->create( ReporterConfig( config ) ) ); return reporters; } Totals runTests( Ptr const& config ) { Ptr iconfig = config.get(); Ptr reporter = makeReporter( config ); reporter = addListeners( iconfig, reporter ); RunContext context( iconfig, reporter ); Totals totals; context.testGroupStarting( config->name(), 1, 1 ); TestSpec testSpec = config->testSpec(); if( !testSpec.hasFilters() ) testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "~[.]" ).testSpec(); // All not hidden tests std::vector const& allTestCases = getAllTestCasesSorted( *iconfig ); for( std::vector::const_iterator it = allTestCases.begin(), itEnd = allTestCases.end(); it != itEnd; ++it ) { if( !context.aborting() && matchTest( *it, testSpec, *iconfig ) ) totals += context.runTest( *it ); else reporter->skipTest( *it ); } context.testGroupEnded( iconfig->name(), totals, 1, 1 ); return totals; } void applyFilenamesAsTags( IConfig const& config ) { std::vector const& tests = getAllTestCasesSorted( config ); for(std::size_t i = 0; i < tests.size(); ++i ) { TestCase& test = const_cast( tests[i] ); std::set tags = test.tags; std::string filename = test.lineInfo.file; std::string::size_type lastSlash = filename.find_last_of( "\\/" ); if( lastSlash != std::string::npos ) filename = filename.substr( lastSlash+1 ); std::string::size_type lastDot = filename.find_last_of( "." ); if( lastDot != std::string::npos ) filename = filename.substr( 0, lastDot ); tags.insert( "#" + filename ); setTags( test, tags ); } } class Session : NonCopyable { static bool alreadyInstantiated; public: struct OnUnusedOptions { enum DoWhat { Ignore, Fail }; }; Session() : m_cli( makeCommandLineParser() ) { if( alreadyInstantiated ) { std::string msg = "Only one instance of Catch::Session can ever be used"; Catch::cerr() << msg << std::endl; throw std::logic_error( msg ); } alreadyInstantiated = true; } ~Session() { Catch::cleanUp(); } void showHelp( std::string const& processName ) { Catch::cout() << "\nCatch v" << libraryVersion << "\n"; m_cli.usage( Catch::cout(), processName ); Catch::cout() << "For more detail usage please see the project docs\n" << std::endl; } int applyCommandLine( int argc, char const* argv[], OnUnusedOptions::DoWhat unusedOptionBehaviour = OnUnusedOptions::Fail ) { try { m_cli.setThrowOnUnrecognisedTokens( unusedOptionBehaviour == OnUnusedOptions::Fail ); m_unusedTokens = m_cli.parseInto( argc, argv, m_configData ); if( m_configData.showHelp ) showHelp( m_configData.processName ); m_config.reset(); } catch( std::exception& ex ) { { Colour colourGuard( Colour::Red ); Catch::cerr() << "\nError(s) in input:\n" << Text( ex.what(), TextAttributes().setIndent(2) ) << "\n\n"; } m_cli.usage( Catch::cout(), m_configData.processName ); return (std::numeric_limits::max)(); } return 0; } void useConfigData( ConfigData const& _configData ) { m_configData = _configData; m_config.reset(); } int run( int argc, char const* argv[] ) { int returnCode = applyCommandLine( argc, argv ); if( returnCode == 0 ) returnCode = run(); return returnCode; } int run( int argc, char* argv[] ) { return run( argc, const_cast( argv ) ); } int run() { if( m_configData.showHelp ) return 0; try { config(); // Force config to be constructed seedRng( *m_config ); if( m_configData.filenamesAsTags ) applyFilenamesAsTags( *m_config ); // Handle list request if( Option listed = list( config() ) ) return static_cast( *listed ); return static_cast( runTests( m_config ).assertions.failed ); } catch( std::exception& ex ) { Catch::cerr() << ex.what() << std::endl; return (std::numeric_limits::max)(); } } Clara::CommandLine const& cli() const { return m_cli; } std::vector const& unusedTokens() const { return m_unusedTokens; } ConfigData& configData() { return m_configData; } Config& config() { if( !m_config ) m_config = new Config( m_configData ); return *m_config; } private: Clara::CommandLine m_cli; std::vector m_unusedTokens; ConfigData m_configData; Ptr m_config; }; bool Session::alreadyInstantiated = false; } // end namespace Catch // #included from: catch_registry_hub.hpp #define TWOBLUECUBES_CATCH_REGISTRY_HUB_HPP_INCLUDED // #included from: catch_test_case_registry_impl.hpp #define TWOBLUECUBES_CATCH_TEST_CASE_REGISTRY_IMPL_HPP_INCLUDED #include #include #include #include #include namespace Catch { struct LexSort { bool operator() (TestCase i,TestCase j) const { return (i sortTests( IConfig const& config, std::vector const& unsortedTestCases ) { std::vector sorted = unsortedTestCases; switch( config.runOrder() ) { case RunTests::InLexicographicalOrder: std::sort( sorted.begin(), sorted.end(), LexSort() ); break; case RunTests::InRandomOrder: { seedRng( config ); RandomNumberGenerator rng; std::random_shuffle( sorted.begin(), sorted.end(), rng ); } break; case RunTests::InDeclarationOrder: // already in declaration order break; } return sorted; } bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ) { return testSpec.matches( testCase ) && ( config.allowThrows() || !testCase.throws() ); } void enforceNoDuplicateTestCases( std::vector const& functions ) { std::set seenFunctions; for( std::vector::const_iterator it = functions.begin(), itEnd = functions.end(); it != itEnd; ++it ) { std::pair::const_iterator, bool> prev = seenFunctions.insert( *it ); if( !prev.second ){ Catch::cerr() << Colour( Colour::Red ) << "error: TEST_CASE( \"" << it->name << "\" ) already defined.\n" << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << "\n" << "\tRedefined at " << it->getTestCaseInfo().lineInfo << std::endl; exit(1); } } } std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ) { std::vector filtered; filtered.reserve( testCases.size() ); for( std::vector::const_iterator it = testCases.begin(), itEnd = testCases.end(); it != itEnd; ++it ) if( matchTest( *it, testSpec, config ) ) filtered.push_back( *it ); return filtered; } std::vector const& getAllTestCasesSorted( IConfig const& config ) { return getRegistryHub().getTestCaseRegistry().getAllTestsSorted( config ); } class TestRegistry : public ITestCaseRegistry { public: TestRegistry() : m_currentSortOrder( RunTests::InDeclarationOrder ), m_unnamedCount( 0 ) {} virtual ~TestRegistry(); virtual void registerTest( TestCase const& testCase ) { std::string name = testCase.getTestCaseInfo().name; if( name == "" ) { std::ostringstream oss; oss << "Anonymous test case " << ++m_unnamedCount; return registerTest( testCase.withName( oss.str() ) ); } m_functions.push_back( testCase ); } virtual std::vector const& getAllTests() const { return m_functions; } virtual std::vector const& getAllTestsSorted( IConfig const& config ) const { if( m_sortedFunctions.empty() ) enforceNoDuplicateTestCases( m_functions ); if( m_currentSortOrder != config.runOrder() || m_sortedFunctions.empty() ) { m_sortedFunctions = sortTests( config, m_functions ); m_currentSortOrder = config.runOrder(); } return m_sortedFunctions; } private: std::vector m_functions; mutable RunTests::InWhatOrder m_currentSortOrder; mutable std::vector m_sortedFunctions; size_t m_unnamedCount; std::ios_base::Init m_ostreamInit; // Forces cout/ cerr to be initialised }; /////////////////////////////////////////////////////////////////////////// class FreeFunctionTestCase : public SharedImpl { public: FreeFunctionTestCase( TestFunction fun ) : m_fun( fun ) {} virtual void invoke() const { m_fun(); } private: virtual ~FreeFunctionTestCase(); TestFunction m_fun; }; inline std::string extractClassName( std::string const& classOrQualifiedMethodName ) { std::string className = classOrQualifiedMethodName; if( startsWith( className, "&" ) ) { std::size_t lastColons = className.rfind( "::" ); std::size_t penultimateColons = className.rfind( "::", lastColons-1 ); if( penultimateColons == std::string::npos ) penultimateColons = 1; className = className.substr( penultimateColons, lastColons-penultimateColons ); } return className; } void registerTestCase ( ITestCase* testCase, char const* classOrQualifiedMethodName, NameAndDesc const& nameAndDesc, SourceLineInfo const& lineInfo ) { getMutableRegistryHub().registerTest ( makeTestCase ( testCase, extractClassName( classOrQualifiedMethodName ), nameAndDesc.name, nameAndDesc.description, lineInfo ) ); } void registerTestCaseFunction ( TestFunction function, SourceLineInfo const& lineInfo, NameAndDesc const& nameAndDesc ) { registerTestCase( new FreeFunctionTestCase( function ), "", nameAndDesc, lineInfo ); } /////////////////////////////////////////////////////////////////////////// AutoReg::AutoReg ( TestFunction function, SourceLineInfo const& lineInfo, NameAndDesc const& nameAndDesc ) { registerTestCaseFunction( function, lineInfo, nameAndDesc ); } AutoReg::~AutoReg() {} } // end namespace Catch // #included from: catch_reporter_registry.hpp #define TWOBLUECUBES_CATCH_REPORTER_REGISTRY_HPP_INCLUDED #include namespace Catch { class ReporterRegistry : public IReporterRegistry { public: virtual ~ReporterRegistry() CATCH_OVERRIDE {} virtual IStreamingReporter* create( std::string const& name, Ptr const& config ) const CATCH_OVERRIDE { FactoryMap::const_iterator it = m_factories.find( name ); if( it == m_factories.end() ) return CATCH_NULL; return it->second->create( ReporterConfig( config ) ); } void registerReporter( std::string const& name, Ptr const& factory ) { m_factories.insert( std::make_pair( name, factory ) ); } void registerListener( Ptr const& factory ) { m_listeners.push_back( factory ); } virtual FactoryMap const& getFactories() const CATCH_OVERRIDE { return m_factories; } virtual Listeners const& getListeners() const CATCH_OVERRIDE { return m_listeners; } private: FactoryMap m_factories; Listeners m_listeners; }; } // #included from: catch_exception_translator_registry.hpp #define TWOBLUECUBES_CATCH_EXCEPTION_TRANSLATOR_REGISTRY_HPP_INCLUDED #ifdef __OBJC__ #import "Foundation/Foundation.h" #endif namespace Catch { class ExceptionTranslatorRegistry : public IExceptionTranslatorRegistry { public: ~ExceptionTranslatorRegistry() { deleteAll( m_translators ); } virtual void registerTranslator( const IExceptionTranslator* translator ) { m_translators.push_back( translator ); } virtual std::string translateActiveException() const { try { #ifdef __OBJC__ // In Objective-C try objective-c exceptions first @try { return tryTranslators(); } @catch (NSException *exception) { return Catch::toString( [exception description] ); } #else return tryTranslators(); #endif } catch( TestFailureException& ) { throw; } catch( std::exception& ex ) { return ex.what(); } catch( std::string& msg ) { return msg; } catch( const char* msg ) { return msg; } catch(...) { return "Unknown exception"; } } std::string tryTranslators() const { if( m_translators.empty() ) throw; else return m_translators[0]->translate( m_translators.begin()+1, m_translators.end() ); } private: std::vector m_translators; }; } namespace Catch { namespace { class RegistryHub : public IRegistryHub, public IMutableRegistryHub { RegistryHub( RegistryHub const& ); void operator=( RegistryHub const& ); public: // IRegistryHub RegistryHub() { } virtual IReporterRegistry const& getReporterRegistry() const CATCH_OVERRIDE { return m_reporterRegistry; } virtual ITestCaseRegistry const& getTestCaseRegistry() const CATCH_OVERRIDE { return m_testCaseRegistry; } virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() CATCH_OVERRIDE { return m_exceptionTranslatorRegistry; } public: // IMutableRegistryHub virtual void registerReporter( std::string const& name, Ptr const& factory ) CATCH_OVERRIDE { m_reporterRegistry.registerReporter( name, factory ); } virtual void registerListener( Ptr const& factory ) CATCH_OVERRIDE { m_reporterRegistry.registerListener( factory ); } virtual void registerTest( TestCase const& testInfo ) CATCH_OVERRIDE { m_testCaseRegistry.registerTest( testInfo ); } virtual void registerTranslator( const IExceptionTranslator* translator ) CATCH_OVERRIDE { m_exceptionTranslatorRegistry.registerTranslator( translator ); } private: TestRegistry m_testCaseRegistry; ReporterRegistry m_reporterRegistry; ExceptionTranslatorRegistry m_exceptionTranslatorRegistry; }; // Single, global, instance inline RegistryHub*& getTheRegistryHub() { static RegistryHub* theRegistryHub = CATCH_NULL; if( !theRegistryHub ) theRegistryHub = new RegistryHub(); return theRegistryHub; } } IRegistryHub& getRegistryHub() { return *getTheRegistryHub(); } IMutableRegistryHub& getMutableRegistryHub() { return *getTheRegistryHub(); } void cleanUp() { delete getTheRegistryHub(); getTheRegistryHub() = CATCH_NULL; cleanUpContext(); } std::string translateActiveException() { return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException(); } } // end namespace Catch // #included from: catch_notimplemented_exception.hpp #define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_HPP_INCLUDED #include namespace Catch { NotImplementedException::NotImplementedException( SourceLineInfo const& lineInfo ) : m_lineInfo( lineInfo ) { std::ostringstream oss; oss << lineInfo << ": function "; oss << "not implemented"; m_what = oss.str(); } const char* NotImplementedException::what() const CATCH_NOEXCEPT { return m_what.c_str(); } } // end namespace Catch // #included from: catch_context_impl.hpp #define TWOBLUECUBES_CATCH_CONTEXT_IMPL_HPP_INCLUDED // #included from: catch_stream.hpp #define TWOBLUECUBES_CATCH_STREAM_HPP_INCLUDED #include #include #include namespace Catch { template class StreamBufImpl : public StreamBufBase { char data[bufferSize]; WriterF m_writer; public: StreamBufImpl() { setp( data, data + sizeof(data) ); } ~StreamBufImpl() CATCH_NOEXCEPT { sync(); } private: int overflow( int c ) { sync(); if( c != EOF ) { if( pbase() == epptr() ) m_writer( std::string( 1, static_cast( c ) ) ); else sputc( static_cast( c ) ); } return 0; } int sync() { if( pbase() != pptr() ) { m_writer( std::string( pbase(), static_cast( pptr() - pbase() ) ) ); setp( pbase(), epptr() ); } return 0; } }; /////////////////////////////////////////////////////////////////////////// FileStream::FileStream( std::string const& filename ) { m_ofs.open( filename.c_str() ); if( m_ofs.fail() ) { std::ostringstream oss; oss << "Unable to open file: '" << filename << "'"; throw std::domain_error( oss.str() ); } } std::ostream& FileStream::stream() const { return m_ofs; } struct OutputDebugWriter { void operator()( std::string const&str ) { writeToDebugConsole( str ); } }; DebugOutStream::DebugOutStream() : m_streamBuf( new StreamBufImpl() ), m_os( m_streamBuf.get() ) {} std::ostream& DebugOutStream::stream() const { return m_os; } // Store the streambuf from cout up-front because // cout may get redirected when running tests CoutStream::CoutStream() : m_os( Catch::cout().rdbuf() ) {} std::ostream& CoutStream::stream() const { return m_os; } #ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement these functions std::ostream& cout() { return std::cout; } std::ostream& cerr() { return std::cerr; } #endif } namespace Catch { class Context : public IMutableContext { Context() : m_config( CATCH_NULL ), m_runner( CATCH_NULL ), m_resultCapture( CATCH_NULL ) {} Context( Context const& ); void operator=( Context const& ); public: // IContext virtual IResultCapture* getResultCapture() { return m_resultCapture; } virtual IRunner* getRunner() { return m_runner; } virtual size_t getGeneratorIndex( std::string const& fileInfo, size_t totalSize ) { return getGeneratorsForCurrentTest() .getGeneratorInfo( fileInfo, totalSize ) .getCurrentIndex(); } virtual bool advanceGeneratorsForCurrentTest() { IGeneratorsForTest* generators = findGeneratorsForCurrentTest(); return generators && generators->moveNext(); } virtual Ptr getConfig() const { return m_config; } public: // IMutableContext virtual void setResultCapture( IResultCapture* resultCapture ) { m_resultCapture = resultCapture; } virtual void setRunner( IRunner* runner ) { m_runner = runner; } virtual void setConfig( Ptr const& config ) { m_config = config; } friend IMutableContext& getCurrentMutableContext(); private: IGeneratorsForTest* findGeneratorsForCurrentTest() { std::string testName = getResultCapture()->getCurrentTestName(); std::map::const_iterator it = m_generatorsByTestName.find( testName ); return it != m_generatorsByTestName.end() ? it->second : CATCH_NULL; } IGeneratorsForTest& getGeneratorsForCurrentTest() { IGeneratorsForTest* generators = findGeneratorsForCurrentTest(); if( !generators ) { std::string testName = getResultCapture()->getCurrentTestName(); generators = createGeneratorsForTest(); m_generatorsByTestName.insert( std::make_pair( testName, generators ) ); } return *generators; } private: Ptr m_config; IRunner* m_runner; IResultCapture* m_resultCapture; std::map m_generatorsByTestName; }; namespace { Context* currentContext = CATCH_NULL; } IMutableContext& getCurrentMutableContext() { if( !currentContext ) currentContext = new Context(); return *currentContext; } IContext& getCurrentContext() { return getCurrentMutableContext(); } void cleanUpContext() { delete currentContext; currentContext = CATCH_NULL; } } // #included from: catch_console_colour_impl.hpp #define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_IMPL_HPP_INCLUDED namespace Catch { namespace { struct IColourImpl { virtual ~IColourImpl() {} virtual void use( Colour::Code _colourCode ) = 0; }; struct NoColourImpl : IColourImpl { void use( Colour::Code ) {} static IColourImpl* instance() { static NoColourImpl s_instance; return &s_instance; } }; } // anon namespace } // namespace Catch #if !defined( CATCH_CONFIG_COLOUR_NONE ) && !defined( CATCH_CONFIG_COLOUR_WINDOWS ) && !defined( CATCH_CONFIG_COLOUR_ANSI ) # ifdef CATCH_PLATFORM_WINDOWS # define CATCH_CONFIG_COLOUR_WINDOWS # else # define CATCH_CONFIG_COLOUR_ANSI # endif #endif #if defined ( CATCH_CONFIG_COLOUR_WINDOWS ) ///////////////////////////////////////// #ifndef NOMINMAX #define NOMINMAX #endif #ifdef __AFXDLL #include #else #include #endif namespace Catch { namespace { class Win32ColourImpl : public IColourImpl { public: Win32ColourImpl() : stdoutHandle( GetStdHandle(STD_OUTPUT_HANDLE) ) { CONSOLE_SCREEN_BUFFER_INFO csbiInfo; GetConsoleScreenBufferInfo( stdoutHandle, &csbiInfo ); originalForegroundAttributes = csbiInfo.wAttributes & ~( BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY ); originalBackgroundAttributes = csbiInfo.wAttributes & ~( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY ); } virtual void use( Colour::Code _colourCode ) { switch( _colourCode ) { case Colour::None: return setTextAttribute( originalForegroundAttributes ); case Colour::White: return setTextAttribute( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); case Colour::Red: return setTextAttribute( FOREGROUND_RED ); case Colour::Green: return setTextAttribute( FOREGROUND_GREEN ); case Colour::Blue: return setTextAttribute( FOREGROUND_BLUE ); case Colour::Cyan: return setTextAttribute( FOREGROUND_BLUE | FOREGROUND_GREEN ); case Colour::Yellow: return setTextAttribute( FOREGROUND_RED | FOREGROUND_GREEN ); case Colour::Grey: return setTextAttribute( 0 ); case Colour::LightGrey: return setTextAttribute( FOREGROUND_INTENSITY ); case Colour::BrightRed: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED ); case Colour::BrightGreen: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN ); case Colour::BrightWhite: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); case Colour::Bright: throw std::logic_error( "not a colour" ); } } private: void setTextAttribute( WORD _textAttribute ) { SetConsoleTextAttribute( stdoutHandle, _textAttribute | originalBackgroundAttributes ); } HANDLE stdoutHandle; WORD originalForegroundAttributes; WORD originalBackgroundAttributes; }; IColourImpl* platformColourInstance() { static Win32ColourImpl s_instance; Ptr config = getCurrentContext().getConfig(); UseColour::YesOrNo colourMode = config ? config->useColour() : UseColour::Auto; if( colourMode == UseColour::Auto ) colourMode = !isDebuggerActive() ? UseColour::Yes : UseColour::No; return colourMode == UseColour::Yes ? &s_instance : NoColourImpl::instance(); } } // end anon namespace } // end namespace Catch #elif defined( CATCH_CONFIG_COLOUR_ANSI ) ////////////////////////////////////// #include namespace Catch { namespace { // use POSIX/ ANSI console terminal codes // Thanks to Adam Strzelecki for original contribution // (http://github.com/nanoant) // https://github.com/philsquared/Catch/pull/131 class PosixColourImpl : public IColourImpl { public: virtual void use( Colour::Code _colourCode ) { switch( _colourCode ) { case Colour::None: case Colour::White: return setColour( "[0m" ); case Colour::Red: return setColour( "[0;31m" ); case Colour::Green: return setColour( "[0;32m" ); case Colour::Blue: return setColour( "[0:34m" ); case Colour::Cyan: return setColour( "[0;36m" ); case Colour::Yellow: return setColour( "[0;33m" ); case Colour::Grey: return setColour( "[1;30m" ); case Colour::LightGrey: return setColour( "[0;37m" ); case Colour::BrightRed: return setColour( "[1;31m" ); case Colour::BrightGreen: return setColour( "[1;32m" ); case Colour::BrightWhite: return setColour( "[1;37m" ); case Colour::Bright: throw std::logic_error( "not a colour" ); } } static IColourImpl* instance() { static PosixColourImpl s_instance; return &s_instance; } private: void setColour( const char* _escapeCode ) { Catch::cout() << '\033' << _escapeCode; } }; IColourImpl* platformColourInstance() { Ptr config = getCurrentContext().getConfig(); UseColour::YesOrNo colourMode = config ? config->useColour() : UseColour::Auto; if( colourMode == UseColour::Auto ) colourMode = (!isDebuggerActive() && isatty(STDOUT_FILENO) ) ? UseColour::Yes : UseColour::No; return colourMode == UseColour::Yes ? PosixColourImpl::instance() : NoColourImpl::instance(); } } // end anon namespace } // end namespace Catch #else // not Windows or ANSI /////////////////////////////////////////////// namespace Catch { static IColourImpl* platformColourInstance() { return NoColourImpl::instance(); } } // end namespace Catch #endif // Windows/ ANSI/ None namespace Catch { Colour::Colour( Code _colourCode ) : m_moved( false ) { use( _colourCode ); } Colour::Colour( Colour const& _other ) : m_moved( false ) { const_cast( _other ).m_moved = true; } Colour::~Colour(){ if( !m_moved ) use( None ); } void Colour::use( Code _colourCode ) { static IColourImpl* impl = platformColourInstance(); impl->use( _colourCode ); } } // end namespace Catch // #included from: catch_generators_impl.hpp #define TWOBLUECUBES_CATCH_GENERATORS_IMPL_HPP_INCLUDED #include #include #include namespace Catch { struct GeneratorInfo : IGeneratorInfo { GeneratorInfo( std::size_t size ) : m_size( size ), m_currentIndex( 0 ) {} bool moveNext() { if( ++m_currentIndex == m_size ) { m_currentIndex = 0; return false; } return true; } std::size_t getCurrentIndex() const { return m_currentIndex; } std::size_t m_size; std::size_t m_currentIndex; }; /////////////////////////////////////////////////////////////////////////// class GeneratorsForTest : public IGeneratorsForTest { public: ~GeneratorsForTest() { deleteAll( m_generatorsInOrder ); } IGeneratorInfo& getGeneratorInfo( std::string const& fileInfo, std::size_t size ) { std::map::const_iterator it = m_generatorsByName.find( fileInfo ); if( it == m_generatorsByName.end() ) { IGeneratorInfo* info = new GeneratorInfo( size ); m_generatorsByName.insert( std::make_pair( fileInfo, info ) ); m_generatorsInOrder.push_back( info ); return *info; } return *it->second; } bool moveNext() { std::vector::const_iterator it = m_generatorsInOrder.begin(); std::vector::const_iterator itEnd = m_generatorsInOrder.end(); for(; it != itEnd; ++it ) { if( (*it)->moveNext() ) return true; } return false; } private: std::map m_generatorsByName; std::vector m_generatorsInOrder; }; IGeneratorsForTest* createGeneratorsForTest() { return new GeneratorsForTest(); } } // end namespace Catch // #included from: catch_assertionresult.hpp #define TWOBLUECUBES_CATCH_ASSERTIONRESULT_HPP_INCLUDED namespace Catch { AssertionInfo::AssertionInfo( std::string const& _macroName, SourceLineInfo const& _lineInfo, std::string const& _capturedExpression, ResultDisposition::Flags _resultDisposition ) : macroName( _macroName ), lineInfo( _lineInfo ), capturedExpression( _capturedExpression ), resultDisposition( _resultDisposition ) {} AssertionResult::AssertionResult() {} AssertionResult::AssertionResult( AssertionInfo const& info, AssertionResultData const& data ) : m_info( info ), m_resultData( data ) {} AssertionResult::~AssertionResult() {} // Result was a success bool AssertionResult::succeeded() const { return Catch::isOk( m_resultData.resultType ); } // Result was a success, or failure is suppressed bool AssertionResult::isOk() const { return Catch::isOk( m_resultData.resultType ) || shouldSuppressFailure( m_info.resultDisposition ); } ResultWas::OfType AssertionResult::getResultType() const { return m_resultData.resultType; } bool AssertionResult::hasExpression() const { return !m_info.capturedExpression.empty(); } bool AssertionResult::hasMessage() const { return !m_resultData.message.empty(); } std::string AssertionResult::getExpression() const { if( isFalseTest( m_info.resultDisposition ) ) return "!" + m_info.capturedExpression; else return m_info.capturedExpression; } std::string AssertionResult::getExpressionInMacro() const { if( m_info.macroName.empty() ) return m_info.capturedExpression; else return m_info.macroName + "( " + m_info.capturedExpression + " )"; } bool AssertionResult::hasExpandedExpression() const { return hasExpression() && getExpandedExpression() != getExpression(); } std::string AssertionResult::getExpandedExpression() const { return m_resultData.reconstructedExpression; } std::string AssertionResult::getMessage() const { return m_resultData.message; } SourceLineInfo AssertionResult::getSourceInfo() const { return m_info.lineInfo; } std::string AssertionResult::getTestMacroName() const { return m_info.macroName; } } // end namespace Catch // #included from: catch_test_case_info.hpp #define TWOBLUECUBES_CATCH_TEST_CASE_INFO_HPP_INCLUDED namespace Catch { inline TestCaseInfo::SpecialProperties parseSpecialTag( std::string const& tag ) { if( startsWith( tag, "." ) || tag == "hide" || tag == "!hide" ) return TestCaseInfo::IsHidden; else if( tag == "!throws" ) return TestCaseInfo::Throws; else if( tag == "!shouldfail" ) return TestCaseInfo::ShouldFail; else if( tag == "!mayfail" ) return TestCaseInfo::MayFail; else return TestCaseInfo::None; } inline bool isReservedTag( std::string const& tag ) { return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !isalnum( tag[0] ); } inline void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) { if( isReservedTag( tag ) ) { { Colour colourGuard( Colour::Red ); Catch::cerr() << "Tag name [" << tag << "] not allowed.\n" << "Tag names starting with non alpha-numeric characters are reserved\n"; } { Colour colourGuard( Colour::FileName ); Catch::cerr() << _lineInfo << std::endl; } exit(1); } } TestCase makeTestCase( ITestCase* _testCase, std::string const& _className, std::string const& _name, std::string const& _descOrTags, SourceLineInfo const& _lineInfo ) { bool isHidden( startsWith( _name, "./" ) ); // Legacy support // Parse out tags std::set tags; std::string desc, tag; bool inTag = false; for( std::size_t i = 0; i < _descOrTags.size(); ++i ) { char c = _descOrTags[i]; if( !inTag ) { if( c == '[' ) inTag = true; else desc += c; } else { if( c == ']' ) { TestCaseInfo::SpecialProperties prop = parseSpecialTag( tag ); if( prop == TestCaseInfo::IsHidden ) isHidden = true; else if( prop == TestCaseInfo::None ) enforceNotReservedTag( tag, _lineInfo ); tags.insert( tag ); tag.clear(); inTag = false; } else tag += c; } } if( isHidden ) { tags.insert( "hide" ); tags.insert( "." ); } TestCaseInfo info( _name, _className, desc, tags, _lineInfo ); return TestCase( _testCase, info ); } void setTags( TestCaseInfo& testCaseInfo, std::set const& tags ) { testCaseInfo.tags = tags; testCaseInfo.lcaseTags.clear(); std::ostringstream oss; for( std::set::const_iterator it = tags.begin(), itEnd = tags.end(); it != itEnd; ++it ) { oss << "[" << *it << "]"; std::string lcaseTag = toLower( *it ); testCaseInfo.properties = static_cast( testCaseInfo.properties | parseSpecialTag( lcaseTag ) ); testCaseInfo.lcaseTags.insert( lcaseTag ); } testCaseInfo.tagsAsString = oss.str(); } TestCaseInfo::TestCaseInfo( std::string const& _name, std::string const& _className, std::string const& _description, std::set const& _tags, SourceLineInfo const& _lineInfo ) : name( _name ), className( _className ), description( _description ), lineInfo( _lineInfo ), properties( None ) { setTags( *this, _tags ); } TestCaseInfo::TestCaseInfo( TestCaseInfo const& other ) : name( other.name ), className( other.className ), description( other.description ), tags( other.tags ), lcaseTags( other.lcaseTags ), tagsAsString( other.tagsAsString ), lineInfo( other.lineInfo ), properties( other.properties ) {} bool TestCaseInfo::isHidden() const { return ( properties & IsHidden ) != 0; } bool TestCaseInfo::throws() const { return ( properties & Throws ) != 0; } bool TestCaseInfo::okToFail() const { return ( properties & (ShouldFail | MayFail ) ) != 0; } bool TestCaseInfo::expectedToFail() const { return ( properties & (ShouldFail ) ) != 0; } TestCase::TestCase( ITestCase* testCase, TestCaseInfo const& info ) : TestCaseInfo( info ), test( testCase ) {} TestCase::TestCase( TestCase const& other ) : TestCaseInfo( other ), test( other.test ) {} TestCase TestCase::withName( std::string const& _newName ) const { TestCase other( *this ); other.name = _newName; return other; } void TestCase::swap( TestCase& other ) { test.swap( other.test ); name.swap( other.name ); className.swap( other.className ); description.swap( other.description ); tags.swap( other.tags ); lcaseTags.swap( other.lcaseTags ); tagsAsString.swap( other.tagsAsString ); std::swap( TestCaseInfo::properties, static_cast( other ).properties ); std::swap( lineInfo, other.lineInfo ); } void TestCase::invoke() const { test->invoke(); } bool TestCase::operator == ( TestCase const& other ) const { return test.get() == other.test.get() && name == other.name && className == other.className; } bool TestCase::operator < ( TestCase const& other ) const { return name < other.name; } TestCase& TestCase::operator = ( TestCase const& other ) { TestCase temp( other ); swap( temp ); return *this; } TestCaseInfo const& TestCase::getTestCaseInfo() const { return *this; } } // end namespace Catch // #included from: catch_version.hpp #define TWOBLUECUBES_CATCH_VERSION_HPP_INCLUDED namespace Catch { Version::Version ( unsigned int _majorVersion, unsigned int _minorVersion, unsigned int _patchNumber, std::string const& _branchName, unsigned int _buildNumber ) : majorVersion( _majorVersion ), minorVersion( _minorVersion ), patchNumber( _patchNumber ), branchName( _branchName ), buildNumber( _buildNumber ) {} std::ostream& operator << ( std::ostream& os, Version const& version ) { os << version.majorVersion << "." << version.minorVersion << "." << version.patchNumber; if( !version.branchName.empty() ) { os << "-" << version.branchName << "." << version.buildNumber; } return os; } Version libraryVersion( 1, 4, 0, "", 0 ); } // #included from: catch_message.hpp #define TWOBLUECUBES_CATCH_MESSAGE_HPP_INCLUDED namespace Catch { MessageInfo::MessageInfo( std::string const& _macroName, SourceLineInfo const& _lineInfo, ResultWas::OfType _type ) : macroName( _macroName ), lineInfo( _lineInfo ), type( _type ), sequence( ++globalCount ) {} // This may need protecting if threading support is added unsigned int MessageInfo::globalCount = 0; //////////////////////////////////////////////////////////////////////////// ScopedMessage::ScopedMessage( MessageBuilder const& builder ) : m_info( builder.m_info ) { m_info.message = builder.m_stream.str(); getResultCapture().pushScopedMessage( m_info ); } ScopedMessage::ScopedMessage( ScopedMessage const& other ) : m_info( other.m_info ) {} ScopedMessage::~ScopedMessage() { getResultCapture().popScopedMessage( m_info ); } } // end namespace Catch // #included from: catch_legacy_reporter_adapter.hpp #define TWOBLUECUBES_CATCH_LEGACY_REPORTER_ADAPTER_HPP_INCLUDED // #included from: catch_legacy_reporter_adapter.h #define TWOBLUECUBES_CATCH_LEGACY_REPORTER_ADAPTER_H_INCLUDED namespace Catch { // Deprecated struct IReporter : IShared { virtual ~IReporter(); virtual bool shouldRedirectStdout() const = 0; virtual void StartTesting() = 0; virtual void EndTesting( Totals const& totals ) = 0; virtual void StartGroup( std::string const& groupName ) = 0; virtual void EndGroup( std::string const& groupName, Totals const& totals ) = 0; virtual void StartTestCase( TestCaseInfo const& testInfo ) = 0; virtual void EndTestCase( TestCaseInfo const& testInfo, Totals const& totals, std::string const& stdOut, std::string const& stdErr ) = 0; virtual void StartSection( std::string const& sectionName, std::string const& description ) = 0; virtual void EndSection( std::string const& sectionName, Counts const& assertions ) = 0; virtual void NoAssertionsInSection( std::string const& sectionName ) = 0; virtual void NoAssertionsInTestCase( std::string const& testName ) = 0; virtual void Aborted() = 0; virtual void Result( AssertionResult const& result ) = 0; }; class LegacyReporterAdapter : public SharedImpl { public: LegacyReporterAdapter( Ptr const& legacyReporter ); virtual ~LegacyReporterAdapter(); virtual ReporterPreferences getPreferences() const; virtual void noMatchingTestCases( std::string const& ); virtual void testRunStarting( TestRunInfo const& ); virtual void testGroupStarting( GroupInfo const& groupInfo ); virtual void testCaseStarting( TestCaseInfo const& testInfo ); virtual void sectionStarting( SectionInfo const& sectionInfo ); virtual void assertionStarting( AssertionInfo const& ); virtual bool assertionEnded( AssertionStats const& assertionStats ); virtual void sectionEnded( SectionStats const& sectionStats ); virtual void testCaseEnded( TestCaseStats const& testCaseStats ); virtual void testGroupEnded( TestGroupStats const& testGroupStats ); virtual void testRunEnded( TestRunStats const& testRunStats ); virtual void skipTest( TestCaseInfo const& ); private: Ptr m_legacyReporter; }; } namespace Catch { LegacyReporterAdapter::LegacyReporterAdapter( Ptr const& legacyReporter ) : m_legacyReporter( legacyReporter ) {} LegacyReporterAdapter::~LegacyReporterAdapter() {} ReporterPreferences LegacyReporterAdapter::getPreferences() const { ReporterPreferences prefs; prefs.shouldRedirectStdOut = m_legacyReporter->shouldRedirectStdout(); return prefs; } void LegacyReporterAdapter::noMatchingTestCases( std::string const& ) {} void LegacyReporterAdapter::testRunStarting( TestRunInfo const& ) { m_legacyReporter->StartTesting(); } void LegacyReporterAdapter::testGroupStarting( GroupInfo const& groupInfo ) { m_legacyReporter->StartGroup( groupInfo.name ); } void LegacyReporterAdapter::testCaseStarting( TestCaseInfo const& testInfo ) { m_legacyReporter->StartTestCase( testInfo ); } void LegacyReporterAdapter::sectionStarting( SectionInfo const& sectionInfo ) { m_legacyReporter->StartSection( sectionInfo.name, sectionInfo.description ); } void LegacyReporterAdapter::assertionStarting( AssertionInfo const& ) { // Not on legacy interface } bool LegacyReporterAdapter::assertionEnded( AssertionStats const& assertionStats ) { if( assertionStats.assertionResult.getResultType() != ResultWas::Ok ) { for( std::vector::const_iterator it = assertionStats.infoMessages.begin(), itEnd = assertionStats.infoMessages.end(); it != itEnd; ++it ) { if( it->type == ResultWas::Info ) { ResultBuilder rb( it->macroName.c_str(), it->lineInfo, "", ResultDisposition::Normal ); rb << it->message; rb.setResultType( ResultWas::Info ); AssertionResult result = rb.build(); m_legacyReporter->Result( result ); } } } m_legacyReporter->Result( assertionStats.assertionResult ); return true; } void LegacyReporterAdapter::sectionEnded( SectionStats const& sectionStats ) { if( sectionStats.missingAssertions ) m_legacyReporter->NoAssertionsInSection( sectionStats.sectionInfo.name ); m_legacyReporter->EndSection( sectionStats.sectionInfo.name, sectionStats.assertions ); } void LegacyReporterAdapter::testCaseEnded( TestCaseStats const& testCaseStats ) { m_legacyReporter->EndTestCase ( testCaseStats.testInfo, testCaseStats.totals, testCaseStats.stdOut, testCaseStats.stdErr ); } void LegacyReporterAdapter::testGroupEnded( TestGroupStats const& testGroupStats ) { if( testGroupStats.aborting ) m_legacyReporter->Aborted(); m_legacyReporter->EndGroup( testGroupStats.groupInfo.name, testGroupStats.totals ); } void LegacyReporterAdapter::testRunEnded( TestRunStats const& testRunStats ) { m_legacyReporter->EndTesting( testRunStats.totals ); } void LegacyReporterAdapter::skipTest( TestCaseInfo const& ) { } } // #included from: catch_timer.hpp #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wc++11-long-long" #endif #ifdef CATCH_PLATFORM_WINDOWS #include #else #include #endif namespace Catch { namespace { #ifdef CATCH_PLATFORM_WINDOWS uint64_t getCurrentTicks() { static uint64_t hz=0, hzo=0; if (!hz) { QueryPerformanceFrequency( reinterpret_cast( &hz ) ); QueryPerformanceCounter( reinterpret_cast( &hzo ) ); } uint64_t t; QueryPerformanceCounter( reinterpret_cast( &t ) ); return ((t-hzo)*1000000)/hz; } #else uint64_t getCurrentTicks() { timeval t; gettimeofday(&t,CATCH_NULL); return static_cast( t.tv_sec ) * 1000000ull + static_cast( t.tv_usec ); } #endif } void Timer::start() { m_ticks = getCurrentTicks(); } unsigned int Timer::getElapsedMicroseconds() const { return static_cast(getCurrentTicks() - m_ticks); } unsigned int Timer::getElapsedMilliseconds() const { return static_cast(getElapsedMicroseconds()/1000); } double Timer::getElapsedSeconds() const { return getElapsedMicroseconds()/1000000.0; } } // namespace Catch #ifdef __clang__ #pragma clang diagnostic pop #endif // #included from: catch_common.hpp #define TWOBLUECUBES_CATCH_COMMON_HPP_INCLUDED namespace Catch { bool startsWith( std::string const& s, std::string const& prefix ) { return s.size() >= prefix.size() && s.substr( 0, prefix.size() ) == prefix; } bool endsWith( std::string const& s, std::string const& suffix ) { return s.size() >= suffix.size() && s.substr( s.size()-suffix.size(), suffix.size() ) == suffix; } bool contains( std::string const& s, std::string const& infix ) { return s.find( infix ) != std::string::npos; } void toLowerInPlace( std::string& s ) { std::transform( s.begin(), s.end(), s.begin(), ::tolower ); } std::string toLower( std::string const& s ) { std::string lc = s; toLowerInPlace( lc ); return lc; } std::string trim( std::string const& str ) { static char const* whitespaceChars = "\n\r\t "; std::string::size_type start = str.find_first_not_of( whitespaceChars ); std::string::size_type end = str.find_last_not_of( whitespaceChars ); return start != std::string::npos ? str.substr( start, 1+end-start ) : ""; } bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ) { bool replaced = false; std::size_t i = str.find( replaceThis ); while( i != std::string::npos ) { replaced = true; str = str.substr( 0, i ) + withThis + str.substr( i+replaceThis.size() ); if( i < str.size()-withThis.size() ) i = str.find( replaceThis, i+withThis.size() ); else i = std::string::npos; } return replaced; } pluralise::pluralise( std::size_t count, std::string const& label ) : m_count( count ), m_label( label ) {} std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ) { os << pluraliser.m_count << " " << pluraliser.m_label; if( pluraliser.m_count != 1 ) os << "s"; return os; } SourceLineInfo::SourceLineInfo() : line( 0 ){} SourceLineInfo::SourceLineInfo( char const* _file, std::size_t _line ) : file( _file ), line( _line ) {} SourceLineInfo::SourceLineInfo( SourceLineInfo const& other ) : file( other.file ), line( other.line ) {} bool SourceLineInfo::empty() const { return file.empty(); } bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const { return line == other.line && file == other.file; } bool SourceLineInfo::operator < ( SourceLineInfo const& other ) const { return line < other.line || ( line == other.line && file < other.file ); } void seedRng( IConfig const& config ) { if( config.rngSeed() != 0 ) std::srand( config.rngSeed() ); } unsigned int rngSeed() { return getCurrentContext().getConfig()->rngSeed(); } std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) { #ifndef __GNUG__ os << info.file << "(" << info.line << ")"; #else os << info.file << ":" << info.line; #endif return os; } void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo ) { std::ostringstream oss; oss << locationInfo << ": Internal Catch error: '" << message << "'"; if( alwaysTrue() ) throw std::logic_error( oss.str() ); } } // #included from: catch_section.hpp #define TWOBLUECUBES_CATCH_SECTION_HPP_INCLUDED namespace Catch { SectionInfo::SectionInfo ( SourceLineInfo const& _lineInfo, std::string const& _name, std::string const& _description ) : name( _name ), description( _description ), lineInfo( _lineInfo ) {} Section::Section( SectionInfo const& info ) : m_info( info ), m_sectionIncluded( getResultCapture().sectionStarted( m_info, m_assertions ) ) { m_timer.start(); } Section::~Section() { if( m_sectionIncluded ) { SectionEndInfo endInfo( m_info, m_assertions, m_timer.getElapsedSeconds() ); if( std::uncaught_exception() ) getResultCapture().sectionEndedEarly( endInfo ); else getResultCapture().sectionEnded( endInfo ); } } // This indicates whether the section should be executed or not Section::operator bool() const { return m_sectionIncluded; } } // end namespace Catch // #included from: catch_debugger.hpp #define TWOBLUECUBES_CATCH_DEBUGGER_HPP_INCLUDED #include #ifdef CATCH_PLATFORM_MAC #include #include #include #include #include namespace Catch{ // The following function is taken directly from the following technical note: // http://developer.apple.com/library/mac/#qa/qa2004/qa1361.html // Returns true if the current process is being debugged (either // running under the debugger or has a debugger attached post facto). bool isDebuggerActive(){ int mib[4]; struct kinfo_proc info; size_t size; // Initialize the flags so that, if sysctl fails for some bizarre // reason, we get a predictable result. info.kp_proc.p_flag = 0; // Initialize mib, which tells sysctl the info we want, in this case // we're looking for information about a specific process ID. mib[0] = CTL_KERN; mib[1] = KERN_PROC; mib[2] = KERN_PROC_PID; mib[3] = getpid(); // Call sysctl. size = sizeof(info); if( sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, CATCH_NULL, 0) != 0 ) { Catch::cerr() << "\n** Call to sysctl failed - unable to determine if debugger is active **\n" << std::endl; return false; } // We're being debugged if the P_TRACED flag is set. return ( (info.kp_proc.p_flag & P_TRACED) != 0 ); } } // namespace Catch #elif defined(_MSC_VER) extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); namespace Catch { bool isDebuggerActive() { return IsDebuggerPresent() != 0; } } #elif defined(__MINGW32__) extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); namespace Catch { bool isDebuggerActive() { return IsDebuggerPresent() != 0; } } #else namespace Catch { inline bool isDebuggerActive() { return false; } } #endif // Platform #ifdef CATCH_PLATFORM_WINDOWS extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA( const char* ); namespace Catch { void writeToDebugConsole( std::string const& text ) { ::OutputDebugStringA( text.c_str() ); } } #else namespace Catch { void writeToDebugConsole( std::string const& text ) { // !TBD: Need a version for Mac/ XCode and other IDEs Catch::cout() << text; } } #endif // Platform // #included from: catch_tostring.hpp #define TWOBLUECUBES_CATCH_TOSTRING_HPP_INCLUDED namespace Catch { namespace Detail { const std::string unprintableString = "{?}"; namespace { const int hexThreshold = 255; struct Endianness { enum Arch { Big, Little }; static Arch which() { union _{ int asInt; char asChar[sizeof (int)]; } u; u.asInt = 1; return ( u.asChar[sizeof(int)-1] == 1 ) ? Big : Little; } }; } std::string rawMemoryToString( const void *object, std::size_t size ) { // Reverse order for little endian architectures int i = 0, end = static_cast( size ), inc = 1; if( Endianness::which() == Endianness::Little ) { i = end-1; end = inc = -1; } unsigned char const *bytes = static_cast(object); std::ostringstream os; os << "0x" << std::setfill('0') << std::hex; for( ; i != end; i += inc ) os << std::setw(2) << static_cast(bytes[i]); return os.str(); } } std::string toString( std::string const& value ) { std::string s = value; if( getCurrentContext().getConfig()->showInvisibles() ) { for(size_t i = 0; i < s.size(); ++i ) { std::string subs; switch( s[i] ) { case '\n': subs = "\\n"; break; case '\t': subs = "\\t"; break; default: break; } if( !subs.empty() ) { s = s.substr( 0, i ) + subs + s.substr( i+1 ); ++i; } } } return "\"" + s + "\""; } std::string toString( std::wstring const& value ) { std::string s; s.reserve( value.size() ); for(size_t i = 0; i < value.size(); ++i ) s += value[i] <= 0xff ? static_cast( value[i] ) : '?'; return Catch::toString( s ); } std::string toString( const char* const value ) { return value ? Catch::toString( std::string( value ) ) : std::string( "{null string}" ); } std::string toString( char* const value ) { return Catch::toString( static_cast( value ) ); } std::string toString( const wchar_t* const value ) { return value ? Catch::toString( std::wstring(value) ) : std::string( "{null string}" ); } std::string toString( wchar_t* const value ) { return Catch::toString( static_cast( value ) ); } std::string toString( int value ) { std::ostringstream oss; oss << value; if( value > Detail::hexThreshold ) oss << " (0x" << std::hex << value << ")"; return oss.str(); } std::string toString( unsigned long value ) { std::ostringstream oss; oss << value; if( value > Detail::hexThreshold ) oss << " (0x" << std::hex << value << ")"; return oss.str(); } std::string toString( unsigned int value ) { return Catch::toString( static_cast( value ) ); } template std::string fpToString( T value, int precision ) { std::ostringstream oss; oss << std::setprecision( precision ) << std::fixed << value; std::string d = oss.str(); std::size_t i = d.find_last_not_of( '0' ); if( i != std::string::npos && i != d.size()-1 ) { if( d[i] == '.' ) i++; d = d.substr( 0, i+1 ); } return d; } std::string toString( const double value ) { return fpToString( value, 10 ); } std::string toString( const float value ) { return fpToString( value, 5 ) + "f"; } std::string toString( bool value ) { return value ? "true" : "false"; } std::string toString( char value ) { return value < ' ' ? toString( static_cast( value ) ) : Detail::makeString( value ); } std::string toString( signed char value ) { return toString( static_cast( value ) ); } std::string toString( unsigned char value ) { return toString( static_cast( value ) ); } #ifdef CATCH_CONFIG_CPP11_LONG_LONG std::string toString( long long value ) { std::ostringstream oss; oss << value; if( value > Detail::hexThreshold ) oss << " (0x" << std::hex << value << ")"; return oss.str(); } std::string toString( unsigned long long value ) { std::ostringstream oss; oss << value; if( value > Detail::hexThreshold ) oss << " (0x" << std::hex << value << ")"; return oss.str(); } #endif #ifdef CATCH_CONFIG_CPP11_NULLPTR std::string toString( std::nullptr_t ) { return "nullptr"; } #endif #ifdef __OBJC__ std::string toString( NSString const * const& nsstring ) { if( !nsstring ) return "nil"; return "@" + toString([nsstring UTF8String]); } std::string toString( NSString * CATCH_ARC_STRONG const& nsstring ) { if( !nsstring ) return "nil"; return "@" + toString([nsstring UTF8String]); } std::string toString( NSObject* const& nsObject ) { return toString( [nsObject description] ); } #endif } // end namespace Catch // #included from: catch_result_builder.hpp #define TWOBLUECUBES_CATCH_RESULT_BUILDER_HPP_INCLUDED namespace Catch { std::string capturedExpressionWithSecondArgument( std::string const& capturedExpression, std::string const& secondArg ) { return secondArg.empty() || secondArg == "\"\"" ? capturedExpression : capturedExpression + ", " + secondArg; } ResultBuilder::ResultBuilder( char const* macroName, SourceLineInfo const& lineInfo, char const* capturedExpression, ResultDisposition::Flags resultDisposition, char const* secondArg ) : m_assertionInfo( macroName, lineInfo, capturedExpressionWithSecondArgument( capturedExpression, secondArg ), resultDisposition ), m_shouldDebugBreak( false ), m_shouldThrow( false ) {} ResultBuilder& ResultBuilder::setResultType( ResultWas::OfType result ) { m_data.resultType = result; return *this; } ResultBuilder& ResultBuilder::setResultType( bool result ) { m_data.resultType = result ? ResultWas::Ok : ResultWas::ExpressionFailed; return *this; } ResultBuilder& ResultBuilder::setLhs( std::string const& lhs ) { m_exprComponents.lhs = lhs; return *this; } ResultBuilder& ResultBuilder::setRhs( std::string const& rhs ) { m_exprComponents.rhs = rhs; return *this; } ResultBuilder& ResultBuilder::setOp( std::string const& op ) { m_exprComponents.op = op; return *this; } void ResultBuilder::endExpression() { m_exprComponents.testFalse = isFalseTest( m_assertionInfo.resultDisposition ); captureExpression(); } void ResultBuilder::useActiveException( ResultDisposition::Flags resultDisposition ) { m_assertionInfo.resultDisposition = resultDisposition; m_stream.oss << Catch::translateActiveException(); captureResult( ResultWas::ThrewException ); } void ResultBuilder::captureResult( ResultWas::OfType resultType ) { setResultType( resultType ); captureExpression(); } void ResultBuilder::captureExpectedException( std::string const& expectedMessage ) { if( expectedMessage.empty() ) captureExpectedException( Matchers::Impl::Generic::AllOf() ); else captureExpectedException( Matchers::Equals( expectedMessage ) ); } void ResultBuilder::captureExpectedException( Matchers::Impl::Matcher const& matcher ) { assert( m_exprComponents.testFalse == false ); AssertionResultData data = m_data; data.resultType = ResultWas::Ok; data.reconstructedExpression = m_assertionInfo.capturedExpression; std::string actualMessage = Catch::translateActiveException(); if( !matcher.match( actualMessage ) ) { data.resultType = ResultWas::ExpressionFailed; data.reconstructedExpression = actualMessage; } AssertionResult result( m_assertionInfo, data ); handleResult( result ); } void ResultBuilder::captureExpression() { AssertionResult result = build(); handleResult( result ); } void ResultBuilder::handleResult( AssertionResult const& result ) { getResultCapture().assertionEnded( result ); if( !result.isOk() ) { if( getCurrentContext().getConfig()->shouldDebugBreak() ) m_shouldDebugBreak = true; if( getCurrentContext().getRunner()->aborting() || (m_assertionInfo.resultDisposition & ResultDisposition::Normal) ) m_shouldThrow = true; } } void ResultBuilder::react() { if( m_shouldThrow ) throw Catch::TestFailureException(); } bool ResultBuilder::shouldDebugBreak() const { return m_shouldDebugBreak; } bool ResultBuilder::allowThrows() const { return getCurrentContext().getConfig()->allowThrows(); } AssertionResult ResultBuilder::build() const { assert( m_data.resultType != ResultWas::Unknown ); AssertionResultData data = m_data; // Flip bool results if testFalse is set if( m_exprComponents.testFalse ) { if( data.resultType == ResultWas::Ok ) data.resultType = ResultWas::ExpressionFailed; else if( data.resultType == ResultWas::ExpressionFailed ) data.resultType = ResultWas::Ok; } data.message = m_stream.oss.str(); data.reconstructedExpression = reconstructExpression(); if( m_exprComponents.testFalse ) { if( m_exprComponents.op == "" ) data.reconstructedExpression = "!" + data.reconstructedExpression; else data.reconstructedExpression = "!(" + data.reconstructedExpression + ")"; } return AssertionResult( m_assertionInfo, data ); } std::string ResultBuilder::reconstructExpression() const { if( m_exprComponents.op == "" ) return m_exprComponents.lhs.empty() ? m_assertionInfo.capturedExpression : m_exprComponents.op + m_exprComponents.lhs; else if( m_exprComponents.op == "matches" ) return m_exprComponents.lhs + " " + m_exprComponents.rhs; else if( m_exprComponents.op != "!" ) { if( m_exprComponents.lhs.size() + m_exprComponents.rhs.size() < 40 && m_exprComponents.lhs.find("\n") == std::string::npos && m_exprComponents.rhs.find("\n") == std::string::npos ) return m_exprComponents.lhs + " " + m_exprComponents.op + " " + m_exprComponents.rhs; else return m_exprComponents.lhs + "\n" + m_exprComponents.op + "\n" + m_exprComponents.rhs; } else return "{can't expand - use " + m_assertionInfo.macroName + "_FALSE( " + m_assertionInfo.capturedExpression.substr(1) + " ) instead of " + m_assertionInfo.macroName + "( " + m_assertionInfo.capturedExpression + " ) for better diagnostics}"; } } // end namespace Catch // #included from: catch_tag_alias_registry.hpp #define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_HPP_INCLUDED // #included from: catch_tag_alias_registry.h #define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_H_INCLUDED #include namespace Catch { class TagAliasRegistry : public ITagAliasRegistry { public: virtual ~TagAliasRegistry(); virtual Option find( std::string const& alias ) const; virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const; void add( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); static TagAliasRegistry& get(); private: std::map m_registry; }; } // end namespace Catch #include #include namespace Catch { TagAliasRegistry::~TagAliasRegistry() {} Option TagAliasRegistry::find( std::string const& alias ) const { std::map::const_iterator it = m_registry.find( alias ); if( it != m_registry.end() ) return it->second; else return Option(); } std::string TagAliasRegistry::expandAliases( std::string const& unexpandedTestSpec ) const { std::string expandedTestSpec = unexpandedTestSpec; for( std::map::const_iterator it = m_registry.begin(), itEnd = m_registry.end(); it != itEnd; ++it ) { std::size_t pos = expandedTestSpec.find( it->first ); if( pos != std::string::npos ) { expandedTestSpec = expandedTestSpec.substr( 0, pos ) + it->second.tag + expandedTestSpec.substr( pos + it->first.size() ); } } return expandedTestSpec; } void TagAliasRegistry::add( char const* alias, char const* tag, SourceLineInfo const& lineInfo ) { if( !startsWith( alias, "[@" ) || !endsWith( alias, "]" ) ) { std::ostringstream oss; oss << "error: tag alias, \"" << alias << "\" is not of the form [@alias name].\n" << lineInfo; throw std::domain_error( oss.str().c_str() ); } if( !m_registry.insert( std::make_pair( alias, TagAlias( tag, lineInfo ) ) ).second ) { std::ostringstream oss; oss << "error: tag alias, \"" << alias << "\" already registered.\n" << "\tFirst seen at " << find(alias)->lineInfo << "\n" << "\tRedefined at " << lineInfo; throw std::domain_error( oss.str().c_str() ); } } TagAliasRegistry& TagAliasRegistry::get() { static TagAliasRegistry instance; return instance; } ITagAliasRegistry::~ITagAliasRegistry() {} ITagAliasRegistry const& ITagAliasRegistry::get() { return TagAliasRegistry::get(); } RegistrarForTagAliases::RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ) { try { TagAliasRegistry::get().add( alias, tag, lineInfo ); } catch( std::exception& ex ) { Colour colourGuard( Colour::Red ); Catch::cerr() << ex.what() << std::endl; exit(1); } } } // end namespace Catch // #included from: ../reporters/catch_reporter_multi.hpp #define TWOBLUECUBES_CATCH_REPORTER_MULTI_HPP_INCLUDED namespace Catch { class MultipleReporters : public SharedImpl { typedef std::vector > Reporters; Reporters m_reporters; public: void add( Ptr const& reporter ) { m_reporters.push_back( reporter ); } public: // IStreamingReporter virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE { return m_reporters[0]->getPreferences(); } virtual void noMatchingTestCases( std::string const& spec ) CATCH_OVERRIDE { for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); it != itEnd; ++it ) (*it)->noMatchingTestCases( spec ); } virtual void testRunStarting( TestRunInfo const& testRunInfo ) CATCH_OVERRIDE { for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); it != itEnd; ++it ) (*it)->testRunStarting( testRunInfo ); } virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE { for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); it != itEnd; ++it ) (*it)->testGroupStarting( groupInfo ); } virtual void testCaseStarting( TestCaseInfo const& testInfo ) CATCH_OVERRIDE { for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); it != itEnd; ++it ) (*it)->testCaseStarting( testInfo ); } virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE { for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); it != itEnd; ++it ) (*it)->sectionStarting( sectionInfo ); } virtual void assertionStarting( AssertionInfo const& assertionInfo ) CATCH_OVERRIDE { for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); it != itEnd; ++it ) (*it)->assertionStarting( assertionInfo ); } // The return value indicates if the messages buffer should be cleared: virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE { bool clearBuffer = false; for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); it != itEnd; ++it ) clearBuffer |= (*it)->assertionEnded( assertionStats ); return clearBuffer; } virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE { for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); it != itEnd; ++it ) (*it)->sectionEnded( sectionStats ); } virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE { for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); it != itEnd; ++it ) (*it)->testCaseEnded( testCaseStats ); } virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE { for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); it != itEnd; ++it ) (*it)->testGroupEnded( testGroupStats ); } virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE { for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); it != itEnd; ++it ) (*it)->testRunEnded( testRunStats ); } virtual void skipTest( TestCaseInfo const& testInfo ) CATCH_OVERRIDE { for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); it != itEnd; ++it ) (*it)->skipTest( testInfo ); } }; Ptr addReporter( Ptr const& existingReporter, Ptr const& additionalReporter ) { Ptr resultingReporter; if( existingReporter ) { MultipleReporters* multi = dynamic_cast( existingReporter.get() ); if( !multi ) { multi = new MultipleReporters; resultingReporter = Ptr( multi ); if( existingReporter ) multi->add( existingReporter ); } else resultingReporter = existingReporter; multi->add( additionalReporter ); } else resultingReporter = additionalReporter; return resultingReporter; } } // end namespace Catch // #included from: ../reporters/catch_reporter_xml.hpp #define TWOBLUECUBES_CATCH_REPORTER_XML_HPP_INCLUDED // #included from: catch_reporter_bases.hpp #define TWOBLUECUBES_CATCH_REPORTER_BASES_HPP_INCLUDED #include namespace Catch { struct StreamingReporterBase : SharedImpl { StreamingReporterBase( ReporterConfig const& _config ) : m_config( _config.fullConfig() ), stream( _config.stream() ) { m_reporterPrefs.shouldRedirectStdOut = false; } virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE { return m_reporterPrefs; } virtual ~StreamingReporterBase() CATCH_OVERRIDE; virtual void noMatchingTestCases( std::string const& ) CATCH_OVERRIDE {} virtual void testRunStarting( TestRunInfo const& _testRunInfo ) CATCH_OVERRIDE { currentTestRunInfo = _testRunInfo; } virtual void testGroupStarting( GroupInfo const& _groupInfo ) CATCH_OVERRIDE { currentGroupInfo = _groupInfo; } virtual void testCaseStarting( TestCaseInfo const& _testInfo ) CATCH_OVERRIDE { currentTestCaseInfo = _testInfo; } virtual void sectionStarting( SectionInfo const& _sectionInfo ) CATCH_OVERRIDE { m_sectionStack.push_back( _sectionInfo ); } virtual void sectionEnded( SectionStats const& /* _sectionStats */ ) CATCH_OVERRIDE { m_sectionStack.pop_back(); } virtual void testCaseEnded( TestCaseStats const& /* _testCaseStats */ ) CATCH_OVERRIDE { currentTestCaseInfo.reset(); } virtual void testGroupEnded( TestGroupStats const& /* _testGroupStats */ ) CATCH_OVERRIDE { currentGroupInfo.reset(); } virtual void testRunEnded( TestRunStats const& /* _testRunStats */ ) CATCH_OVERRIDE { currentTestCaseInfo.reset(); currentGroupInfo.reset(); currentTestRunInfo.reset(); } virtual void skipTest( TestCaseInfo const& ) CATCH_OVERRIDE { // Don't do anything with this by default. // It can optionally be overridden in the derived class. } Ptr m_config; std::ostream& stream; LazyStat currentTestRunInfo; LazyStat currentGroupInfo; LazyStat currentTestCaseInfo; std::vector m_sectionStack; ReporterPreferences m_reporterPrefs; }; struct CumulativeReporterBase : SharedImpl { template struct Node : SharedImpl<> { explicit Node( T const& _value ) : value( _value ) {} virtual ~Node() {} typedef std::vector > ChildNodes; T value; ChildNodes children; }; struct SectionNode : SharedImpl<> { explicit SectionNode( SectionStats const& _stats ) : stats( _stats ) {} virtual ~SectionNode(); bool operator == ( SectionNode const& other ) const { return stats.sectionInfo.lineInfo == other.stats.sectionInfo.lineInfo; } bool operator == ( Ptr const& other ) const { return operator==( *other ); } SectionStats stats; typedef std::vector > ChildSections; typedef std::vector Assertions; ChildSections childSections; Assertions assertions; std::string stdOut; std::string stdErr; }; struct BySectionInfo { BySectionInfo( SectionInfo const& other ) : m_other( other ) {} BySectionInfo( BySectionInfo const& other ) : m_other( other.m_other ) {} bool operator() ( Ptr const& node ) const { return node->stats.sectionInfo.lineInfo == m_other.lineInfo; } private: void operator=( BySectionInfo const& ); SectionInfo const& m_other; }; typedef Node TestCaseNode; typedef Node TestGroupNode; typedef Node TestRunNode; CumulativeReporterBase( ReporterConfig const& _config ) : m_config( _config.fullConfig() ), stream( _config.stream() ) { m_reporterPrefs.shouldRedirectStdOut = false; } ~CumulativeReporterBase(); virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE { return m_reporterPrefs; } virtual void testRunStarting( TestRunInfo const& ) CATCH_OVERRIDE {} virtual void testGroupStarting( GroupInfo const& ) CATCH_OVERRIDE {} virtual void testCaseStarting( TestCaseInfo const& ) CATCH_OVERRIDE {} virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE { SectionStats incompleteStats( sectionInfo, Counts(), 0, false ); Ptr node; if( m_sectionStack.empty() ) { if( !m_rootSection ) m_rootSection = new SectionNode( incompleteStats ); node = m_rootSection; } else { SectionNode& parentNode = *m_sectionStack.back(); SectionNode::ChildSections::const_iterator it = std::find_if( parentNode.childSections.begin(), parentNode.childSections.end(), BySectionInfo( sectionInfo ) ); if( it == parentNode.childSections.end() ) { node = new SectionNode( incompleteStats ); parentNode.childSections.push_back( node ); } else node = *it; } m_sectionStack.push_back( node ); m_deepestSection = node; } virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE {} virtual bool assertionEnded( AssertionStats const& assertionStats ) { assert( !m_sectionStack.empty() ); SectionNode& sectionNode = *m_sectionStack.back(); sectionNode.assertions.push_back( assertionStats ); return true; } virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE { assert( !m_sectionStack.empty() ); SectionNode& node = *m_sectionStack.back(); node.stats = sectionStats; m_sectionStack.pop_back(); } virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE { Ptr node = new TestCaseNode( testCaseStats ); assert( m_sectionStack.size() == 0 ); node->children.push_back( m_rootSection ); m_testCases.push_back( node ); m_rootSection.reset(); assert( m_deepestSection ); m_deepestSection->stdOut = testCaseStats.stdOut; m_deepestSection->stdErr = testCaseStats.stdErr; } virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE { Ptr node = new TestGroupNode( testGroupStats ); node->children.swap( m_testCases ); m_testGroups.push_back( node ); } virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE { Ptr node = new TestRunNode( testRunStats ); node->children.swap( m_testGroups ); m_testRuns.push_back( node ); testRunEndedCumulative(); } virtual void testRunEndedCumulative() = 0; virtual void skipTest( TestCaseInfo const& ) CATCH_OVERRIDE {} Ptr m_config; std::ostream& stream; std::vector m_assertions; std::vector > > m_sections; std::vector > m_testCases; std::vector > m_testGroups; std::vector > m_testRuns; Ptr m_rootSection; Ptr m_deepestSection; std::vector > m_sectionStack; ReporterPreferences m_reporterPrefs; }; template char const* getLineOfChars() { static char line[CATCH_CONFIG_CONSOLE_WIDTH] = {0}; if( !*line ) { memset( line, C, CATCH_CONFIG_CONSOLE_WIDTH-1 ); line[CATCH_CONFIG_CONSOLE_WIDTH-1] = 0; } return line; } struct TestEventListenerBase : StreamingReporterBase { TestEventListenerBase( ReporterConfig const& _config ) : StreamingReporterBase( _config ) {} virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE {} virtual bool assertionEnded( AssertionStats const& ) CATCH_OVERRIDE { return false; } }; } // end namespace Catch // #included from: ../internal/catch_reporter_registrars.hpp #define TWOBLUECUBES_CATCH_REPORTER_REGISTRARS_HPP_INCLUDED namespace Catch { template class LegacyReporterRegistrar { class ReporterFactory : public IReporterFactory { virtual IStreamingReporter* create( ReporterConfig const& config ) const { return new LegacyReporterAdapter( new T( config ) ); } virtual std::string getDescription() const { return T::getDescription(); } }; public: LegacyReporterRegistrar( std::string const& name ) { getMutableRegistryHub().registerReporter( name, new ReporterFactory() ); } }; template class ReporterRegistrar { class ReporterFactory : public SharedImpl { // *** Please Note ***: // - If you end up here looking at a compiler error because it's trying to register // your custom reporter class be aware that the native reporter interface has changed // to IStreamingReporter. The "legacy" interface, IReporter, is still supported via // an adapter. Just use REGISTER_LEGACY_REPORTER to take advantage of the adapter. // However please consider updating to the new interface as the old one is now // deprecated and will probably be removed quite soon! // Please contact me via github if you have any questions at all about this. // In fact, ideally, please contact me anyway to let me know you've hit this - as I have // no idea who is actually using custom reporters at all (possibly no-one!). // The new interface is designed to minimise exposure to interface changes in the future. virtual IStreamingReporter* create( ReporterConfig const& config ) const { return new T( config ); } virtual std::string getDescription() const { return T::getDescription(); } }; public: ReporterRegistrar( std::string const& name ) { getMutableRegistryHub().registerReporter( name, new ReporterFactory() ); } }; template class ListenerRegistrar { class ListenerFactory : public SharedImpl { virtual IStreamingReporter* create( ReporterConfig const& config ) const { return new T( config ); } virtual std::string getDescription() const { return ""; } }; public: ListenerRegistrar() { getMutableRegistryHub().registerListener( new ListenerFactory() ); } }; } #define INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) \ namespace{ Catch::LegacyReporterRegistrar catch_internal_RegistrarFor##reporterType( name ); } #define INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) \ namespace{ Catch::ReporterRegistrar catch_internal_RegistrarFor##reporterType( name ); } #define INTERNAL_CATCH_REGISTER_LISTENER( listenerType ) \ namespace{ Catch::ListenerRegistrar catch_internal_RegistrarFor##listenerType; } // #included from: ../internal/catch_xmlwriter.hpp #define TWOBLUECUBES_CATCH_XMLWRITER_HPP_INCLUDED #include #include #include #include namespace Catch { class XmlEncode { public: enum ForWhat { ForTextNodes, ForAttributes }; XmlEncode( std::string const& str, ForWhat forWhat = ForTextNodes ) : m_str( str ), m_forWhat( forWhat ) {} void encodeTo( std::ostream& os ) const { // Apostrophe escaping not necessary if we always use " to write attributes // (see: http://www.w3.org/TR/xml/#syntax) for( std::size_t i = 0; i < m_str.size(); ++ i ) { char c = m_str[i]; switch( c ) { case '<': os << "<"; break; case '&': os << "&"; break; case '>': // See: http://www.w3.org/TR/xml/#syntax if( i > 2 && m_str[i-1] == ']' && m_str[i-2] == ']' ) os << ">"; else os << c; break; case '\"': if( m_forWhat == ForAttributes ) os << """; else os << c; break; default: // Escape control chars - based on contribution by @espenalb in PR #465 if ( ( c < '\x09' ) || ( c > '\x0D' && c < '\x20') || c=='\x7F' ) os << "&#x" << std::uppercase << std::hex << static_cast( c ); else os << c; } } } friend std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ) { xmlEncode.encodeTo( os ); return os; } private: std::string m_str; ForWhat m_forWhat; }; class XmlWriter { public: class ScopedElement { public: ScopedElement( XmlWriter* writer ) : m_writer( writer ) {} ScopedElement( ScopedElement const& other ) : m_writer( other.m_writer ){ other.m_writer = CATCH_NULL; } ~ScopedElement() { if( m_writer ) m_writer->endElement(); } ScopedElement& writeText( std::string const& text, bool indent = true ) { m_writer->writeText( text, indent ); return *this; } template ScopedElement& writeAttribute( std::string const& name, T const& attribute ) { m_writer->writeAttribute( name, attribute ); return *this; } private: mutable XmlWriter* m_writer; }; XmlWriter() : m_tagIsOpen( false ), m_needsNewline( false ), m_os( &Catch::cout() ) {} XmlWriter( std::ostream& os ) : m_tagIsOpen( false ), m_needsNewline( false ), m_os( &os ) {} ~XmlWriter() { while( !m_tags.empty() ) endElement(); } XmlWriter& startElement( std::string const& name ) { ensureTagClosed(); newlineIfNecessary(); stream() << m_indent << "<" << name; m_tags.push_back( name ); m_indent += " "; m_tagIsOpen = true; return *this; } ScopedElement scopedElement( std::string const& name ) { ScopedElement scoped( this ); startElement( name ); return scoped; } XmlWriter& endElement() { newlineIfNecessary(); m_indent = m_indent.substr( 0, m_indent.size()-2 ); if( m_tagIsOpen ) { stream() << "/>\n"; m_tagIsOpen = false; } else { stream() << m_indent << "\n"; } m_tags.pop_back(); return *this; } XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ) { if( !name.empty() && !attribute.empty() ) stream() << " " << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << "\""; return *this; } XmlWriter& writeAttribute( std::string const& name, bool attribute ) { stream() << " " << name << "=\"" << ( attribute ? "true" : "false" ) << "\""; return *this; } template XmlWriter& writeAttribute( std::string const& name, T const& attribute ) { std::ostringstream oss; oss << attribute; return writeAttribute( name, oss.str() ); } XmlWriter& writeText( std::string const& text, bool indent = true ) { if( !text.empty() ){ bool tagWasOpen = m_tagIsOpen; ensureTagClosed(); if( tagWasOpen && indent ) stream() << m_indent; stream() << XmlEncode( text ); m_needsNewline = true; } return *this; } XmlWriter& writeComment( std::string const& text ) { ensureTagClosed(); stream() << m_indent << ""; m_needsNewline = true; return *this; } XmlWriter& writeBlankLine() { ensureTagClosed(); stream() << "\n"; return *this; } void setStream( std::ostream& os ) { m_os = &os; } private: XmlWriter( XmlWriter const& ); void operator=( XmlWriter const& ); std::ostream& stream() { return *m_os; } void ensureTagClosed() { if( m_tagIsOpen ) { stream() << ">\n"; m_tagIsOpen = false; } } void newlineIfNecessary() { if( m_needsNewline ) { stream() << "\n"; m_needsNewline = false; } } bool m_tagIsOpen; bool m_needsNewline; std::vector m_tags; std::string m_indent; std::ostream* m_os; }; } // #included from: catch_reenable_warnings.h #define TWOBLUECUBES_CATCH_REENABLE_WARNINGS_H_INCLUDED #ifdef __clang__ # ifdef __ICC // icpc defines the __clang__ macro # pragma warning(pop) # else # pragma clang diagnostic pop # endif #elif defined __GNUC__ # pragma GCC diagnostic pop #endif namespace Catch { class XmlReporter : public StreamingReporterBase { public: XmlReporter( ReporterConfig const& _config ) : StreamingReporterBase( _config ), m_sectionDepth( 0 ) { m_reporterPrefs.shouldRedirectStdOut = true; } virtual ~XmlReporter() CATCH_OVERRIDE; static std::string getDescription() { return "Reports test results as an XML document"; } public: // StreamingReporterBase virtual void noMatchingTestCases( std::string const& s ) CATCH_OVERRIDE { StreamingReporterBase::noMatchingTestCases( s ); } virtual void testRunStarting( TestRunInfo const& testInfo ) CATCH_OVERRIDE { StreamingReporterBase::testRunStarting( testInfo ); m_xml.setStream( stream ); m_xml.startElement( "Catch" ); if( !m_config->name().empty() ) m_xml.writeAttribute( "name", m_config->name() ); } virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE { StreamingReporterBase::testGroupStarting( groupInfo ); m_xml.startElement( "Group" ) .writeAttribute( "name", groupInfo.name ); } virtual void testCaseStarting( TestCaseInfo const& testInfo ) CATCH_OVERRIDE { StreamingReporterBase::testCaseStarting(testInfo); m_xml.startElement( "TestCase" ).writeAttribute( "name", trim( testInfo.name ) ); if ( m_config->showDurations() == ShowDurations::Always ) m_testCaseTimer.start(); } virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE { StreamingReporterBase::sectionStarting( sectionInfo ); if( m_sectionDepth++ > 0 ) { m_xml.startElement( "Section" ) .writeAttribute( "name", trim( sectionInfo.name ) ) .writeAttribute( "description", sectionInfo.description ); } } virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE { } virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE { const AssertionResult& assertionResult = assertionStats.assertionResult; // Print any info messages in tags. if( assertionStats.assertionResult.getResultType() != ResultWas::Ok ) { for( std::vector::const_iterator it = assertionStats.infoMessages.begin(), itEnd = assertionStats.infoMessages.end(); it != itEnd; ++it ) { if( it->type == ResultWas::Info ) { m_xml.scopedElement( "Info" ) .writeText( it->message ); } else if ( it->type == ResultWas::Warning ) { m_xml.scopedElement( "Warning" ) .writeText( it->message ); } } } // Drop out if result was successful but we're not printing them. if( !m_config->includeSuccessfulResults() && isOk(assertionResult.getResultType()) ) return true; // Print the expression if there is one. if( assertionResult.hasExpression() ) { m_xml.startElement( "Expression" ) .writeAttribute( "success", assertionResult.succeeded() ) .writeAttribute( "type", assertionResult.getTestMacroName() ) .writeAttribute( "filename", assertionResult.getSourceInfo().file ) .writeAttribute( "line", assertionResult.getSourceInfo().line ); m_xml.scopedElement( "Original" ) .writeText( assertionResult.getExpression() ); m_xml.scopedElement( "Expanded" ) .writeText( assertionResult.getExpandedExpression() ); } // And... Print a result applicable to each result type. switch( assertionResult.getResultType() ) { case ResultWas::ThrewException: m_xml.scopedElement( "Exception" ) .writeAttribute( "filename", assertionResult.getSourceInfo().file ) .writeAttribute( "line", assertionResult.getSourceInfo().line ) .writeText( assertionResult.getMessage() ); break; case ResultWas::FatalErrorCondition: m_xml.scopedElement( "Fatal Error Condition" ) .writeAttribute( "filename", assertionResult.getSourceInfo().file ) .writeAttribute( "line", assertionResult.getSourceInfo().line ) .writeText( assertionResult.getMessage() ); break; case ResultWas::Info: m_xml.scopedElement( "Info" ) .writeText( assertionResult.getMessage() ); break; case ResultWas::Warning: // Warning will already have been written break; case ResultWas::ExplicitFailure: m_xml.scopedElement( "Failure" ) .writeText( assertionResult.getMessage() ); break; default: break; } if( assertionResult.hasExpression() ) m_xml.endElement(); return true; } virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE { StreamingReporterBase::sectionEnded( sectionStats ); if( --m_sectionDepth > 0 ) { XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResults" ); e.writeAttribute( "successes", sectionStats.assertions.passed ); e.writeAttribute( "failures", sectionStats.assertions.failed ); e.writeAttribute( "expectedFailures", sectionStats.assertions.failedButOk ); if ( m_config->showDurations() == ShowDurations::Always ) e.writeAttribute( "durationInSeconds", sectionStats.durationInSeconds ); m_xml.endElement(); } } virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE { StreamingReporterBase::testCaseEnded( testCaseStats ); XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResult" ); e.writeAttribute( "success", testCaseStats.totals.assertions.allOk() ); if ( m_config->showDurations() == ShowDurations::Always ) e.writeAttribute( "durationInSeconds", m_testCaseTimer.getElapsedSeconds() ); m_xml.endElement(); } virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE { StreamingReporterBase::testGroupEnded( testGroupStats ); // TODO: Check testGroupStats.aborting and act accordingly. m_xml.scopedElement( "OverallResults" ) .writeAttribute( "successes", testGroupStats.totals.assertions.passed ) .writeAttribute( "failures", testGroupStats.totals.assertions.failed ) .writeAttribute( "expectedFailures", testGroupStats.totals.assertions.failedButOk ); m_xml.endElement(); } virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE { StreamingReporterBase::testRunEnded( testRunStats ); m_xml.scopedElement( "OverallResults" ) .writeAttribute( "successes", testRunStats.totals.assertions.passed ) .writeAttribute( "failures", testRunStats.totals.assertions.failed ) .writeAttribute( "expectedFailures", testRunStats.totals.assertions.failedButOk ); m_xml.endElement(); } private: Timer m_testCaseTimer; XmlWriter m_xml; int m_sectionDepth; }; INTERNAL_CATCH_REGISTER_REPORTER( "xml", XmlReporter ) } // end namespace Catch // #included from: ../reporters/catch_reporter_junit.hpp #define TWOBLUECUBES_CATCH_REPORTER_JUNIT_HPP_INCLUDED #include namespace Catch { class JunitReporter : public CumulativeReporterBase { public: JunitReporter( ReporterConfig const& _config ) : CumulativeReporterBase( _config ), xml( _config.stream() ) { m_reporterPrefs.shouldRedirectStdOut = true; } virtual ~JunitReporter() CATCH_OVERRIDE; static std::string getDescription() { return "Reports test results in an XML format that looks like Ant's junitreport target"; } virtual void noMatchingTestCases( std::string const& /*spec*/ ) CATCH_OVERRIDE {} virtual void testRunStarting( TestRunInfo const& runInfo ) CATCH_OVERRIDE { CumulativeReporterBase::testRunStarting( runInfo ); xml.startElement( "testsuites" ); } virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE { suiteTimer.start(); stdOutForSuite.str(""); stdErrForSuite.str(""); unexpectedExceptions = 0; CumulativeReporterBase::testGroupStarting( groupInfo ); } virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE { if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException ) unexpectedExceptions++; return CumulativeReporterBase::assertionEnded( assertionStats ); } virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE { stdOutForSuite << testCaseStats.stdOut; stdErrForSuite << testCaseStats.stdErr; CumulativeReporterBase::testCaseEnded( testCaseStats ); } virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE { double suiteTime = suiteTimer.getElapsedSeconds(); CumulativeReporterBase::testGroupEnded( testGroupStats ); writeGroup( *m_testGroups.back(), suiteTime ); } virtual void testRunEndedCumulative() CATCH_OVERRIDE { xml.endElement(); } void writeGroup( TestGroupNode const& groupNode, double suiteTime ) { XmlWriter::ScopedElement e = xml.scopedElement( "testsuite" ); TestGroupStats const& stats = groupNode.value; xml.writeAttribute( "name", stats.groupInfo.name ); xml.writeAttribute( "errors", unexpectedExceptions ); xml.writeAttribute( "failures", stats.totals.assertions.failed-unexpectedExceptions ); xml.writeAttribute( "tests", stats.totals.assertions.total() ); xml.writeAttribute( "hostname", "tbd" ); // !TBD if( m_config->showDurations() == ShowDurations::Never ) xml.writeAttribute( "time", "" ); else xml.writeAttribute( "time", suiteTime ); xml.writeAttribute( "timestamp", "tbd" ); // !TBD // Write test cases for( TestGroupNode::ChildNodes::const_iterator it = groupNode.children.begin(), itEnd = groupNode.children.end(); it != itEnd; ++it ) writeTestCase( **it ); xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite.str() ), false ); xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite.str() ), false ); } void writeTestCase( TestCaseNode const& testCaseNode ) { TestCaseStats const& stats = testCaseNode.value; // All test cases have exactly one section - which represents the // test case itself. That section may have 0-n nested sections assert( testCaseNode.children.size() == 1 ); SectionNode const& rootSection = *testCaseNode.children.front(); std::string className = stats.testInfo.className; if( className.empty() ) { if( rootSection.childSections.empty() ) className = "global"; } writeSection( className, "", rootSection ); } void writeSection( std::string const& className, std::string const& rootName, SectionNode const& sectionNode ) { std::string name = trim( sectionNode.stats.sectionInfo.name ); if( !rootName.empty() ) name = rootName + "/" + name; if( !sectionNode.assertions.empty() || !sectionNode.stdOut.empty() || !sectionNode.stdErr.empty() ) { XmlWriter::ScopedElement e = xml.scopedElement( "testcase" ); if( className.empty() ) { xml.writeAttribute( "classname", name ); xml.writeAttribute( "name", "root" ); } else { xml.writeAttribute( "classname", className ); xml.writeAttribute( "name", name ); } xml.writeAttribute( "time", Catch::toString( sectionNode.stats.durationInSeconds ) ); writeAssertions( sectionNode ); if( !sectionNode.stdOut.empty() ) xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), false ); if( !sectionNode.stdErr.empty() ) xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), false ); } for( SectionNode::ChildSections::const_iterator it = sectionNode.childSections.begin(), itEnd = sectionNode.childSections.end(); it != itEnd; ++it ) if( className.empty() ) writeSection( name, "", **it ); else writeSection( className, name, **it ); } void writeAssertions( SectionNode const& sectionNode ) { for( SectionNode::Assertions::const_iterator it = sectionNode.assertions.begin(), itEnd = sectionNode.assertions.end(); it != itEnd; ++it ) writeAssertion( *it ); } void writeAssertion( AssertionStats const& stats ) { AssertionResult const& result = stats.assertionResult; if( !result.isOk() ) { std::string elementName; switch( result.getResultType() ) { case ResultWas::ThrewException: case ResultWas::FatalErrorCondition: elementName = "error"; break; case ResultWas::ExplicitFailure: elementName = "failure"; break; case ResultWas::ExpressionFailed: elementName = "failure"; break; case ResultWas::DidntThrowException: elementName = "failure"; break; // We should never see these here: case ResultWas::Info: case ResultWas::Warning: case ResultWas::Ok: case ResultWas::Unknown: case ResultWas::FailureBit: case ResultWas::Exception: elementName = "internalError"; break; } XmlWriter::ScopedElement e = xml.scopedElement( elementName ); xml.writeAttribute( "message", result.getExpandedExpression() ); xml.writeAttribute( "type", result.getTestMacroName() ); std::ostringstream oss; if( !result.getMessage().empty() ) oss << result.getMessage() << "\n"; for( std::vector::const_iterator it = stats.infoMessages.begin(), itEnd = stats.infoMessages.end(); it != itEnd; ++it ) if( it->type == ResultWas::Info ) oss << it->message << "\n"; oss << "at " << result.getSourceInfo(); xml.writeText( oss.str(), false ); } } XmlWriter xml; Timer suiteTimer; std::ostringstream stdOutForSuite; std::ostringstream stdErrForSuite; unsigned int unexpectedExceptions; }; INTERNAL_CATCH_REGISTER_REPORTER( "junit", JunitReporter ) } // end namespace Catch // #included from: ../reporters/catch_reporter_console.hpp #define TWOBLUECUBES_CATCH_REPORTER_CONSOLE_HPP_INCLUDED namespace Catch { struct ConsoleReporter : StreamingReporterBase { ConsoleReporter( ReporterConfig const& _config ) : StreamingReporterBase( _config ), m_headerPrinted( false ) {} virtual ~ConsoleReporter() CATCH_OVERRIDE; static std::string getDescription() { return "Reports test results as plain lines of text"; } virtual void noMatchingTestCases( std::string const& spec ) CATCH_OVERRIDE { stream << "No test cases matched '" << spec << "'" << std::endl; } virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE { } virtual bool assertionEnded( AssertionStats const& _assertionStats ) CATCH_OVERRIDE { AssertionResult const& result = _assertionStats.assertionResult; bool printInfoMessages = true; // Drop out if result was successful and we're not printing those if( !m_config->includeSuccessfulResults() && result.isOk() ) { if( result.getResultType() != ResultWas::Warning ) return false; printInfoMessages = false; } lazyPrint(); AssertionPrinter printer( stream, _assertionStats, printInfoMessages ); printer.print(); stream << std::endl; return true; } virtual void sectionStarting( SectionInfo const& _sectionInfo ) CATCH_OVERRIDE { m_headerPrinted = false; StreamingReporterBase::sectionStarting( _sectionInfo ); } virtual void sectionEnded( SectionStats const& _sectionStats ) CATCH_OVERRIDE { if( _sectionStats.missingAssertions ) { lazyPrint(); Colour colour( Colour::ResultError ); if( m_sectionStack.size() > 1 ) stream << "\nNo assertions in section"; else stream << "\nNo assertions in test case"; stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl; } if( m_headerPrinted ) { if( m_config->showDurations() == ShowDurations::Always ) stream << "Completed in " << _sectionStats.durationInSeconds << "s" << std::endl; m_headerPrinted = false; } else { if( m_config->showDurations() == ShowDurations::Always ) stream << _sectionStats.sectionInfo.name << " completed in " << _sectionStats.durationInSeconds << "s" << std::endl; } StreamingReporterBase::sectionEnded( _sectionStats ); } virtual void testCaseEnded( TestCaseStats const& _testCaseStats ) CATCH_OVERRIDE { StreamingReporterBase::testCaseEnded( _testCaseStats ); m_headerPrinted = false; } virtual void testGroupEnded( TestGroupStats const& _testGroupStats ) CATCH_OVERRIDE { if( currentGroupInfo.used ) { printSummaryDivider(); stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n"; printTotals( _testGroupStats.totals ); stream << "\n" << std::endl; } StreamingReporterBase::testGroupEnded( _testGroupStats ); } virtual void testRunEnded( TestRunStats const& _testRunStats ) CATCH_OVERRIDE { printTotalsDivider( _testRunStats.totals ); printTotals( _testRunStats.totals ); stream << std::endl; StreamingReporterBase::testRunEnded( _testRunStats ); } private: class AssertionPrinter { void operator= ( AssertionPrinter const& ); public: AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages ) : stream( _stream ), stats( _stats ), result( _stats.assertionResult ), colour( Colour::None ), message( result.getMessage() ), messages( _stats.infoMessages ), printInfoMessages( _printInfoMessages ) { switch( result.getResultType() ) { case ResultWas::Ok: colour = Colour::Success; passOrFail = "PASSED"; //if( result.hasMessage() ) if( _stats.infoMessages.size() == 1 ) messageLabel = "with message"; if( _stats.infoMessages.size() > 1 ) messageLabel = "with messages"; break; case ResultWas::ExpressionFailed: if( result.isOk() ) { colour = Colour::Success; passOrFail = "FAILED - but was ok"; } else { colour = Colour::Error; passOrFail = "FAILED"; } if( _stats.infoMessages.size() == 1 ) messageLabel = "with message"; if( _stats.infoMessages.size() > 1 ) messageLabel = "with messages"; break; case ResultWas::ThrewException: colour = Colour::Error; passOrFail = "FAILED"; messageLabel = "due to unexpected exception with message"; break; case ResultWas::FatalErrorCondition: colour = Colour::Error; passOrFail = "FAILED"; messageLabel = "due to a fatal error condition"; break; case ResultWas::DidntThrowException: colour = Colour::Error; passOrFail = "FAILED"; messageLabel = "because no exception was thrown where one was expected"; break; case ResultWas::Info: messageLabel = "info"; break; case ResultWas::Warning: messageLabel = "warning"; break; case ResultWas::ExplicitFailure: passOrFail = "FAILED"; colour = Colour::Error; if( _stats.infoMessages.size() == 1 ) messageLabel = "explicitly with message"; if( _stats.infoMessages.size() > 1 ) messageLabel = "explicitly with messages"; break; // These cases are here to prevent compiler warnings case ResultWas::Unknown: case ResultWas::FailureBit: case ResultWas::Exception: passOrFail = "** internal error **"; colour = Colour::Error; break; } } void print() const { printSourceInfo(); if( stats.totals.assertions.total() > 0 ) { if( result.isOk() ) stream << "\n"; printResultType(); printOriginalExpression(); printReconstructedExpression(); } else { stream << "\n"; } printMessage(); } private: void printResultType() const { if( !passOrFail.empty() ) { Colour colourGuard( colour ); stream << passOrFail << ":\n"; } } void printOriginalExpression() const { if( result.hasExpression() ) { Colour colourGuard( Colour::OriginalExpression ); stream << " "; stream << result.getExpressionInMacro(); stream << "\n"; } } void printReconstructedExpression() const { if( result.hasExpandedExpression() ) { stream << "with expansion:\n"; Colour colourGuard( Colour::ReconstructedExpression ); stream << Text( result.getExpandedExpression(), TextAttributes().setIndent(2) ) << "\n"; } } void printMessage() const { if( !messageLabel.empty() ) stream << messageLabel << ":" << "\n"; for( std::vector::const_iterator it = messages.begin(), itEnd = messages.end(); it != itEnd; ++it ) { // If this assertion is a warning ignore any INFO messages if( printInfoMessages || it->type != ResultWas::Info ) stream << Text( it->message, TextAttributes().setIndent(2) ) << "\n"; } } void printSourceInfo() const { Colour colourGuard( Colour::FileName ); stream << result.getSourceInfo() << ": "; } std::ostream& stream; AssertionStats const& stats; AssertionResult const& result; Colour::Code colour; std::string passOrFail; std::string messageLabel; std::string message; std::vector messages; bool printInfoMessages; }; void lazyPrint() { if( !currentTestRunInfo.used ) lazyPrintRunInfo(); if( !currentGroupInfo.used ) lazyPrintGroupInfo(); if( !m_headerPrinted ) { printTestCaseAndSectionHeader(); m_headerPrinted = true; } } void lazyPrintRunInfo() { stream << "\n" << getLineOfChars<'~'>() << "\n"; Colour colour( Colour::SecondaryText ); stream << currentTestRunInfo->name << " is a Catch v" << libraryVersion << " host application.\n" << "Run with -? for options\n\n"; if( m_config->rngSeed() != 0 ) stream << "Randomness seeded to: " << m_config->rngSeed() << "\n\n"; currentTestRunInfo.used = true; } void lazyPrintGroupInfo() { if( !currentGroupInfo->name.empty() && currentGroupInfo->groupsCounts > 1 ) { printClosedHeader( "Group: " + currentGroupInfo->name ); currentGroupInfo.used = true; } } void printTestCaseAndSectionHeader() { assert( !m_sectionStack.empty() ); printOpenHeader( currentTestCaseInfo->name ); if( m_sectionStack.size() > 1 ) { Colour colourGuard( Colour::Headers ); std::vector::const_iterator it = m_sectionStack.begin()+1, // Skip first section (test case) itEnd = m_sectionStack.end(); for( ; it != itEnd; ++it ) printHeaderString( it->name, 2 ); } SourceLineInfo lineInfo = m_sectionStack.front().lineInfo; if( !lineInfo.empty() ){ stream << getLineOfChars<'-'>() << "\n"; Colour colourGuard( Colour::FileName ); stream << lineInfo << "\n"; } stream << getLineOfChars<'.'>() << "\n" << std::endl; } void printClosedHeader( std::string const& _name ) { printOpenHeader( _name ); stream << getLineOfChars<'.'>() << "\n"; } void printOpenHeader( std::string const& _name ) { stream << getLineOfChars<'-'>() << "\n"; { Colour colourGuard( Colour::Headers ); printHeaderString( _name ); } } // if string has a : in first line will set indent to follow it on // subsequent lines void printHeaderString( std::string const& _string, std::size_t indent = 0 ) { std::size_t i = _string.find( ": " ); if( i != std::string::npos ) i+=2; else i = 0; stream << Text( _string, TextAttributes() .setIndent( indent+i) .setInitialIndent( indent ) ) << "\n"; } struct SummaryColumn { SummaryColumn( std::string const& _label, Colour::Code _colour ) : label( _label ), colour( _colour ) {} SummaryColumn addRow( std::size_t count ) { std::ostringstream oss; oss << count; std::string row = oss.str(); for( std::vector::iterator it = rows.begin(); it != rows.end(); ++it ) { while( it->size() < row.size() ) *it = " " + *it; while( it->size() > row.size() ) row = " " + row; } rows.push_back( row ); return *this; } std::string label; Colour::Code colour; std::vector rows; }; void printTotals( Totals const& totals ) { if( totals.testCases.total() == 0 ) { stream << Colour( Colour::Warning ) << "No tests ran\n"; } else if( totals.assertions.total() > 0 && totals.testCases.allPassed() ) { stream << Colour( Colour::ResultSuccess ) << "All tests passed"; stream << " (" << pluralise( totals.assertions.passed, "assertion" ) << " in " << pluralise( totals.testCases.passed, "test case" ) << ")" << "\n"; } else { std::vector columns; columns.push_back( SummaryColumn( "", Colour::None ) .addRow( totals.testCases.total() ) .addRow( totals.assertions.total() ) ); columns.push_back( SummaryColumn( "passed", Colour::Success ) .addRow( totals.testCases.passed ) .addRow( totals.assertions.passed ) ); columns.push_back( SummaryColumn( "failed", Colour::ResultError ) .addRow( totals.testCases.failed ) .addRow( totals.assertions.failed ) ); columns.push_back( SummaryColumn( "failed as expected", Colour::ResultExpectedFailure ) .addRow( totals.testCases.failedButOk ) .addRow( totals.assertions.failedButOk ) ); printSummaryRow( "test cases", columns, 0 ); printSummaryRow( "assertions", columns, 1 ); } } void printSummaryRow( std::string const& label, std::vector const& cols, std::size_t row ) { for( std::vector::const_iterator it = cols.begin(); it != cols.end(); ++it ) { std::string value = it->rows[row]; if( it->label.empty() ) { stream << label << ": "; if( value != "0" ) stream << value; else stream << Colour( Colour::Warning ) << "- none -"; } else if( value != "0" ) { stream << Colour( Colour::LightGrey ) << " | "; stream << Colour( it->colour ) << value << " " << it->label; } } stream << "\n"; } static std::size_t makeRatio( std::size_t number, std::size_t total ) { std::size_t ratio = total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number/ total : 0; return ( ratio == 0 && number > 0 ) ? 1 : ratio; } static std::size_t& findMax( std::size_t& i, std::size_t& j, std::size_t& k ) { if( i > j && i > k ) return i; else if( j > k ) return j; else return k; } void printTotalsDivider( Totals const& totals ) { if( totals.testCases.total() > 0 ) { std::size_t failedRatio = makeRatio( totals.testCases.failed, totals.testCases.total() ); std::size_t failedButOkRatio = makeRatio( totals.testCases.failedButOk, totals.testCases.total() ); std::size_t passedRatio = makeRatio( totals.testCases.passed, totals.testCases.total() ); while( failedRatio + failedButOkRatio + passedRatio < CATCH_CONFIG_CONSOLE_WIDTH-1 ) findMax( failedRatio, failedButOkRatio, passedRatio )++; while( failedRatio + failedButOkRatio + passedRatio > CATCH_CONFIG_CONSOLE_WIDTH-1 ) findMax( failedRatio, failedButOkRatio, passedRatio )--; stream << Colour( Colour::Error ) << std::string( failedRatio, '=' ); stream << Colour( Colour::ResultExpectedFailure ) << std::string( failedButOkRatio, '=' ); if( totals.testCases.allPassed() ) stream << Colour( Colour::ResultSuccess ) << std::string( passedRatio, '=' ); else stream << Colour( Colour::Success ) << std::string( passedRatio, '=' ); } else { stream << Colour( Colour::Warning ) << std::string( CATCH_CONFIG_CONSOLE_WIDTH-1, '=' ); } stream << "\n"; } void printSummaryDivider() { stream << getLineOfChars<'-'>() << "\n"; } private: bool m_headerPrinted; }; INTERNAL_CATCH_REGISTER_REPORTER( "console", ConsoleReporter ) } // end namespace Catch // #included from: ../reporters/catch_reporter_compact.hpp #define TWOBLUECUBES_CATCH_REPORTER_COMPACT_HPP_INCLUDED namespace Catch { struct CompactReporter : StreamingReporterBase { CompactReporter( ReporterConfig const& _config ) : StreamingReporterBase( _config ) {} virtual ~CompactReporter(); static std::string getDescription() { return "Reports test results on a single line, suitable for IDEs"; } virtual ReporterPreferences getPreferences() const { ReporterPreferences prefs; prefs.shouldRedirectStdOut = false; return prefs; } virtual void noMatchingTestCases( std::string const& spec ) { stream << "No test cases matched '" << spec << "'" << std::endl; } virtual void assertionStarting( AssertionInfo const& ) { } virtual bool assertionEnded( AssertionStats const& _assertionStats ) { AssertionResult const& result = _assertionStats.assertionResult; bool printInfoMessages = true; // Drop out if result was successful and we're not printing those if( !m_config->includeSuccessfulResults() && result.isOk() ) { if( result.getResultType() != ResultWas::Warning ) return false; printInfoMessages = false; } AssertionPrinter printer( stream, _assertionStats, printInfoMessages ); printer.print(); stream << std::endl; return true; } virtual void testRunEnded( TestRunStats const& _testRunStats ) { printTotals( _testRunStats.totals ); stream << "\n" << std::endl; StreamingReporterBase::testRunEnded( _testRunStats ); } private: class AssertionPrinter { void operator= ( AssertionPrinter const& ); public: AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages ) : stream( _stream ) , stats( _stats ) , result( _stats.assertionResult ) , messages( _stats.infoMessages ) , itMessage( _stats.infoMessages.begin() ) , printInfoMessages( _printInfoMessages ) {} void print() { printSourceInfo(); itMessage = messages.begin(); switch( result.getResultType() ) { case ResultWas::Ok: printResultType( Colour::ResultSuccess, passedString() ); printOriginalExpression(); printReconstructedExpression(); if ( ! result.hasExpression() ) printRemainingMessages( Colour::None ); else printRemainingMessages(); break; case ResultWas::ExpressionFailed: if( result.isOk() ) printResultType( Colour::ResultSuccess, failedString() + std::string( " - but was ok" ) ); else printResultType( Colour::Error, failedString() ); printOriginalExpression(); printReconstructedExpression(); printRemainingMessages(); break; case ResultWas::ThrewException: printResultType( Colour::Error, failedString() ); printIssue( "unexpected exception with message:" ); printMessage(); printExpressionWas(); printRemainingMessages(); break; case ResultWas::FatalErrorCondition: printResultType( Colour::Error, failedString() ); printIssue( "fatal error condition with message:" ); printMessage(); printExpressionWas(); printRemainingMessages(); break; case ResultWas::DidntThrowException: printResultType( Colour::Error, failedString() ); printIssue( "expected exception, got none" ); printExpressionWas(); printRemainingMessages(); break; case ResultWas::Info: printResultType( Colour::None, "info" ); printMessage(); printRemainingMessages(); break; case ResultWas::Warning: printResultType( Colour::None, "warning" ); printMessage(); printRemainingMessages(); break; case ResultWas::ExplicitFailure: printResultType( Colour::Error, failedString() ); printIssue( "explicitly" ); printRemainingMessages( Colour::None ); break; // These cases are here to prevent compiler warnings case ResultWas::Unknown: case ResultWas::FailureBit: case ResultWas::Exception: printResultType( Colour::Error, "** internal error **" ); break; } } private: // Colour::LightGrey static Colour::Code dimColour() { return Colour::FileName; } #ifdef CATCH_PLATFORM_MAC static const char* failedString() { return "FAILED"; } static const char* passedString() { return "PASSED"; } #else static const char* failedString() { return "failed"; } static const char* passedString() { return "passed"; } #endif void printSourceInfo() const { Colour colourGuard( Colour::FileName ); stream << result.getSourceInfo() << ":"; } void printResultType( Colour::Code colour, std::string passOrFail ) const { if( !passOrFail.empty() ) { { Colour colourGuard( colour ); stream << " " << passOrFail; } stream << ":"; } } void printIssue( std::string issue ) const { stream << " " << issue; } void printExpressionWas() { if( result.hasExpression() ) { stream << ";"; { Colour colour( dimColour() ); stream << " expression was:"; } printOriginalExpression(); } } void printOriginalExpression() const { if( result.hasExpression() ) { stream << " " << result.getExpression(); } } void printReconstructedExpression() const { if( result.hasExpandedExpression() ) { { Colour colour( dimColour() ); stream << " for: "; } stream << result.getExpandedExpression(); } } void printMessage() { if ( itMessage != messages.end() ) { stream << " '" << itMessage->message << "'"; ++itMessage; } } void printRemainingMessages( Colour::Code colour = dimColour() ) { if ( itMessage == messages.end() ) return; // using messages.end() directly yields compilation error: std::vector::const_iterator itEnd = messages.end(); const std::size_t N = static_cast( std::distance( itMessage, itEnd ) ); { Colour colourGuard( colour ); stream << " with " << pluralise( N, "message" ) << ":"; } for(; itMessage != itEnd; ) { // If this assertion is a warning ignore any INFO messages if( printInfoMessages || itMessage->type != ResultWas::Info ) { stream << " '" << itMessage->message << "'"; if ( ++itMessage != itEnd ) { Colour colourGuard( dimColour() ); stream << " and"; } } } } private: std::ostream& stream; AssertionStats const& stats; AssertionResult const& result; std::vector messages; std::vector::const_iterator itMessage; bool printInfoMessages; }; // Colour, message variants: // - white: No tests ran. // - red: Failed [both/all] N test cases, failed [both/all] M assertions. // - white: Passed [both/all] N test cases (no assertions). // - red: Failed N tests cases, failed M assertions. // - green: Passed [both/all] N tests cases with M assertions. std::string bothOrAll( std::size_t count ) const { return count == 1 ? "" : count == 2 ? "both " : "all " ; } void printTotals( const Totals& totals ) const { if( totals.testCases.total() == 0 ) { stream << "No tests ran."; } else if( totals.testCases.failed == totals.testCases.total() ) { Colour colour( Colour::ResultError ); const std::string qualify_assertions_failed = totals.assertions.failed == totals.assertions.total() ? bothOrAll( totals.assertions.failed ) : ""; stream << "Failed " << bothOrAll( totals.testCases.failed ) << pluralise( totals.testCases.failed, "test case" ) << ", " "failed " << qualify_assertions_failed << pluralise( totals.assertions.failed, "assertion" ) << "."; } else if( totals.assertions.total() == 0 ) { stream << "Passed " << bothOrAll( totals.testCases.total() ) << pluralise( totals.testCases.total(), "test case" ) << " (no assertions)."; } else if( totals.assertions.failed ) { Colour colour( Colour::ResultError ); stream << "Failed " << pluralise( totals.testCases.failed, "test case" ) << ", " "failed " << pluralise( totals.assertions.failed, "assertion" ) << "."; } else { Colour colour( Colour::ResultSuccess ); stream << "Passed " << bothOrAll( totals.testCases.passed ) << pluralise( totals.testCases.passed, "test case" ) << " with " << pluralise( totals.assertions.passed, "assertion" ) << "."; } } }; INTERNAL_CATCH_REGISTER_REPORTER( "compact", CompactReporter ) } // end namespace Catch namespace Catch { // These are all here to avoid warnings about not having any out of line // virtual methods NonCopyable::~NonCopyable() {} IShared::~IShared() {} IStream::~IStream() CATCH_NOEXCEPT {} FileStream::~FileStream() CATCH_NOEXCEPT {} CoutStream::~CoutStream() CATCH_NOEXCEPT {} DebugOutStream::~DebugOutStream() CATCH_NOEXCEPT {} StreamBufBase::~StreamBufBase() CATCH_NOEXCEPT {} IContext::~IContext() {} IResultCapture::~IResultCapture() {} ITestCase::~ITestCase() {} ITestCaseRegistry::~ITestCaseRegistry() {} IRegistryHub::~IRegistryHub() {} IMutableRegistryHub::~IMutableRegistryHub() {} IExceptionTranslator::~IExceptionTranslator() {} IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() {} IReporter::~IReporter() {} IReporterFactory::~IReporterFactory() {} IReporterRegistry::~IReporterRegistry() {} IStreamingReporter::~IStreamingReporter() {} AssertionStats::~AssertionStats() {} SectionStats::~SectionStats() {} TestCaseStats::~TestCaseStats() {} TestGroupStats::~TestGroupStats() {} TestRunStats::~TestRunStats() {} CumulativeReporterBase::SectionNode::~SectionNode() {} CumulativeReporterBase::~CumulativeReporterBase() {} StreamingReporterBase::~StreamingReporterBase() {} ConsoleReporter::~ConsoleReporter() {} CompactReporter::~CompactReporter() {} IRunner::~IRunner() {} IMutableContext::~IMutableContext() {} IConfig::~IConfig() {} XmlReporter::~XmlReporter() {} JunitReporter::~JunitReporter() {} TestRegistry::~TestRegistry() {} FreeFunctionTestCase::~FreeFunctionTestCase() {} IGeneratorInfo::~IGeneratorInfo() {} IGeneratorsForTest::~IGeneratorsForTest() {} WildcardPattern::~WildcardPattern() {} TestSpec::Pattern::~Pattern() {} TestSpec::NamePattern::~NamePattern() {} TestSpec::TagPattern::~TagPattern() {} TestSpec::ExcludedPattern::~ExcludedPattern() {} Matchers::Impl::StdString::Equals::~Equals() {} Matchers::Impl::StdString::Contains::~Contains() {} Matchers::Impl::StdString::StartsWith::~StartsWith() {} Matchers::Impl::StdString::EndsWith::~EndsWith() {} void Config::dummy() {} namespace TestCaseTracking { ITracker::~ITracker() {} TrackerBase::~TrackerBase() {} SectionTracker::~SectionTracker() {} IndexTracker::~IndexTracker() {} } } #ifdef __clang__ #pragma clang diagnostic pop #endif #endif #ifdef CATCH_CONFIG_MAIN // #included from: internal/catch_default_main.hpp #define TWOBLUECUBES_CATCH_DEFAULT_MAIN_HPP_INCLUDED #ifndef __OBJC__ // Standard C/C++ main entry point int main (int argc, char * argv[]) { return Catch::Session().run( argc, argv ); } #else // __OBJC__ // Objective-C entry point int main (int argc, char * const argv[]) { #if !CATCH_ARC_ENABLED NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; #endif Catch::registerTestMethods(); int result = Catch::Session().run( argc, (char* const*)argv ); #if !CATCH_ARC_ENABLED [pool drain]; #endif return result; } #endif // __OBJC__ #endif #ifdef CLARA_CONFIG_MAIN_NOT_DEFINED # undef CLARA_CONFIG_MAIN #endif ////// // If this config identifier is defined then all CATCH macros are prefixed with CATCH_ #ifdef CATCH_CONFIG_PREFIX_ALL #define CATCH_REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE" ) #define CATCH_REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, "CATCH_REQUIRE_FALSE" ) #define CATCH_REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, "", "CATCH_REQUIRE_THROWS" ) #define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THROWS_AS" ) #define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, matcher, "CATCH_REQUIRE_THROWS_WITH" ) #define CATCH_REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_NOTHROW" ) #define CATCH_CHECK( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK" ) #define CATCH_CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, "CATCH_CHECK_FALSE" ) #define CATCH_CHECKED_IF( expr ) INTERNAL_CATCH_IF( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECKED_IF" ) #define CATCH_CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECKED_ELSE" ) #define CATCH_CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, "CATCH_CHECK_NOFAIL" ) #define CATCH_CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THROWS" ) #define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THROWS_AS" ) #define CATCH_CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, matcher, "CATCH_CHECK_THROWS_WITH" ) #define CATCH_CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_NOTHROW" ) #define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THAT" ) #define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THAT" ) #define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( msg, "CATCH_INFO" ) #define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, "CATCH_WARN", msg ) #define CATCH_SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( msg, "CATCH_INFO" ) #define CATCH_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CATCH_CAPTURE" ) #define CATCH_SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CATCH_CAPTURE" ) #ifdef CATCH_CONFIG_VARIADIC_MACROS #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) #define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) #define CATCH_REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) #define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) #define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "CATCH_FAIL", __VA_ARGS__ ) #define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "CATCH_SUCCEED", __VA_ARGS__ ) #else #define CATCH_TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description ) #define CATCH_TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description ) #define CATCH_METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description ) #define CATCH_REGISTER_TEST_CASE( function, name, description ) INTERNAL_CATCH_REGISTER_TESTCASE( function, name, description ) #define CATCH_SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description ) #define CATCH_FAIL( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "CATCH_FAIL", msg ) #define CATCH_SUCCEED( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "CATCH_SUCCEED", msg ) #endif #define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" ) #define CATCH_REGISTER_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) #define CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) #define CATCH_GENERATE( expr) INTERNAL_CATCH_GENERATE( expr ) // "BDD-style" convenience wrappers #ifdef CATCH_CONFIG_VARIADIC_MACROS #define CATCH_SCENARIO( ... ) CATCH_TEST_CASE( "Scenario: " __VA_ARGS__ ) #define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ ) #else #define CATCH_SCENARIO( name, tags ) CATCH_TEST_CASE( "Scenario: " name, tags ) #define CATCH_SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags ) #endif #define CATCH_GIVEN( desc ) CATCH_SECTION( std::string( "Given: ") + desc, "" ) #define CATCH_WHEN( desc ) CATCH_SECTION( std::string( " When: ") + desc, "" ) #define CATCH_AND_WHEN( desc ) CATCH_SECTION( std::string( " And: ") + desc, "" ) #define CATCH_THEN( desc ) CATCH_SECTION( std::string( " Then: ") + desc, "" ) #define CATCH_AND_THEN( desc ) CATCH_SECTION( std::string( " And: ") + desc, "" ) // If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required #else #define REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "REQUIRE" ) #define REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, "REQUIRE_FALSE" ) #define REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, "", "REQUIRE_THROWS" ) #define REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::Normal, "REQUIRE_THROWS_AS" ) #define REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, matcher, "REQUIRE_THROWS_WITH" ) #define REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::Normal, "REQUIRE_NOTHROW" ) #define CHECK( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK" ) #define CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, "CHECK_FALSE" ) #define CHECKED_IF( expr ) INTERNAL_CATCH_IF( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECKED_IF" ) #define CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECKED_ELSE" ) #define CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, "CHECK_NOFAIL" ) #define CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, "", "CHECK_THROWS" ) #define CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THROWS_AS" ) #define CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, matcher, "CHECK_THROWS_WITH" ) #define CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK_NOTHROW" ) #define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THAT" ) #define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::Normal, "REQUIRE_THAT" ) #define INFO( msg ) INTERNAL_CATCH_INFO( msg, "INFO" ) #define WARN( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, "WARN", msg ) #define SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( msg, "INFO" ) #define CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CAPTURE" ) #define SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CAPTURE" ) #ifdef CATCH_CONFIG_VARIADIC_MACROS #define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) #define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) #define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) #define REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) #define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) #define FAIL( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "FAIL", __VA_ARGS__ ) #define SUCCEED( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "SUCCEED", __VA_ARGS__ ) #else #define TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description ) #define TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description ) #define METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description ) #define REGISTER_TEST_CASE( method, name, description ) INTERNAL_CATCH_REGISTER_TESTCASE( method, name, description ) #define SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description ) #define FAIL( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "FAIL", msg ) #define SUCCEED( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "SUCCEED", msg ) #endif #define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" ) #define REGISTER_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) #define REGISTER_LEGACY_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) #define GENERATE( expr) INTERNAL_CATCH_GENERATE( expr ) #endif #define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) // "BDD-style" convenience wrappers #ifdef CATCH_CONFIG_VARIADIC_MACROS #define SCENARIO( ... ) TEST_CASE( "Scenario: " __VA_ARGS__ ) #define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ ) #else #define SCENARIO( name, tags ) TEST_CASE( "Scenario: " name, tags ) #define SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags ) #endif #define GIVEN( desc ) SECTION( std::string(" Given: ") + desc, "" ) #define WHEN( desc ) SECTION( std::string(" When: ") + desc, "" ) #define AND_WHEN( desc ) SECTION( std::string("And when: ") + desc, "" ) #define THEN( desc ) SECTION( std::string(" Then: ") + desc, "" ) #define AND_THEN( desc ) SECTION( std::string(" And: ") + desc, "" ) using Catch::Detail::Approx; #endif // TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED bpfcc-0.12.0/tests/cc/dummy_proc_map.txt000066400000000000000000000113021357404205000201470ustar00rootroot0000000000000000400000-007c8000 r-xp 00000000 00:1b 11644523 /opt/some/path/tobinary/bin/binary 7f151476e000-7f1514779000 r-xp 00000000 00:1b 72809479 /some/other/path/tolibs/lib/libnss_files-2.26.so 7f1514779000-7f1514978000 ---p 0000b000 00:1b 72809479 /some/other/path/tolibs/lib/libnss_files-2.26.so 7f1514978000-7f1514979000 r--p 0000a000 00:1b 72809479 /some/other/path/tolibs/lib/libnss_files-2.26.so 7f1514979000-7f151497a000 rw-p 0000b000 00:1b 72809479 /some/other/path/tolibs/lib/libnss_files-2.26.so 7f1515b7e000-7f1515b7f000 rw-p 00009000 00:1b 72809418 /some/other/path/tolibs/lib/libcrypt-2.26.so 7f1515bad000-7f1515baf000 r-xp 00000000 00:1b 72809526 /some/other/path/tolibs/lib/libutil-2.26.so 7f1515baf000-7f1515dae000 ---p 00002000 00:1b 72809526 /some/other/path/tolibs/lib/libutil-2.26.so 7f1515dae000-7f1515daf000 r--p 00001000 00:1b 72809526 /some/other/path/tolibs/lib/libutil-2.26.so 7f1515daf000-7f1515db0000 rw-p 00002000 00:1b 72809526 /some/other/path/tolibs/lib/libutil-2.26.so 7f1515db0000-7f151601c000 r-xp 00000000 00:1b 72809420 /some/other/path/tolibs/lib/libcrypto.so.1.1 7f151601c000-7f151621c000 ---p 0026c000 00:1b 72809420 /some/other/path/tolibs/lib/libcrypto.so.1.1 7f151621c000-7f151623a000 r--p 0026c000 00:1b 72809420 /some/other/path/tolibs/lib/libcrypto.so.1.1 7f151623a000-7f1516244000 rw-p 0028a000 00:1b 72809420 /some/other/path/tolibs/lib/libcrypto.so.1.1 7f1516247000-7f15162ac000 r-xp 00000000 00:1b 72809514 /some/other/path/tolibs/lib/libssl.so.1.1 7f15162ac000-7f15164ab000 ---p 00065000 00:1b 72809514 /some/other/path/tolibs/lib/libssl.so.1.1 7f15164ab000-7f15164af000 r--p 00064000 00:1b 72809514 /some/other/path/tolibs/lib/libssl.so.1.1 7f15164af000-7f15164b5000 rw-p 00068000 00:1b 72809514 /some/other/path/tolibs/lib/libssl.so.1.1 7f15164b5000-7f15164cf000 r-xp 00000000 00:1b 72809538 /some/other/path/tolibs/lib/libz.so.1.2.8 7f15164cf000-7f15166ce000 ---p 0001a000 00:1b 72809538 /some/other/path/tolibs/lib/libz.so.1.2.8 7f15166ce000-7f15166cf000 r--p 00019000 00:1b 72809538 /some/other/path/tolibs/lib/libz.so.1.2.8 7f15166cf000-7f15166d0000 rw-p 0001a000 00:1b 72809538 /some/other/path/tolibs/lib/libz.so.1.2.8 7f15166d0000-7f15166d1000 r-xp 0001b064 00:1b 72809538 /some/other/path/tolibs/lib/libz.so.1.2.8 7f15168d4000-7f1516a23000 r-xp 00000000 00:1b 72809463 /some/other/path/tolibs/lib/libm-2.26.so 7f1516a23000-7f1516c22000 ---p 0014f000 00:1b 72809463 /some/other/path/tolibs/lib/libm-2.26.so 7f1516c22000-7f1516c23000 r--p 0014e000 00:1b 72809463 /some/other/path/tolibs/lib/libm-2.26.so 7f1516c23000-7f1516c24000 rw-p 0014f000 00:1b 72809463 /some/other/path/tolibs/lib/libm-2.26.so 7f1516c24000-7f1516c3e000 r-xp 00000000 00:1b 72809495 /some/other/path/tolibs/lib/libpthread-2.26.so 7f1516c3e000-7f1516e3d000 ---p 0001a000 00:1b 72809495 /some/other/path/tolibs/lib/libpthread-2.26.so 7f1516e3d000-7f1516e3e000 r--p 00019000 00:1b 72809495 /some/other/path/tolibs/lib/libpthread-2.26.so 7f1516e3e000-7f1516e3f000 rw-p 0001a000 00:1b 72809495 /some/other/path/tolibs/lib/libpthread-2.26.so 7f1516e43000-7f1516e6a000 r-xp 00000000 00:1b 72809393 /some/other/path/tolibs/lib/ld-2.26.so 7f1517010000-7f1517011000 rw-s 00000000 00:05 1117877022 /dev/zero (deleted) 7f1517011000-7f1517012000 rw-s 00000000 00:05 1117877021 /dev/zero (deleted) 7f1517012000-7f1517013000 rw-s 00000000 00:05 1117877020 /dev/zero (deleted) 7f1517013000-7f151701c000 rw-s 00000000 00:05 1117899207 /dev/zero (deleted) 7f151701c000-7f1517069000 rw-p 00000000 00:00 0 7f1517069000-7f151706a000 r--p 00026000 00:1b 72809393 /some/other/path/tolibs/lib/ld-2.26.so 7f151706a000-7f151706b000 rw-p 00027000 00:1b 72809393 /some/other/path/tolibs/lib/ld-2.26.so 7f151706b000-7f151706c000 rw-p 00000000 00:00 0 7ffd5070d000-7ffd5073d000 rwxp 00000000 00:00 0 [stack] 7ffd5073d000-7ffd5073e000 rw-p 00000000 00:00 0 7ffd507d7000-7ffd507da000 r--p 00000000 00:00 0 [vvar] 7ffd507da000-7ffd507dc000 r-xp 00000000 00:00 0 [vdso] ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall] bpfcc-0.12.0/tests/cc/test_array_table.cc000066400000000000000000000070731357404205000202400ustar00rootroot00000000000000/* * Copyright (c) 2017 Politecnico di Torino * * 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. */ #include "BPF.h" #include "catch.hpp" #include #include #include TEST_CASE("test array table", "[array_table]") { const std::string BPF_PROGRAM = R"( BPF_TABLE("hash", int, int, myhash, 128); BPF_TABLE("array", int, int, myarray, 128); )"; // turn off the rw_engine ebpf::BPF bpf(0, nullptr, false); ebpf::StatusTuple res(0); res = bpf.init(BPF_PROGRAM); REQUIRE(res.code() == 0); ebpf::BPFArrayTable t = bpf.get_array_table("myarray"); SECTION("bad table type") { // try to get table of wrong type auto f1 = [&](){ bpf.get_array_table("myhash"); }; REQUIRE_THROWS(f1()); } SECTION("standard methods") { int i, v1, v2; i = 1; v1 = 42; // update element res = t.update_value(i, v1); REQUIRE(res.code() == 0); res = t.get_value(i, v2); REQUIRE(res.code() == 0); REQUIRE(v2 == 42); // update another element i = 2; v1 = 69; res = t.update_value(i, v1); REQUIRE(res.code() == 0); res = t.get_value(i, v2); REQUIRE(res.code() == 0); REQUIRE(v2 == 69); // get non existing element i = 1024; res = t.get_value(i, v2); REQUIRE(res.code() != 0); } SECTION("full table") { // random number generator std::mt19937 rng; rng.seed(std::random_device()()); std::uniform_int_distribution dist; std::vector localtable(128); for(int i = 0; i < 128; i++) { int v = dist(rng); res = t.update_value(i, v); REQUIRE(res.code() == 0); // save it in the local table to compare later on localtable[i] = v; } std::vector offlinetable = t.get_table_offline(); REQUIRE(localtable == offlinetable); } } #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 6, 0) TEST_CASE("percpu array table", "[percpu_array_table]") { const std::string BPF_PROGRAM = R"( BPF_TABLE("percpu_hash", int, u64, myhash, 128); BPF_TABLE("percpu_array", int, u64, myarray, 64); )"; ebpf::BPF bpf; ebpf::StatusTuple res(0); res = bpf.init(BPF_PROGRAM); REQUIRE(res.code() == 0); ebpf::BPFPercpuArrayTable t = bpf.get_percpu_array_table("myarray"); size_t ncpus = ebpf::BPFTable::get_possible_cpu_count(); SECTION("bad table type") { // try to get table of wrong type auto f1 = [&](){ bpf.get_percpu_array_table("myhash"); }; REQUIRE_THROWS(f1()); } SECTION("standard methods") { int i; std::vector v1(ncpus); std::vector v2; for (size_t j = 0; j < ncpus; j++) { v1[j] = 42 * j; } i = 1; // update element res = t.update_value(i, v1); REQUIRE(res.code() == 0); res = t.get_value(i, v2); REQUIRE(res.code() == 0); REQUIRE(v2.size() == ncpus); for (size_t j = 0; j < ncpus; j++) { REQUIRE(v2.at(j) == 42 * j); } // get non existing element i = 1024; res = t.get_value(i, v2); REQUIRE(res.code() != 0); } } #endif bpfcc-0.12.0/tests/cc/test_bpf_table.cc000066400000000000000000000170311357404205000176640ustar00rootroot00000000000000/* * Copyright (c) 2017 Politecnico di Torino * * 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. */ #include #include #include #include "BPF.h" #include "catch.hpp" TEST_CASE("test bpf table", "[bpf_table]") { const std::string BPF_PROGRAM = R"( BPF_TABLE("hash", int, int, myhash, 128); )"; ebpf::BPF *bpf(new ebpf::BPF); ebpf::StatusTuple res(0); std::vector> elements; res = bpf->init(BPF_PROGRAM); REQUIRE(res.code() == 0); ebpf::BPFTable t = bpf->get_table("myhash"); // update element std::string value; res = t.update_value("0x07", "0x42"); REQUIRE(res.code() == 0); res = t.get_value("0x7", value); REQUIRE(res.code() == 0); REQUIRE(value == "0x42"); // update another element res = t.update_value("0x11", "0x777"); REQUIRE(res.code() == 0); res = t.get_value("0x11", value); REQUIRE(res.code() == 0); REQUIRE(value == "0x777"); // remove value res = t.remove_value("0x11"); REQUIRE(res.code() == 0); res = t.get_value("0x11", value); REQUIRE(res.code() != 0); res = t.update_value("0x15", "0x888"); REQUIRE(res.code() == 0); res = t.get_table_offline(elements); REQUIRE(res.code() == 0); REQUIRE(elements.size() == 2); // check that elements match what is in the table for (auto &it : elements) { if (it.first == "0x15") { REQUIRE(it.second == "0x888"); } else if (it.first == "0x7") { REQUIRE(it.second == "0x42"); } else { FAIL("Element " + it.first + " should not be on the table", it.first); } } res = t.clear_table_non_atomic(); REQUIRE(res.code() == 0); res = t.get_table_offline(elements); REQUIRE(res.code() == 0); REQUIRE(elements.size() == 0); // delete bpf_module, call to key/leaf printf/scanf must fail delete bpf; res = t.update_value("0x07", "0x42"); REQUIRE(res.code() != 0); res = t.get_value("0x07", value); REQUIRE(res.code() != 0); res = t.remove_value("0x07"); REQUIRE(res.code() != 0); } #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 6, 0) TEST_CASE("test bpf percpu tables", "[bpf_percpu_table]") { const std::string BPF_PROGRAM = R"( BPF_TABLE("percpu_hash", int, u64, myhash, 128); )"; ebpf::BPF bpf; ebpf::StatusTuple res(0); res = bpf.init(BPF_PROGRAM); REQUIRE(res.code() == 0); ebpf::BPFTable t = bpf.get_table("myhash"); size_t ncpus = ebpf::BPFTable::get_possible_cpu_count(); std::vector v1(ncpus); for (size_t i = 0; i < ncpus; i++) { v1.at(i) = std::to_string(42 * i); } // update element std::vector value; res = t.update_value("0x07", v1); REQUIRE(res.code() == 0); res = t.get_value("0x07", value); REQUIRE(res.code() == 0); for (size_t i = 0; i < ncpus; i++) { REQUIRE(42 * i == std::stoul(value.at(i), nullptr, 16)); } } #endif TEST_CASE("test bpf hash table", "[bpf_hash_table]") { const std::string BPF_PROGRAM = R"( BPF_HASH(myhash, int, int, 128); )"; ebpf::BPF bpf; ebpf::StatusTuple res(0); res = bpf.init(BPF_PROGRAM); REQUIRE(res.code() == 0); auto t = bpf.get_hash_table("myhash"); int key, value; // updaate element key = 0x08; value = 0x43; res = t.update_value(key, value); REQUIRE(res.code() == 0); REQUIRE(t[key] == value); // update another element key = 0x12; value = 0x778; res = t.update_value(key, value); REQUIRE(res.code() == 0); key = 0x31; value = 0x123; res = t.update_value(key, value); REQUIRE(res.code() == 0); key = 0x12; value = 0; res = t.get_value(key, value); REQUIRE(res.code() == 0); REQUIRE(value == 0x778); // remove value and dump table key = 0x12; res = t.remove_value(key); REQUIRE(res.code() == 0); auto values = t.get_table_offline(); REQUIRE(values.size() == 2); // clear table res = t.clear_table_non_atomic(); REQUIRE(res.code() == 0); values = t.get_table_offline(); REQUIRE(values.size() == 0); } TEST_CASE("test bpf stack table", "[bpf_stack_table]") { #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 6, 0) const std::string BPF_PROGRAM = R"( BPF_HASH(id, int, int, 1); BPF_STACK_TRACE(stack_traces, 8); int on_sys_getuid(void *ctx) { int stack_id = stack_traces.get_stackid(ctx, BPF_F_REUSE_STACKID); int zero = 0, *val; val = id.lookup_or_try_init(&zero, &stack_id); if (val) { (*val) = stack_id; } return 0; } )"; ebpf::BPF bpf; ebpf::StatusTuple res(0); res = bpf.init(BPF_PROGRAM); REQUIRE(res.code() == 0); std::string getuid_fnname = bpf.get_syscall_fnname("getuid"); res = bpf.attach_kprobe(getuid_fnname, "on_sys_getuid"); REQUIRE(res.code() == 0); REQUIRE(getuid() >= 0); res = bpf.detach_kprobe(getuid_fnname); REQUIRE(res.code() == 0); auto id = bpf.get_hash_table("id"); auto stack_traces = bpf.get_stack_table("stack_traces"); int stack_id = id[0]; REQUIRE(stack_id >= 0); auto addrs = stack_traces.get_stack_addr(stack_id); auto symbols = stack_traces.get_stack_symbol(stack_id, -1); REQUIRE(addrs.size() > 0); REQUIRE(addrs.size() == symbols.size()); bool found = false; for (const auto &symbol : symbols) if (symbol.find("sys_getuid") != std::string::npos) { found = true; break; } REQUIRE(found); stack_traces.clear_table_non_atomic(); addrs = stack_traces.get_stack_addr(stack_id); REQUIRE(addrs.size() == 0); #endif } TEST_CASE("test bpf stack_id table", "[bpf_stack_table]") { #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0) const std::string BPF_PROGRAM = R"( BPF_HASH(id, int, int, 1); BPF_STACK_TRACE_BUILDID(stack_traces, 8); int on_sys_getuid(void *ctx) { int stack_id = stack_traces.get_stackid(ctx, BPF_F_USER_STACK); int zero = 0, *val; val = id.lookup_or_try_init(&zero, &stack_id); if (val) { (*val) = stack_id; } return 0; } )"; ebpf::BPF bpf; ebpf::StatusTuple res(0); res = bpf.init(BPF_PROGRAM); REQUIRE(res.code() == 0); std::string getuid_fnname = bpf.get_syscall_fnname("getuid"); res = bpf.attach_kprobe(getuid_fnname, "on_sys_getuid"); REQUIRE(res.code() == 0); REQUIRE(getuid() >= 0); res = bpf.detach_kprobe(getuid_fnname); REQUIRE(res.code() == 0); auto id = bpf.get_hash_table("id"); auto stack_traces = bpf.get_stackbuildid_table("stack_traces"); /* libc locations on different distributions are added below*/ bpf.add_module("/lib/x86_64-linux-gnu/libc.so.6"); //Location of libc in ubuntu bpf.add_module("/lib64/libc.so.6"); //Location of libc fedora machine int stack_id = id[0]; REQUIRE(stack_id >= 0); auto addrs = stack_traces.get_stack_addr(stack_id); auto symbols = stack_traces.get_stack_symbol(stack_id); REQUIRE(addrs.size() > 0); REQUIRE(addrs.size() == symbols.size()); bool found = false; for (const auto &symbol : symbols) { if (symbol.find("getuid") != std::string::npos) { found = true; break; } } REQUIRE(found); stack_traces.clear_table_non_atomic(); addrs = stack_traces.get_stack_addr(stack_id); REQUIRE(addrs.size()==0); #endif } bpfcc-0.12.0/tests/cc/test_c_api.cc000066400000000000000000000426161357404205000170300ustar00rootroot00000000000000/* * Copyright (c) 2016 GitHub, Inc. * * 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. */ #include #include #include #include #include #include #include #include #include #include #include #include "bcc_elf.h" #include "bcc_perf_map.h" #include "bcc_proc.h" #include "bcc_syms.h" #include "common.h" #include "vendor/tinyformat.hpp" #include "catch.hpp" using namespace std; static pid_t spawn_child(void *, bool, bool, int (*)(void *)); TEST_CASE("language detection", "[c_api]") { const char *c = bcc_procutils_language(getpid()); REQUIRE(c); REQUIRE(string(c).compare("c") == 0); } TEST_CASE("shared object resolution", "[c_api]") { char *libm = bcc_procutils_which_so("m", 0); REQUIRE(libm); REQUIRE(libm[0] == '/'); REQUIRE(string(libm).find("libm.so") != string::npos); free(libm); } TEST_CASE("shared object resolution using loaded libraries", "[c_api]") { char *libelf = bcc_procutils_which_so("elf", getpid()); REQUIRE(libelf); REQUIRE(libelf[0] == '/'); REQUIRE(string(libelf).find("libelf") != string::npos); free(libelf); } TEST_CASE("binary resolution with `which`", "[c_api]") { char *ld = bcc_procutils_which("ld"); REQUIRE(ld); REQUIRE(ld[0] == '/'); free(ld); } static void _test_ksym(const char *sym, const char *mod, uint64_t addr, void *_) { if (!strcmp(sym, "startup_64")) REQUIRE(addr != 0x0ull); } TEST_CASE("list all kernel symbols", "[c_api]") { if (geteuid() != 0) return; bcc_procutils_each_ksym(_test_ksym, NULL); } TEST_CASE("file-backed mapping identification") { CHECK(bcc_mapping_is_file_backed("/bin/ls") == 1); CHECK(bcc_mapping_is_file_backed("") == 0); CHECK(bcc_mapping_is_file_backed("//anon") == 0); CHECK(bcc_mapping_is_file_backed("/dev/zero") == 0); CHECK(bcc_mapping_is_file_backed("/anon_hugepage") == 0); CHECK(bcc_mapping_is_file_backed("/anon_hugepage (deleted)") == 0); CHECK(bcc_mapping_is_file_backed("[stack") == 0); CHECK(bcc_mapping_is_file_backed("/SYSV") == 0); CHECK(bcc_mapping_is_file_backed("[heap]") == 0); } TEST_CASE("resolve symbol name in external library", "[c_api]") { struct bcc_symbol sym; REQUIRE(bcc_resolve_symname("c", "malloc", 0x0, 0, nullptr, &sym) == 0); REQUIRE(string(sym.module).find("libc.so") != string::npos); REQUIRE(sym.module[0] == '/'); REQUIRE(sym.offset != 0); bcc_procutils_free(sym.module); } TEST_CASE("resolve symbol name in external library using loaded libraries", "[c_api]") { struct bcc_symbol sym; REQUIRE(bcc_resolve_symname("bcc", "bcc_procutils_which", 0x0, getpid(), nullptr, &sym) == 0); REQUIRE(string(sym.module).find(LIBBCC_NAME) != string::npos); REQUIRE(sym.module[0] == '/'); REQUIRE(sym.offset != 0); bcc_procutils_free(sym.module); } extern "C" int _a_test_function(const char *a_string) { int i; for (i = 0; a_string[i]; ++i) ; return i; } static int setup_tmp_mnts(void) { // Disconnect this mount namespace from its parent if (mount(NULL, "/", NULL, MS_REC|MS_PRIVATE, NULL) < 0) { fprintf(stderr, "unable to mark / PRIVATE: %s\n", strerror(errno)); return -1; } // create a new tmpfs mounted on /tmp if (mount("tmpfs", "/tmp", "tmpfs", 0, NULL) < 0) { fprintf(stderr, "unable to mount /tmp in mntns: %s\n", strerror(errno)); return -1; } return 0; } static int mntns_func(void *arg) { int in_fd, out_fd; char buf[4096]; char libpath[1024]; ssize_t rb; void *dlhdl; struct link_map *lm; if (setup_tmp_mnts() < 0) { return -1; } // Find libz.so.1, if it's installed dlhdl = dlopen("libz.so.1", RTLD_LAZY); if (dlhdl == NULL) { fprintf(stderr, "Unable to dlopen libz.so.1: %s\n", dlerror()); return -1; } if (dlinfo(dlhdl, RTLD_DI_LINKMAP, &lm) < 0) { fprintf(stderr, "Unable to find origin of libz.so.1: %s\n", dlerror()); return -1; } strncpy(libpath, lm->l_name, 1024); dlclose(dlhdl); dlhdl = NULL; // Copy a shared library from shared mntns to private /tmp snprintf(buf, 4096, "%s", libpath); in_fd = open(buf, O_RDONLY); if (in_fd < 0) { fprintf(stderr, "Unable to open %s: %s\n", buf, strerror(errno)); return -1; } out_fd = open("/tmp/libz.so.1", O_RDWR|O_CREAT|O_EXCL, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH); if (out_fd < 0) { fprintf(stderr, "Unable to open /tmp/libz.so.1: %s\n", strerror(errno)); return -1; } memset(buf, 0, sizeof (buf)); while ((rb = read(in_fd, buf, sizeof (buf))) > 0) { if (write(out_fd, buf, rb) < 0) { fprintf(stderr, "Write error: %s\n", strerror(errno)); return -1; } } close(in_fd); close(out_fd); dlhdl = dlopen("/tmp/libz.so.1", RTLD_NOW); if (dlhdl == NULL) { fprintf(stderr, "dlopen error: %s\n", dlerror()); return -1; } sleep(5); dlclose(dlhdl); return 0; } extern int cmd_scanf(const char *cmd, const char *fmt, ...); TEST_CASE("resolve symbol addresses for a given PID", "[c_api]") { struct bcc_symbol sym; struct bcc_symbol lazy_sym; static struct bcc_symbol_option lazy_opt{ .use_debug_file = 1, .check_debug_file_crc = 1, .lazy_symbolize = 1, #if defined(__powerpc64__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ .use_symbol_type = BCC_SYM_ALL_TYPES | (1 << STT_PPC64LE_SYM_LEP), #else .use_symbol_type = BCC_SYM_ALL_TYPES, #endif }; void *resolver = bcc_symcache_new(getpid(), nullptr); void *lazy_resolver = bcc_symcache_new(getpid(), &lazy_opt); REQUIRE(resolver); REQUIRE(lazy_resolver); SECTION("resolve in our own binary memory space") { REQUIRE(bcc_symcache_resolve(resolver, (uint64_t)&_a_test_function, &sym) == 0); char *this_exe = realpath("/proc/self/exe", NULL); REQUIRE(string(this_exe) == sym.module); free(this_exe); REQUIRE(string("_a_test_function") == sym.name); REQUIRE(bcc_symcache_resolve(lazy_resolver, (uint64_t)&_a_test_function, &lazy_sym) == 0); REQUIRE(string(lazy_sym.name) == sym.name); REQUIRE(string(lazy_sym.module) == sym.module); } SECTION("resolve in " LIBBCC_NAME) { void *libbcc = dlopen(LIBBCC_NAME, RTLD_LAZY | RTLD_NOLOAD); REQUIRE(libbcc); void *libbcc_fptr = dlsym(libbcc, "bcc_resolve_symname"); REQUIRE(libbcc_fptr); REQUIRE(bcc_symcache_resolve(resolver, (uint64_t)libbcc_fptr, &sym) == 0); REQUIRE(string(sym.module).find(LIBBCC_NAME) != string::npos); REQUIRE(string("bcc_resolve_symname") == sym.name); REQUIRE(bcc_symcache_resolve(lazy_resolver, (uint64_t)libbcc_fptr, &lazy_sym) == 0); REQUIRE(string(lazy_sym.module) == sym.module); REQUIRE(string(lazy_sym.name) == sym.name); } SECTION("resolve in libc") { void *libc_fptr = dlsym(NULL, "strtok"); REQUIRE(libc_fptr); REQUIRE(bcc_symcache_resolve(resolver, (uint64_t)libc_fptr, &sym) == 0); REQUIRE(sym.module); REQUIRE(sym.module[0] == '/'); REQUIRE(string(sym.module).find("libc") != string::npos); REQUIRE(bcc_symcache_resolve(lazy_resolver, (uint64_t)libc_fptr, &lazy_sym) == 0); REQUIRE(string(lazy_sym.module) == sym.module); REQUIRE(string(lazy_sym.name) == sym.name); // In some cases, a symbol may have multiple aliases. Since // bcc_symcache_resolve() returns only the first alias of a // symbol, this may not always be "strtok" even if it points // to the same address. bool sym_match = (string("strtok") == sym.name); if (!sym_match) { uint64_t exp_addr, sym_addr; char cmd[256]; const char *cmdfmt = "nm %s | grep \" %s$\" | cut -f 1 -d \" \""; // Find address of symbol by the expected name sprintf(cmd, cmdfmt, sym.module, "strtok"); REQUIRE(cmd_scanf(cmd, "%lx", &exp_addr) == 0); // Find address of symbol by the name that was // returned by bcc_symcache_resolve() sprintf(cmd, cmdfmt, sym.module, sym.name); REQUIRE(cmd_scanf(cmd, "%lx", &sym_addr) == 0); // If both addresses match, they are definitely // aliases of the same symbol sym_match = (exp_addr == sym_addr); } REQUIRE(sym_match); } SECTION("resolve in separate mount namespace") { pid_t child; uint64_t addr = 0; uint64_t lazy_addr = 0; child = spawn_child(0, true, true, mntns_func); REQUIRE(child > 0); void *resolver = bcc_symcache_new(child, nullptr); REQUIRE(resolver); REQUIRE(bcc_symcache_resolve_name(resolver, "/tmp/libz.so.1", "zlibVersion", &addr) == 0); REQUIRE(addr != 0); void *lazy_resolver = bcc_symcache_new(child, &lazy_opt); REQUIRE(lazy_resolver); REQUIRE(bcc_symcache_resolve_name(lazy_resolver, "/tmp/libz.so.1", "zlibVersion", &lazy_addr) == 0); REQUIRE(lazy_addr == addr); } } #define STACK_SIZE (1024 * 1024) static char child_stack[STACK_SIZE]; static string perf_map_path(pid_t pid) { return tfm::format("/tmp/perf-%d.map", pid); } static int make_perf_map_file(string &path, unsigned long long map_addr) { FILE *file = fopen(path.c_str(), "w"); if (file == NULL) { return -1; } fprintf(file, "%llx 10 dummy_fn\n", map_addr); fprintf(file, "%llx 10 right_next_door_fn\n", map_addr + 0x10); fclose(file); return 0; } static int perf_map_func(void *arg) { string path = perf_map_path(getpid()); if (make_perf_map_file(path, (unsigned long long)arg) < 0) return -1; sleep(5); unlink(path.c_str()); return 0; } static int perf_map_func_mntns(void *arg) { string path = perf_map_path(getpid()); if (setup_tmp_mnts() < 0) { return -1; } if (make_perf_map_file(path, (unsigned long long)arg) < 0) return -1; sleep(5); unlink(path.c_str()); return 0; } static int perf_map_func_noop(void *arg) { if (setup_tmp_mnts() < 0) { return -1; } sleep(5); return 0; } static pid_t spawn_child(void *map_addr, bool own_pidns, bool own_mntns, int (*child_func)(void *)) { int flags = SIGCHLD; if (own_pidns) flags |= CLONE_NEWPID; if (own_mntns) flags |= CLONE_NEWNS; pid_t child = clone(child_func, /* stack grows down */ child_stack + STACK_SIZE, flags, (void*)map_addr); if (child < 0) return -1; sleep(1); // let the child get set up return child; } TEST_CASE("resolve symbols using /tmp/perf-pid.map", "[c_api]") { const int map_sz = 4096; void *map_addr = mmap(NULL, map_sz, PROT_READ | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); REQUIRE(map_addr != MAP_FAILED); struct bcc_symbol sym; pid_t child = -1; SECTION("same namespace") { child = spawn_child(map_addr, /* own_pidns */ false, false, perf_map_func); REQUIRE(child > 0); void *resolver = bcc_symcache_new(child, nullptr); REQUIRE(resolver); REQUIRE(bcc_symcache_resolve(resolver, (unsigned long long)map_addr, &sym) == 0); REQUIRE(sym.module); REQUIRE(string(sym.module) == perf_map_path(child)); REQUIRE(string("dummy_fn") == sym.name); REQUIRE(bcc_symcache_resolve(resolver, (unsigned long long)map_addr + 0x10, &sym) == 0); REQUIRE(sym.module); REQUIRE(string(sym.module) == perf_map_path(child)); REQUIRE(string("right_next_door_fn") == sym.name); } SECTION("separate namespace") { child = spawn_child(map_addr, /* own_pidns */ true, false, perf_map_func); REQUIRE(child > 0); void *resolver = bcc_symcache_new(child, nullptr); REQUIRE(resolver); REQUIRE(bcc_symcache_resolve(resolver, (unsigned long long)map_addr, &sym) == 0); REQUIRE(sym.module); // child is PID 1 in its namespace REQUIRE(string(sym.module) == perf_map_path(1)); REQUIRE(string("dummy_fn") == sym.name); unlink("/tmp/perf-1.map"); } SECTION("separate pid and mount namespace") { child = spawn_child(map_addr, /* own_pidns */ true, true, perf_map_func_mntns); REQUIRE(child > 0); void *resolver = bcc_symcache_new(child, nullptr); REQUIRE(resolver); REQUIRE(bcc_symcache_resolve(resolver, (unsigned long long)map_addr, &sym) == 0); REQUIRE(sym.module); // child is PID 1 in its namespace REQUIRE(string(sym.module) == perf_map_path(1)); REQUIRE(string("dummy_fn") == sym.name); } SECTION("separate pid and mount namespace, perf-map in host") { child = spawn_child(map_addr, /* own_pidns */ true, true, perf_map_func_noop); REQUIRE(child > 0); string path = perf_map_path(child); REQUIRE(make_perf_map_file(path, (unsigned long long)map_addr) == 0); void *resolver = bcc_symcache_new(child, nullptr); REQUIRE(resolver); REQUIRE(bcc_symcache_resolve(resolver, (unsigned long long)map_addr, &sym) == 0); REQUIRE(sym.module); // child is PID 1 in its namespace REQUIRE(string(sym.module) == perf_map_path(child)); REQUIRE(string("dummy_fn") == sym.name); unlink(path.c_str()); } munmap(map_addr, map_sz); } // must match exactly the defitinion of mod_search in bcc_syms.cc struct mod_search { const char *name; uint64_t inode; uint64_t dev_major; uint64_t dev_minor; uint64_t addr; uint8_t inode_match_only; uint64_t start; uint64_t file_offset; }; TEST_CASE("searching for modules in /proc/[pid]/maps", "[c_api]") { FILE *dummy_maps = fopen("dummy_proc_map.txt", "r"); REQUIRE(dummy_maps != NULL); SECTION("name match") { fseek(dummy_maps, 0, SEEK_SET); struct mod_search search; memset(&search, 0, sizeof(struct mod_search)); search.name = "/some/other/path/tolibs/lib/libutil-2.26.so"; search.addr = 0x1; int res = _procfs_maps_each_module(dummy_maps, 42, _bcc_syms_find_module, &search); REQUIRE(res == 0); REQUIRE(search.start == 0x7f1515bad000); } SECTION("expected failure to match (name only search)") { fseek(dummy_maps, 0, SEEK_SET); struct mod_search search; memset(&search, 0, sizeof(struct mod_search)); search.name = "/lib/that/isnt/in/maps/libdoesntexist.so"; search.addr = 0x1; int res = _procfs_maps_each_module(dummy_maps, 42, _bcc_syms_find_module, &search); REQUIRE(res == -1); } SECTION("inode+dev match, names different") { fseek(dummy_maps, 0, SEEK_SET); struct mod_search search; memset(&search, 0, sizeof(struct mod_search)); search.name = "/proc/5/root/some/other/path/tolibs/lib/libz.so.1.2.8"; search.inode = 72809538; search.dev_major = 0x00; search.dev_minor = 0x1b; search.addr = 0x2; int res = _procfs_maps_each_module(dummy_maps, 42, _bcc_syms_find_module, &search); REQUIRE(res == 0); REQUIRE(search.start == 0x7f15164b5000); } SECTION("inode+dev don't match, names same") { fseek(dummy_maps, 0, SEEK_SET); struct mod_search search; memset(&search, 0, sizeof(struct mod_search)); search.name = "/some/other/path/tolibs/lib/libutil-2.26.so"; search.inode = 9999999; search.dev_major = 0x42; search.dev_minor = 0x1b; search.addr = 0x2; int res = _procfs_maps_each_module(dummy_maps, 42, _bcc_syms_find_module, &search); REQUIRE(res == -1); } SECTION("inodes match, dev_major/minor don't, expected failure") { fseek(dummy_maps, 0, SEEK_SET); struct mod_search search; memset(&search, 0, sizeof(struct mod_search)); search.name = "/some/other/path/tolibs/lib/libutil-2.26.so"; search.inode = 72809526; search.dev_major = 0x11; search.dev_minor = 0x11; search.addr = 0x2; int res = _procfs_maps_each_module(dummy_maps, 42, _bcc_syms_find_module, &search); REQUIRE(res == -1); } SECTION("inodes match, dev_major/minor don't, match inode only") { fseek(dummy_maps, 0, SEEK_SET); struct mod_search search; memset(&search, 0, sizeof(struct mod_search)); search.name = "/some/other/path/tolibs/lib/libutil-2.26.so"; search.inode = 72809526; search.dev_major = 0x11; search.dev_minor = 0x11; search.addr = 0x2; search.inode_match_only = 1; int res = _procfs_maps_each_module(dummy_maps, 42, _bcc_syms_find_module, &search); REQUIRE(res == 0); REQUIRE(search.start == 0x7f1515bad000); } fclose(dummy_maps); } TEST_CASE("resolve global addr in libc in this process", "[c_api]") { int pid = getpid(); char *sopath = bcc_procutils_which_so("c", pid); uint64_t local_addr = 0x15; uint64_t global_addr; struct mod_search search; memset(&search, 0, sizeof(struct mod_search)); search.name = sopath; int res = bcc_procutils_each_module(pid, _bcc_syms_find_module, &search); REQUIRE(res == 0); REQUIRE(search.start != 0); res = bcc_resolve_global_addr(pid, sopath, local_addr, 0, &global_addr); REQUIRE(res == 0); REQUIRE(global_addr == (search.start + local_addr - search.file_offset)); } TEST_CASE("get online CPUs", "[c_api]") { std::vector cpus = ebpf::get_online_cpus(); int num_cpus = sysconf(_SC_NPROCESSORS_ONLN); REQUIRE(cpus.size() == num_cpus); } bpfcc-0.12.0/tests/cc/test_hash_table.cc000066400000000000000000000113551357404205000200430ustar00rootroot00000000000000/* * Copyright (c) 2017 Politecnico di Torino * * 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. */ #include "BPF.h" #include #include "catch.hpp" TEST_CASE("test hash table", "[hash_table]") { const std::string BPF_PROGRAM = R"( BPF_TABLE("hash", int, int, myhash, 1024); BPF_TABLE("array", int, int, myarray, 1024); )"; ebpf::BPF bpf; ebpf::StatusTuple res(0); res = bpf.init(BPF_PROGRAM); REQUIRE(res.code() == 0); ebpf::BPFHashTable t = bpf.get_hash_table("myhash"); SECTION("bad table type") { // try to get table of wrong type auto f1 = [&](){ bpf.get_hash_table("myarray"); }; REQUIRE_THROWS(f1()); } SECTION("standard methods") { int k, v1, v2; k = 1; v1 = 42; // create new element res = t.update_value(k, v1); REQUIRE(res.code() == 0); res = t.get_value(k, v2); REQUIRE(res.code() == 0); REQUIRE(v2 == 42); // update existing element v1 = 69; res = t.update_value(k, v1); REQUIRE(res.code() == 0); res = t.get_value(k, v2); REQUIRE(res.code() == 0); REQUIRE(v2 == 69); // remove existing element res = t.remove_value(k); REQUIRE(res.code() == 0); // remove non existing element res = t.remove_value(k); REQUIRE(res.code() != 0); // get non existing element res = t.get_value(k, v2); REQUIRE(res.code() != 0); } SECTION("walk table") { for (int i = 1; i <= 10; i++) { res = t.update_value(i * 3, i); REQUIRE(res.code() == 0); } auto offline = t.get_table_offline(); REQUIRE(offline.size() == 10); for (const auto &pair : offline) { REQUIRE(pair.first % 3 == 0); REQUIRE(pair.first / 3 == pair.second); } // clear table t.clear_table_non_atomic(); REQUIRE(t.get_table_offline().size() == 0); } } #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,6,0) TEST_CASE("percpu hash table", "[percpu_hash_table]") { const std::string BPF_PROGRAM = R"( BPF_TABLE("percpu_hash", int, u64, myhash, 128); BPF_TABLE("percpu_array", int, u64, myarray, 64); )"; ebpf::BPF bpf; ebpf::StatusTuple res(0); res = bpf.init(BPF_PROGRAM); REQUIRE(res.code() == 0); ebpf::BPFPercpuHashTable t = bpf.get_percpu_hash_table("myhash"); size_t ncpus = ebpf::BPFTable::get_possible_cpu_count(); SECTION("bad table type") { // try to get table of wrong type auto f1 = [&](){ bpf.get_percpu_hash_table("myarray"); }; REQUIRE_THROWS(f1()); } SECTION("standard methods") { int k; std::vector v1(ncpus); std::vector v2; for (size_t j = 0; j < ncpus; j++) { v1[j] = 42 * j; } k = 1; // create new element res = t.update_value(k, v1); REQUIRE(res.code() == 0); res = t.get_value(k, v2); REQUIRE(res.code() == 0); for (size_t j = 0; j < ncpus; j++) { REQUIRE(v2.at(j) == 42 * j); } // update existing element for (size_t j = 0; j < ncpus; j++) { v1[j] = 69 * j; } res = t.update_value(k, v1); REQUIRE(res.code() == 0); res = t.get_value(k, v2); REQUIRE(res.code() == 0); for (size_t j = 0; j < ncpus; j++) { REQUIRE(v2.at(j) == 69 * j); } // remove existing element res = t.remove_value(k); REQUIRE(res.code() == 0); // remove non existing element res = t.remove_value(k); REQUIRE(res.code() != 0); // get non existing element res = t.get_value(k, v2); REQUIRE(res.code() != 0); } SECTION("walk table") { std::vector v(ncpus); for (int k = 3; k <= 30; k+=3) { for (size_t cpu = 0; cpu < ncpus; cpu++) { v[cpu] = k * cpu; } res = t.update_value(k, v); REQUIRE(res.code() == 0); } // get whole table auto offline = t.get_table_offline(); REQUIRE(offline.size() == 10); for (int i = 0; i < 10; i++) { // check the key REQUIRE(offline.at(i).first % 3 == 0); // check value for (size_t cpu = 0; cpu < ncpus; cpu++) { REQUIRE(offline.at(i).second.at(cpu) == cpu * offline.at(i).first); } } // clear table t.clear_table_non_atomic(); REQUIRE(t.get_table_offline().size() == 0); } } #endif bpfcc-0.12.0/tests/cc/test_libbcc.cc000066400000000000000000000000571357404205000171640ustar00rootroot00000000000000#define CATCH_CONFIG_MAIN #include "catch.hpp" bpfcc-0.12.0/tests/cc/test_map_in_map.cc000066400000000000000000000136741357404205000200570ustar00rootroot00000000000000/* * Copyright (c) 2019 Facebook, Inc. * * 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. */ #include #include #include #include "BPF.h" #include "catch.hpp" #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) typedef unsigned long long __u64; TEST_CASE("test hash of maps", "[hash_of_maps]") { { const std::string BPF_PROGRAM = R"( BPF_ARRAY(cntl, int, 1); BPF_ARRAY(ex1, int, 1024); BPF_ARRAY(ex2, int, 1024); BPF_ARRAY(ex3, u64, 1024); BPF_HASH_OF_MAPS(maps_hash, "ex1", 10); int syscall__getuid(void *ctx) { int key = 0, data, *val, cntl_val; void *inner_map; val = cntl.lookup(&key); if (!val || *val == 0) return 0; // cntl_val == 1 : lookup and update cntl_val = *val; inner_map = maps_hash.lookup(&key); if (!inner_map) return 0; if (cntl_val == 1) { val = bpf_map_lookup_elem(inner_map, &key); if (val) { data = 1; bpf_map_update_elem(inner_map, &key, &data, 0); } } return 0; } )"; ebpf::BPF bpf; ebpf::StatusTuple res(0); res = bpf.init(BPF_PROGRAM); REQUIRE(res.code() == 0); auto t = bpf.get_map_in_map_table("maps_hash"); auto ex1_table = bpf.get_array_table("ex1"); auto ex2_table = bpf.get_array_table("ex2"); auto ex3_table = bpf.get_array_table<__u64>("ex3"); int ex1_fd = ex1_table.get_fd(); int ex2_fd = ex2_table.get_fd(); int ex3_fd = ex3_table.get_fd(); int key = 0, value = 0; res = t.update_value(key, ex1_fd); REQUIRE(res.code() == 0); // updating already-occupied slot will succeed. res = t.update_value(key, ex2_fd); REQUIRE(res.code() == 0); res = t.update_value(key, ex1_fd); REQUIRE(res.code() == 0); // an in-compatible map key = 1; res = t.update_value(key, ex3_fd); REQUIRE(res.code() == -1); // hash table, any valid key should work as long // as hash table is not full. key = 10; res = t.update_value(key, ex2_fd); REQUIRE(res.code() == 0); res = t.remove_value(key); REQUIRE(res.code() == 0); // test effectiveness of map-in-map key = 0; std::string getuid_fnname = bpf.get_syscall_fnname("getuid"); res = bpf.attach_kprobe(getuid_fnname, "syscall__getuid"); REQUIRE(res.code() == 0); auto cntl_table = bpf.get_array_table("cntl"); cntl_table.update_value(0, 1); REQUIRE(getuid() >= 0); res = ex1_table.get_value(key, value); REQUIRE(res.code() == 0); REQUIRE(value > 0); res = bpf.detach_kprobe(getuid_fnname); REQUIRE(res.code() == 0); res = t.remove_value(key); REQUIRE(res.code() == 0); } } TEST_CASE("test array of maps", "[array_of_maps]") { { const std::string BPF_PROGRAM = R"( BPF_ARRAY(cntl, int, 1); BPF_TABLE("hash", int, int, ex1, 1024); BPF_TABLE("hash", int, int, ex2, 1024); BPF_TABLE("hash", u64, u64, ex3, 1024); BPF_ARRAY_OF_MAPS(maps_array, "ex1", 10); int syscall__getuid(void *ctx) { int key = 0, data, *val, cntl_val; void *inner_map; val = cntl.lookup(&key); if (!val || *val == 0) return 0; // cntl_val == 1 : lookup and update // cntl_val == 2 : delete cntl_val = *val; inner_map = maps_array.lookup(&key); if (!inner_map) return 0; if (cntl_val == 1) { val = bpf_map_lookup_elem(inner_map, &key); if (!val) { data = 1; bpf_map_update_elem(inner_map, &key, &data, 0); } } else if (cntl_val == 2) { bpf_map_delete_elem(inner_map, &key); } return 0; } )"; ebpf::BPF bpf; ebpf::StatusTuple res(0); res = bpf.init(BPF_PROGRAM); REQUIRE(res.code() == 0); auto t = bpf.get_map_in_map_table("maps_array"); auto ex1_table = bpf.get_hash_table("ex1"); auto ex2_table = bpf.get_hash_table("ex2"); auto ex3_table = bpf.get_hash_table<__u64, __u64>("ex3"); int ex1_fd = ex1_table.get_fd(); int ex2_fd = ex2_table.get_fd(); int ex3_fd = ex3_table.get_fd(); int key = 0, value = 0; res = t.update_value(key, ex1_fd); REQUIRE(res.code() == 0); // updating already-occupied slot will succeed. res = t.update_value(key, ex2_fd); REQUIRE(res.code() == 0); res = t.update_value(key, ex1_fd); REQUIRE(res.code() == 0); // an in-compatible map key = 1; res = t.update_value(key, ex3_fd); REQUIRE(res.code() == -1); // array table, out of bound access key = 10; res = t.update_value(key, ex2_fd); REQUIRE(res.code() == -1); // test effectiveness of map-in-map key = 0; std::string getuid_fnname = bpf.get_syscall_fnname("getuid"); res = bpf.attach_kprobe(getuid_fnname, "syscall__getuid"); REQUIRE(res.code() == 0); auto cntl_table = bpf.get_array_table("cntl"); cntl_table.update_value(0, 1); REQUIRE(getuid() >= 0); res = ex1_table.get_value(key, value); REQUIRE(res.code() == 0); REQUIRE(value == 1); cntl_table.update_value(0, 2); REQUIRE(getuid() >= 0); res = ex1_table.get_value(key, value); REQUIRE(res.code() == -1); res = bpf.detach_kprobe(getuid_fnname); REQUIRE(res.code() == 0); res = t.remove_value(key); REQUIRE(res.code() == 0); } } #endif bpfcc-0.12.0/tests/cc/test_perf_event.cc000066400000000000000000000114121357404205000201000ustar00rootroot00000000000000/* * Copyright (c) 2017 Facebook, Inc. * * 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. */ #include #include #include #include #include "BPF.h" #include "catch.hpp" TEST_CASE("test read perf event", "[bpf_perf_event]") { // The basic bpf_perf_event_read is supported since Kernel 4.3. However in that // version it only supported HARDWARE and RAW events. On the other hand, our // tests running on Jenkins won't have availiable HARDWARE counters since they // are running on VMs. The support of other types of events such as SOFTWARE are // only added since Kernel 4.13, hence we can only run the test since that. #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0) const std::string BPF_PROGRAM = R"( BPF_PERF_ARRAY(cnt, NUM_CPUS); BPF_HASH(val, int, u64, 1); BPF_HASH(ret, int, int, 1); BPF_HASH(counter, int, struct bpf_perf_event_value, 1); int on_sys_getuid(void *ctx) { int zero = 0; u64 v = cnt.perf_read(CUR_CPU_IDENTIFIER); if (((s64)v < 0) && ((s64)v > -256)) return 0; val.update(&zero, &v); #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0) u32 cpu = bpf_get_smp_processor_id(); struct bpf_perf_event_value c = {0}; int r = cnt.perf_counter_value(cpu, &c, sizeof(c)); ret.update(&zero, &r); counter.update(&zero, &c); #endif return 0; } )"; ebpf::BPF bpf; ebpf::StatusTuple res(0); res = bpf.init( BPF_PROGRAM, {"-DNUM_CPUS=" + std::to_string(sysconf(_SC_NPROCESSORS_ONLN))}, {}); REQUIRE(res.code() == 0); res = bpf.open_perf_event("cnt", PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_CLOCK); REQUIRE(res.code() == 0); std::string getuid_fnname = bpf.get_syscall_fnname("getuid"); res = bpf.attach_kprobe(getuid_fnname, "on_sys_getuid"); REQUIRE(res.code() == 0); REQUIRE(getuid() >= 0); res = bpf.detach_kprobe(getuid_fnname); REQUIRE(res.code() == 0); res = bpf.close_perf_event("cnt"); REQUIRE(res.code() == 0); auto val = bpf.get_hash_table("val"); REQUIRE(val[0] >= 0); #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0) auto counter_table = bpf.get_hash_table("counter"); auto counter = counter_table[0]; auto ret = bpf.get_hash_table("ret"); REQUIRE(ret[0] == 0); REQUIRE(counter.counter >= 0); REQUIRE(counter.enabled > 0); REQUIRE(counter.running >= 0); REQUIRE(counter.running <= counter.enabled); #endif } TEST_CASE("test attach perf event", "[bpf_perf_event]") { #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0) const std::string BPF_PROGRAM = R"( BPF_HASH(pid, int, u64, 1); BPF_HASH(ret, int, int, 1); BPF_HASH(counter, int, struct bpf_perf_event_value, 1); int on_event(void *ctx) { int zero = 0; u64 p = bpf_get_current_pid_tgid(); pid.update(&zero, &p); #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0) struct bpf_perf_event_value c = {0}; int r = bpf_perf_prog_read_value(ctx, &c, sizeof(c)); ret.update(&zero, &r); counter.update(&zero, &c); #endif return 0; } )"; ebpf::BPF bpf; ebpf::StatusTuple res(0); res = bpf.init(BPF_PROGRAM); REQUIRE(res.code() == 0); res = bpf.attach_perf_event(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_CLOCK, "on_event", 0, 1000); REQUIRE(res.code() == 0); sleep(1); res = bpf.detach_perf_event(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_CLOCK); REQUIRE(res.code() == 0); auto pid = bpf.get_hash_table("pid"); REQUIRE(pid[0] >= 0); #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0) auto counter_table = bpf.get_hash_table("counter"); auto counter = counter_table[0]; auto ret = bpf.get_hash_table("ret"); REQUIRE(ret[0] == 0); REQUIRE(counter.counter >= 0); // the program slept one second between perf_event attachment and detachment // in the above, so the enabled counter should be 1000000000ns or // more. But in reality, most of counters (if not all) are 9xxxxxxxx, // and I also saw one 8xxxxxxxx. So let us a little bit conservative here. REQUIRE(counter.enabled >= 800000000); REQUIRE(counter.running >= 0); REQUIRE(counter.running <= counter.enabled); #endif } bpfcc-0.12.0/tests/cc/test_pinned_table.cc000066400000000000000000000044651357404205000204010ustar00rootroot00000000000000/* * Copyright (c) 2019 Kinvolk GmbH * * 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. */ #include #include #include #include #include "BPF.h" #include "catch.hpp" #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) TEST_CASE("test pinned table", "[pinned_table]") { bool mounted = false; if (system("mount | grep /sys/fs/bpf")) { REQUIRE(system("mkdir -p /sys/fs/bpf") == 0); REQUIRE(system("mount -o nosuid,nodev,noexec,mode=700 -t bpf bpf /sys/fs/bpf") == 0); mounted = true; } // prepare test by pinning table to bpffs { const std::string BPF_PROGRAM = R"( BPF_TABLE("hash", u64, u64, ids, 1024); )"; ebpf::BPF bpf; ebpf::StatusTuple res(0); res = bpf.init(BPF_PROGRAM); REQUIRE(res.code() == 0); REQUIRE(bpf_obj_pin(bpf.get_hash_table("ids").get_fd(), "/sys/fs/bpf/test_pinned_table") == 0); } // test table access { const std::string BPF_PROGRAM = R"( BPF_TABLE_PINNED("hash", u64, u64, ids, 1024, "/sys/fs/bpf/test_pinned_table"); )"; ebpf::BPF bpf; ebpf::StatusTuple res(0); res = bpf.init(BPF_PROGRAM); unlink("/sys/fs/bpf/test_pinned_table"); // can delete table here already REQUIRE(res.code() == 0); auto t = bpf.get_hash_table("ids"); int key, value; // write element key = 0x08; value = 0x43; res = t.update_value(key, value); REQUIRE(res.code() == 0); REQUIRE(t[key] == value); } // test failure { const std::string BPF_PROGRAM = R"( BPF_TABLE_PINNED("hash", u64, u64, ids, 1024, "/sys/fs/bpf/test_pinned_table"); )"; ebpf::BPF bpf; ebpf::StatusTuple res(0); res = bpf.init(BPF_PROGRAM); REQUIRE(res.code() != 0); } if (mounted) { REQUIRE(umount("/sys/fs/bpf") == 0); } } #endif bpfcc-0.12.0/tests/cc/test_prog_table.cc000066400000000000000000000031431357404205000200630ustar00rootroot00000000000000/* * Copyright (c) 2018 Politecnico di Torino * * 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. */ #include "BPF.h" #include "catch.hpp" TEST_CASE("test prog table", "[prog_table]") { const std::string BPF_PROGRAM = R"( BPF_TABLE("prog", int, int, myprog, 16); )"; const std::string BPF_PROGRAM2 = R"( int hello(struct __sk_buff *skb) { return 1; } )"; ebpf::StatusTuple res(0); ebpf::BPF bpf; res = bpf.init(BPF_PROGRAM); REQUIRE(res.code() == 0); ebpf::BPFProgTable t = bpf.get_prog_table("myprog"); ebpf::BPF bpf2; res = bpf2.init(BPF_PROGRAM2); REQUIRE(res.code() == 0); int fd; res = bpf2.load_func("hello", BPF_PROG_TYPE_SCHED_CLS, fd); REQUIRE(res.code() == 0); SECTION("update and remove") { // update element res = t.update_value(0, fd); REQUIRE(res.code() == 0); // remove element res = t.remove_value(0); REQUIRE(res.code() == 0); // update out of range element res = t.update_value(17, fd); REQUIRE(res.code() != 0); // remove out of range element res = t.remove_value(17); REQUIRE(res.code() != 0); } } bpfcc-0.12.0/tests/cc/test_shared_table.cc000066400000000000000000000045271357404205000203710ustar00rootroot00000000000000/* * Copyright (c) 2018 Politecnico di Torino * * 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. */ #include "BPF.h" #include "catch.hpp" const std::string BPF_PROGRAM1 = R"( BPF_TABLE_SHARED("array", int, int, mysharedtable, 1024); )"; const std::string BPF_PROGRAM2 = R"( BPF_TABLE("extern", int, int, mysharedtable, 1024); )"; TEST_CASE("test shared table", "[shared_table]") { // deploy 4 ebpf programs: _ns1_a and _ns1_b are in ns1, _ns2_a and _ns2_b in ns2 ebpf::BPF bpf_ns1_a(0, nullptr, false, "ns1"); ebpf::BPF bpf_ns1_b(0, nullptr, false, "ns1"); ebpf::BPF bpf_ns2_a(0, nullptr, false, "ns2"); ebpf::BPF bpf_ns2_b(0, nullptr, false, "ns2"); ebpf::StatusTuple res(0); res = bpf_ns1_a.init(BPF_PROGRAM1); REQUIRE(res.code() == 0); res = bpf_ns1_b.init(BPF_PROGRAM2); REQUIRE(res.code() == 0); res = bpf_ns2_a.init(BPF_PROGRAM1); REQUIRE(res.code() == 0); res = bpf_ns2_b.init(BPF_PROGRAM2); REQUIRE(res.code() == 0); // get references to all tables ebpf::BPFArrayTable t_ns1_a = bpf_ns1_a.get_array_table("mysharedtable"); ebpf::BPFArrayTable t_ns1_b = bpf_ns1_b.get_array_table("mysharedtable"); ebpf::BPFArrayTable t_ns2_a = bpf_ns2_a.get_array_table("mysharedtable"); ebpf::BPFArrayTable t_ns2_b = bpf_ns2_b.get_array_table("mysharedtable"); // test that tables within the same ns are shared int v1, v2, v3; res = t_ns1_a.update_value(13, 42); REQUIRE(res.code() == 0); res = t_ns1_b.get_value(13, v1); REQUIRE(res.code() == 0); REQUIRE(v1 == 42); // test that tables are isolated within different ns res = t_ns2_a.update_value(13, 69); REQUIRE(res.code() == 0); res = t_ns2_b.get_value(13, v2); REQUIRE(res.code() == 0); REQUIRE(v2 == 69); res = t_ns1_b.get_value(13, v3); REQUIRE(res.code() == 0); REQUIRE(v3 == 42); // value should still be 42 } bpfcc-0.12.0/tests/cc/test_static.c000066400000000000000000000003151357404205000170670ustar00rootroot00000000000000#include "bcc_common.h" int main(int argc, char **argv) { void *mod = bpf_module_create_c_from_string("BPF_TABLE(\"array\", int, int, stats, 10);\n", 4, NULL, 0, true, NULL); return !(mod != NULL); } bpfcc-0.12.0/tests/cc/test_usdt_args.cc000066400000000000000000000161251357404205000177440ustar00rootroot00000000000000/* * Copyright (c) 2016 GitHub, Inc. * * 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. */ #include #include #include "catch.hpp" #include "usdt.h" using std::experimental::optional; using std::experimental::nullopt; static void verify_register(USDT::ArgumentParser &parser, int arg_size, int constant) { USDT::Argument arg; REQUIRE(parser.parse(&arg)); REQUIRE(arg.arg_size() == arg_size); REQUIRE(arg.constant()); REQUIRE(arg.constant() == constant); } static void verify_register(USDT::ArgumentParser &parser, int arg_size, const std::string ®name, optional deref_offset = nullopt, optional deref_ident = nullopt, optional index_regname = nullopt, optional scale = nullopt) { USDT::Argument arg; REQUIRE(parser.parse(&arg)); REQUIRE(arg.arg_size() == arg_size); REQUIRE(arg.base_register_name()); REQUIRE(arg.base_register_name() == regname); REQUIRE(arg.deref_offset() == deref_offset); REQUIRE(arg.deref_ident() == deref_ident); REQUIRE(arg.index_register_name() == index_regname); REQUIRE(arg.scale() == scale); } TEST_CASE("test usdt argument parsing", "[usdt]") { SECTION("parse failure") { #ifdef __aarch64__ USDT::ArgumentParser_aarch64 parser("4@[x32,200]"); #elif __powerpc64__ USDT::ArgumentParser_powerpc64 parser("4@-12(42)"); #elif __s390x__ USDT::ArgumentParser_s390x parser("4@-12(%r42)"); #elif defined(__x86_64__) USDT::ArgumentParser_x64 parser("4@i%ra+1r"); #endif USDT::Argument arg; REQUIRE(!parser.parse(&arg)); int i; for (i = 0; i < 10 && !parser.done(); ++i) { parser.parse(&arg); } // Make sure we reach termination REQUIRE(i < 10); } SECTION("argument examples from the Python implementation") { #ifdef __aarch64__ USDT::ArgumentParser_aarch64 parser("-1@x0 4@5 8@[x12] -4@[x31,-40]"); verify_register(parser, -1, "regs[0]"); verify_register(parser, 4, 5); verify_register(parser, 8, "regs[12]", 0); verify_register(parser, -4, "regs[31]", -40); #elif __powerpc64__ USDT::ArgumentParser_powerpc64 parser( "-4@0 8@%r0 8@i0 4@0(%r0) -2@0(0) " "1@0 -2@%r3 -8@i9 -1@0(%r4) -4@16(6) " "2@7 4@%r11 4@i-67 8@-16(%r17) 1@-52(11) " "-8@13 -8@%r25 2@i-11 -2@14(%r26) -8@-32(24) " "4@29 2@%r17 -8@i-693 -1@-23(%r31) 4@28(30) " "-2@31 -4@%r30 2@i1097 4@108(%r30) -2@-4(31)"); verify_register(parser, -4, "gpr[0]"); verify_register(parser, 8, "gpr[0]"); verify_register(parser, 8, 0); verify_register(parser, 4, "gpr[0]", 0); verify_register(parser, -2, "gpr[0]", 0); verify_register(parser, 1, "gpr[0]"); verify_register(parser, -2, "gpr[3]"); verify_register(parser, -8, 9); verify_register(parser, -1, "gpr[4]", 0); verify_register(parser, -4, "gpr[6]", 16); verify_register(parser, 2, "gpr[7]"); verify_register(parser, 4, "gpr[11]"); verify_register(parser, 4, -67); verify_register(parser, 8, "gpr[17]", -16); verify_register(parser, 1, "gpr[11]", -52); verify_register(parser, -8, "gpr[13]"); verify_register(parser, -8, "gpr[25]"); verify_register(parser, 2, -11); verify_register(parser, -2, "gpr[26]", 14); verify_register(parser, -8, "gpr[24]", -32); verify_register(parser, 4, "gpr[29]"); verify_register(parser, 2, "gpr[17]"); verify_register(parser, -8, -693); verify_register(parser, -1, "gpr[31]", -23); verify_register(parser, 4, "gpr[30]", 28); verify_register(parser, -2, "gpr[31]"); verify_register(parser, -4, "gpr[30]"); verify_register(parser, 2, 1097); verify_register(parser, 4, "gpr[30]", 108); verify_register(parser, -2, "gpr[31]", -4); #elif __s390x__ USDT::ArgumentParser_s390x parser( "-4@%r0 8@%r0 8@0 4@0(%r0) -2@0(%r0) " "1@%r0 -2@%r3 -8@9 -1@0(%r4) -4@16(%r6) " "2@%r7 4@%r11 4@-67 8@-16(%r15) 1@-52(%r11) " "-8@%r4 -8@%r14 2@-11 -2@14(%r13) -8@-32(%r12) " "4@%r5 2@%r11 -8@-693 -1@-23(%r10) 4@28(%r9) " "-2@%r3 -4@%r8 2@1097 4@108(%r7) -2@-4(%r6)"); verify_register(parser, -4, "gprs[0]"); verify_register(parser, 8, "gprs[0]"); verify_register(parser, 8, 0); verify_register(parser, 4, "gprs[0]", 0); verify_register(parser, -2, "gprs[0]", 0); verify_register(parser, 1, "gprs[0]"); verify_register(parser, -2, "gprs[3]"); verify_register(parser, -8, 9); verify_register(parser, -1, "gprs[4]", 0); verify_register(parser, -4, "gprs[6]", 16); verify_register(parser, 2, "gprs[7]"); verify_register(parser, 4, "gprs[11]"); verify_register(parser, 4, -67); verify_register(parser, 8, "gprs[15]", -16); verify_register(parser, 1, "gprs[11]", -52); verify_register(parser, -8, "gprs[4]"); verify_register(parser, -8, "gprs[14]"); verify_register(parser, 2, -11); verify_register(parser, -2, "gprs[13]", 14); verify_register(parser, -8, "gprs[12]", -32); verify_register(parser, 4, "gprs[5]"); verify_register(parser, 2, "gprs[11]"); verify_register(parser, -8, -693); verify_register(parser, -1, "gprs[10]", -23); verify_register(parser, 4, "gprs[9]", 28); verify_register(parser, -2, "gprs[3]"); verify_register(parser, -4, "gprs[8]"); verify_register(parser, 2, 1097); verify_register(parser, 4, "gprs[7]", 108); verify_register(parser, -2, "gprs[6]", -4); #elif defined(__x86_64__) USDT::ArgumentParser_x64 parser( "-4@$0 8@$1234 %rdi %rax %rsi " "-8@%rbx 4@%r12 8@-8(%rbp) 4@(%rax) " "-4@global_max_action(%rip) " "8@24+mp_(%rip) " "-4@CheckpointStats+40(%rip) " "4@glob-2(%rip) " "8@(%rax,%rdx,8) " "4@(%rbx,%rcx)"); verify_register(parser, -4, 0); verify_register(parser, 8, 1234); verify_register(parser, 8, "di"); verify_register(parser, 8, "ax"); verify_register(parser, 8, "si"); verify_register(parser, -8, "bx"); verify_register(parser, 4, "r12"); verify_register(parser, 8, "bp", -8); verify_register(parser, 4, "ax", 0); verify_register(parser, -4, "ip", 0, std::string("global_max_action")); verify_register(parser, 8, "ip", 24, std::string("mp_")); verify_register(parser, -4, "ip", 40, std::string("CheckpointStats")); verify_register(parser, 4, "ip", -2, std::string("glob")); verify_register(parser, 8, "ax", 0, nullopt, std::string("dx"), 8); verify_register(parser, 4, "bx", 0, nullopt, std::string("cx")); #endif REQUIRE(parser.done()); } } bpfcc-0.12.0/tests/cc/test_usdt_probes.cc000066400000000000000000000210301357404205000202710ustar00rootroot00000000000000/* * Copyright (c) 2016 GitHub, Inc. * * 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. */ #include #include #include #include #include "catch.hpp" #include "usdt.h" #include "api/BPF.h" /* required to insert USDT probes on this very executable -- * we're gonna be testing them live! */ #include "folly/tracing/StaticTracepoint.h" static int a_probed_function() { int an_int = 23 + getpid(); void *a_pointer = malloc(4); FOLLY_SDT(libbcc_test, sample_probe_1, an_int, a_pointer); free(a_pointer); return an_int; } extern "C" int lib_probed_function(); int call_shared_lib_func() { return lib_probed_function(); } TEST_CASE("test finding a probe in our own process", "[usdt]") { USDT::Context ctx(getpid()); REQUIRE(ctx.num_probes() >= 1); SECTION("our test probe") { auto probe = ctx.get("sample_probe_1"); REQUIRE(probe); if(probe->in_shared_object(probe->bin_path())) return; REQUIRE(probe->name() == "sample_probe_1"); REQUIRE(probe->provider() == "libbcc_test"); REQUIRE(probe->bin_path().find("/test_libbcc") != std::string::npos); REQUIRE(probe->num_locations() == 1); REQUIRE(probe->num_arguments() == 2); REQUIRE(probe->need_enable() == false); REQUIRE(a_probed_function() != 0); } } TEST_CASE("test fine a probe in our own binary with C++ API", "[usdt]") { ebpf::BPF bpf; ebpf::USDT u("/proc/self/exe", "libbcc_test", "sample_probe_1", "on_event"); auto res = bpf.init("int on_event() { return 0; }", {}, {u}); REQUIRE(res.code() == 0); res = bpf.attach_usdt(u); REQUIRE(res.code() == 0); res = bpf.detach_usdt(u); REQUIRE(res.code() == 0); } TEST_CASE("test fine a probe in our Process with C++ API", "[usdt]") { ebpf::BPF bpf; ebpf::USDT u(::getpid(), "libbcc_test", "sample_probe_1", "on_event"); auto res = bpf.init("int on_event() { return 0; }", {}, {u}); REQUIRE(res.code() == 0); res = bpf.attach_usdt(u); REQUIRE(res.code() == 0); res = bpf.detach_usdt(u); REQUIRE(res.code() == 0); } TEST_CASE("test find a probe in our process' shared libs with c++ API", "[usdt]") { ebpf::BPF bpf; ebpf::USDT u(::getpid(), "libbcc_test", "sample_lib_probe_1", "on_event"); auto res = bpf.init("int on_event() { return 0; }", {}, {u}); REQUIRE(res.msg() == ""); REQUIRE(res.code() == 0); } TEST_CASE("test usdt partial init w/ fail init_usdt", "[usdt]") { ebpf::BPF bpf; ebpf::USDT u(::getpid(), "libbcc_test", "sample_lib_probe_nonexistent", "on_event"); ebpf::USDT p(::getpid(), "libbcc_test", "sample_lib_probe_1", "on_event"); // We should be able to fail initialization and subsequently do bpf.init w/o USDT // successfully auto res = bpf.init_usdt(u); REQUIRE(res.msg() != ""); REQUIRE(res.code() != 0); // Shouldn't be necessary to re-init bpf object either after failure to init w/ // bad USDT res = bpf.init("int on_event() { return 0; }", {}, {u}); REQUIRE(res.msg() != ""); REQUIRE(res.code() != 0); res = bpf.init_usdt(p); REQUIRE(res.msg() == ""); REQUIRE(res.code() == 0); res = bpf.init("int on_event() { return 0; }", {}, {}); REQUIRE(res.msg() == ""); REQUIRE(res.code() == 0); } class ChildProcess { pid_t pid_; public: ChildProcess(const char *name, char *const argv[]) { pid_ = fork(); if (pid_ == 0) { execvp(name, argv); exit(0); } if (spawned()) { usleep(250000); if (kill(pid_, 0) < 0) pid_ = -1; } } ~ChildProcess() { if (spawned()) { int status; kill(pid_, SIGKILL); if (waitpid(pid_, &status, 0) != pid_) abort(); } } bool spawned() const { return pid_ > 0; } pid_t pid() const { return pid_; } }; extern int cmd_scanf(const char *cmd, const char *fmt, ...); static int probe_num_locations(const char *bin_path, const char *func_name) { int num_locations; char cmd[512]; const char *cmdfmt = "readelf -n %s | grep -c \"Name: %s$\""; sprintf(cmd, cmdfmt, bin_path, func_name); if (cmd_scanf(cmd, "%d", &num_locations) != 0) { return -1; } return num_locations; } static int probe_num_arguments(const char *bin_path, const char *func_name) { int num_arguments; char cmd[512]; const char *cmdfmt = "readelf -n %s | grep -m 1 -A 2 \" %s$\" | " \ "tail -1 | cut -d \" \" -f 6- | wc -w"; sprintf(cmd, cmdfmt, bin_path, func_name); if (cmd_scanf(cmd, "%d", &num_arguments) != 0) { return -1; } return num_arguments; } TEST_CASE("test listing all USDT probes in Ruby/MRI", "[usdt]") { size_t mri_probe_count = 0; SECTION("without a running Ruby process") { USDT::Context ctx("ruby"); if (!ctx.loaded()) return; REQUIRE(ctx.num_probes() > 10); mri_probe_count = ctx.num_probes(); SECTION("GC static probe") { auto name = "gc__mark__begin"; auto probe = ctx.get(name); REQUIRE(probe); REQUIRE(probe->in_shared_object(probe->bin_path()) == true); REQUIRE(probe->name() == name); REQUIRE(probe->provider() == "ruby"); auto bin_path = probe->bin_path(); bool bin_path_match = (bin_path.find("/ruby") != std::string::npos) || (bin_path.find("/libruby") != std::string::npos); REQUIRE(bin_path_match); int exp_locations, exp_arguments; exp_locations = probe_num_locations(bin_path.c_str(), name); exp_arguments = probe_num_arguments(bin_path.c_str(), name); REQUIRE(probe->num_locations() == exp_locations); REQUIRE(probe->num_arguments() == exp_arguments); REQUIRE(probe->need_enable() == true); } SECTION("object creation probe") { auto name = "object__create"; auto probe = ctx.get(name); REQUIRE(probe); REQUIRE(probe->in_shared_object(probe->bin_path()) == true); REQUIRE(probe->name() == name); REQUIRE(probe->provider() == "ruby"); auto bin_path = probe->bin_path(); bool bin_path_match = (bin_path.find("/ruby") != std::string::npos) || (bin_path.find("/libruby") != std::string::npos); REQUIRE(bin_path_match); int exp_locations, exp_arguments; exp_locations = probe_num_locations(bin_path.c_str(), name); exp_arguments = probe_num_arguments(bin_path.c_str(), name); REQUIRE(probe->num_locations() == exp_locations); REQUIRE(probe->num_arguments() == exp_arguments); REQUIRE(probe->need_enable() == true); } SECTION("array creation probe") { auto name = "array__create"; auto probe = ctx.get(name); REQUIRE(probe); REQUIRE(probe->name() == name); auto bin_path = probe->bin_path().c_str(); int exp_locations, exp_arguments; exp_locations = probe_num_locations(bin_path, name); exp_arguments = probe_num_arguments(bin_path, name); REQUIRE(probe->num_locations() == exp_locations); REQUIRE(probe->num_arguments() == exp_arguments); REQUIRE(probe->need_enable() == true); } } SECTION("with a running Ruby process") { static char _ruby[] = "ruby"; char *const argv[2] = {_ruby, NULL}; ChildProcess ruby(argv[0], argv); if (!ruby.spawned()) return; USDT::Context ctx(ruby.pid()); REQUIRE(ctx.num_probes() >= mri_probe_count); SECTION("get probe in running process") { auto name = "gc__mark__begin"; auto probe = ctx.get(name); REQUIRE(probe); REQUIRE(probe->in_shared_object(probe->bin_path()) == true); REQUIRE(probe->name() == name); REQUIRE(probe->provider() == "ruby"); auto bin_path = probe->bin_path(); bool bin_path_match = (bin_path.find("/ruby") != std::string::npos) || (bin_path.find("/libruby") != std::string::npos); REQUIRE(bin_path_match); int exp_locations, exp_arguments; exp_locations = probe_num_locations(bin_path.c_str(), name); exp_arguments = probe_num_arguments(bin_path.c_str(), name); REQUIRE(probe->num_locations() == exp_locations); REQUIRE(probe->num_arguments() == exp_arguments); REQUIRE(probe->need_enable() == true); } } } bpfcc-0.12.0/tests/cc/usdt_test_lib.c000066400000000000000000000003341357404205000174060ustar00rootroot00000000000000#include #include #include "folly/tracing/StaticTracepoint.h" int lib_probed_function() { int an_int = 42 + getpid(); FOLLY_SDT(libbcc_test, sample_lib_probe_1, an_int); return an_int; } bpfcc-0.12.0/tests/cc/utils.cc000066400000000000000000000016141357404205000160470ustar00rootroot00000000000000/* * Copyright (c) 2017 IBM Corporation * * 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. */ #include #include int cmd_scanf(const char *cmd, const char *fmt, ...) { va_list args; FILE *pipe; va_start(args, fmt); pipe = popen(cmd, "r"); if (pipe == NULL) { va_end(args); return -1; } vfscanf(pipe, fmt, args); va_end(args); pclose(pipe); return 0; } bpfcc-0.12.0/tests/lua/000077500000000000000000000000001357404205000145725ustar00rootroot00000000000000bpfcc-0.12.0/tests/lua/.busted000066400000000000000000000002241357404205000160570ustar00rootroot00000000000000-- Configuration for unit tests -- See: http://olivinelabs.com/busted/ return { default = { lpath = "./?.lua", ["auto-insulate"] = false, } }bpfcc-0.12.0/tests/lua/.luacheckrc000066400000000000000000000004311357404205000166750ustar00rootroot00000000000000std = "luajit" ignore = { "211", "212", "411", "412", "421", "431", "542" } files["examples"] = { new_globals = { "pkt", "time", "xadd", "c" } } files["bpf/builtins.lua"] = { ignore = { "122" } } files["spec"] = { std = "+busted", new_globals = { "pkt", "time", "xadd", "c" } }bpfcc-0.12.0/tests/lua/CMakeLists.txt000066400000000000000000000016101357404205000173300ustar00rootroot00000000000000find_program(LUAJIT luajit) find_program(BUSTED busted) if(LUAJIT) add_test(NAME lua_test_clang WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND ${TEST_WRAPPER} lua_test_clang sudo ${LUAJIT} test_clang.lua) add_test(NAME lua_test_uprobes WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND ${TEST_WRAPPER} lua_test_uprobes sudo ${LUAJIT} test_uprobes.lua) add_test(NAME lua_test_dump WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND ${TEST_WRAPPER} lua_test_dump sudo ${LUAJIT} test_dump.lua) add_test(NAME lua_test_standalone WORKING_DIRECTORY ${CMAKE_BINARY_DIR} COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/test_standalone.sh) if(BUSTED) add_test(NAME lua_test_busted WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND busted --lua=${LUAJIT} -m "${CMAKE_CURRENT_SOURCE_DIR}/../../src/lua/?.lua" -m "${CMAKE_CURRENT_SOURCE_DIR}/../../src/lua/?/init.lua;") endif() endif() bpfcc-0.12.0/tests/lua/luaunit.lua000066400000000000000000002255541357404205000167730ustar00rootroot00000000000000--[[ luaunit.lua Description: A unit testing framework Homepage: https://github.com/bluebird75/luaunit Development by Philippe Fremy Based on initial work of Ryu, Gwang (http://www.gpgstudy.com/gpgiki/LuaUnit) License: BSD License, see LICENSE.txt Version: 3.2 ]]-- require("math") local M={} -- private exported functions (for testing) M.private = {} M.VERSION='3.2' --[[ Some people like assertEquals( actual, expected ) and some people prefer assertEquals( expected, actual ). ]]-- M.ORDER_ACTUAL_EXPECTED = true M.PRINT_TABLE_REF_IN_ERROR_MSG = false M.TABLE_EQUALS_KEYBYCONTENT = true M.LINE_LENGTH=80 -- set this to false to debug luaunit local STRIP_LUAUNIT_FROM_STACKTRACE=true M.VERBOSITY_DEFAULT = 10 M.VERBOSITY_LOW = 1 M.VERBOSITY_QUIET = 0 M.VERBOSITY_VERBOSE = 20 -- set EXPORT_ASSERT_TO_GLOBALS to have all asserts visible as global values -- EXPORT_ASSERT_TO_GLOBALS = true -- we need to keep a copy of the script args before it is overriden local cmdline_argv = rawget(_G, "arg") M.FAILURE_PREFIX = 'LuaUnit test FAILURE: ' -- prefix string for failed tests M.USAGE=[[Usage: lua [options] [testname1 [testname2] ... ] Options: -h, --help: Print this help --version: Print version information -v, --verbose: Increase verbosity -q, --quiet: Set verbosity to minimum -e, --error: Stop on first error -f, --failure: Stop on first failure or error -o, --output OUTPUT: Set output type to OUTPUT Possible values: text, tap, junit, nil -n, --name NAME: For junit only, mandatory name of xml file -p, --pattern PATTERN: Execute all test names matching the Lua PATTERN May be repeated to include severals patterns Make sure you escape magic chars like +? with % testname1, testname2, ... : tests to run in the form of testFunction, TestClass or TestClass.testMethod ]] ---------------------------------------------------------------- -- -- general utility functions -- ---------------------------------------------------------------- local crossTypeOrdering = { number = 1, boolean = 2, string = 3, table = 4, other = 5 } local crossTypeComparison = { number = function(a, b) return a < b end, string = function(a, b) return a < b end, other = function(a, b) return tostring(a) < tostring(b) end, } local function crossTypeSort(a, b) local type_a, type_b = type(a), type(b) if type_a == type_b then local func = crossTypeComparison[type_a] or crossTypeComparison.other return func(a, b) end type_a = crossTypeOrdering[type_a] or crossTypeOrdering.other type_b = crossTypeOrdering[type_b] or crossTypeOrdering.other return type_a < type_b end local function __genSortedIndex( t ) -- Returns a sequence consisting of t's keys, sorted. local sortedIndex = {} for key,_ in pairs(t) do table.insert(sortedIndex, key) end table.sort(sortedIndex, crossTypeSort) return sortedIndex end M.private.__genSortedIndex = __genSortedIndex local function sortedNext(state, control) -- Equivalent of the next() function of table iteration, but returns the -- keys in sorted order (see __genSortedIndex and crossTypeSort). -- The state is a temporary variable during iteration and contains the -- sorted key table (state.sortedIdx). It also stores the last index (into -- the keys) used by the iteration, to find the next one quickly. local key --print("sortedNext: control = "..tostring(control) ) if control == nil then -- start of iteration state.lastIdx = 1 key = state.sortedIdx[1] return key, state.t[key] end -- normally, we expect the control variable to match the last key used if control ~= state.sortedIdx[state.lastIdx] then -- strange, we have to find the next value by ourselves -- the key table is sorted in crossTypeSort() order! -> use bisection local count = #state.sortedIdx local lower, upper = 1, count repeat state.lastIdx = math.modf((lower + upper) / 2) key = state.sortedIdx[state.lastIdx] if key == control then break; end -- key found (and thus prev index) if crossTypeSort(key, control) then -- key < control, continue search "right" (towards upper bound) lower = state.lastIdx + 1 else -- key > control, continue search "left" (towards lower bound) upper = state.lastIdx - 1 end until lower > upper if lower > upper then -- only true if the key wasn't found, ... state.lastIdx = count -- ... so ensure no match for the code below end end -- proceed by retrieving the next value (or nil) from the sorted keys state.lastIdx = state.lastIdx + 1 key = state.sortedIdx[state.lastIdx] if key then return key, state.t[key] end -- getting here means returning `nil`, which will end the iteration end local function sortedPairs(tbl) -- Equivalent of the pairs() function on tables. Allows to iterate in -- sorted order. As required by "generic for" loops, this will return the -- iterator (function), an "invariant state", and the initial control value. -- (see http://www.lua.org/pil/7.2.html) return sortedNext, {t = tbl, sortedIdx = __genSortedIndex(tbl)}, nil end M.private.sortedPairs = sortedPairs local function strsplit(delimiter, text) -- Split text into a list consisting of the strings in text, -- separated by strings matching delimiter (which may be a pattern). -- example: strsplit(",%s*", "Anna, Bob, Charlie,Dolores") if string.find("", delimiter, 1, true) then -- this would result in endless loops error("delimiter matches empty string!") end local list, pos, first, last = {}, 1 while true do first, last = text:find(delimiter, pos, true) if first then -- found? table.insert(list, text:sub(pos, first - 1)) pos = last + 1 else table.insert(list, text:sub(pos)) break end end return list end M.private.strsplit = strsplit local function hasNewLine( s ) -- return true if s has a newline return (string.find(s, '\n', 1, true) ~= nil) end M.private.hasNewLine = hasNewLine local function prefixString( prefix, s ) -- Prefix all the lines of s with prefix return prefix .. table.concat(strsplit('\n', s), '\n' .. prefix) end M.private.prefixString = prefixString local function strMatch(s, pattern, start, final ) -- return true if s matches completely the pattern from index start to index end -- return false in every other cases -- if start is nil, matches from the beginning of the string -- if final is nil, matches to the end of the string start = start or 1 final = final or string.len(s) local foundStart, foundEnd = string.find(s, pattern, start, false) return foundStart == start and foundEnd == final end M.private.strMatch = strMatch local function xmlEscape( s ) -- Return s escaped for XML attributes -- escapes table: -- " " -- ' ' -- < < -- > > -- & & return string.gsub( s, '.', { ['&'] = "&", ['"'] = """, ["'"] = "'", ['<'] = "<", ['>'] = ">", } ) end M.private.xmlEscape = xmlEscape local function xmlCDataEscape( s ) -- Return s escaped for CData section, escapes: "]]>" return string.gsub( s, ']]>', ']]>' ) end M.private.xmlCDataEscape = xmlCDataEscape local function stripLuaunitTrace( stackTrace ) --[[ -- Example of a traceback: < [C]: in function 'xpcall' ./luaunit.lua:1449: in function 'protectedCall' ./luaunit.lua:1508: in function 'execOneFunction' ./luaunit.lua:1596: in function 'runSuiteByInstances' ./luaunit.lua:1660: in function 'runSuiteByNames' ./luaunit.lua:1736: in function 'runSuite' example_with_luaunit.lua:140: in main chunk [C]: in ?>> Other example: < [C]: in function 'xpcall' ./luaunit.lua:1517: in function 'protectedCall' ./luaunit.lua:1578: in function 'execOneFunction' ./luaunit.lua:1677: in function 'runSuiteByInstances' ./luaunit.lua:1730: in function 'runSuiteByNames' ./luaunit.lua:1806: in function 'runSuite' example_with_luaunit.lua:140: in main chunk [C]: in ?>> < [C]: in function 'xpcall' luaunit2/luaunit.lua:1532: in function 'protectedCall' luaunit2/luaunit.lua:1591: in function 'execOneFunction' luaunit2/luaunit.lua:1679: in function 'runSuiteByInstances' luaunit2/luaunit.lua:1743: in function 'runSuiteByNames' luaunit2/luaunit.lua:1819: in function 'runSuite' luaunit2/example_with_luaunit.lua:140: in main chunk [C]: in ?>> -- first line is "stack traceback": KEEP -- next line may be luaunit line: REMOVE -- next lines are call in the program under testOk: REMOVE -- next lines are calls from luaunit to call the program under test: KEEP -- Strategy: -- keep first line -- remove lines that are part of luaunit -- kepp lines until we hit a luaunit line ]] local function isLuaunitInternalLine( s ) -- return true if line of stack trace comes from inside luaunit return s:find('[/\\]luaunit%.lua:%d+: ') ~= nil end -- print( '<<'..stackTrace..'>>' ) local t = strsplit( '\n', stackTrace ) -- print( prettystr(t) ) local idx = 2 -- remove lines that are still part of luaunit while t[idx] and isLuaunitInternalLine( t[idx] ) do -- print('Removing : '..t[idx] ) table.remove(t, idx) end -- keep lines until we hit luaunit again while t[idx] and (not isLuaunitInternalLine(t[idx])) do -- print('Keeping : '..t[idx] ) idx = idx + 1 end -- remove remaining luaunit lines while t[idx] do -- print('Removing : '..t[idx] ) table.remove(t, idx) end -- print( prettystr(t) ) return table.concat( t, '\n') end M.private.stripLuaunitTrace = stripLuaunitTrace local function prettystr_sub(v, indentLevel, keeponeline, printTableRefs, recursionTable ) local type_v = type(v) if "string" == type_v then if keeponeline then v = v:gsub("\n", "\\n") end -- use clever delimiters according to content: -- enclose with single quotes if string contains ", but no ' if v:find('"', 1, true) and not v:find("'", 1, true) then return "'" .. v .. "'" end -- use double quotes otherwise, escape embedded " return '"' .. v:gsub('"', '\\"') .. '"' elseif "table" == type_v then --if v.__class__ then -- return string.gsub( tostring(v), 'table', v.__class__ ) --end return M.private._table_tostring(v, indentLevel, printTableRefs, recursionTable) end return tostring(v) end local function prettystr( v, keeponeline ) --[[ Better string conversion, to display nice variable content: For strings, if keeponeline is set to true, string is displayed on one line, with visible \n * string are enclosed with " by default, or with ' if string contains a " * if table is a class, display class name * tables are expanded ]]-- local recursionTable = {} local s = prettystr_sub(v, 1, keeponeline, M.PRINT_TABLE_REF_IN_ERROR_MSG, recursionTable) if recursionTable.recursionDetected and not M.PRINT_TABLE_REF_IN_ERROR_MSG then -- some table contain recursive references, -- so we must recompute the value by including all table references -- else the result looks like crap recursionTable = {} s = prettystr_sub(v, 1, keeponeline, true, recursionTable) end return s end M.prettystr = prettystr local function prettystrPadded(value1, value2, suffix_a, suffix_b) --[[ This function helps with the recurring task of constructing the "expected vs. actual" error messages. It takes two arbitrary values and formats corresponding strings with prettystr(). To keep the (possibly complex) output more readable in case the resulting strings contain line breaks, they get automatically prefixed with additional newlines. Both suffixes are optional (default to empty strings), and get appended to the "value1" string. "suffix_a" is used if line breaks were encountered, "suffix_b" otherwise. Returns the two formatted strings (including padding/newlines). ]] local str1, str2 = prettystr(value1), prettystr(value2) if hasNewLine(str1) or hasNewLine(str2) then -- line break(s) detected, add padding return "\n" .. str1 .. (suffix_a or ""), "\n" .. str2 end return str1 .. (suffix_b or ""), str2 end M.private.prettystrPadded = prettystrPadded local function _table_keytostring(k) -- like prettystr but do not enclose with "" if the string is just alphanumerical -- this is better for displaying table keys who are often simple strings if "string" == type(k) and k:match("^[_%a][_%w]*$") then return k end return prettystr(k) end M.private._table_keytostring = _table_keytostring local TABLE_TOSTRING_SEP = ", " local TABLE_TOSTRING_SEP_LEN = string.len(TABLE_TOSTRING_SEP) local function _table_tostring( tbl, indentLevel, printTableRefs, recursionTable ) printTableRefs = printTableRefs or M.PRINT_TABLE_REF_IN_ERROR_MSG recursionTable = recursionTable or {} recursionTable[tbl] = true local result, dispOnMultLines = {}, false local entry, count, seq_index = nil, 0, 1 for k, v in sortedPairs( tbl ) do if k == seq_index then -- for the sequential part of tables, we'll skip the "=" output entry = '' seq_index = seq_index + 1 else entry = _table_keytostring( k ) .. "=" end if recursionTable[v] then -- recursion detected! recursionTable.recursionDetected = true entry = entry .. "<"..tostring(v)..">" else entry = entry .. prettystr_sub( v, indentLevel+1, true, printTableRefs, recursionTable ) end count = count + 1 result[count] = entry end -- set dispOnMultLines if the maximum LINE_LENGTH would be exceeded local totalLength = 0 for k, v in ipairs( result ) do totalLength = totalLength + string.len( v ) if totalLength >= M.LINE_LENGTH then dispOnMultLines = true break end end if not dispOnMultLines then -- adjust with length of separator(s): -- two items need 1 sep, three items two seps, ... plus len of '{}' if count > 0 then totalLength = totalLength + TABLE_TOSTRING_SEP_LEN * (count - 1) end dispOnMultLines = totalLength + 2 >= M.LINE_LENGTH end -- now reformat the result table (currently holding element strings) if dispOnMultLines then local indentString = string.rep(" ", indentLevel - 1) result = {"{\n ", indentString, table.concat(result, ",\n " .. indentString), "\n", indentString, "}"} else result = {"{", table.concat(result, TABLE_TOSTRING_SEP), "}"} end if printTableRefs then table.insert(result, 1, "<"..tostring(tbl).."> ") -- prepend table ref end return table.concat(result) end M.private._table_tostring = _table_tostring -- prettystr_sub() needs it local function _table_contains(t, element) if t then for _, value in pairs(t) do if type(value) == type(element) then if type(element) == 'table' then -- if we wanted recursive items content comparison, we could use -- _is_table_items_equals(v, expected) but one level of just comparing -- items is sufficient if M.private._is_table_equals( value, element ) then return true end else if value == element then return true end end end end end return false end local function _is_table_items_equals(actual, expected ) if (type(actual) == 'table') and (type(expected) == 'table') then for k,v in pairs(actual) do if not _table_contains(expected, v) then return false end end for k,v in pairs(expected) do if not _table_contains(actual, v) then return false end end return true elseif type(actual) ~= type(expected) then return false elseif actual == expected then return true end return false end local function _is_table_equals(actual, expected) if (type(actual) == 'table') and (type(expected) == 'table') then if (#actual ~= #expected) then return false end local actualTableKeys = {} for k,v in pairs(actual) do if M.TABLE_EQUALS_KEYBYCONTENT and type(k) == "table" then -- If the keys are tables, things get a bit tricky here as we -- can have _is_table_equals(k1, k2) and t[k1] ~= t[k2]. So we -- collect actual's table keys, group them by length for -- performance, and then for each table key in expected we look -- it up in actualTableKeys. if not actualTableKeys[#k] then actualTableKeys[#k] = {} end table.insert(actualTableKeys[#k], k) else if not _is_table_equals(v, expected[k]) then return false end end end for k,v in pairs(expected) do if M.TABLE_EQUALS_KEYBYCONTENT and type(k) == "table" then local candidates = actualTableKeys[#k] if not candidates then return false end local found for i, candidate in pairs(candidates) do if _is_table_equals(candidate, k) then found = candidate -- Remove the candidate we matched against from the list -- of candidates, so each key in actual can only match -- one key in expected. candidates[i] = nil break end end if not(found and _is_table_equals(actual[found], v)) then return false end else if not _is_table_equals(v, actual[k]) then return false end end end if M.TABLE_EQUALS_KEYBYCONTENT then for _, keys in pairs(actualTableKeys) do -- if there are any keys left in any actualTableKeys[i] then -- that is a key in actual with no matching key in expected, -- and so the tables aren't equal. if next(keys) then return false end end end return true elseif type(actual) ~= type(expected) then return false elseif actual == expected then return true end return false end M.private._is_table_equals = _is_table_equals local function failure(msg, level) -- raise an error indicating a test failure -- for error() compatibility we adjust "level" here (by +1), to report the -- calling context error(M.FAILURE_PREFIX .. msg, (level or 1) + 1) end local function fail_fmt(level, ...) -- failure with printf-style formatted message and given error level failure(string.format(...), (level or 1) + 1) end M.private.fail_fmt = fail_fmt local function error_fmt(level, ...) -- printf-style error() error(string.format(...), (level or 1) + 1) end ---------------------------------------------------------------- -- -- assertions -- ---------------------------------------------------------------- local function errorMsgEquality(actual, expected) if not M.ORDER_ACTUAL_EXPECTED then expected, actual = actual, expected end if type(expected) == 'string' or type(expected) == 'table' then expected, actual = prettystrPadded(expected, actual) return string.format("expected: %s\nactual: %s", expected, actual) end return string.format("expected: %s, actual: %s", prettystr(expected), prettystr(actual)) end function M.assertError(f, ...) -- assert that calling f with the arguments will raise an error -- example: assertError( f, 1, 2 ) => f(1,2) should generate an error if pcall( f, ... ) then failure( "Expected an error when calling function but no error generated", 2 ) end end function M.assertTrue(value) if not value then failure("expected: true, actual: " ..prettystr(value), 2) end end function M.assertFalse(value) if value then failure("expected: false, actual: " ..prettystr(value), 2) end end function M.assertIsNil(value) if value ~= nil then failure("expected: nil, actual: " ..prettystr(value), 2) end end function M.assertNotIsNil(value) if value == nil then failure("expected non nil value, received nil", 2) end end function M.assertEquals(actual, expected) if type(actual) == 'table' and type(expected) == 'table' then if not _is_table_equals(actual, expected) then failure( errorMsgEquality(actual, expected), 2 ) end elseif type(actual) ~= type(expected) then failure( errorMsgEquality(actual, expected), 2 ) elseif actual ~= expected then failure( errorMsgEquality(actual, expected), 2 ) end end -- Help Lua in corner cases like almostEquals(1.1, 1.0, 0.1), which by default -- may not work. We need to give margin a small boost; EPSILON defines the -- default value to use for this: local EPSILON = 0.00000000001 function M.almostEquals( actual, expected, margin, margin_boost ) if type(actual) ~= 'number' or type(expected) ~= 'number' or type(margin) ~= 'number' then error_fmt(3, 'almostEquals: must supply only number arguments.\nArguments supplied: %s, %s, %s', prettystr(actual), prettystr(expected), prettystr(margin)) end if margin <= 0 then error('almostEquals: margin must be positive, current value is ' .. margin, 3) end local realmargin = margin + (margin_boost or EPSILON) return math.abs(expected - actual) <= realmargin end function M.assertAlmostEquals( actual, expected, margin ) -- check that two floats are close by margin if not M.almostEquals(actual, expected, margin) then if not M.ORDER_ACTUAL_EXPECTED then expected, actual = actual, expected end fail_fmt(2, 'Values are not almost equal\nExpected: %s with margin of %s, received: %s', expected, margin, actual) end end function M.assertNotEquals(actual, expected) if type(actual) ~= type(expected) then return end if type(actual) == 'table' and type(expected) == 'table' then if not _is_table_equals(actual, expected) then return end elseif actual ~= expected then return end fail_fmt(2, 'Received the not expected value: %s', prettystr(actual)) end function M.assertNotAlmostEquals( actual, expected, margin ) -- check that two floats are not close by margin if M.almostEquals(actual, expected, margin) then if not M.ORDER_ACTUAL_EXPECTED then expected, actual = actual, expected end fail_fmt(2, 'Values are almost equal\nExpected: %s with a difference above margin of %s, received: %s', expected, margin, actual) end end function M.assertStrContains( str, sub, useRe ) -- this relies on lua string.find function -- a string always contains the empty string if not string.find(str, sub, 1, not useRe) then sub, str = prettystrPadded(sub, str, '\n') fail_fmt(2, 'Error, %s %s was not found in string %s', useRe and 'regexp' or 'substring', sub, str) end end function M.assertStrIContains( str, sub ) -- this relies on lua string.find function -- a string always contains the empty string if not string.find(str:lower(), sub:lower(), 1, true) then sub, str = prettystrPadded(sub, str, '\n') fail_fmt(2, 'Error, substring %s was not found (case insensitively) in string %s', sub, str) end end function M.assertNotStrContains( str, sub, useRe ) -- this relies on lua string.find function -- a string always contains the empty string if string.find(str, sub, 1, not useRe) then sub, str = prettystrPadded(sub, str, '\n') fail_fmt(2, 'Error, %s %s was found in string %s', useRe and 'regexp' or 'substring', sub, str) end end function M.assertNotStrIContains( str, sub ) -- this relies on lua string.find function -- a string always contains the empty string if string.find(str:lower(), sub:lower(), 1, true) then sub, str = prettystrPadded(sub, str, '\n') fail_fmt(2, 'Error, substring %s was found (case insensitively) in string %s', sub, str) end end function M.assertStrMatches( str, pattern, start, final ) -- Verify a full match for the string -- for a partial match, simply use assertStrContains with useRe set to true if not strMatch( str, pattern, start, final ) then pattern, str = prettystrPadded(pattern, str, '\n') fail_fmt(2, 'Error, pattern %s was not matched by string %s', pattern, str) end end function M.assertErrorMsgEquals( expectedMsg, func, ... ) -- assert that calling f with the arguments will raise an error -- example: assertError( f, 1, 2 ) => f(1,2) should generate an error local no_error, error_msg = pcall( func, ... ) if no_error then failure( 'No error generated when calling function but expected error: "'..expectedMsg..'"', 2 ) end if error_msg ~= expectedMsg then error_msg, expectedMsg = prettystrPadded(error_msg, expectedMsg) fail_fmt(2, 'Exact error message expected: %s\nError message received: %s\n', expectedMsg, error_msg) end end function M.assertErrorMsgContains( partialMsg, func, ... ) -- assert that calling f with the arguments will raise an error -- example: assertError( f, 1, 2 ) => f(1,2) should generate an error local no_error, error_msg = pcall( func, ... ) if no_error then failure( 'No error generated when calling function but expected error containing: '..prettystr(partialMsg), 2 ) end if not string.find( error_msg, partialMsg, nil, true ) then error_msg, partialMsg = prettystrPadded(error_msg, partialMsg) fail_fmt(2, 'Error message does not contain: %s\nError message received: %s\n', partialMsg, error_msg) end end function M.assertErrorMsgMatches( expectedMsg, func, ... ) -- assert that calling f with the arguments will raise an error -- example: assertError( f, 1, 2 ) => f(1,2) should generate an error local no_error, error_msg = pcall( func, ... ) if no_error then failure( 'No error generated when calling function but expected error matching: "'..expectedMsg..'"', 2 ) end if not strMatch( error_msg, expectedMsg ) then expectedMsg, error_msg = prettystrPadded(expectedMsg, error_msg) fail_fmt(2, 'Error message does not match: %s\nError message received: %s\n', expectedMsg, error_msg) end end --[[ Add type assertion functions to the module table M. Each of these functions takes a single parameter "value", and checks that its Lua type matches the expected string (derived from the function name): M.assertIsXxx(value) -> ensure that type(value) conforms to "xxx" ]] for _, funcName in ipairs( {'assertIsNumber', 'assertIsString', 'assertIsTable', 'assertIsBoolean', 'assertIsFunction', 'assertIsUserdata', 'assertIsThread'} ) do local typeExpected = funcName:match("^assertIs([A-Z]%a*)$") -- Lua type() always returns lowercase, also make sure the match() succeeded typeExpected = typeExpected and typeExpected:lower() or error("bad function name '"..funcName.."' for type assertion") M[funcName] = function(value) if type(value) ~= typeExpected then fail_fmt(2, 'Expected: a %s value, actual: type %s, value %s', typeExpected, type(value), prettystrPadded(value)) end end end --[[ Add non-type assertion functions to the module table M. Each of these functions takes a single parameter "value", and checks that its Lua type differs from the expected string (derived from the function name): M.assertNotIsXxx(value) -> ensure that type(value) is not "xxx" ]] for _, funcName in ipairs( {'assertNotIsNumber', 'assertNotIsString', 'assertNotIsTable', 'assertNotIsBoolean', 'assertNotIsFunction', 'assertNotIsUserdata', 'assertNotIsThread'} ) do local typeUnexpected = funcName:match("^assertNotIs([A-Z]%a*)$") -- Lua type() always returns lowercase, also make sure the match() succeeded typeUnexpected = typeUnexpected and typeUnexpected:lower() or error("bad function name '"..funcName.."' for type assertion") M[funcName] = function(value) if type(value) == typeUnexpected then fail_fmt(2, 'Not expected: a %s type, actual: value %s', typeUnexpected, prettystrPadded(value)) end end end function M.assertIs(actual, expected) if actual ~= expected then if not M.ORDER_ACTUAL_EXPECTED then actual, expected = expected, actual end expected, actual = prettystrPadded(expected, actual, '\n', ', ') fail_fmt(2, 'Expected object and actual object are not the same\nExpected: %sactual: %s', expected, actual) end end function M.assertNotIs(actual, expected) if actual == expected then if not M.ORDER_ACTUAL_EXPECTED then expected = actual end fail_fmt(2, 'Expected object and actual object are the same object: %s', prettystrPadded(expected)) end end function M.assertItemsEquals(actual, expected) -- checks that the items of table expected -- are contained in table actual. Warning, this function -- is at least O(n^2) if not _is_table_items_equals(actual, expected ) then expected, actual = prettystrPadded(expected, actual) fail_fmt(2, 'Contents of the tables are not identical:\nExpected: %s\nActual: %s', expected, actual) end end ---------------------------------------------------------------- -- Compatibility layer ---------------------------------------------------------------- -- for compatibility with LuaUnit v2.x function M.wrapFunctions(...) io.stderr:write( [[Use of WrapFunction() is no longer needed. Just prefix your test function names with "test" or "Test" and they will be picked up and run by LuaUnit.]] ) -- In LuaUnit version <= 2.1 , this function was necessary to include -- a test function inside the global test suite. Nowadays, the functions -- are simply run directly as part of the test discovery process. -- so just do nothing ! --[[ local testClass, testFunction testClass = {} local function storeAsMethod(idx, testName) testFunction = _G[testName] testClass[testName] = testFunction end for i,v in ipairs({...}) do storeAsMethod( i, v ) end return testClass ]] end local list_of_funcs = { -- { official function name , alias } -- general assertions { 'assertEquals' , 'assert_equals' }, { 'assertItemsEquals' , 'assert_items_equals' }, { 'assertNotEquals' , 'assert_not_equals' }, { 'assertAlmostEquals' , 'assert_almost_equals' }, { 'assertNotAlmostEquals' , 'assert_not_almost_equals' }, { 'assertTrue' , 'assert_true' }, { 'assertFalse' , 'assert_false' }, { 'assertStrContains' , 'assert_str_contains' }, { 'assertStrIContains' , 'assert_str_icontains' }, { 'assertNotStrContains' , 'assert_not_str_contains' }, { 'assertNotStrIContains' , 'assert_not_str_icontains' }, { 'assertStrMatches' , 'assert_str_matches' }, { 'assertError' , 'assert_error' }, { 'assertErrorMsgEquals' , 'assert_error_msg_equals' }, { 'assertErrorMsgContains' , 'assert_error_msg_contains' }, { 'assertErrorMsgMatches' , 'assert_error_msg_matches' }, { 'assertIs' , 'assert_is' }, { 'assertNotIs' , 'assert_not_is' }, { 'wrapFunctions' , 'WrapFunctions' }, { 'wrapFunctions' , 'wrap_functions' }, -- type assertions: assertIsXXX -> assert_is_xxx { 'assertIsNumber' , 'assert_is_number' }, { 'assertIsString' , 'assert_is_string' }, { 'assertIsTable' , 'assert_is_table' }, { 'assertIsBoolean' , 'assert_is_boolean' }, { 'assertIsNil' , 'assert_is_nil' }, { 'assertIsFunction' , 'assert_is_function' }, { 'assertIsThread' , 'assert_is_thread' }, { 'assertIsUserdata' , 'assert_is_userdata' }, -- type assertions: assertIsXXX -> assertXxx { 'assertIsNumber' , 'assertNumber' }, { 'assertIsString' , 'assertString' }, { 'assertIsTable' , 'assertTable' }, { 'assertIsBoolean' , 'assertBoolean' }, { 'assertIsNil' , 'assertNil' }, { 'assertIsFunction' , 'assertFunction' }, { 'assertIsThread' , 'assertThread' }, { 'assertIsUserdata' , 'assertUserdata' }, -- type assertions: assertIsXXX -> assert_xxx (luaunit v2 compat) { 'assertIsNumber' , 'assert_number' }, { 'assertIsString' , 'assert_string' }, { 'assertIsTable' , 'assert_table' }, { 'assertIsBoolean' , 'assert_boolean' }, { 'assertIsNil' , 'assert_nil' }, { 'assertIsFunction' , 'assert_function' }, { 'assertIsThread' , 'assert_thread' }, { 'assertIsUserdata' , 'assert_userdata' }, -- type assertions: assertNotIsXXX -> assert_not_is_xxx { 'assertNotIsNumber' , 'assert_not_is_number' }, { 'assertNotIsString' , 'assert_not_is_string' }, { 'assertNotIsTable' , 'assert_not_is_table' }, { 'assertNotIsBoolean' , 'assert_not_is_boolean' }, { 'assertNotIsNil' , 'assert_not_is_nil' }, { 'assertNotIsFunction' , 'assert_not_is_function' }, { 'assertNotIsThread' , 'assert_not_is_thread' }, { 'assertNotIsUserdata' , 'assert_not_is_userdata' }, -- type assertions: assertNotIsXXX -> assertNotXxx (luaunit v2 compat) { 'assertNotIsNumber' , 'assertNotNumber' }, { 'assertNotIsString' , 'assertNotString' }, { 'assertNotIsTable' , 'assertNotTable' }, { 'assertNotIsBoolean' , 'assertNotBoolean' }, { 'assertNotIsNil' , 'assertNotNil' }, { 'assertNotIsFunction' , 'assertNotFunction' }, { 'assertNotIsThread' , 'assertNotThread' }, { 'assertNotIsUserdata' , 'assertNotUserdata' }, -- type assertions: assertNotIsXXX -> assert_not_xxx { 'assertNotIsNumber' , 'assert_not_number' }, { 'assertNotIsString' , 'assert_not_string' }, { 'assertNotIsTable' , 'assert_not_table' }, { 'assertNotIsBoolean' , 'assert_not_boolean' }, { 'assertNotIsNil' , 'assert_not_nil' }, { 'assertNotIsFunction' , 'assert_not_function' }, { 'assertNotIsThread' , 'assert_not_thread' }, { 'assertNotIsUserdata' , 'assert_not_userdata' }, -- all assertions with Coroutine duplicate Thread assertions { 'assertIsThread' , 'assertIsCoroutine' }, { 'assertIsThread' , 'assertCoroutine' }, { 'assertIsThread' , 'assert_is_coroutine' }, { 'assertIsThread' , 'assert_coroutine' }, { 'assertNotIsThread' , 'assertNotIsCoroutine' }, { 'assertNotIsThread' , 'assertNotCoroutine' }, { 'assertNotIsThread' , 'assert_not_is_coroutine' }, { 'assertNotIsThread' , 'assert_not_coroutine' }, } -- Create all aliases in M for _,v in ipairs( list_of_funcs ) do funcname, alias = v[1], v[2] M[alias] = M[funcname] if EXPORT_ASSERT_TO_GLOBALS then _G[funcname] = M[funcname] _G[alias] = M[funcname] end end ---------------------------------------------------------------- -- -- Outputters -- ---------------------------------------------------------------- ---------------------------------------------------------------- -- class TapOutput ---------------------------------------------------------------- local TapOutput = { __class__ = 'TapOutput' } -- class local TapOutput_MT = { __index = TapOutput } -- metatable -- For a good reference for TAP format, check: http://testanything.org/tap-specification.html function TapOutput:new() return setmetatable( { verbosity = M.VERBOSITY_LOW }, TapOutput_MT) end function TapOutput:startSuite() print("1.."..self.result.testCount) print('# Started on '..self.result.startDate) end function TapOutput:startClass(className) if className ~= '[TestFunctions]' then print('# Starting class: '..className) end end function TapOutput:startTest(testName) end function TapOutput:addFailure( node ) io.stdout:write("not ok ", self.result.currentTestNumber, "\t", node.testName, "\n") if self.verbosity > M.VERBOSITY_LOW then print( prefixString( ' ', node.msg ) ) end if self.verbosity > M.VERBOSITY_DEFAULT then print( prefixString( ' ', node.stackTrace ) ) end end TapOutput.addError = TapOutput.addFailure function TapOutput:endTest( node ) if node:isPassed() then io.stdout:write("ok ", self.result.currentTestNumber, "\t", node.testName, "\n") end end function TapOutput:endClass() end function TapOutput:endSuite() print( '# '..M.LuaUnit.statusLine( self.result ) ) return self.result.notPassedCount end -- class TapOutput end ---------------------------------------------------------------- -- class JUnitOutput ---------------------------------------------------------------- -- See directory junitxml for more information about the junit format local JUnitOutput = { __class__ = 'JUnitOutput' } -- class local JUnitOutput_MT = { __index = JUnitOutput } -- metatable function JUnitOutput:new() return setmetatable( { testList = {}, verbosity = M.VERBOSITY_LOW }, JUnitOutput_MT) end function JUnitOutput:startSuite() -- open xml file early to deal with errors if self.fname == nil then error('With Junit, an output filename must be supplied with --name!') end if string.sub(self.fname,-4) ~= '.xml' then self.fname = self.fname..'.xml' end self.fd = io.open(self.fname, "w") if self.fd == nil then error("Could not open file for writing: "..self.fname) end print('# XML output to '..self.fname) print('# Started on '..self.result.startDate) end function JUnitOutput:startClass(className) if className ~= '[TestFunctions]' then print('# Starting class: '..className) end end function JUnitOutput:startTest(testName) print('# Starting test: '..testName) end function JUnitOutput:addFailure( node ) print('# Failure: ' .. node.msg) -- print('# ' .. node.stackTrace) end function JUnitOutput:addError( node ) print('# Error: ' .. node.msg) -- print('# ' .. node.stackTrace) end function JUnitOutput:endTest( node ) end function JUnitOutput:endClass() end function JUnitOutput:endSuite() print( '# '..M.LuaUnit.statusLine(self.result)) -- XML file writing self.fd:write('\n') self.fd:write('\n') self.fd:write(string.format( ' \n', self.result.runCount, self.result.startIsodate, self.result.duration, self.result.errorCount, self.result.failureCount )) self.fd:write(" \n") self.fd:write(string.format(' \n', _VERSION ) ) self.fd:write(string.format(' \n', M.VERSION) ) -- XXX please include system name and version if possible self.fd:write(" \n") for i,node in ipairs(self.result.tests) do self.fd:write(string.format(' \n', node.className, node.testName, node.duration ) ) if node:isNotPassed() then self.fd:write(node:statusXML()) end self.fd:write(' \n') end -- Next two lines are needed to validate junit ANT xsd, but really not useful in general: self.fd:write(' \n') self.fd:write(' \n') self.fd:write(' \n') self.fd:write('\n') self.fd:close() return self.result.notPassedCount end -- class TapOutput end ---------------------------------------------------------------- -- class TextOutput ---------------------------------------------------------------- --[[ -- Python Non verbose: For each test: . or F or E If some failed tests: ============== ERROR / FAILURE: TestName (testfile.testclass) --------- Stack trace then -------------- then "Ran x tests in 0.000s" then OK or FAILED (failures=1, error=1) -- Python Verbose: testname (filename.classname) ... ok testname (filename.classname) ... FAIL testname (filename.classname) ... ERROR then -------------- then "Ran x tests in 0.000s" then OK or FAILED (failures=1, error=1) -- Ruby: Started . Finished in 0.002695 seconds. 1 tests, 2 assertions, 0 failures, 0 errors -- Ruby: >> ruby tc_simple_number2.rb Loaded suite tc_simple_number2 Started F.. Finished in 0.038617 seconds. 1) Failure: test_failure(TestSimpleNumber) [tc_simple_number2.rb:16]: Adding doesn't work. <3> expected but was <4>. 3 tests, 4 assertions, 1 failures, 0 errors -- Java Junit .......F. Time: 0,003 There was 1 failure: 1) testCapacity(junit.samples.VectorTest)junit.framework.AssertionFailedError at junit.samples.VectorTest.testCapacity(VectorTest.java:87) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) FAILURES!!! Tests run: 8, Failures: 1, Errors: 0 -- Maven # mvn test ------------------------------------------------------- T E S T S ------------------------------------------------------- Running math.AdditionTest Tests run: 2, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 0.03 sec <<< FAILURE! Results : Failed tests: testLireSymbole(math.AdditionTest) Tests run: 2, Failures: 1, Errors: 0, Skipped: 0 -- LuaUnit ---- non verbose * display . or F or E when running tests ---- verbose * display test name + ok/fail ---- * blank line * number) ERROR or FAILURE: TestName Stack trace * blank line * number) ERROR or FAILURE: TestName Stack trace then -------------- then "Ran x tests in 0.000s (%d not selected, %d skipped)" then OK or FAILED (failures=1, error=1) ]] local TextOutput = { __class__ = 'TextOutput' } -- class local TextOutput_MT = { __index = TextOutput } -- metatable function TextOutput:new() return setmetatable( { errorList = {}, verbosity = M.VERBOSITY_DEFAULT }, TextOutput_MT ) end function TextOutput:startSuite() if self.verbosity > M.VERBOSITY_DEFAULT then print( 'Started on '.. self.result.startDate ) end end function TextOutput:startClass(className) -- display nothing when starting a new class end function TextOutput:startTest(testName) if self.verbosity > M.VERBOSITY_DEFAULT then io.stdout:write( " ", self.result.currentNode.testName, " ... " ) end end function TextOutput:addFailure( node ) -- nothing end function TextOutput:addError( node ) -- nothing end function TextOutput:endTest( node ) if node:isPassed() then if self.verbosity > M.VERBOSITY_DEFAULT then io.stdout:write("Ok\n") else io.stdout:write(".") end else if self.verbosity > M.VERBOSITY_DEFAULT then print( node.status ) print( node.msg ) --[[ -- find out when to do this: if self.verbosity > M.VERBOSITY_DEFAULT then print( node.stackTrace ) end ]] else -- write only the first character of status io.stdout:write(string.sub(node.status, 1, 1)) end end end function TextOutput:endClass() -- nothing end function TextOutput:displayOneFailedTest( index, failure ) print(index..") "..failure.testName ) print( failure.msg ) print( failure.stackTrace ) print() end function TextOutput:displayFailedTests() if self.result.notPassedCount == 0 then return end print("Failed tests:") print("-------------") for i,v in ipairs(self.result.notPassed) do self:displayOneFailedTest( i, v ) end end function TextOutput:endSuite() if self.verbosity > M.VERBOSITY_DEFAULT then print("=========================================================") else print() end self:displayFailedTests() print( M.LuaUnit.statusLine( self.result ) ) local ignoredString = "" if self.result.notPassedCount == 0 then print('OK') end end -- class TextOutput end ---------------------------------------------------------------- -- class NilOutput ---------------------------------------------------------------- local function nopCallable() --print(42) return nopCallable end local NilOutput = { __class__ = 'NilOuptut' } -- class local NilOutput_MT = { __index = nopCallable } -- metatable function NilOutput:new() return setmetatable( { __class__ = 'NilOutput' }, NilOutput_MT ) end ---------------------------------------------------------------- -- -- class LuaUnit -- ---------------------------------------------------------------- M.LuaUnit = { outputType = TextOutput, verbosity = M.VERBOSITY_DEFAULT, __class__ = 'LuaUnit' } local LuaUnit_MT = { __index = M.LuaUnit } if EXPORT_ASSERT_TO_GLOBALS then LuaUnit = M.LuaUnit end function M.LuaUnit:new() return setmetatable( {}, LuaUnit_MT ) end -----------------[[ Utility methods ]]--------------------- function M.LuaUnit.asFunction(aObject) -- return "aObject" if it is a function, and nil otherwise if 'function' == type(aObject) then return aObject end end function M.LuaUnit.isClassMethod(aName) -- return true if aName contains a class + a method name in the form class:method return string.find(aName, '.', nil, true) ~= nil end function M.LuaUnit.splitClassMethod(someName) -- return a pair className, methodName for a name in the form class:method -- return nil if not a class + method name -- name is class + method local hasMethod, methodName, className hasMethod = string.find(someName, '.', nil, true ) if not hasMethod then return nil end methodName = string.sub(someName, hasMethod+1) className = string.sub(someName,1,hasMethod-1) return className, methodName end function M.LuaUnit.isMethodTestName( s ) -- return true is the name matches the name of a test method -- default rule is that is starts with 'Test' or with 'test' return string.sub(s, 1, 4):lower() == 'test' end function M.LuaUnit.isTestName( s ) -- return true is the name matches the name of a test -- default rule is that is starts with 'Test' or with 'test' return string.sub(s, 1, 4):lower() == 'test' end function M.LuaUnit.collectTests() -- return a list of all test names in the global namespace -- that match LuaUnit.isTestName local testNames = {} for k, v in pairs(_G) do if M.LuaUnit.isTestName( k ) then table.insert( testNames , k ) end end table.sort( testNames ) return testNames end function M.LuaUnit.parseCmdLine( cmdLine ) -- parse the command line -- Supported command line parameters: -- --verbose, -v: increase verbosity -- --quiet, -q: silence output -- --error, -e: treat errors as fatal (quit program) -- --output, -o, + name: select output type -- --pattern, -p, + pattern: run test matching pattern, may be repeated -- --name, -n, + fname: name of output file for junit, default to stdout -- [testnames, ...]: run selected test names -- -- Returns a table with the following fields: -- verbosity: nil, M.VERBOSITY_DEFAULT, M.VERBOSITY_QUIET, M.VERBOSITY_VERBOSE -- output: nil, 'tap', 'junit', 'text', 'nil' -- testNames: nil or a list of test names to run -- pattern: nil or a list of patterns local result = {} local state = nil local SET_OUTPUT = 1 local SET_PATTERN = 2 local SET_FNAME = 3 if cmdLine == nil then return result end local function parseOption( option ) if option == '--help' or option == '-h' then result['help'] = true return elseif option == '--version' then result['version'] = true return elseif option == '--verbose' or option == '-v' then result['verbosity'] = M.VERBOSITY_VERBOSE return elseif option == '--quiet' or option == '-q' then result['verbosity'] = M.VERBOSITY_QUIET return elseif option == '--error' or option == '-e' then result['quitOnError'] = true return elseif option == '--failure' or option == '-f' then result['quitOnFailure'] = true return elseif option == '--output' or option == '-o' then state = SET_OUTPUT return state elseif option == '--name' or option == '-n' then state = SET_FNAME return state elseif option == '--pattern' or option == '-p' then state = SET_PATTERN return state end error('Unknown option: '..option,3) end local function setArg( cmdArg, state ) if state == SET_OUTPUT then result['output'] = cmdArg return elseif state == SET_FNAME then result['fname'] = cmdArg return elseif state == SET_PATTERN then if result['pattern'] then table.insert( result['pattern'], cmdArg ) else result['pattern'] = { cmdArg } end return end error('Unknown parse state: '.. state) end for i, cmdArg in ipairs(cmdLine) do if state ~= nil then setArg( cmdArg, state, result ) state = nil else if cmdArg:sub(1,1) == '-' then state = parseOption( cmdArg ) else if result['testNames'] then table.insert( result['testNames'], cmdArg ) else result['testNames'] = { cmdArg } end end end end if result['help'] then M.LuaUnit.help() end if result['version'] then M.LuaUnit.version() end if state ~= nil then error('Missing argument after '..cmdLine[ #cmdLine ],2 ) end return result end function M.LuaUnit.help() print(M.USAGE) os.exit(0) end function M.LuaUnit.version() print('LuaUnit v'..M.VERSION..' by Philippe Fremy ') os.exit(0) end function M.LuaUnit.patternInclude( patternFilter, expr ) -- check if any of patternFilter is contained in expr. If so, return true. -- return false if None of the patterns are contained in expr -- if patternFilter is nil, return true (no filtering) if patternFilter == nil then return true end for i,pattern in ipairs(patternFilter) do if string.find(expr, pattern) then return true end end return false end ---------------------------------------------------------------- -- class NodeStatus ---------------------------------------------------------------- local NodeStatus = { __class__ = 'NodeStatus' } -- class local NodeStatus_MT = { __index = NodeStatus } -- metatable M.NodeStatus = NodeStatus -- values of status NodeStatus.PASS = 'PASS' NodeStatus.FAIL = 'FAIL' NodeStatus.ERROR = 'ERROR' function NodeStatus:new( number, testName, className ) local t = { number = number, testName = testName, className = className } setmetatable( t, NodeStatus_MT ) t:pass() return t end function NodeStatus:pass() self.status = self.PASS -- useless but we know it's the field we want to use self.msg = nil self.stackTrace = nil end function NodeStatus:fail(msg, stackTrace) self.status = self.FAIL self.msg = msg self.stackTrace = stackTrace end function NodeStatus:error(msg, stackTrace) self.status = self.ERROR self.msg = msg self.stackTrace = stackTrace end function NodeStatus:isPassed() return self.status == NodeStatus.PASS end function NodeStatus:isNotPassed() -- print('hasFailure: '..prettystr(self)) return self.status ~= NodeStatus.PASS end function NodeStatus:isFailure() return self.status == NodeStatus.FAIL end function NodeStatus:isError() return self.status == NodeStatus.ERROR end function NodeStatus:statusXML() if self:isError() then return table.concat( {' \n', ' \n'}) elseif self:isFailure() then return table.concat( {' \n', ' \n'}) end return ' \n' -- (not XSD-compliant! normally shouldn't get here) end --------------[[ Output methods ]]------------------------- function M.LuaUnit.statusLine(result) -- return status line string according to results local s = string.format('Ran %d tests in %0.3f seconds, %d successes', result.runCount, result.duration, result.passedCount ) if result.notPassedCount > 0 then if result.failureCount > 0 then s = s..string.format(', %d failures', result.failureCount ) end if result.errorCount > 0 then s = s..string.format(', %d errors', result.errorCount ) end else s = s..', 0 failures' end if result.nonSelectedCount > 0 then s = s..string.format(", %d non-selected", result.nonSelectedCount ) end return s end function M.LuaUnit:startSuite(testCount, nonSelectedCount) self.result = {} self.result.testCount = testCount self.result.nonSelectedCount = nonSelectedCount self.result.passedCount = 0 self.result.runCount = 0 self.result.currentTestNumber = 0 self.result.currentClassName = "" self.result.currentNode = nil self.result.suiteStarted = true self.result.startTime = os.clock() self.result.startDate = os.date(os.getenv('LUAUNIT_DATEFMT')) self.result.startIsodate = os.date('%Y-%m-%dT%H:%M:%S') self.result.patternFilter = self.patternFilter self.result.tests = {} self.result.failures = {} self.result.errors = {} self.result.notPassed = {} self.outputType = self.outputType or TextOutput self.output = self.outputType:new() self.output.runner = self self.output.result = self.result self.output.verbosity = self.verbosity self.output.fname = self.fname self.output:startSuite() end function M.LuaUnit:startClass( className ) self.result.currentClassName = className self.output:startClass( className ) end function M.LuaUnit:startTest( testName ) self.result.currentTestNumber = self.result.currentTestNumber + 1 self.result.runCount = self.result.runCount + 1 self.result.currentNode = NodeStatus:new( self.result.currentTestNumber, testName, self.result.currentClassName ) self.result.currentNode.startTime = os.clock() table.insert( self.result.tests, self.result.currentNode ) self.output:startTest( testName ) end function M.LuaUnit:addStatus( err ) -- "err" is expected to be a table / result from protectedCall() if err.status == NodeStatus.PASS then return end local node = self.result.currentNode --[[ As a first approach, we will report only one error or one failure for one test. However, we can have the case where the test is in failure, and the teardown is in error. In such case, it's a good idea to report both a failure and an error in the test suite. This is what Python unittest does for example. However, it mixes up counts so need to be handled carefully: for example, there could be more (failures + errors) count that tests. What happens to the current node ? We will do this more intelligent version later. ]] -- if the node is already in failure/error, just don't report the new error (see above) if node.status ~= NodeStatus.PASS then return end table.insert( self.result.notPassed, node ) if err.status == NodeStatus.FAIL then node:fail( err.msg, err.trace ) table.insert( self.result.failures, node ) self.output:addFailure( node ) elseif err.status == NodeStatus.ERROR then node:error( err.msg, err.trace ) table.insert( self.result.errors, node ) self.output:addError( node ) end end function M.LuaUnit:endTest() local node = self.result.currentNode -- print( 'endTest() '..prettystr(node)) -- print( 'endTest() '..prettystr(node:isNotPassed())) node.duration = os.clock() - node.startTime node.startTime = nil self.output:endTest( node ) if node:isPassed() then self.result.passedCount = self.result.passedCount + 1 elseif node:isError() then if self.quitOnError or self.quitOnFailure then -- Runtime error - abort test execution as requested by -- "--error" option. This is done by setting a special -- flag that gets handled in runSuiteByInstances(). print("\nERROR during LuaUnit test execution:\n" .. node.msg) self.result.aborted = true end elseif node:isFailure() then if self.quitOnFailure then -- Failure - abort test execution as requested by -- "--failure" option. This is done by setting a special -- flag that gets handled in runSuiteByInstances(). print("\nFailure during LuaUnit test execution:\n" .. node.msg) self.result.aborted = true end end self.result.currentNode = nil end function M.LuaUnit:endClass() self.output:endClass() end function M.LuaUnit:endSuite() if self.result.suiteStarted == false then error('LuaUnit:endSuite() -- suite was already ended' ) end self.result.duration = os.clock()-self.result.startTime self.result.suiteStarted = false -- Expose test counts for outputter's endSuite(). This could be managed -- internally instead, but unit tests (and existing use cases) might -- rely on these fields being present. self.result.notPassedCount = #self.result.notPassed self.result.failureCount = #self.result.failures self.result.errorCount = #self.result.errors self.output:endSuite() end function M.LuaUnit:setOutputType(outputType) -- default to text -- tap produces results according to TAP format if outputType:upper() == "NIL" then self.outputType = NilOutput return end if outputType:upper() == "TAP" then self.outputType = TapOutput return end if outputType:upper() == "JUNIT" then self.outputType = JUnitOutput return end if outputType:upper() == "TEXT" then self.outputType = TextOutput return end error( 'No such format: '..outputType,2) end --------------[[ Runner ]]----------------- function M.LuaUnit:protectedCall(classInstance, methodInstance, prettyFuncName) -- if classInstance is nil, this is just a function call -- else, it's method of a class being called. local function err_handler(e) -- transform error into a table, adding the traceback information return { status = NodeStatus.ERROR, msg = e, trace = string.sub(debug.traceback("", 3), 2) } end local ok, err if classInstance then -- stupid Lua < 5.2 does not allow xpcall with arguments so let's use a workaround ok, err = xpcall( function () methodInstance(classInstance) end, err_handler ) else ok, err = xpcall( function () methodInstance() end, err_handler ) end if ok then return {status = NodeStatus.PASS} end -- determine if the error was a failed test: -- We do this by stripping the failure prefix from the error message, -- while keeping track of the gsub() count. A non-zero value -> failure local failed err.msg, failed = err.msg:gsub(M.FAILURE_PREFIX, "", 1) if failed > 0 then err.status = NodeStatus.FAIL end -- reformat / improve the stack trace if prettyFuncName then -- we do have the real method name err.trace = err.trace:gsub("in (%a+) 'methodInstance'", "in %1 '"..prettyFuncName.."'") end if STRIP_LUAUNIT_FROM_STACKTRACE then err.trace = stripLuaunitTrace(err.trace) end return err -- return the error "object" (table) end function M.LuaUnit:execOneFunction(className, methodName, classInstance, methodInstance) -- When executing a test function, className and classInstance must be nil -- When executing a class method, all parameters must be set if type(methodInstance) ~= 'function' then error( tostring(methodName)..' must be a function, not '..type(methodInstance)) end local prettyFuncName if className == nil then className = '[TestFunctions]' prettyFuncName = methodName else prettyFuncName = className..'.'..methodName end if self.lastClassName ~= className then if self.lastClassName ~= nil then self:endClass() end self:startClass( className ) self.lastClassName = className end self:startTest(prettyFuncName) -- run setUp first (if any) if classInstance then local func = self.asFunction( classInstance.setUp ) or self.asFunction( classInstance.Setup ) or self.asFunction( classInstance.setup ) or self.asFunction( classInstance.SetUp ) if func then self:addStatus(self:protectedCall(classInstance, func, className..'.setUp')) end end -- run testMethod() if self.result.currentNode:isPassed() then self:addStatus(self:protectedCall(classInstance, methodInstance, prettyFuncName)) end -- lastly, run tearDown (if any) if classInstance then local func = self.asFunction( classInstance.tearDown ) or self.asFunction( classInstance.TearDown ) or self.asFunction( classInstance.teardown ) or self.asFunction( classInstance.Teardown ) if func then self:addStatus(self:protectedCall(classInstance, func, className..'.tearDown')) end end self:endTest() end function M.LuaUnit.expandOneClass( result, className, classInstance ) -- add all test methods of classInstance to result for methodName, methodInstance in sortedPairs(classInstance) do if M.LuaUnit.asFunction(methodInstance) and M.LuaUnit.isMethodTestName( methodName ) then table.insert( result, { className..'.'..methodName, classInstance } ) end end end function M.LuaUnit.expandClasses( listOfNameAndInst ) -- expand all classes (provided as {className, classInstance}) to a list of {className.methodName, classInstance} -- functions and methods remain untouched local result = {} for i,v in ipairs( listOfNameAndInst ) do local name, instance = v[1], v[2] if M.LuaUnit.asFunction(instance) then table.insert( result, { name, instance } ) else if type(instance) ~= 'table' then error( 'Instance must be a table or a function, not a '..type(instance)..', value '..prettystr(instance)) end if M.LuaUnit.isClassMethod( name ) then local className, methodName = M.LuaUnit.splitClassMethod( name ) local methodInstance = instance[methodName] if methodInstance == nil then error( "Could not find method in class "..tostring(className).." for method "..tostring(methodName) ) end table.insert( result, { name, instance } ) else M.LuaUnit.expandOneClass( result, name, instance ) end end end return result end function M.LuaUnit.applyPatternFilter( patternFilter, listOfNameAndInst ) local included, excluded = {}, {} for i, v in ipairs( listOfNameAndInst ) do -- local name, instance = v[1], v[2] if M.LuaUnit.patternInclude( patternFilter, v[1] ) then table.insert( included, v ) else table.insert( excluded, v ) end end return included, excluded end function M.LuaUnit:runSuiteByInstances( listOfNameAndInst ) -- Run an explicit list of tests. All test instances and names must be supplied. -- each test must be one of: -- * { function name, function instance } -- * { class name, class instance } -- * { class.method name, class instance } local expandedList, filteredList, filteredOutList, className, methodName, methodInstance expandedList = self.expandClasses( listOfNameAndInst ) filteredList, filteredOutList = self.applyPatternFilter( self.patternFilter, expandedList ) self:startSuite( #filteredList, #filteredOutList ) for i,v in ipairs( filteredList ) do local name, instance = v[1], v[2] if M.LuaUnit.asFunction(instance) then self:execOneFunction( nil, name, nil, instance ) else if type(instance) ~= 'table' then error( 'Instance must be a table or a function, not a '..type(instance)..', value '..prettystr(instance)) else assert( M.LuaUnit.isClassMethod( name ) ) className, methodName = M.LuaUnit.splitClassMethod( name ) methodInstance = instance[methodName] if methodInstance == nil then error( "Could not find method in class "..tostring(className).." for method "..tostring(methodName) ) end self:execOneFunction( className, methodName, instance, methodInstance ) end end if self.result.aborted then break end -- "--error" or "--failure" option triggered end if self.lastClassName ~= nil then self:endClass() end self:endSuite() if self.result.aborted then print("LuaUnit ABORTED (as requested by --error or --failure option)") os.exit(-2) end end function M.LuaUnit:runSuiteByNames( listOfName ) -- Run an explicit list of test names local className, methodName, instanceName, instance, methodInstance local listOfNameAndInst = {} for i,name in ipairs( listOfName ) do if M.LuaUnit.isClassMethod( name ) then className, methodName = M.LuaUnit.splitClassMethod( name ) instanceName = className instance = _G[instanceName] if instance == nil then error( "No such name in global space: "..instanceName ) end if type(instance) ~= 'table' then error( 'Instance of '..instanceName..' must be a table, not '..type(instance)) end methodInstance = instance[methodName] if methodInstance == nil then error( "Could not find method in class "..tostring(className).." for method "..tostring(methodName) ) end else -- for functions and classes instanceName = name instance = _G[instanceName] end if instance == nil then error( "No such name in global space: "..instanceName ) end if (type(instance) ~= 'table' and type(instance) ~= 'function') then error( 'Name must match a function or a table: '..instanceName ) end table.insert( listOfNameAndInst, { name, instance } ) end self:runSuiteByInstances( listOfNameAndInst ) end function M.LuaUnit.run(...) -- Run some specific test classes. -- If no arguments are passed, run the class names specified on the -- command line. If no class name is specified on the command line -- run all classes whose name starts with 'Test' -- -- If arguments are passed, they must be strings of the class names -- that you want to run or generic command line arguments (-o, -p, -v, ...) local runner = M.LuaUnit.new() return runner:runSuite(...) end function M.LuaUnit:runSuite( ... ) local args = {...} if type(args[1]) == 'table' and args[1].__class__ == 'LuaUnit' then -- run was called with the syntax M.LuaUnit:runSuite() -- we support both M.LuaUnit.run() and M.LuaUnit:run() -- strip out the first argument table.remove(args,1) end if #args == 0 then args = cmdline_argv end local no_error, val = pcall( M.LuaUnit.parseCmdLine, args ) if not no_error then print(val) -- error message print() print(M.USAGE) os.exit(-1) end local options = val -- We expect these option fields to be either `nil` or contain -- valid values, so it's safe to always copy them directly. self.verbosity = options.verbosity self.quitOnError = options.quitOnError self.quitOnFailure = options.quitOnFailure self.fname = options.fname self.patternFilter = options.pattern if options.output and options.output:lower() == 'junit' and options.fname == nil then print('With junit output, a filename must be supplied with -n or --name') os.exit(-1) end if options.output then no_error, val = pcall(self.setOutputType, self, options.output) if not no_error then print(val) -- error message print() print(M.USAGE) os.exit(-1) end end self:runSuiteByNames( options.testNames or M.LuaUnit.collectTests() ) return self.result.notPassedCount end -- class LuaUnit -- For compatbility with LuaUnit v2 M.run = M.LuaUnit.run M.Run = M.LuaUnit.run function M:setVerbosity( verbosity ) M.LuaUnit.verbosity = verbosity end M.set_verbosity = M.setVerbosity M.SetVerbosity = M.setVerbosity return M bpfcc-0.12.0/tests/lua/spec000077700000000000000000000000001357404205000204362src/lua/bpf/specustar00rootroot00000000000000bpfcc-0.12.0/tests/lua/test_clang.lua000066400000000000000000000200101357404205000174110ustar00rootroot00000000000000local suite = require("test_helper") local TestClang = {} function TestClang:test_probe_read1() local text = [[ #include #include int count_sched(struct pt_regs *ctx, struct task_struct *prev) { pid_t p = prev->pid; return (p != -1); } ]] local b = BPF:new{text=text, debug=0} local fn = b:load_func("count_sched", 'BPF_PROG_TYPE_KPROBE') end function TestClang:test_probe_read2() local text = [[ #include #include int count_foo(struct pt_regs *ctx, unsigned long a, unsigned long b) { return (a != b); } ]] local b = BPF:new{text=text, debug=0} local fn = b:load_func("count_foo", 'BPF_PROG_TYPE_KPROBE') end function TestClang:test_probe_read_keys() local text = [[ #include #include BPF_HASH(start, struct request *); int do_request(struct pt_regs *ctx, struct request *req) { u64 ts = bpf_ktime_get_ns(); start.update(&req, &ts); return 0; } int do_completion(struct pt_regs *ctx, struct request *req) { u64 *tsp = start.lookup(&req); if (tsp != 0) { start.delete(&req); } return 0; } ]] local b = BPF:new{text=text, debug=0} local fns = b:load_funcs('BPF_PROG_TYPE_KPROBE') end function TestClang:test_sscanf() local text = [[ BPF_HASH(stats, int, struct { u64 a; u64 b; u32 c:18; u32 d:14; struct { u32 a; u32 b; } s; }, 10); int foo(void *ctx) { return 0; } ]] local b = BPF:new{text=text, debug=0} local fn = b:load_func("foo", 'BPF_PROG_TYPE_KPROBE') local t = b:get_table("stats") local s1 = t:key_sprintf(2) assert_equals(s1, "0x2") local s2 = t:leaf_sprintf({{2, 3, 4, 1, {5, 6}}}) local l = t:leaf_scanf(s2) assert_equals(tonumber(l.a), 2) assert_equals(tonumber(l.b), 3) assert_equals(tonumber(l.c), 4) assert_equals(tonumber(l.d), 1) assert_equals(tonumber(l.s.a), 5) assert_equals(tonumber(l.s.b), 6) end function TestClang:test_sscanf_array() local text = [[ BPF_HASH(stats, int, struct { u32 a[3]; u32 b; }, 10); ]] local b = BPF:new{text=text, debug=0} local t = b:get_table("stats") local s1 = t:key_sprintf(2) assert_equals(s1, "0x2") local s2 = t:leaf_sprintf({{{1, 2, 3}, 4}}) assert_equals(s2, "{ [ 0x1 0x2 0x3 ] 0x4 }") local l = t:leaf_scanf(s2) assert_equals(l.a[0], 1) assert_equals(l.a[1], 2) assert_equals(l.a[2], 3) assert_equals(l.b, 4) end function TestClang:test_iosnoop() local text = [[ #include #include struct key_t { struct request *req; }; BPF_HASH(start, struct key_t, u64, 1024); int do_request(struct pt_regs *ctx, struct request *req) { struct key_t key = {}; bpf_trace_printk("traced start %d\\n", req->__data_len); return 0; } ]] local b = BPF:new{text=text, debug=0} local fn = b:load_func("do_request", 'BPF_PROG_TYPE_KPROBE') end function TestClang:test_blk_start_request() local text = [[ #include #include int do_request(struct pt_regs *ctx, int req) { bpf_trace_printk("req ptr: 0x%x\n", req); return 0; } ]] local b = BPF:new{text=text, debug=0} local fn = b:load_func("do_request", 'BPF_PROG_TYPE_KPROBE') end function TestClang:test_bpf_hash() local text = [[ BPF_HASH(table1); BPF_HASH(table2, u32); BPF_HASH(table3, u32, int); ]] local b = BPF:new{text=text, debug=0} end function TestClang:test_consecutive_probe_read() local text = [[ #include #include BPF_HASH(table1, struct super_block *); int trace_entry(struct pt_regs *ctx, struct file *file) { if (!file) return 0; struct vfsmount *mnt = file->f_path.mnt; if (mnt) { struct super_block *k = mnt->mnt_sb; u64 zero = 0; table1.update(&k, &zero); k = mnt->mnt_sb; table1.update(&k, &zero); } return 0; } ]] local b = BPF:new{text=text, debug=0} local fn = b:load_func("trace_entry", 'BPF_PROG_TYPE_KPROBE') end function TestClang:test_nested_probe_read() local text = [[ #include int trace_entry(struct pt_regs *ctx, struct file *file) { if (!file) return 0; const char *name = file->f_path.dentry->d_name.name; bpf_trace_printk("%s\\n", name); return 0; } ]] local b = BPF:new{text=text, debug=0} local fn = b:load_func("trace_entry", 'BPF_PROG_TYPE_KPROBE') end function TestClang:test_char_array_probe() local b = BPF:new{text=[[#include int kprobe__blk_update_request(struct pt_regs *ctx, struct request *req) { bpf_trace_printk("%s\\n", req->rq_disk->disk_name); return 0; }]]} end function TestClang:test_probe_read_helper() local b = BPF:new{text=[[ #include static void print_file_name(struct file *file) { if (!file) return; const char *name = file->f_path.dentry->d_name.name; bpf_trace_printk("%s\\n", name); } static void print_file_name2(int unused, struct file *file) { print_file_name(file); } int trace_entry1(struct pt_regs *ctx, struct file *file) { print_file_name(file); return 0; } int trace_entry2(struct pt_regs *ctx, int unused, struct file *file) { print_file_name2(unused, file); return 0; } ]]} local fn1 = b:load_func("trace_entry1", 'BPF_PROG_TYPE_KPROBE') local fn2 = b:load_func("trace_entry2", 'BPF_PROG_TYPE_KPROBE') end function TestClang:test_probe_struct_assign() local b = BPF:new{text = [[ #include struct args_t { const char *filename; int flags; int mode; }; int kprobe__sys_open(struct pt_regs *ctx, const char *filename, int flags, int mode) { struct args_t args = {}; args.filename = filename; args.flags = flags; args.mode = mode; bpf_trace_printk("%s\\n", args.filename); return 0; }; ]]} end function TestClang:test_task_switch() local b = BPF:new{text=[[ #include #include struct key_t { u32 prev_pid; u32 curr_pid; }; BPF_HASH(stats, struct key_t, u64, 1024); int kprobe__finish_task_switch(struct pt_regs *ctx, struct task_struct *prev) { struct key_t key = {}; u64 zero = 0, *val; key.curr_pid = bpf_get_current_pid_tgid(); key.prev_pid = prev->pid; val = stats.lookup_or_try_init(&key, &zero); if (val) { (*val)++; } return 0; } ]]} end function TestClang:test_probe_simple_assign() local b = BPF:new{text=[[ #include #include struct leaf { size_t size; }; BPF_HASH(simple_map, u32, struct leaf); int kprobe____kmalloc(struct pt_regs *ctx, size_t size) { u32 pid = bpf_get_current_pid_tgid(); struct leaf* leaf = simple_map.lookup(&pid); if (leaf) leaf->size += size; return 0; }]]} end function TestClang:test_unop_probe_read() local text = [[ #include int trace_entry(struct pt_regs *ctx, struct request *req) { if (!(req->bio->bi_flags & 1)) return 1; if (((req->bio->bi_flags))) return 1; return 0; } ]] local b = BPF:new{text=text} local fn = b:load_func("trace_entry", 'BPF_PROG_TYPE_KPROBE') end function TestClang:test_complex_leaf_types() local text = [[ struct list; struct list { struct list *selfp; struct list *another_selfp; struct list *selfp_array[2]; }; struct empty { }; union emptyu { struct empty *em1; struct empty em2; struct empty em3; struct empty em4; }; BPF_ARRAY(t1, struct list, 1); BPF_ARRAY(t2, struct list *, 1); BPF_ARRAY(t3, union emptyu, 1); ]] local b = BPF:new{text=text} local ffi = require("ffi") -- TODO: ptrs? assert_equals(ffi.sizeof(b:get_table("t3").c_leaf), 8) end function TestClang:test_cflags() local text = [[ #ifndef MYFLAG #error "MYFLAG not set as expected" #endif ]] local b = BPF:new{text=text, cflags={"-DMYFLAG"}} end function TestClang:test_exported_maps() local b1 = BPF{text=[[BPF_TABLE_PUBLIC("hash", int, int, table1, 10);]]} local b2 = BPF{text=[[BPF_TABLE("extern", int, int, table1, 10);]]} end function TestClang:test_syntax_error() assert_error_msg_contains( "failed to compile BPF module", BPF.new, BPF, {text=[[int failure(void *ctx) { if (); return 0; }]]}) end suite("TestClang", TestClang) bpfcc-0.12.0/tests/lua/test_dump.lua000066400000000000000000000004661357404205000173070ustar00rootroot00000000000000local suite = require("test_helper") local TestDump = {} function TestDump:test_dump_func() local raw = "\xb7\x00\x00\x00\x01\x00\x00\x00\x95\x00\x00\x00\x00\x00\x00\x00" local b = BPF:new{text=[[int entry(void) { return 1; }]]} assert_equals(b:dump_func("entry"), raw) end suite("TestDump", TestDump) bpfcc-0.12.0/tests/lua/test_helper.lua000066400000000000000000000010231357404205000176070ustar00rootroot00000000000000function setup_path() local str = require("debug").getinfo(2, "S").source:sub(2) local cwd = str:match("(.*/)") local bpf_path = cwd.."/../../src/lua/?.lua;" local test_path = cwd.."/?.lua;" package.path = bpf_path..test_path..package.path end setup_path() USE_EXPECTED_ACTUAL_IN_ASSERT_EQUALS = false EXPORT_ASSERT_TO_GLOBALS = true require("luaunit") rawset(_G, "BCC", require("bcc.init")) rawset(_G, "BPF", BCC.BPF) log.enabled = false return function (name, f) rawset(_G, name, f) os.exit(LuaUnit.run()) end bpfcc-0.12.0/tests/lua/test_standalone.sh000077500000000000000000000012301357404205000203140ustar00rootroot00000000000000#!/bin/bash # Copyright (c) GitHub, Inc. # Licensed under the Apache License, Version 2.0 (the "License") set -xe cd "src/lua" function fail { echo "test failed: $1" >&2 exit 1 } if [[ ! -x bcc-lua ]]; then echo "bcc-lua not built --- skipping" exit 0 fi LIBRARY=$(ldd bcc-lua | grep luajit) if [ $? -ne 0 -o -z "$LIBRARY" ] ; then fail "bcc-lua depends on libluajit" fi rm -f probe.lua echo "return function(BPF) print(\"Hello world\") end" > probe.lua PROBE="../../../examples/lua/offcputime.lua" if ! sudo ./bcc-lua "$PROBE" -d 1 >/dev/null 2>/dev/null; then fail "bcc-lua cannot run complex probes" fi rm -f libbcc.so probe.lua bpfcc-0.12.0/tests/lua/test_uprobes.lua000066400000000000000000000031551357404205000200170ustar00rootroot00000000000000local suite = require("test_helper") local ffi = require("ffi") local TestUprobes = {} ffi.cdef[[ int getpid(void); void malloc_stats(void); ]] function TestUprobes:test_simple_library() local text = [[ #include BPF_ARRAY(stats, u64, 1); static void incr(int idx) { u64 *ptr = stats.lookup(&idx); if (ptr) ++(*ptr); } int count(struct pt_regs *ctx) { u32 pid = bpf_get_current_pid_tgid(); if (pid == PID) incr(0); return 0; }]] local pid = tonumber(ffi.C.getpid()) local text = text:gsub("PID", tostring(pid)) local b = BPF:new{text=text} b:attach_uprobe{name="c", sym="malloc_stats", fn_name="count", pid=pid} b:attach_uprobe{name="c", sym="malloc_stats", fn_name="count", pid=pid, retprobe=true} assert_equals(BPF.num_open_uprobes(), 2) ffi.C.malloc_stats() local stats = b:get_table("stats") assert_equals(tonumber(stats:get(0)), 2) end function TestUprobes:test_simple_binary() local text = [[ #include BPF_ARRAY(stats, u64, 1); static void incr(int idx) { u64 *ptr = stats.lookup(&idx); if (ptr) ++(*ptr); } int count(struct pt_regs *ctx) { u32 pid = bpf_get_current_pid_tgid(); incr(0); return 0; }]] local b = BPF:new{text=text} b:attach_uprobe{name="/usr/bin/python", sym="main", fn_name="count"} b:attach_uprobe{name="/usr/bin/python", sym="main", fn_name="count", retprobe=true} os.spawn("/usr/bin/python -V") local stats = b:get_table("stats") assert_true(tonumber(stats:get(0)) >= 2) end function TestUprobes:teardown() BPF.cleanup() end suite("TestUprobes", TestUprobes) bpfcc-0.12.0/tests/python/000077500000000000000000000000001357404205000153325ustar00rootroot00000000000000bpfcc-0.12.0/tests/python/CMakeLists.txt000066400000000000000000000144611357404205000201000ustar00rootroot00000000000000# Copyright (c) PLUMgrid, Inc. # Licensed under the Apache License, Version 2.0 (the "License") find_program(ARPING arping) if(ARPING STREQUAL "ARPING-NOTFOUND") message(WARNING "Recommended test program 'arping' not found") endif() find_program(NETPERF netperf) if(NETPERF STREQUAL "NETPERF-NOTFOUND") message(WARNING "Recommended test program 'netperf' not found") endif() find_program(IPERF iperf) if(IPERF STREQUAL "IPERF-NOTFOUND") find_program(IPERF3 iperf3) if(IPERF3 STREQUAL "IPERF3-NOTFOUND") message(WARNING "Recommended test program 'iperf' or 'iperf3' not found") endif() endif() add_test(NAME py_test_stat1_b WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND ${TEST_WRAPPER} py_stat1_b namespace ${CMAKE_CURRENT_SOURCE_DIR}/test_stat1.py test_stat1.b proto.b) add_test(NAME py_test_bpf_log WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND ${TEST_WRAPPER} py_bpf_prog sudo ${CMAKE_CURRENT_SOURCE_DIR}/test_bpf_log.py) add_test(NAME py_test_stat1_c WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND ${TEST_WRAPPER} py_stat1_c namespace ${CMAKE_CURRENT_SOURCE_DIR}/test_stat1.py test_stat1.c) #add_test(NAME py_test_xlate1_b WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} # COMMAND ${TEST_WRAPPER} py_xlate1_b namespace ${CMAKE_CURRENT_SOURCE_DIR}/test_xlate1.py test_xlate1.b proto.b) add_test(NAME py_test_xlate1_c WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND ${TEST_WRAPPER} py_xlate1_c namespace ${CMAKE_CURRENT_SOURCE_DIR}/test_xlate1.py test_xlate1.c) add_test(NAME py_test_call1 WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND ${TEST_WRAPPER} py_call1_c namespace ${CMAKE_CURRENT_SOURCE_DIR}/test_call1.py test_call1.c) add_test(NAME py_test_trace1 WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND ${TEST_WRAPPER} py_trace1 sudo ${CMAKE_CURRENT_SOURCE_DIR}/test_trace1.py test_trace1.b kprobe.b) add_test(NAME py_test_trace2 WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND ${TEST_WRAPPER} py_trace2 sudo ${CMAKE_CURRENT_SOURCE_DIR}/test_trace2.py) add_test(NAME py_test_trace3_c WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND ${TEST_WRAPPER} py_trace3_c sudo ${CMAKE_CURRENT_SOURCE_DIR}/test_trace3.py test_trace3.c) add_test(NAME py_test_trace4 WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND ${TEST_WRAPPER} py_trace4 sudo ${CMAKE_CURRENT_SOURCE_DIR}/test_trace4.py) add_test(NAME py_test_trace_maxactive WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND ${TEST_WRAPPER} py_trace_maxactive sudo ${CMAKE_CURRENT_SOURCE_DIR}/test_trace_maxactive.py) add_test(NAME py_test_probe_count WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND ${TEST_WRAPPER} py_probe_count sudo ${CMAKE_CURRENT_SOURCE_DIR}/test_probe_count.py) add_test(NAME py_test_debuginfo WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND ${TEST_WRAPPER} py_test_debuginfo sudo ${CMAKE_CURRENT_SOURCE_DIR}/test_debuginfo.py) add_test(NAME py_test_brb WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND ${TEST_WRAPPER} py_brb_c sudo ${CMAKE_CURRENT_SOURCE_DIR}/test_brb.py test_brb.c) add_test(NAME py_test_brb2 WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND ${TEST_WRAPPER} py_brb2_c sudo ${CMAKE_CURRENT_SOURCE_DIR}/test_brb2.py test_brb2.c) add_test(NAME py_test_clang WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND ${TEST_WRAPPER} py_clang sudo ${CMAKE_CURRENT_SOURCE_DIR}/test_clang.py) add_test(NAME py_test_histogram WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND ${TEST_WRAPPER} py_histogram sudo ${CMAKE_CURRENT_SOURCE_DIR}/test_histogram.py) add_test(NAME py_array WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND ${TEST_WRAPPER} py_array sudo ${CMAKE_CURRENT_SOURCE_DIR}/test_array.py) add_test(NAME py_uprobes WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND ${TEST_WRAPPER} py_uprobes sudo ${CMAKE_CURRENT_SOURCE_DIR}/test_uprobes.py) add_test(NAME py_test_stackid WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND ${TEST_WRAPPER} py_stackid sudo ${CMAKE_CURRENT_SOURCE_DIR}/test_stackid.py) add_test(NAME py_test_tracepoint WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND ${TEST_WRAPPER} py_test_tracepoint sudo ${CMAKE_CURRENT_SOURCE_DIR}/test_tracepoint.py) add_test(NAME py_test_perf_event WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND ${TEST_WRAPPER} py_test_perf_event sudo ${CMAKE_CURRENT_SOURCE_DIR}/test_perf_event.py) add_test(NAME py_test_utils WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND ${TEST_WRAPPER} py_test_utils sudo ${CMAKE_CURRENT_SOURCE_DIR}/test_utils.py) add_test(NAME py_test_percpu WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND ${TEST_WRAPPER} py_test_percpu sudo ${CMAKE_CURRENT_SOURCE_DIR}/test_percpu.py) add_test(NAME py_test_dump_func WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND ${TEST_WRAPPER} py_dump_func simple ${CMAKE_CURRENT_SOURCE_DIR}/test_dump_func.py) add_test(NAME py_test_disassembler WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND ${TEST_WRAPPER} py_test_disassembler sudo ${CMAKE_CURRENT_SOURCE_DIR}/test_disassembler.py) add_test(NAME py_test_tools_smoke WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND ${TEST_WRAPPER} py_test_tools_smoke sudo ${CMAKE_CURRENT_SOURCE_DIR}/test_tools_smoke.py) add_test(NAME py_test_tools_memleak WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND ${TEST_WRAPPER} py_test_tools_memleak sudo ${CMAKE_CURRENT_SOURCE_DIR}/test_tools_memleak.py) add_test(NAME py_test_usdt WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND ${TEST_WRAPPER} py_test_usdt sudo ${CMAKE_CURRENT_SOURCE_DIR}/test_usdt.py) add_test(NAME py_test_usdt2 WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND ${TEST_WRAPPER} py_test_usdt2 sudo ${CMAKE_CURRENT_SOURCE_DIR}/test_usdt2.py) add_test(NAME py_test_usdt3 WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND ${TEST_WRAPPER} py_test_usdt3 sudo ${CMAKE_CURRENT_SOURCE_DIR}/test_usdt3.py) add_test(NAME py_test_license WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND ${TEST_WRAPPER} py_test_license sudo ${CMAKE_CURRENT_SOURCE_DIR}/test_license.py) add_test(NAME py_test_free_bcc_memory WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND ${TEST_WRAPPER} py_test_free_bcc_memory sudo ${CMAKE_CURRENT_SOURCE_DIR}/test_free_bcc_memory.py) add_test(NAME py_test_rlimit WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND ${TEST_WRAPPER} py_test_rlimit sudo ${CMAKE_CURRENT_SOURCE_DIR}/test_rlimit.py) bpfcc-0.12.0/tests/python/dummy.cc000066400000000000000000000005411357404205000167740ustar00rootroot00000000000000#include #include namespace some_namespace { static __attribute__((noinline)) int some_function(int x, int y) { volatile int z = x + y; return z; } } int main() { printf("%p\n", &some_namespace::some_function); fflush(stdout); printf("result = %d\n", some_namespace::some_function(42, 11)); sleep(1000); return 0; } bpfcc-0.12.0/tests/python/include/000077500000000000000000000000001357404205000167555ustar00rootroot00000000000000bpfcc-0.12.0/tests/python/include/folly/000077500000000000000000000000001357404205000201025ustar00rootroot00000000000000bpfcc-0.12.0/tests/python/include/folly/tracing/000077500000000000000000000000001357404205000215315ustar00rootroot00000000000000bpfcc-0.12.0/tests/python/include/folly/tracing/StaticTracepoint-ELF.h000066400000000000000000000145421357404205000255740ustar00rootroot00000000000000/* * Copyright 2017 Facebook, Inc. * * 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. */ #pragma once // Default constraint for the probe arguments as operands. #ifndef FOLLY_SDT_ARG_CONSTRAINT #if defined(__powerpc64__) || defined(__powerpc__) #define FOLLY_SDT_ARG_CONSTRAINT "nZr" #else #define FOLLY_SDT_ARG_CONSTRAINT "nor" #endif #endif // Instruction to emit for the probe. #define FOLLY_SDT_NOP nop // Note section properties. #define FOLLY_SDT_NOTE_NAME "stapsdt" #define FOLLY_SDT_NOTE_TYPE 3 // Size of address depending on platform. #ifdef __LP64__ #define FOLLY_SDT_ASM_ADDR .8byte #else #define FOLLY_SDT_ASM_ADDR .4byte #endif // Assembler helper Macros. #define FOLLY_SDT_S(x) #x #define FOLLY_SDT_ASM_1(x) FOLLY_SDT_S(x) "\n" #define FOLLY_SDT_ASM_2(a, b) FOLLY_SDT_S(a) "," FOLLY_SDT_S(b) "\n" #define FOLLY_SDT_ASM_3(a, b, c) FOLLY_SDT_S(a) "," FOLLY_SDT_S(b) "," \ FOLLY_SDT_S(c) "\n" #define FOLLY_SDT_ASM_STRING(x) FOLLY_SDT_ASM_1(.asciz FOLLY_SDT_S(x)) // Helper to determine the size of an argument. #define FOLLY_SDT_ISARRAY(x) (__builtin_classify_type(x) == 14) #define FOLLY_SDT_ARGSIZE(x) (FOLLY_SDT_ISARRAY(x) ? sizeof(void*) : sizeof(x)) // Format of each probe arguments as operand. // Size of the arugment tagged with FOLLY_SDT_Sn, with "n" constraint. // Value of the argument tagged with FOLLY_SDT_An, with configured constraint. #define FOLLY_SDT_ARG(n, x) \ [FOLLY_SDT_S##n] "n" ((size_t)FOLLY_SDT_ARGSIZE(x)), \ [FOLLY_SDT_A##n] FOLLY_SDT_ARG_CONSTRAINT (x) // Templates to append arguments as operands. #define FOLLY_SDT_OPERANDS_0() [__sdt_dummy] "g" (0) #define FOLLY_SDT_OPERANDS_1(_1) FOLLY_SDT_ARG(1, _1) #define FOLLY_SDT_OPERANDS_2(_1, _2) \ FOLLY_SDT_OPERANDS_1(_1), FOLLY_SDT_ARG(2, _2) #define FOLLY_SDT_OPERANDS_3(_1, _2, _3) \ FOLLY_SDT_OPERANDS_2(_1, _2), FOLLY_SDT_ARG(3, _3) #define FOLLY_SDT_OPERANDS_4(_1, _2, _3, _4) \ FOLLY_SDT_OPERANDS_3(_1, _2, _3), FOLLY_SDT_ARG(4, _4) #define FOLLY_SDT_OPERANDS_5(_1, _2, _3, _4, _5) \ FOLLY_SDT_OPERANDS_4(_1, _2, _3, _4), FOLLY_SDT_ARG(5, _5) #define FOLLY_SDT_OPERANDS_6(_1, _2, _3, _4, _5, _6) \ FOLLY_SDT_OPERANDS_5(_1, _2, _3, _4, _5), FOLLY_SDT_ARG(6, _6) #define FOLLY_SDT_OPERANDS_7(_1, _2, _3, _4, _5, _6, _7) \ FOLLY_SDT_OPERANDS_6(_1, _2, _3, _4, _5, _6), FOLLY_SDT_ARG(7, _7) #define FOLLY_SDT_OPERANDS_8(_1, _2, _3, _4, _5, _6, _7, _8) \ FOLLY_SDT_OPERANDS_7(_1, _2, _3, _4, _5, _6, _7), FOLLY_SDT_ARG(8, _8) // Templates to reference the arguments from operands in note section. #if defined(__powerpc64__ ) || defined(__powerpc__) #define FOLLY_SDT_ARGTMPL(id) %I[id]%[id] #elif defined(__i386__) #define FOLLY_SDT_ARGTMPL(id) %w[id] #else #define FOLLY_SDT_ARGTMPL(id) %[id] #endif #define FOLLY_SDT_ARGFMT(no) %n[FOLLY_SDT_S##no]@FOLLY_SDT_ARGTMPL(FOLLY_SDT_A##no) #define FOLLY_SDT_ARG_TEMPLATE_0 /*No arguments*/ #define FOLLY_SDT_ARG_TEMPLATE_1 FOLLY_SDT_ARGFMT(1) #define FOLLY_SDT_ARG_TEMPLATE_2 FOLLY_SDT_ARG_TEMPLATE_1 FOLLY_SDT_ARGFMT(2) #define FOLLY_SDT_ARG_TEMPLATE_3 FOLLY_SDT_ARG_TEMPLATE_2 FOLLY_SDT_ARGFMT(3) #define FOLLY_SDT_ARG_TEMPLATE_4 FOLLY_SDT_ARG_TEMPLATE_3 FOLLY_SDT_ARGFMT(4) #define FOLLY_SDT_ARG_TEMPLATE_5 FOLLY_SDT_ARG_TEMPLATE_4 FOLLY_SDT_ARGFMT(5) #define FOLLY_SDT_ARG_TEMPLATE_6 FOLLY_SDT_ARG_TEMPLATE_5 FOLLY_SDT_ARGFMT(6) #define FOLLY_SDT_ARG_TEMPLATE_7 FOLLY_SDT_ARG_TEMPLATE_6 FOLLY_SDT_ARGFMT(7) #define FOLLY_SDT_ARG_TEMPLATE_8 FOLLY_SDT_ARG_TEMPLATE_7 FOLLY_SDT_ARGFMT(8) // Structure of note section for the probe. #define FOLLY_SDT_NOTE_CONTENT(provider, name, arg_template) \ FOLLY_SDT_ASM_1(990: FOLLY_SDT_NOP) \ FOLLY_SDT_ASM_3( .pushsection .note.stapsdt,"","note") \ FOLLY_SDT_ASM_1( .balign 4) \ FOLLY_SDT_ASM_3( .4byte 992f-991f, 994f-993f, FOLLY_SDT_NOTE_TYPE) \ FOLLY_SDT_ASM_1(991: .asciz FOLLY_SDT_NOTE_NAME) \ FOLLY_SDT_ASM_1(992: .balign 4) \ FOLLY_SDT_ASM_1(993: FOLLY_SDT_ASM_ADDR 990b) \ FOLLY_SDT_ASM_1( FOLLY_SDT_ASM_ADDR 0) /*Reserved for Semaphore address*/\ FOLLY_SDT_ASM_1( FOLLY_SDT_ASM_ADDR 0) /*Reserved for Semaphore name*/ \ FOLLY_SDT_ASM_STRING(provider) \ FOLLY_SDT_ASM_STRING(name) \ FOLLY_SDT_ASM_STRING(arg_template) \ FOLLY_SDT_ASM_1(994: .balign 4) \ FOLLY_SDT_ASM_1( .popsection) // Main probe Macro. #define FOLLY_SDT_PROBE(provider, name, n, arglist) \ __asm__ __volatile__ ( \ FOLLY_SDT_NOTE_CONTENT(provider, name, FOLLY_SDT_ARG_TEMPLATE_##n) \ :: FOLLY_SDT_OPERANDS_##n arglist \ ) \ // Helper Macros to handle variadic arguments. #define FOLLY_SDT_NARG_(_0, _1, _2, _3, _4, _5, _6, _7, _8, N, ...) N #define FOLLY_SDT_NARG(...) \ FOLLY_SDT_NARG_(__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1, 0) #define FOLLY_SDT_PROBE_N(provider, name, N, ...) \ FOLLY_SDT_PROBE(provider, name, N, (__VA_ARGS__)) bpfcc-0.12.0/tests/python/include/folly/tracing/StaticTracepoint.h000066400000000000000000000022171357404205000251640ustar00rootroot00000000000000/* * Copyright 2017 Facebook, Inc. * * 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. */ #pragma once #if defined(__ELF__) && \ (defined(__powerpc64__) || defined(__powerpc__) || defined(__aarch64__) || \ defined(__x86_64__) || defined(__i386__)) #include #define FOLLY_SDT(provider, name, ...) \ FOLLY_SDT_PROBE_N( \ provider, name, FOLLY_SDT_NARG(0, ##__VA_ARGS__), ##__VA_ARGS__) #else #define FOLLY_SDT(provider, name, ...) do {} while(0) #endif bpfcc-0.12.0/tests/python/kprobe.b000066400000000000000000000005231357404205000167570ustar00rootroot00000000000000// Copyright (c) PLUMgrid, Inc. // Licensed under the Apache License, Version 2.0 (the "License") #packed "false" struct pt_regs { u64 r15:64; u64 r14:64; u64 r13:64; u64 r12:64; u64 bp:64; u64 bx:64; u64 r11:64; u64 r10:64; u64 r9:64; u64 r8:64; u64 ax:64; u64 cx:64; u64 dx:64; u64 si:64; u64 di:64; }; bpfcc-0.12.0/tests/python/proto.b000066400000000000000000000036331357404205000166450ustar00rootroot00000000000000// Copyright (c) PLUMgrid, Inc. // Licensed under the Apache License, Version 2.0 (the "License") #packed "true" struct ethernet { u64 dst:48; u64 src:48; u32 type:16; }; state ethernet { switch $ethernet.type { case 0x0800 { next proto::ip; }; case 0x8100 { next proto::dot1q; }; case * { goto EOP; }; } } struct dot1q { u32 pri:3; u32 cfi:1; u32 vlanid:12; u32 type:16; }; state dot1q { switch $dot1q.type { case 0x0800 { next proto::ip; }; case * { goto EOP; }; } } struct ip { u32 ver:4; u32 hlen:4; u32 tos:8; u32 tlen:16; u32 identification:16; u32 ffo_unused:1; u32 df:1; u32 mf:1; u32 foffset:13; u32 ttl:8; u32 nextp:8; u32 hchecksum:16; u32 src:32; u32 dst:32; }; state ip { switch $ip.nextp { case 6 { next proto::tcp; }; case 17 { next proto::udp; }; case 47 { next proto::gre; }; case * { goto EOP; }; } } struct udp { u32 sport:16; u32 dport:16; u32 length:16; u32 crc:16; }; state udp { switch $udp.dport { case 8472 { next proto::vxlan; }; case * { goto EOP; }; } } struct tcp { u16 src_port:16; u16 dst_port:16; u32 seq_num:32; u32 ack_num:32; u8 offset:4; u8 reserved:4; u8 flag_cwr:1; u8 flag_ece:1; u8 flag_urg:1; u8 flag_ack:1; u8 flag_psh:1; u8 flag_rst:1; u8 flag_syn:1; u8 flag_fin:1; u16 rcv_wnd:16; u16 cksum:16; u16 urg_ptr:16; }; state tcp { goto EOP; } struct vxlan { u32 rsv1:4; u32 iflag:1; u32 rsv2:3; u32 rsv3:24; u32 key:24; u32 rsv4:8; }; state vxlan { goto EOP; } struct gre { u32 cflag:1; u32 rflag:1; u32 kflag:1; u32 snflag:1; u32 srflag:1; u32 recurflag:3; u32 reserved:5; u32 vflag:3; u32 protocol:16; u32 key:32; }; state gre { switch $gre.protocol { case * { goto EOP; }; } } bpfcc-0.12.0/tests/python/simulation.py000077700000000000000000000000001357404205000272542../../examples/networking/simulation.pyustar00rootroot00000000000000bpfcc-0.12.0/tests/python/test_array.py000077500000000000000000000065461357404205000200770ustar00rootroot00000000000000#!/usr/bin/env python # Copyright (c) PLUMgrid, Inc. # Licensed under the Apache License, Version 2.0 (the "License") from bcc import BPF import ctypes as ct import random import time import subprocess from bcc.utils import get_online_cpus from unittest import main, TestCase class TestArray(TestCase): def test_simple(self): b = BPF(text="""BPF_ARRAY(table1, u64, 128);""") t1 = b["table1"] t1[ct.c_int(0)] = ct.c_ulonglong(100) t1[ct.c_int(127)] = ct.c_ulonglong(1000) for i, v in t1.items(): if i.value == 0: self.assertEqual(v.value, 100) if i.value == 127: self.assertEqual(v.value, 1000) self.assertEqual(len(t1), 128) def test_native_type(self): b = BPF(text="""BPF_ARRAY(table1, u64, 128);""") t1 = b["table1"] t1[0] = ct.c_ulonglong(100) t1[-2] = ct.c_ulonglong(37) t1[127] = ct.c_ulonglong(1000) for i, v in t1.items(): if i.value == 0: self.assertEqual(v.value, 100) if i.value == 127: self.assertEqual(v.value, 1000) self.assertEqual(len(t1), 128) self.assertEqual(t1[-2].value, 37) self.assertEqual(t1[-1].value, t1[127].value) def test_perf_buffer(self): self.counter = 0 class Data(ct.Structure): _fields_ = [("ts", ct.c_ulonglong)] def cb(cpu, data, size): self.assertGreater(size, ct.sizeof(Data)) event = ct.cast(data, ct.POINTER(Data)).contents self.counter += 1 def lost_cb(lost): self.assertGreater(lost, 0) text = """ BPF_PERF_OUTPUT(events); int do_sys_nanosleep(void *ctx) { struct { u64 ts; } data = {bpf_ktime_get_ns()}; events.perf_submit(ctx, &data, sizeof(data)); return 0; } """ b = BPF(text=text) b.attach_kprobe(event=b.get_syscall_fnname("nanosleep"), fn_name="do_sys_nanosleep") b["events"].open_perf_buffer(cb, lost_cb=lost_cb) subprocess.call(['sleep', '0.1']) b.perf_buffer_poll() self.assertGreater(self.counter, 0) b.cleanup() def test_perf_buffer_for_each_cpu(self): self.events = [] class Data(ct.Structure): _fields_ = [("cpu", ct.c_ulonglong)] def cb(cpu, data, size): self.assertGreater(size, ct.sizeof(Data)) event = ct.cast(data, ct.POINTER(Data)).contents self.events.append(event) def lost_cb(lost): self.assertGreater(lost, 0) text = """ BPF_PERF_OUTPUT(events); int do_sys_nanosleep(void *ctx) { struct { u64 cpu; } data = {bpf_get_smp_processor_id()}; events.perf_submit(ctx, &data, sizeof(data)); return 0; } """ b = BPF(text=text) b.attach_kprobe(event=b.get_syscall_fnname("nanosleep"), fn_name="do_sys_nanosleep") b["events"].open_perf_buffer(cb, lost_cb=lost_cb) online_cpus = get_online_cpus() for cpu in online_cpus: subprocess.call(['taskset', '-c', str(cpu), 'sleep', '0.1']) b.perf_buffer_poll() b.cleanup() self.assertGreaterEqual(len(self.events), len(online_cpus), 'Received only {}/{} events'.format(len(self.events), len(online_cpus))) if __name__ == "__main__": main() bpfcc-0.12.0/tests/python/test_bpf_log.py000077500000000000000000000031011357404205000203510ustar00rootroot00000000000000#!/usr/bin/env python # Copyright (c) PLUMgrid, Inc. # Licensed under the Apache License, Version 2.0 (the "License") from bcc import BPF from simulation import Simulation import sys import os import tempfile from unittest import main, TestCase error_msg = "R0 invalid mem access 'map_value_or_null'\n" text = """ #include #include BPF_HASH(t1, int, int, 10); int sim_port(struct __sk_buff *skb) { int x = 0, *y; """ repeat = """ y = t1.lookup(&x); if (!y) return 0; x = *y; """ end = """ y = t1.lookup(&x); x = *y; return 0; } """ for i in range(0,300): text += repeat text += end class TestBPFProgLoad(TestCase): def setUp(self): self.fp = tempfile.TemporaryFile() os.dup2(self.fp.fileno(), sys.stderr.fileno()) def tearDown(self): self.fp.close() def test_log_debug(self): b = BPF(text=text, debug=2) try: ingress = b.load_func("sim_port",BPF.SCHED_CLS) except Exception: self.fp.flush() self.fp.seek(0) self.assertEqual(error_msg in self.fp.read().decode(), True) def test_log_no_debug(self): b = BPF(text=text, debug=0) try: ingress = b.load_func("sim_port",BPF.SCHED_CLS) except Exception: self.fp.flush() self.fp.seek(0) self.assertEqual(error_msg in self.fp.read().decode(), True) if __name__ == "__main__": main() bpfcc-0.12.0/tests/python/test_brb.c000066400000000000000000000141311357404205000173020ustar00rootroot00000000000000// Copyright (c) PLUMgrid, Inc. // Licensed under the Apache License, Version 2.0 (the "License") #include #define _memcpy __builtin_memcpy // meta data passed between bpf programs typedef struct bpf_metadata { u32 prog_id; u32 rx_port_id; } bpf_metadata_t; typedef struct bpf_dest { u32 prog_id; u32 port_id; } bpf_dest_t; // use u64 to represent eth_addr. // maintain the structure though to indicate the semantics typedef struct eth_addr { u64 addr; } eth_addr_t; // Program table definitions for tail calls BPF_PROG_ARRAY(jump, 16); // physical endpoint manager (pem) tables which connects to boeht bridge 1 and bridge 2 // BPF_ARRAY(pem_dest, bpf_dest_t, 256); // BPF_ARRAY(pem_port, u32, 256); // BPF_HASH(pem_ifindex, u32, u32, 256); // <0, tx2vm_pkts> BPF_ARRAY(pem_stats, u32, 1); // bridge 1 (br1) tables // BPF_ARRAY(br1_dest, bpf_dest_t, 256); // BPF_HASH(br1_mac, eth_addr_t, u32, 256); // <0, rtr_ifindex> BPF_ARRAY(br1_rtr, u32, 1); // BPF_HASH(br1_mac_ifindex, eth_addr_t, u32, 1); // bridge 2 (br2) tables // BPF_ARRAY(br2_dest, bpf_dest_t, 256); // BPF_HASH(br2_mac, eth_addr_t, u32, 256); // <0, rtr_ifindex> BPF_ARRAY(br2_rtr, u32, 1); // BPF_HASH(br2_mac_ifindex, eth_addr_t, u32, 1); int pem(struct __sk_buff *skb) { bpf_metadata_t meta = {}; u32 ifindex; u32 *tx_port_id_p; u32 tx_port_id; u32 rx_port; u32 *ifindex_p; bpf_dest_t *dest_p; // pem does not look at packet data if (skb->tc_index == 0) { skb->tc_index = 1; skb->cb[0] = skb->cb[1] = 0; meta.prog_id = meta.rx_port_id = 0; } else { meta.prog_id = skb->cb[0]; asm volatile("" ::: "memory"); meta.rx_port_id = skb->cb[1]; } if (!meta.prog_id) { /* from external */ ifindex = skb->ingress_ifindex; tx_port_id_p = pem_ifindex.lookup(&ifindex); if (tx_port_id_p) { tx_port_id = *tx_port_id_p; dest_p = pem_dest.lookup(&tx_port_id); if (dest_p) { skb->cb[0] = dest_p->prog_id; skb->cb[1] = dest_p->port_id; jump.call(skb, dest_p->prog_id); } } } else { /* from internal */ rx_port = meta.rx_port_id; ifindex_p = pem_port.lookup(&rx_port); if (ifindex_p) { #if 1 /* accumulate stats, may hurt performance slightly */ u32 index = 0; u32 *value = pem_stats.lookup(&index); if (value) lock_xadd(value, 1); #endif bpf_clone_redirect(skb, *ifindex_p, 0); } } return 1; } static int br_common(struct __sk_buff *skb, int which_br) { u8 *cursor = 0; u16 proto; u16 arpop; eth_addr_t dmac; u8 *mac_p; u32 dip; u32 *tx_port_id_p; u32 tx_port_id; bpf_dest_t *dest_p; u32 index, *rtrif_p; struct ethernet_t *ethernet = cursor_advance(cursor, sizeof(*ethernet)); /* handle ethernet packet header */ { dmac.addr = ethernet->dst; /* skb->tc_index may be preserved across router namespace if router simply rewrite packet * and send it back. */ if (skb->tc_index == 1) { /* packet from pem, send to the router, set tc_index to 2 */ skb->tc_index = 2; if (dmac.addr == 0xffffffffffffULL) { index = 0; if (which_br == 1) rtrif_p = br1_rtr.lookup(&index); else rtrif_p = br2_rtr.lookup(&index); if (rtrif_p) bpf_clone_redirect(skb, *rtrif_p, 0); } else { /* the dmac address should match the router's */ if (which_br == 1) rtrif_p = br1_mac_ifindex.lookup(&dmac); else rtrif_p = br2_mac_ifindex.lookup(&dmac); if (rtrif_p) bpf_clone_redirect(skb, *rtrif_p, 0); } return 1; } /* set the tc_index to 1 so pem knows it is from internal */ skb->tc_index = 1; switch (ethernet->type) { case ETH_P_IP: goto ip; case ETH_P_ARP: goto arp; case ETH_P_8021Q: goto dot1q; default: goto EOP; } } dot1q: { struct dot1q_t *dot1q = cursor_advance(cursor, sizeof(*dot1q)); switch(dot1q->type) { case ETH_P_IP: goto ip; case ETH_P_ARP: goto arp; default: goto EOP; } } arp: { struct arp_t *arp = cursor_advance(cursor, sizeof(*arp)); /* mac learning */ arpop = arp->oper; if (arpop == 2) { index = 0; if (which_br == 1) rtrif_p = br1_rtr.lookup(&index); else rtrif_p = br2_rtr.lookup(&index); if (rtrif_p) { __u32 ifindex = *rtrif_p; eth_addr_t smac; smac.addr = ethernet->src; if (which_br == 1) br1_mac_ifindex.update(&smac, &ifindex); else br2_mac_ifindex.update(&smac, &ifindex); } } goto xmit; } ip: { struct ip_t *ip = cursor_advance(cursor, sizeof(*ip)); goto xmit; } xmit: if (which_br == 1) tx_port_id_p = br1_mac.lookup(&dmac); else tx_port_id_p = br2_mac.lookup(&dmac); if (tx_port_id_p) { tx_port_id = *tx_port_id_p; if (which_br == 1) dest_p = br1_dest.lookup(&tx_port_id); else dest_p = br2_dest.lookup(&tx_port_id); if (dest_p) { skb->cb[0] = dest_p->prog_id; skb->cb[1] = dest_p->port_id; jump.call(skb, dest_p->prog_id); } } EOP: return 1; } int br1(struct __sk_buff *skb) { return br_common(skb, 1); } int br2(struct __sk_buff *skb) { return br_common(skb, 2); } bpfcc-0.12.0/tests/python/test_brb.py000077500000000000000000000226021357404205000175150ustar00rootroot00000000000000#!/usr/bin/env python # Copyright (c) PLUMgrid, Inc. # Licensed under the Apache License, Version 2.0 (the "License") # This program implements a topology likes below: # pem: physical endpoint manager, implemented as a bpf program # # vm1 <--------+ +----> bridge1 <----+ # V V V # pem router # ^ ^ ^ # vm2 <--------+ +----> bridge2 <----+ # # The vm1, vm2 and router are implemented as namespaces. # The bridge is implemented with limited functionality in bpf program. # # vm1 and vm2 are in different subnet. For vm1 to communicate to vm2, # the packet will have to travel from vm1 to pem, bridge1, router, bridge2, pem, and # then come to vm2. # # When this test is run with verbose mode (ctest -R -V), # the following printout is observed on my local box: # # ...... # 8: ARPING 100.1.1.254 from 100.1.1.1 eth0 # 8: Unicast reply from 100.1.1.254 [76:62:B5:5C:8C:6F] 0.533ms # 8: Sent 1 probes (1 broadcast(s)) # 8: Received 1 response(s) # 8: ARPING 200.1.1.254 from 200.1.1.1 eth0 # 8: Unicast reply from 200.1.1.254 [F2:F0:B4:ED:7B:1B] 0.524ms # 8: Sent 1 probes (1 broadcast(s)) # 8: Received 1 response(s) # 8: PING 200.1.1.1 (200.1.1.1) 56(84) bytes of data. # 8: 64 bytes from 200.1.1.1: icmp_req=1 ttl=63 time=0.074 ms # 8: 64 bytes from 200.1.1.1: icmp_req=2 ttl=63 time=0.061 ms # 8: # 8: --- 200.1.1.1 ping statistics --- # 8: 2 packets transmitted, 2 received, 0% packet loss, time 999ms # 8: rtt min/avg/max/mdev = 0.061/0.067/0.074/0.010 ms # 8: [ ID] Interval Transfer Bandwidth # 8: [ 5] 0.0- 1.0 sec 4.00 GBytes 34.3 Gbits/sec # 8: Starting netserver with host 'IN(6)ADDR_ANY' port '12865' and family AF_UNSPEC # 8: MIGRATED TCP STREAM TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 200.1.1.1 (200.1.1.1) port 0 AF_INET : demo # 8: Recv Send Send # 8: Socket Socket Message Elapsed # 8: Size Size Size Time Throughput # 8: bytes bytes bytes secs. 10^6bits/sec # 8: # 8: 87380 16384 65160 1.00 41991.68 # 8: MIGRATED TCP REQUEST/RESPONSE TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 200.1.1.1 (200.1.1.1) port 0 AF_INET : demo : first burst 0 # 8: Local /Remote # 8: Socket Size Request Resp. Elapsed Trans. # 8: Send Recv Size Size Time Rate # 8: bytes Bytes bytes bytes secs. per sec # 8: # 8: 16384 87380 1 1 1.00 48645.53 # 8: 16384 87380 # 8: . # 8: ---------------------------------------------------------------------- # 8: Ran 1 test in 11.296s # 8: # 8: OK from ctypes import c_uint from netaddr import IPAddress, EUI from bcc import BPF from pyroute2 import IPRoute, NetNS, IPDB, NSPopen from utils import NSPopenWithCheck import sys from time import sleep from unittest import main, TestCase from simulation import Simulation arg1 = sys.argv.pop(1) ipr = IPRoute() ipdb = IPDB(nl=ipr) sim = Simulation(ipdb) class TestBPFSocket(TestCase): def set_default_const(self): self.ns1 = "ns1" self.ns2 = "ns2" self.ns_router = "ns_router" self.vm1_ip = "100.1.1.1" self.vm2_ip = "200.1.1.1" self.vm1_rtr_ip = "100.1.1.254" self.vm2_rtr_ip = "200.1.1.254" self.vm1_rtr_mask = "100.1.1.0/24" self.vm2_rtr_mask = "200.1.1.0/24" def get_table(self, b): self.jump = b.get_table("jump") self.pem_dest = b.get_table("pem_dest") self.pem_port = b.get_table("pem_port") self.pem_ifindex = b.get_table("pem_ifindex") self.pem_stats = b.get_table("pem_stats") self.br1_dest = b.get_table("br1_dest") self.br1_mac = b.get_table("br1_mac") self.br1_rtr = b.get_table("br1_rtr") self.br2_dest = b.get_table("br2_dest") self.br2_mac = b.get_table("br2_mac") self.br2_rtr = b.get_table("br2_rtr") def connect_ports(self, prog_id_pem, prog_id_br, curr_pem_pid, curr_br_pid, br_dest_map, br_mac_map, ifindex, vm_mac, vm_ip): self.pem_dest[c_uint(curr_pem_pid)] = self.pem_dest.Leaf(prog_id_br, curr_br_pid) br_dest_map[c_uint(curr_br_pid)] = br_dest_map.Leaf(prog_id_pem, curr_pem_pid) self.pem_port[c_uint(curr_pem_pid)] = c_uint(ifindex) self.pem_ifindex[c_uint(ifindex)] = c_uint(curr_pem_pid) mac_addr = br_mac_map.Key(int(EUI(vm_mac))) br_mac_map[mac_addr] = c_uint(curr_br_pid) def config_maps(self): # program id prog_id_pem = 1 prog_id_br1 = 2 prog_id_br2 = 3 # initial port id and table pointers curr_pem_pid = 0 curr_br1_pid = 0 curr_br2_pid = 0 # configure jump table self.jump[c_uint(prog_id_pem)] = c_uint(self.pem_fn.fd) self.jump[c_uint(prog_id_br1)] = c_uint(self.br1_fn.fd) self.jump[c_uint(prog_id_br2)] = c_uint(self.br2_fn.fd) # connect pem and br1 curr_pem_pid = curr_pem_pid + 1 curr_br1_pid = curr_br1_pid + 1 self.connect_ports(prog_id_pem, prog_id_br1, curr_pem_pid, curr_br1_pid, self.br1_dest, self.br1_mac, self.ns1_eth_out.index, self.vm1_mac, self.vm1_ip) # connect pem and br2 curr_pem_pid = curr_pem_pid + 1 curr_br2_pid = curr_br2_pid + 1 self.connect_ports(prog_id_pem, prog_id_br2, curr_pem_pid, curr_br2_pid, self.br2_dest, self.br2_mac, self.ns2_eth_out.index, self.vm2_mac, self.vm2_ip) # connect and self.br1_rtr[c_uint(0)] = c_uint(self.nsrtr_eth0_out.index) self.br2_rtr[c_uint(0)] = c_uint(self.nsrtr_eth1_out.index) def test_brb(self): try: b = BPF(src_file=arg1, debug=0) self.pem_fn = b.load_func("pem", BPF.SCHED_CLS) self.br1_fn = b.load_func("br1", BPF.SCHED_CLS) self.br2_fn = b.load_func("br2", BPF.SCHED_CLS) self.get_table(b) # set up the topology self.set_default_const() (ns1_ipdb, self.ns1_eth_out, _) = sim._create_ns(self.ns1, ipaddr=self.vm1_ip+'/24', fn=self.pem_fn, action='drop', disable_ipv6=True) (ns2_ipdb, self.ns2_eth_out, _) = sim._create_ns(self.ns2, ipaddr=self.vm2_ip+'/24', fn=self.pem_fn, action='drop', disable_ipv6=True) ns1_ipdb.routes.add({'dst': self.vm2_rtr_mask, 'gateway': self.vm1_rtr_ip}).commit() ns2_ipdb.routes.add({'dst': self.vm1_rtr_mask, 'gateway': self.vm2_rtr_ip}).commit() self.vm1_mac = ns1_ipdb.interfaces['eth0'].address self.vm2_mac = ns2_ipdb.interfaces['eth0'].address (_, self.nsrtr_eth0_out, _) = sim._create_ns(self.ns_router, ipaddr=self.vm1_rtr_ip+'/24', fn=self.br1_fn, action='drop', disable_ipv6=True) (rt_ipdb, self.nsrtr_eth1_out, _) = sim._ns_add_ifc(self.ns_router, "eth1", "ns_router2", ipaddr=self.vm2_rtr_ip+'/24', fn=self.br2_fn, action='drop', disable_ipv6=True) nsp = NSPopen(rt_ipdb.nl.netns, ["sysctl", "-w", "net.ipv4.ip_forward=1"]) nsp.wait(); nsp.release() # configure maps self.config_maps() # our bridge is not smart enough, so send arping for router learning to prevent router # from sending out arp request nsp = NSPopen(ns1_ipdb.nl.netns, ["arping", "-w", "1", "-c", "1", "-I", "eth0", self.vm1_rtr_ip]) nsp.wait(); nsp.release() nsp = NSPopen(ns2_ipdb.nl.netns, ["arping", "-w", "1", "-c", "1", "-I", "eth0", self.vm2_rtr_ip]) nsp.wait(); nsp.release() # ping nsp = NSPopen(ns1_ipdb.nl.netns, ["ping", self.vm2_ip, "-c", "2"]) nsp.wait(); nsp.release() # pem_stats only counts pem->bridge traffic, each VM has 4: arping/arp request/2 icmp request # total 8 packets should be counted self.assertEqual(self.pem_stats[c_uint(0)].value, 8) nsp_server = NSPopenWithCheck(ns2_ipdb.nl.netns, ["iperf", "-s", "-xSC"]) sleep(1) nsp = NSPopen(ns1_ipdb.nl.netns, ["iperf", "-c", self.vm2_ip, "-t", "1", "-xSC"]) nsp.wait(); nsp.release() nsp_server.kill(); nsp_server.wait(); nsp_server.release() nsp_server = NSPopenWithCheck(ns2_ipdb.nl.netns, ["netserver", "-D"]) sleep(1) nsp = NSPopenWithCheck(ns1_ipdb.nl.netns, ["netperf", "-l", "1", "-H", self.vm2_ip, "--", "-m", "65160"]) nsp.wait(); nsp.release() nsp = NSPopen(ns1_ipdb.nl.netns, ["netperf", "-l", "1", "-H", self.vm2_ip, "-t", "TCP_RR"]) nsp.wait(); nsp.release() nsp_server.kill(); nsp_server.wait(); nsp_server.release() finally: sim.release() ipdb.release() if __name__ == "__main__": main() bpfcc-0.12.0/tests/python/test_brb2.c000066400000000000000000000016161357404205000173700ustar00rootroot00000000000000// Copyright (c) PLUMgrid, Inc. // Licensed under the Apache License, Version 2.0 (the "License") #include // physical endpoint manager (pem) tables which connects VMs and bridges // BPF_HASH(pem_dest, u32, u32, 256); // <0, tx_pkts> BPF_ARRAY(pem_stats, u32, 1); int pem(struct __sk_buff *skb) { u32 ifindex_in, *ifindex_p; u8 *cursor = 0; struct ethernet_t *ethernet = cursor_advance(cursor, sizeof(*ethernet)); ifindex_in = skb->ingress_ifindex; ifindex_p = pem_dest.lookup(&ifindex_in); if (ifindex_p) { #if 1 if (ethernet->type == 0x0800 || ethernet->type == 0x0806) { /* accumulate stats */ u32 index = 0; u32 *value = pem_stats.lookup(&index); if (value) lock_xadd(value, 1); } #endif bpf_clone_redirect(skb, *ifindex_p, 0); } return 1; } bpfcc-0.12.0/tests/python/test_brb2.py000077500000000000000000000221171357404205000176000ustar00rootroot00000000000000#!/usr/bin/env python # Copyright (c) PLUMgrid, Inc. # Licensed under the Apache License, Version 2.0 (the "License") # This program implements a topology likes below: # pem: physical endpoint manager, implemented as a bpf program # # vm1 <--------+ +----> bridge1 <----+ # V V V # pem router # ^ ^ ^ # vm2 <--------+ +----> bridge2 <----+ # # The vm1, vm2 and router are implemented as namespaces. # The linux bridge device is used to provice bridge functionality. # pem bpf will be attached to related network devices for vm1, vm1, bridge1 and bridge2. # # vm1 and vm2 are in different subnet. For vm1 to communicate to vm2, # the packet will have to travel from vm1 to pem, bridge1, router, bridge2, pem, and # then come to vm2. # # When this test is run with verbose mode (ctest -R -V), # the following printout is observed on my local box: # # ...... # 9: PING 200.1.1.1 (200.1.1.1) 56(84) bytes of data. # 9: 64 bytes from 200.1.1.1: icmp_req=1 ttl=63 time=0.090 ms # 9: 64 bytes from 200.1.1.1: icmp_req=2 ttl=63 time=0.032 ms # 9: # 9: --- 200.1.1.1 ping statistics --- # 9: 2 packets transmitted, 2 received, 0% packet loss, time 999ms # 9: rtt min/avg/max/mdev = 0.032/0.061/0.090/0.029 ms # 9: [ ID] Interval Transfer Bandwidth # 9: [ 5] 0.0- 1.0 sec 3.80 GBytes 32.6 Gbits/sec # 9: Starting netserver with host 'IN(6)ADDR_ANY' port '12865' and family AF_UNSPEC # 9: MIGRATED TCP STREAM TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 200.1.1.1 (200.1.1.1) port 0 AF_INET : demo # 9: Recv Send Send # 9: Socket Socket Message Elapsed # 9: Size Size Size Time Throughput # 9: bytes bytes bytes secs. 10^6bits/sec # 9: # 9: 87380 16384 65160 1.00 39940.46 # 9: MIGRATED TCP REQUEST/RESPONSE TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 200.1.1.1 (200.1.1.1) port 0 AF_INET : demo : first burst 0 # 9: Local /Remote # 9: Socket Size Request Resp. Elapsed Trans. # 9: Send Recv Size Size Time Rate # 9: bytes Bytes bytes bytes secs. per sec # 9: # 9: 16384 87380 1 1 1.00 46387.80 # 9: 16384 87380 # 9: . # 9: ---------------------------------------------------------------------- # 9: Ran 1 test in 7.495s # 9: # 9: OK from ctypes import c_uint from bcc import BPF from pyroute2 import IPRoute, NetNS, IPDB, NSPopen from utils import NSPopenWithCheck import sys from time import sleep from unittest import main, TestCase import subprocess from simulation import Simulation arg1 = sys.argv.pop(1) ipr = IPRoute() ipdb = IPDB(nl=ipr) sim = Simulation(ipdb) allocated_interfaces = set(ipdb.interfaces.keys()) def get_next_iface(prefix): i = 0 while True: iface = "{0}{1}".format(prefix, i) if iface not in allocated_interfaces: allocated_interfaces.add(iface) return iface i += 1 class TestBPFSocket(TestCase): def setup_br(self, br, veth_rt_2_br, veth_pem_2_br, veth_br_2_pem): # create veth which connecting pem and br with ipdb.create(ifname=veth_pem_2_br, kind="veth", peer=veth_br_2_pem) as v: v.up() ipdb.interfaces[veth_br_2_pem].up().commit() subprocess.call(["sysctl", "-q", "-w", "net.ipv6.conf." + veth_pem_2_br + ".disable_ipv6=1"]) subprocess.call(["sysctl", "-q", "-w", "net.ipv6.conf." + veth_br_2_pem + ".disable_ipv6=1"]) # set up the bridge and add router interface as one of its slaves with ipdb.create(ifname=br, kind="bridge") as br1: br1.add_port(ipdb.interfaces[veth_pem_2_br]) br1.add_port(ipdb.interfaces[veth_rt_2_br]) br1.up() subprocess.call(["sysctl", "-q", "-w", "net.ipv6.conf." + br + ".disable_ipv6=1"]) def set_default_const(self): self.ns1 = "ns1" self.ns2 = "ns2" self.ns_router = "ns_router" self.br1 = get_next_iface("br") self.veth_pem_2_br1 = "v20" self.veth_br1_2_pem = "v21" self.br2 = get_next_iface("br") self.veth_pem_2_br2 = "v22" self.veth_br2_2_pem = "v23" self.vm1_ip = "100.1.1.1" self.vm2_ip = "200.1.1.1" self.vm1_rtr_ip = "100.1.1.254" self.vm2_rtr_ip = "200.1.1.254" self.vm1_rtr_mask = "100.1.1.0/24" self.vm2_rtr_mask = "200.1.1.0/24" def attach_filter(self, ifname, fd, name): ifindex = ipdb.interfaces[ifname].index ipr.tc("add", "ingress", ifindex, "ffff:") ipr.tc("add-filter", "bpf", ifindex, ":1", fd=fd, name=name, parent="ffff:", action="drop", classid=1) def config_maps(self): # pem just relays packets between VM and its corresponding # slave link in the bridge interface ns1_ifindex = self.ns1_eth_out.index ns2_ifindex = self.ns2_eth_out.index br1_ifindex = ipdb.interfaces[self.veth_br1_2_pem].index br2_ifindex = ipdb.interfaces[self.veth_br2_2_pem].index self.pem_dest[c_uint(ns1_ifindex)] = c_uint(br1_ifindex) self.pem_dest[c_uint(br1_ifindex)] = c_uint(ns1_ifindex) self.pem_dest[c_uint(ns2_ifindex)] = c_uint(br2_ifindex) self.pem_dest[c_uint(br2_ifindex)] = c_uint(ns2_ifindex) # tc filter setup with bpf programs attached self.attach_filter(self.veth_br1_2_pem, self.pem_fn.fd, self.pem_fn.name) self.attach_filter(self.veth_br2_2_pem, self.pem_fn.fd, self.pem_fn.name) def test_brb2(self): try: b = BPF(src_file=arg1, debug=0) self.pem_fn = b.load_func("pem", BPF.SCHED_CLS) self.pem_dest= b.get_table("pem_dest") self.pem_stats = b.get_table("pem_stats") # set up the topology self.set_default_const() (ns1_ipdb, self.ns1_eth_out, _) = sim._create_ns(self.ns1, ipaddr=self.vm1_ip+'/24', fn=self.pem_fn, action='drop', disable_ipv6=True) (ns2_ipdb, self.ns2_eth_out, _) = sim._create_ns(self.ns2, ipaddr=self.vm2_ip+'/24', fn=self.pem_fn, action='drop', disable_ipv6=True) ns1_ipdb.routes.add({'dst': self.vm2_rtr_mask, 'gateway': self.vm1_rtr_ip}).commit() ns2_ipdb.routes.add({'dst': self.vm1_rtr_mask, 'gateway': self.vm2_rtr_ip}).commit() (_, self.nsrtr_eth0_out, _) = sim._create_ns(self.ns_router, ipaddr=self.vm1_rtr_ip+'/24', disable_ipv6=True) (rt_ipdb, self.nsrtr_eth1_out, _) = sim._ns_add_ifc(self.ns_router, "eth1", "ns_router2", ipaddr=self.vm2_rtr_ip+'/24', disable_ipv6=True) # enable ip forwarding in router ns nsp = NSPopen(rt_ipdb.nl.netns, ["sysctl", "-w", "net.ipv4.ip_forward=1"]) nsp.wait(); nsp.release() # for each VM connecting to pem, there will be a corresponding veth connecting to the bridge self.setup_br(self.br1, self.nsrtr_eth0_out.ifname, self.veth_pem_2_br1, self.veth_br1_2_pem) self.setup_br(self.br2, self.nsrtr_eth1_out.ifname, self.veth_pem_2_br2, self.veth_br2_2_pem) # load the program and configure maps self.config_maps() # ping nsp = NSPopen(ns1_ipdb.nl.netns, ["ping", self.vm2_ip, "-c", "2"]); nsp.wait(); nsp.release() # one arp request/reply, 2 icmp request/reply per VM, total 6 packets per VM, 12 packets total self.assertEqual(self.pem_stats[c_uint(0)].value, 12) nsp_server = NSPopenWithCheck(ns2_ipdb.nl.netns, ["iperf", "-s", "-xSC"]) sleep(1) nsp = NSPopen(ns1_ipdb.nl.netns, ["iperf", "-c", self.vm2_ip, "-t", "1", "-xSC"]) nsp.wait(); nsp.release() nsp_server.kill(); nsp_server.wait(); nsp_server.release() nsp_server = NSPopenWithCheck(ns2_ipdb.nl.netns, ["netserver", "-D"]) sleep(1) nsp = NSPopenWithCheck(ns1_ipdb.nl.netns, ["netperf", "-l", "1", "-H", self.vm2_ip, "--", "-m", "65160"]) nsp.wait(); nsp.release() nsp = NSPopen(ns1_ipdb.nl.netns, ["netperf", "-l", "1", "-H", self.vm2_ip, "-t", "TCP_RR"]) nsp.wait(); nsp.release() nsp_server.kill(); nsp_server.wait(); nsp_server.release() finally: if self.br1 in ipdb.interfaces: ipdb.interfaces[self.br1].remove().commit() if self.br2 in ipdb.interfaces: ipdb.interfaces[self.br2].remove().commit() if self.veth_pem_2_br1 in ipdb.interfaces: ipdb.interfaces[self.veth_pem_2_br1].remove().commit() if self.veth_pem_2_br2 in ipdb.interfaces: ipdb.interfaces[self.veth_pem_2_br2].remove().commit() sim.release() ipdb.release() if __name__ == "__main__": main() bpfcc-0.12.0/tests/python/test_call1.c000066400000000000000000000021361357404205000175330ustar00rootroot00000000000000// Copyright (c) PLUMgrid, Inc. // Licensed under the Apache License, Version 2.0 (the "License") BPF_PROG_ARRAY(jump, 64); BPF_ARRAY(stats, u64, 64); enum states { S_EOP = 1, S_ETHER, S_ARP, S_IP }; int parse_ether(struct __sk_buff *skb) { size_t cur = 0; size_t next = cur + 14; int key = S_ETHER; u64 *leaf = stats.lookup(&key); if (leaf) (*leaf)++; switch (bpf_dext_pkt(skb, cur + 12, 0, 16)) { case 0x0800: jump.call(skb, S_IP); case 0x0806: jump.call(skb, S_ARP); } jump.call(skb, S_EOP); return 1; } int parse_arp(struct __sk_buff *skb) { size_t cur = 14; // TODO: get from ctx size_t next = cur + 28; int key = S_ARP; u64 *leaf = stats.lookup(&key); if (leaf) (*leaf)++; jump.call(skb, S_EOP); return 1; } int parse_ip(struct __sk_buff *skb) { size_t cur = 14; // TODO: get from ctx size_t next = cur + 20; int key = S_IP; u64 *leaf = stats.lookup(&key); if (leaf) (*leaf)++; jump.call(skb, S_EOP); return 1; } int eop(struct __sk_buff *skb) { int key = S_EOP; u64 *leaf = stats.lookup(&key); if (leaf) (*leaf)++; return 1; } bpfcc-0.12.0/tests/python/test_call1.py000077500000000000000000000031401357404205000177400ustar00rootroot00000000000000#!/usr/bin/env python # Copyright (c) PLUMgrid, Inc. # Licensed under the Apache License, Version 2.0 (the "License") from ctypes import c_ushort, c_int, c_ulonglong from netaddr import IPAddress from bcc import BPF from pyroute2 import IPRoute from socket import socket, AF_INET, SOCK_DGRAM import sys from time import sleep from unittest import main, TestCase arg1 = sys.argv.pop(1) S_EOP = 1 S_ETHER = 2 S_ARP = 3 S_IP = 4 class TestBPFSocket(TestCase): def setUp(self): b = BPF(src_file=arg1, debug=0) ether_fn = b.load_func("parse_ether", BPF.SCHED_CLS) arp_fn = b.load_func("parse_arp", BPF.SCHED_CLS) ip_fn = b.load_func("parse_ip", BPF.SCHED_CLS) eop_fn = b.load_func("eop", BPF.SCHED_CLS) ip = IPRoute() ifindex = ip.link_lookup(ifname="eth0")[0] ip.tc("add", "sfq", ifindex, "1:") ip.tc("add-filter", "bpf", ifindex, ":1", fd=ether_fn.fd, name=ether_fn.name, parent="1:", action="ok", classid=1) self.jump = b.get_table("jump", c_int, c_int) self.jump[c_int(S_ARP)] = c_int(arp_fn.fd) self.jump[c_int(S_IP)] = c_int(ip_fn.fd) self.jump[c_int(S_EOP)] = c_int(eop_fn.fd) self.stats = b.get_table("stats", c_int, c_ulonglong) def test_jumps(self): udp = socket(AF_INET, SOCK_DGRAM) udp.sendto(b"a" * 10, ("172.16.1.1", 5000)) udp.close() self.assertGreater(self.stats[c_int(S_IP)].value, 0) self.assertGreater(self.stats[c_int(S_ARP)].value, 0) self.assertGreater(self.stats[c_int(S_EOP)].value, 1) if __name__ == "__main__": main() bpfcc-0.12.0/tests/python/test_clang.py000077500000000000000000001016451357404205000200410ustar00rootroot00000000000000#!/usr/bin/env python # Copyright (c) PLUMgrid, Inc. # Licensed under the Apache License, Version 2.0 (the "License") from bcc import BPF import ctypes as ct from unittest import main, skipUnless, TestCase import os import sys import socket import struct from contextlib import contextmanager import distutils.version @contextmanager def redirect_stderr(to): stderr_fd = sys.stderr.fileno() with os.fdopen(os.dup(stderr_fd), 'wb') as copied, os.fdopen(to, 'w') as to: sys.stderr.flush() os.dup2(to.fileno(), stderr_fd) try: yield sys.stderr finally: sys.stderr.flush() os.dup2(copied.fileno(), stderr_fd) def kernel_version_ge(major, minor): # True if running kernel is >= X.Y version = distutils.version.LooseVersion(os.uname()[2]).version if version[0] > major: return True if version[0] < major: return False if minor and version[1] < minor: return False return True class TestClang(TestCase): def test_complex(self): b = BPF(src_file="test_clang_complex.c", debug=0) fn = b.load_func("handle_packet", BPF.SCHED_CLS) def test_printk(self): text = """ #include int handle_packet(void *ctx) { u8 *cursor = 0; struct ethernet_t *ethernet = cursor_advance(cursor, sizeof(*ethernet)); bpf_trace_printk("ethernet->dst = %llx, ethernet->src = %llx\\n", ethernet->dst, ethernet->src); return 0; } """ b = BPF(text=text, debug=0) fn = b.load_func("handle_packet", BPF.SCHED_CLS) def test_probe_read1(self): text = """ #include #include int count_sched(struct pt_regs *ctx, struct task_struct *prev) { pid_t p = prev->pid; return (p != -1); } """ b = BPF(text=text, debug=0) fn = b.load_func("count_sched", BPF.KPROBE) def test_probe_read2(self): text = """ #include #include int count_foo(struct pt_regs *ctx, unsigned long a, unsigned long b) { return (a != b); } """ b = BPF(text=text, debug=0) fn = b.load_func("count_foo", BPF.KPROBE) def test_probe_read3(self): text = """ #define KBUILD_MODNAME "foo" #include #define _(P) ({typeof(P) val = 0; bpf_probe_read(&val, sizeof(val), &P); val;}) int count_tcp(struct pt_regs *ctx, struct sk_buff *skb) { return _(TCP_SKB_CB(skb)->tcp_gso_size); } """ b = BPF(text=text) fn = b.load_func("count_tcp", BPF.KPROBE) def test_probe_read4(self): text = """ #define KBUILD_MODNAME "foo" #include #define _(P) ({typeof(P) val = 0; bpf_probe_read(&val, sizeof(val), &P); val;}) int test(struct pt_regs *ctx, struct sk_buff *skb) { return _(TCP_SKB_CB(skb)->tcp_gso_size) + skb->protocol; } """ b = BPF(text=text) fn = b.load_func("test", BPF.KPROBE) def test_probe_read_whitelist1(self): text = """ #define KBUILD_MODNAME "foo" #include int count_tcp(struct pt_regs *ctx, struct sk_buff *skb) { // The below define is in net/tcp.h: // #define TCP_SKB_CB(__skb) ((struct tcp_skb_cb *)&((__skb)->cb[0])) // Note that it has AddrOf in the macro, which will cause current rewriter // failing below statement // return TCP_SKB_CB(skb)->tcp_gso_size; u16 val = 0; bpf_probe_read(&val, sizeof(val), &(TCP_SKB_CB(skb)->tcp_gso_size)); return val; } """ b = BPF(text=text) fn = b.load_func("count_tcp", BPF.KPROBE) def test_probe_read_whitelist2(self): text = """ #define KBUILD_MODNAME "foo" #include int count_tcp(struct pt_regs *ctx, struct sk_buff *skb) { // The below define is in net/tcp.h: // #define TCP_SKB_CB(__skb) ((struct tcp_skb_cb *)&((__skb)->cb[0])) // Note that it has AddrOf in the macro, which will cause current rewriter // failing below statement // return TCP_SKB_CB(skb)->tcp_gso_size; u16 val = 0; bpf_probe_read(&val, sizeof(val), &(TCP_SKB_CB(skb)->tcp_gso_size)); return val + skb->protocol; } """ b = BPF(text=text) fn = b.load_func("count_tcp", BPF.KPROBE) def test_probe_read_keys(self): text = """ #include #include BPF_HASH(start, struct request *); int do_request(struct pt_regs *ctx, struct request *req) { u64 ts = bpf_ktime_get_ns(); start.update(&req, &ts); return 0; } int do_completion(struct pt_regs *ctx, struct request *req) { u64 *tsp = start.lookup(&req); if (tsp != 0) { start.delete(&req); } return 0; } """ b = BPF(text=text, debug=0) fns = b.load_funcs(BPF.KPROBE) def test_sscanf(self): text = """ BPF_HASH(stats, int, struct { u64 a; u64 b; u64 c:36; u64 d:28; struct { u32 a; u32 b; } s; }, 10); int foo(void *ctx) { return 0; } """ b = BPF(text=text, debug=0) fn = b.load_func("foo", BPF.KPROBE) t = b.get_table("stats") s1 = t.key_sprintf(t.Key(2)) self.assertEqual(s1, b"0x2") s2 = t.leaf_sprintf(t.Leaf(2, 3, 4, 1, (5, 6))) l = t.leaf_scanf(s2) self.assertEqual(l.a, 2) self.assertEqual(l.b, 3) self.assertEqual(l.c, 4) self.assertEqual(l.d, 1) self.assertEqual(l.s.a, 5) self.assertEqual(l.s.b, 6) def test_sscanf_array(self): text = """ BPF_HASH(stats, int, struct { u32 a[3]; u32 b; }, 10); """ b = BPF(text=text, debug=0) t = b.get_table("stats") s1 = t.key_sprintf(t.Key(2)) self.assertEqual(s1, b"0x2") s2 = t.leaf_sprintf(t.Leaf((ct.c_uint * 3)(1,2,3), 4)) self.assertEqual(s2, b"{ [ 0x1 0x2 0x3 ] 0x4 }") l = t.leaf_scanf(s2) self.assertEqual(l.a[0], 1) self.assertEqual(l.a[1], 2) self.assertEqual(l.a[2], 3) self.assertEqual(l.b, 4) def test_sscanf_string(self): text = """ struct Symbol { char name[128]; char path[128]; }; struct Event { uint32_t pid; uint32_t tid; struct Symbol stack[64]; }; BPF_TABLE("array", int, struct Event, comms, 1); """ b = BPF(text=text) t = b.get_table("comms") s1 = t.leaf_sprintf(t[0]) fill = b' { "" "" }' * 63 self.assertEqual(s1, b'{ 0x0 0x0 [ { "" "" }%s ] }' % fill) l = t.Leaf(1, 2) name = b"libxyz" path = b"/usr/lib/libxyz.so" l.stack[0].name = name l.stack[0].path = path s2 = t.leaf_sprintf(l) self.assertEqual(s2, b'{ 0x1 0x2 [ { "%s" "%s" }%s ] }' % (name, path, fill)) l = t.leaf_scanf(s2) self.assertEqual(l.pid, 1) self.assertEqual(l.tid, 2) self.assertEqual(l.stack[0].name, name) self.assertEqual(l.stack[0].path, path) def test_iosnoop(self): text = """ #include #include struct key_t { struct request *req; }; BPF_HASH(start, struct key_t, u64, 1024); int do_request(struct pt_regs *ctx, struct request *req) { struct key_t key = {}; bpf_trace_printk("traced start %d\\n", req->__data_len); return 0; } """ b = BPF(text=text, debug=0) fn = b.load_func("do_request", BPF.KPROBE) def test_blk_start_request(self): text = """ #include #include int do_request(struct pt_regs *ctx, int req) { bpf_trace_printk("req ptr: 0x%x\\n", req); return 0; } """ b = BPF(text=text, debug=0) fn = b.load_func("do_request", BPF.KPROBE) def test_bpf_hash(self): text = """ BPF_HASH(table1); BPF_HASH(table2, u32); BPF_HASH(table3, u32, int); """ b = BPF(text=text, debug=0) def test_consecutive_probe_read(self): text = """ #include #include BPF_HASH(table1, struct super_block *); int trace_entry(struct pt_regs *ctx, struct file *file) { if (!file) return 0; struct vfsmount *mnt = file->f_path.mnt; if (mnt) { struct super_block *k = mnt->mnt_sb; u64 zero = 0; table1.update(&k, &zero); k = mnt->mnt_sb; table1.update(&k, &zero); } return 0; } """ b = BPF(text=text, debug=0) fn = b.load_func("trace_entry", BPF.KPROBE) def test_nested_probe_read(self): text = """ #include int trace_entry(struct pt_regs *ctx, struct file *file) { if (!file) return 0; const char *name = file->f_path.dentry->d_name.name; bpf_trace_printk("%s\\n", name); return 0; } """ b = BPF(text=text, debug=0) fn = b.load_func("trace_entry", BPF.KPROBE) def test_nested_probe_read_deref(self): text = """ #include struct sock { u32 *sk_daddr; }; int test(struct pt_regs *ctx, struct sock *skp) { return *(skp->sk_daddr); } """ b = BPF(text=text) fn = b.load_func("test", BPF.KPROBE) def test_char_array_probe(self): BPF(text="""#include int kprobe__blk_update_request(struct pt_regs *ctx, struct request *req) { bpf_trace_printk("%s\\n", req->rq_disk->disk_name); return 0; }""") def test_probe_read_helper(self): b = BPF(text=""" #include static void print_file_name(struct file *file) { if (!file) return; const char *name = file->f_path.dentry->d_name.name; bpf_trace_printk("%s\\n", name); } static void print_file_name2(int unused, struct file *file) { print_file_name(file); } int trace_entry1(struct pt_regs *ctx, struct file *file) { print_file_name(file); return 0; } int trace_entry2(struct pt_regs *ctx, int unused, struct file *file) { print_file_name2(unused, file); return 0; } """) fn = b.load_func("trace_entry1", BPF.KPROBE) fn = b.load_func("trace_entry2", BPF.KPROBE) def test_probe_unnamed_union_deref(self): text = """ #include int trace(struct pt_regs *ctx, struct page *page) { void *p = page->mapping; return p != NULL; } """ # depending on llvm, compile may pass/fail, but at least shouldn't crash try: b = BPF(text=text) except: pass def test_probe_struct_assign(self): b = BPF(text = """ #include struct args_t { const char *filename; int flags; int mode; }; int do_sys_open(struct pt_regs *ctx, const char *filename, int flags, int mode) { struct args_t args = {}; args.filename = filename; args.flags = flags; args.mode = mode; bpf_trace_printk("%s\\n", args.filename); return 0; }; """) b.attach_kprobe(event=b.get_syscall_fnname("open"), fn_name="do_sys_open") def test_task_switch(self): b = BPF(text=""" #include #include struct key_t { u32 prev_pid; u32 curr_pid; }; BPF_HASH(stats, struct key_t, u64, 1024); int kprobe__finish_task_switch(struct pt_regs *ctx, struct task_struct *prev) { struct key_t key = {}; u64 zero = 0, *val; key.curr_pid = bpf_get_current_pid_tgid(); key.prev_pid = prev->pid; val = stats.lookup_or_try_init(&key, &zero); if (val) { (*val)++; } return 0; } """) def test_probe_simple_assign(self): b = BPF(text=""" #include #include struct leaf { size_t size; }; BPF_HASH(simple_map, u32, struct leaf); int kprobe____kmalloc(struct pt_regs *ctx, size_t size) { u32 pid = bpf_get_current_pid_tgid(); struct leaf* leaf = simple_map.lookup(&pid); if (leaf) leaf->size += size; return 0; }""") def test_probe_simple_member_assign(self): b = BPF(text=""" #include #include struct leaf { void *ptr; }; int test(struct pt_regs *ctx, struct sk_buff *skb) { struct leaf l = {}; struct leaf *lp = &l; lp->ptr = skb; return 0; }""") b.load_func("test", BPF.KPROBE) def test_probe_member_expr_deref(self): b = BPF(text=""" #include #include struct leaf { struct sk_buff *ptr; }; int test(struct pt_regs *ctx, struct sk_buff *skb) { struct leaf l = {}; struct leaf *lp = &l; lp->ptr = skb; return lp->ptr->priority; }""") b.load_func("test", BPF.KPROBE) def test_probe_member_expr(self): b = BPF(text=""" #include #include struct leaf { struct sk_buff *ptr; }; int test(struct pt_regs *ctx, struct sk_buff *skb) { struct leaf l = {}; struct leaf *lp = &l; lp->ptr = skb; return l.ptr->priority; }""") b.load_func("test", BPF.KPROBE) def test_unop_probe_read(self): text = """ #include int trace_entry(struct pt_regs *ctx, struct request *req) { if (!(req->bio->bi_flags & 1)) return 1; if (((req->bio->bi_flags))) return 1; return 0; } """ b = BPF(text=text) fn = b.load_func("trace_entry", BPF.KPROBE) def test_probe_read_nested_deref(self): text = """ #include int test(struct pt_regs *ctx, struct sock *sk) { struct sock *ptr1; struct sock **ptr2 = &ptr1; *ptr2 = sk; return ((struct sock *)(*ptr2))->sk_daddr; } """ b = BPF(text=text) fn = b.load_func("test", BPF.KPROBE) def test_probe_read_nested_deref2(self): text = """ #include int test(struct pt_regs *ctx, struct sock *sk) { struct sock *ptr1; struct sock **ptr2 = &ptr1; struct sock ***ptr3 = &ptr2; *ptr2 = sk; *ptr3 = ptr2; return ((struct sock *)(**ptr3))->sk_daddr; } """ b = BPF(text=text) fn = b.load_func("test", BPF.KPROBE) def test_probe_read_nested_deref_func(self): text = """ #include static int subtest(struct sock ***skp) { return ((struct sock *)(**skp))->sk_daddr; } int test(struct pt_regs *ctx, struct sock *sk) { struct sock *ptr1; struct sock **ptr2 = &ptr1; struct sock ***ptr3 = &ptr2; *ptr2 = sk; *ptr3 = ptr2; return subtest(ptr3); } """ b = BPF(text=text) fn = b.load_func("test", BPF.KPROBE) def test_probe_read_nested_member1(self): text = """ #include int test(struct pt_regs *ctx, struct sock *skp) { u32 *daddr = &skp->sk_daddr; return *daddr; } """ b = BPF(text=text) fn = b.load_func("test", BPF.KPROBE) def test_probe_read_nested_member2(self): text = """ #include struct sock { u32 **sk_daddr; }; int test(struct pt_regs *ctx, struct sock *skp) { u32 *daddr = *(skp->sk_daddr); return *daddr; } """ b = BPF(text=text) fn = b.load_func("test", BPF.KPROBE) def test_probe_read_nested_member3(self): text = """ #include struct sock { u32 *sk_daddr; }; int test(struct pt_regs *ctx, struct sock *skp) { return *(&skp->sk_daddr); } """ b = BPF(text=text) fn = b.load_func("test", BPF.KPROBE) def test_paren_probe_read(self): text = """ #include int trace_entry(struct pt_regs *ctx, struct sock *sk) { u16 sport = ((struct inet_sock *)sk)->inet_sport; return sport; } """ b = BPF(text=text) fn = b.load_func("trace_entry", BPF.KPROBE) def test_complex_leaf_types(self): text = """ struct list; struct list { struct list *selfp; struct list *another_selfp; struct list *selfp_array[2]; }; struct empty { }; union emptyu { struct empty *em1; struct empty em2; struct empty em3; struct empty em4; }; BPF_ARRAY(t1, struct list, 1); BPF_ARRAY(t2, struct list *, 1); BPF_ARRAY(t3, union emptyu, 1); """ b = BPF(text=text) self.assertEqual(ct.sizeof(b["t3"].Leaf), 8) def test_cflags(self): text = """ #ifndef MYFLAG #error "MYFLAG not set as expected" #endif """ b = BPF(text=text, cflags=["-DMYFLAG"]) def test_exported_maps(self): b1 = BPF(text="""BPF_TABLE_PUBLIC("hash", int, int, table1, 10);""") b2 = BPF(text="""BPF_TABLE("extern", int, int, table1, 10);""") t = b2["table1"] def test_syntax_error(self): with self.assertRaises(Exception): b = BPF(text="""int failure(void *ctx) { if (); return 0; }""") def test_nested_union(self): text = """ BPF_HASH(t1, struct bpf_tunnel_key, int, 1); """ b = BPF(text=text) t1 = b["t1"] print(t1.Key().remote_ipv4) def test_too_many_args(self): text = """ #include int many(struct pt_regs *ctx, int a, int b, int c, int d, int e, int f, int g) { return 0; } """ with self.assertRaises(Exception): b = BPF(text=text) def test_call_macro_arg(self): text = """ BPF_PROG_ARRAY(jmp, 32); #define JMP_IDX_PIPE (1U << 1) enum action { ACTION_PASS }; int process(struct xdp_md *ctx) { jmp.call((void *)ctx, ACTION_PASS); jmp.call((void *)ctx, JMP_IDX_PIPE); return XDP_PASS; } """ b = BPF(text=text) t = b["jmp"] self.assertEqual(len(t), 32); def test_update_macro_arg(self): text = """ BPF_ARRAY(act, u32, 32); #define JMP_IDX_PIPE (1U << 1) enum action { ACTION_PASS }; int process(struct xdp_md *ctx) { act.increment(ACTION_PASS); act.increment(JMP_IDX_PIPE); return XDP_PASS; } """ b = BPF(text=text) t = b["act"] self.assertEqual(len(t), 32); def test_ext_ptr_maps1(self): bpf_text = """ #include #include #include BPF_HASH(currsock, u32, struct sock *); int trace_entry(struct pt_regs *ctx, struct sock *sk, struct sockaddr *uaddr, int addr_len) { u32 pid = bpf_get_current_pid_tgid(); currsock.update(&pid, &sk); return 0; }; int trace_exit(struct pt_regs *ctx) { u32 pid = bpf_get_current_pid_tgid(); struct sock **skpp; skpp = currsock.lookup(&pid); if (skpp) { struct sock *skp = *skpp; return skp->__sk_common.skc_dport; } return 0; } """ b = BPF(text=bpf_text) b.load_func("trace_entry", BPF.KPROBE) b.load_func("trace_exit", BPF.KPROBE) def test_ext_ptr_maps2(self): bpf_text = """ #include #include #include BPF_HASH(currsock, u32, struct sock *); int trace_entry(struct pt_regs *ctx, struct sock *sk, struct sockaddr *uaddr, int addr_len) { u32 pid = bpf_get_current_pid_tgid(); currsock.update(&pid, &sk); return 0; }; int trace_exit(struct pt_regs *ctx) { u32 pid = bpf_get_current_pid_tgid(); struct sock **skpp = currsock.lookup(&pid); if (skpp) { struct sock *skp = *skpp; return skp->__sk_common.skc_dport; } return 0; } """ b = BPF(text=bpf_text) b.load_func("trace_entry", BPF.KPROBE) b.load_func("trace_exit", BPF.KPROBE) def test_ext_ptr_maps_reverse(self): bpf_text = """ #include #include #include BPF_HASH(currsock, u32, struct sock *); int trace_exit(struct pt_regs *ctx) { u32 pid = bpf_get_current_pid_tgid(); struct sock **skpp; skpp = currsock.lookup(&pid); if (skpp) { struct sock *skp = *skpp; return skp->__sk_common.skc_dport; } return 0; } int trace_entry(struct pt_regs *ctx, struct sock *sk) { u32 pid = bpf_get_current_pid_tgid(); currsock.update(&pid, &sk); return 0; }; """ b = BPF(text=bpf_text) b.load_func("trace_entry", BPF.KPROBE) b.load_func("trace_exit", BPF.KPROBE) def test_ext_ptr_maps_indirect(self): bpf_text = """ #include #include #include BPF_HASH(currsock, u32, struct sock *); int trace_entry(struct pt_regs *ctx, struct sock *sk) { u32 pid = bpf_get_current_pid_tgid(); struct sock **skp = &sk; currsock.update(&pid, skp); return 0; }; int trace_exit(struct pt_regs *ctx) { u32 pid = bpf_get_current_pid_tgid(); struct sock **skpp; skpp = currsock.lookup(&pid); if (skpp) { struct sock *skp = *skpp; return skp->__sk_common.skc_dport; } return 0; } """ b = BPF(text=bpf_text) b.load_func("trace_entry", BPF.KPROBE) b.load_func("trace_exit", BPF.KPROBE) def test_bpf_dins_pkt_rewrite(self): text = """ #include int dns_test(struct __sk_buff *skb) { u8 *cursor = 0; struct ethernet_t *ethernet = cursor_advance(cursor, sizeof(*ethernet)); if(ethernet->type == ETH_P_IP) { struct ip_t *ip = cursor_advance(cursor, sizeof(*ip)); ip->src = ip->dst; return 0; } return -1; } """ b = BPF(text=text) @skipUnless(kernel_version_ge(4,8), "requires kernel >= 4.8") def test_ext_ptr_from_helper(self): text = """ #include int test(struct pt_regs *ctx) { struct task_struct *task = (struct task_struct *)bpf_get_current_task(); return task->prio; } """ b = BPF(text=text) fn = b.load_func("test", BPF.KPROBE) def test_unary_operator(self): text = """ #include #include int trace_read_entry(struct pt_regs *ctx, struct file *file) { return !file->f_op->read_iter; } """ b = BPF(text=text) b.attach_kprobe(event="__vfs_read", fn_name="trace_read_entry") def test_printk_f(self): text = """ #include int trace_entry(struct pt_regs *ctx) { bpf_trace_printk("%0.2f\\n", 1); return 0; } """ r, w = os.pipe() with redirect_stderr(to=w): BPF(text=text) r = os.fdopen(r) output = r.read() expectedWarn = "warning: only %d %u %x %ld %lu %lx %lld %llu %llx %p %s conversion specifiers allowed" self.assertIn(expectedWarn, output) r.close() def test_printk_lf(self): text = """ #include int trace_entry(struct pt_regs *ctx) { bpf_trace_printk("%lf\\n", 1); return 0; } """ r, w = os.pipe() with redirect_stderr(to=w): BPF(text=text) r = os.fdopen(r) output = r.read() expectedWarn = "warning: only %d %u %x %ld %lu %lx %lld %llu %llx %p %s conversion specifiers allowed" self.assertIn(expectedWarn, output) r.close() def test_printk_2s(self): text = """ #include int trace_entry(struct pt_regs *ctx) { char s1[] = "hello", s2[] = "world"; bpf_trace_printk("%s %s\\n", s1, s2); return 0; } """ r, w = os.pipe() with redirect_stderr(to=w): BPF(text=text) r = os.fdopen(r) output = r.read() expectedWarn = "warning: cannot use several %s conversion specifiers" self.assertIn(expectedWarn, output) r.close() def test_map_insert(self): text = """ BPF_HASH(dummy); void do_trace(struct pt_regs *ctx) { u64 key = 0, val = 2; dummy.insert(&key, &val); key = 1; dummy.update(&key, &val); } """ b = BPF(text=text) c_val = ct.c_ulong(1) b["dummy"][ct.c_ulong(0)] = c_val b["dummy"][ct.c_ulong(1)] = c_val b.attach_kprobe(event=b.get_syscall_fnname("sync"), fn_name="do_trace") libc = ct.CDLL("libc.so.6") libc.sync() self.assertEqual(1, b["dummy"][ct.c_ulong(0)].value) self.assertEqual(2, b["dummy"][ct.c_ulong(1)].value) def test_prog_array_delete(self): text = """ BPF_PROG_ARRAY(dummy, 256); """ b1 = BPF(text=text) text = """ int do_next(struct pt_regs *ctx) { return 0; } """ b2 = BPF(text=text) fn = b2.load_func("do_next", BPF.KPROBE) c_key = ct.c_int(0) b1["dummy"][c_key] = ct.c_int(fn.fd) b1["dummy"].__delitem__(c_key); with self.assertRaises(KeyError): b1["dummy"][c_key] def test_invalid_noninline_call(self): text = """ int bar(void) { return 0; } int foo(struct pt_regs *ctx) { return bar(); } """ with self.assertRaises(Exception): b = BPF(text=text) def test_incomplete_type(self): text = """ BPF_HASH(drops, struct key_t); struct key_t { u64 location; }; """ with self.assertRaises(Exception): b = BPF(text=text) def test_enumerations(self): text = """ enum b { CHOICE_A, }; struct a { enum b test; }; BPF_HASH(drops, struct a); """ b = BPF(text=text) t = b['drops'] def test_int128_types(self): text = """ BPF_HASH(table1, unsigned __int128, __int128); """ b = BPF(text=text) table = b['table1'] self.assertEqual(ct.sizeof(table.Key), 16) self.assertEqual(ct.sizeof(table.Leaf), 16) table[ table.Key.from_buffer_copy( socket.inet_pton(socket.AF_INET6, "2001:db8::")) ] = table.Leaf.from_buffer_copy(struct.pack('LL', 42, 123456789)) for k, v in table.items(): self.assertEqual(v[0], 42) self.assertEqual(v[1], 123456789) self.assertEqual(socket.inet_ntop(socket.AF_INET6, struct.pack('LL', k[0], k[1])), "2001:db8::") def test_padding_types(self): text = """ struct key_t { u32 f1_1; /* offset 0 */ struct { char f2_1; /* offset 16 */ __int128 f2_2; /* offset 32 */ }; u8 f1_3; /* offset 48 */ unsigned __int128 f1_4; /* offset 64 */ char f1_5; /* offset 80 */ }; struct value_t { u8 src[4] __attribute__ ((aligned (8))); /* offset 0 */ u8 dst[4] __attribute__ ((aligned (8))); /* offset 8 */ }; BPF_HASH(table1, struct key_t, struct value_t); """ b = BPF(text=text) table = b['table1'] self.assertEqual(ct.sizeof(table.Key), 96) self.assertEqual(ct.sizeof(table.Leaf), 16) @skipUnless(kernel_version_ge(4,7), "requires kernel >= 4.7") def test_probe_read_tracepoint_context(self): text = """ #include TRACEPOINT_PROBE(skb, kfree_skb) { struct sk_buff *skb = (struct sk_buff *)args->skbaddr; return skb->protocol; } """ b = BPF(text=text) def test_probe_read_kprobe_ctx(self): text = """ #include #include int test(struct pt_regs *ctx) { struct sock *sk; sk = (struct sock *)PT_REGS_PARM1(ctx); return sk->sk_dport; } """ b = BPF(text=text) fn = b.load_func("test", BPF.KPROBE) def test_probe_read_ctx_array(self): text = """ #include #include int test(struct pt_regs *ctx) { struct sock *newsk = (struct sock *)PT_REGS_RC(ctx); return newsk->__sk_common.skc_rcv_saddr; } """ b = BPF(text=text) fn = b.load_func("test", BPF.KPROBE) @skipUnless(kernel_version_ge(4,7), "requires kernel >= 4.7") def test_probe_read_tc_ctx(self): text = """ #include #include int test(struct __sk_buff *ctx) { void* data_end = (void*)(long)ctx->data_end; void* data = (void*)(long)ctx->data; if (data + sizeof(struct ethhdr) > data_end) return TC_ACT_SHOT; struct ethhdr *eh = (struct ethhdr *)data; if (eh->h_proto == 0x1) return TC_ACT_SHOT; return TC_ACT_OK; } """ b = BPF(text=text) fn = b.load_func("test", BPF.SCHED_CLS) def test_probe_read_return(self): text = """ #define KBUILD_MODNAME "foo" #include #include static inline unsigned char *my_skb_transport_header(struct sk_buff *skb) { return skb->head + skb->transport_header; } int test(struct pt_regs *ctx, struct sock *sk, struct sk_buff *skb) { struct tcphdr *th = (struct tcphdr *)my_skb_transport_header(skb); return th->seq; } """ b = BPF(text=text) fn = b.load_func("test", BPF.KPROBE) def test_probe_read_multiple_return(self): text = """ #define KBUILD_MODNAME "foo" #include #include static inline u64 error_function() { return 0; } static inline unsigned char *my_skb_transport_header(struct sk_buff *skb) { if (skb) return skb->head + skb->transport_header; return (unsigned char *)error_function(); } int test(struct pt_regs *ctx, struct sock *sk, struct sk_buff *skb) { struct tcphdr *th = (struct tcphdr *)my_skb_transport_header(skb); return th->seq; } """ b = BPF(text=text) fn = b.load_func("test", BPF.KPROBE) def test_probe_read_return_expr(self): text = """ #define KBUILD_MODNAME "foo" #include #include static inline unsigned char *my_skb_transport_header(struct sk_buff *skb) { return skb->head + skb->transport_header; } int test(struct pt_regs *ctx, struct sock *sk, struct sk_buff *skb) { u32 *seq = (u32 *)my_skb_transport_header(skb) + offsetof(struct tcphdr, seq); return *seq; } """ b = BPF(text=text) fn = b.load_func("test", BPF.KPROBE) def test_probe_read_return_call(self): text = """ #define KBUILD_MODNAME "foo" #include #include static inline struct tcphdr *my_skb_transport_header(struct sk_buff *skb) { return (struct tcphdr *)skb->head + skb->transport_header; } int test(struct pt_regs *ctx, struct sock *sk, struct sk_buff *skb) { return my_skb_transport_header(skb)->seq; } """ b = BPF(text=text) fn = b.load_func("test", BPF.KPROBE) def test_no_probe_read_addrof(self): text = """ #include #include static inline int test_help(__be16 *addr) { __be16 val = 0; bpf_probe_read(&val, sizeof(val), addr); return val; } int test(struct pt_regs *ctx) { struct sock *sk; sk = (struct sock *)PT_REGS_PARM1(ctx); return test_help(&sk->sk_dport); } """ b = BPF(text=text) fn = b.load_func("test", BPF.KPROBE) def test_probe_read_array_accesses1(self): text = """ #include #include int test(struct pt_regs *ctx, const struct qstr *name) { return name->name[1]; } """ b = BPF(text=text) fn = b.load_func("test", BPF.KPROBE) def test_probe_read_array_accesses2(self): text = """ #include #include int test(struct pt_regs *ctx, const struct qstr *name) { return name->name [ 1]; } """ b = BPF(text=text) fn = b.load_func("test", BPF.KPROBE) def test_probe_read_array_accesses3(self): text = """ #include #include int test(struct pt_regs *ctx, const struct qstr *name) { return (name->name)[1]; } """ b = BPF(text=text) fn = b.load_func("test", BPF.KPROBE) def test_probe_read_array_accesses4(self): text = """ #include int test(struct pt_regs *ctx, char *name) { return name[1]; } """ b = BPF(text=text) fn = b.load_func("test", BPF.KPROBE) def test_probe_read_array_accesses5(self): text = """ #include int test(struct pt_regs *ctx, char **name) { return (*name)[1]; } """ b = BPF(text=text) fn = b.load_func("test", BPF.KPROBE) def test_probe_read_array_accesses6(self): text = """ #include struct test_t { int tab[5]; }; int test(struct pt_regs *ctx, struct test_t *t) { return *(&t->tab[1]); } """ b = BPF(text=text) fn = b.load_func("test", BPF.KPROBE) def test_probe_read_array_accesses7(self): text = """ #include int test(struct pt_regs *ctx, struct sock *sk) { return sk->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32[0]; } """ b = BPF(text=text) fn = b.load_func("test", BPF.KPROBE) def test_probe_read_array_accesses8(self): text = """ #include int test(struct pt_regs *ctx, struct mm_struct *mm) { return mm->rss_stat.count[MM_ANONPAGES].counter; } """ b = BPF(text=text) fn = b.load_func("test", BPF.KPROBE) def test_arbitrary_increment_simple(self): b = BPF(text=b""" #include struct bpf_map; BPF_HASH(map); int map_delete(struct pt_regs *ctx, struct bpf_map *bpfmap, u64 *k) { map.increment(42, 10); return 0; } """) b.attach_kprobe(event=b"htab_map_delete_elem", fn_name=b"map_delete") b.cleanup() @skipUnless(kernel_version_ge(4,7), "requires kernel >= 4.7") def test_packed_structure(self): b = BPF(text=b""" struct test { u16 a; u32 b; } __packed; BPF_TABLE("hash", u32, struct test, testing, 2); TRACEPOINT_PROBE(kmem, kmalloc) { u32 key = 0; struct test info, *entry; entry = testing.lookup(&key); if (entry == NULL) { info.a = 10; info.b = 20; testing.update(&key, &info); } return 0; } """) if len(b["testing"].items()): st = b["testing"][ct.c_uint(0)] self.assertEqual(st.a, 10) self.assertEqual(st.b, 20) if __name__ == "__main__": main() bpfcc-0.12.0/tests/python/test_clang_complex.c000066400000000000000000000077041357404205000213600ustar00rootroot00000000000000// Copyright (c) PLUMgrid, Inc. // Licensed under the Apache License, Version 2.0 (the "License") #include // hash struct FwdKey { u32 dip:32; }; struct FwdLeaf { u32 fwd_idx:32; }; BPF_HASH(fwd_map, struct FwdKey, struct FwdLeaf, 1); // array struct ConfigKey { u32 index; }; struct ConfigLeaf { u32 bpfdev_ip; u32 slave_ip; }; BPF_TABLE("array", struct ConfigKey, struct ConfigLeaf, config_map, 1); // hash struct MacaddrKey { u32 ip; }; struct MacaddrLeaf { u64 mac; }; BPF_HASH(macaddr_map, struct MacaddrKey, struct MacaddrLeaf, 11); // hash struct SlaveKey { u32 slave_ip; }; struct SlaveLeaf { u32 slave_ifindex; }; BPF_HASH(slave_map, struct SlaveKey, struct SlaveLeaf, 10); int handle_packet(struct __sk_buff *skb) { int ret = 0; u8 *cursor = 0; if (skb->pkt_type == 0) { // tx // make sure configured u32 slave_ip; struct ConfigKey cfg_key = {.index = 0}; struct ConfigLeaf *cfg_leaf = config_map.lookup(&cfg_key); if (cfg_leaf) { slave_ip = cfg_leaf->slave_ip; } else { return 0xffffffff; } // make sure slave configured // tx, default to the single slave struct SlaveKey slave_key = {.slave_ip = slave_ip}; struct SlaveLeaf *slave_leaf = slave_map.lookup(&slave_key); if (slave_leaf) { ret = slave_leaf->slave_ifindex; } else { return 0xffffffff; } } else { // rx, default to stack ret = 0; } struct ethernet_t *ethernet = cursor_advance(cursor, sizeof(*ethernet)); switch (ethernet->type) { case ETH_P_IP: goto ip; case ETH_P_ARP: goto arp; case ETH_P_8021Q: goto dot1q; default: goto EOP; } dot1q: { struct dot1q_t *dot1q = cursor_advance(cursor, sizeof(*dot1q)); switch (dot1q->type) { case ETH_P_IP: goto ip; case ETH_P_ARP: goto arp; default: goto EOP; } } arp: { struct arp_t *arp = cursor_advance(cursor, sizeof(*arp)); if (skb->pkt_type) { if (arp->oper == 1) { struct MacaddrKey mac_key = {.ip=arp->spa}; struct MacaddrLeaf mac_leaf = {.mac=arp->sha}; macaddr_map.update(&mac_key, &mac_leaf); } } goto EOP; } struct ip_t *ip; ip: { ip = cursor_advance(cursor, sizeof(*ip)); switch (ip->nextp) { case 6: goto tcp; case 17: goto udp; default: goto EOP; } } tcp: { struct tcp_t *tcp = cursor_advance(cursor, sizeof(*tcp)); goto EOP; } udp: { struct udp_t *udp = cursor_advance(cursor, sizeof(*udp)); if (udp->dport != 5000) { goto EOP; } if (skb->pkt_type) { // lookup and then forward struct FwdKey fwd_key = {.dip=ip->dst}; struct FwdLeaf *fwd_val = fwd_map.lookup(&fwd_key); if (fwd_val) { return fwd_val->fwd_idx; } } else { // rewrite the packet and send to a pre-configured index if needed u32 new_ip; u32 old_ip; u64 src_mac; u64 dst_mac; struct ConfigKey cfg_key = {.index = 0}; struct ConfigLeaf *cfg_leaf = config_map.lookup(&cfg_key); if (cfg_leaf) { struct MacaddrKey mac_key = {.ip = cfg_leaf->bpfdev_ip}; struct MacaddrLeaf *mac_leaf; mac_key.ip = cfg_leaf->bpfdev_ip; mac_leaf = macaddr_map.lookup(&mac_key); if (mac_leaf) { src_mac = mac_leaf->mac; } else { goto EOP; } mac_key.ip = cfg_leaf->slave_ip; mac_leaf = macaddr_map.lookup(&mac_key); if (mac_leaf) { dst_mac = mac_leaf->mac; } else { goto EOP; } // rewrite ethernet header ethernet->dst = dst_mac; ethernet->src = src_mac; // ip & udp checksum incr_cksum_l4(&udp->crc, ip->src, cfg_leaf->bpfdev_ip, 1); incr_cksum_l4(&udp->crc, ip->dst, cfg_leaf->slave_ip, 1); // rewrite ip src/dst fields ip->src = cfg_leaf->bpfdev_ip; ip->dst = cfg_leaf->slave_ip; } } goto EOP; } EOP: return ret; } bpfcc-0.12.0/tests/python/test_debuginfo.py000077500000000000000000000113351357404205000207130ustar00rootroot00000000000000#!/usr/bin/env python # Copyright (c) Sasha Goldshtein # Licensed under the Apache License, Version 2.0 (the "License") import os import subprocess from bcc import SymbolCache, BPF from unittest import main, TestCase class TestKSyms(TestCase): def grab_sym(self): address = "" aliases = [] # Grab the first symbol in kallsyms that has type 't' or 'T'. # Also, find all aliases of this symbol which are identifiable # by the same address. with open("/proc/kallsyms", "rb") as f: for line in f: # Extract the first 3 columns only. The 4th column # containing the module name may not exist for all # symbols. (addr, t, name) = line.strip().split()[:3] if t == b"t" or t == b"T": if not address: address = addr if addr == address: aliases.append(name) # Return all aliases of the first symbol. return (address, aliases) def test_ksymname(self): sym = BPF.ksymname(b"__kmalloc") self.assertIsNotNone(sym) self.assertNotEqual(sym, 0) def test_ksym(self): (addr, aliases) = self.grab_sym() sym = BPF.ksym(int(addr, 16)) found = sym in aliases self.assertTrue(found) class Harness(TestCase): def setUp(self): self.build_command() subprocess.check_output('objcopy --only-keep-debug dummy dummy.debug' .split()) self.debug_command() subprocess.check_output('strip dummy'.split()) self.process = subprocess.Popen('./dummy', stdout=subprocess.PIPE) # The process prints out the address of some symbol, which we then # try to resolve in the test. self.addr = int(self.process.stdout.readline().strip(), 16) self.syms = SymbolCache(self.process.pid) def tearDown(self): self.process.kill() self.process.wait() self.process.stdout.close() self.process = None def resolve_addr(self): sym, offset, module = self.syms.resolve(self.addr, False) self.assertEqual(sym, self.mangled_name) self.assertEqual(offset, 0) self.assertTrue(module[-5:] == b'dummy') sym, offset, module = self.syms.resolve(self.addr, True) self.assertEqual(sym, b'some_namespace::some_function(int, int)') self.assertEqual(offset, 0) self.assertTrue(module[-5:] == b'dummy') def resolve_name(self): script_dir = os.path.dirname(os.path.realpath(__file__).encode("utf8")) addr = self.syms.resolve_name(os.path.join(script_dir, b'dummy'), self.mangled_name) self.assertEqual(addr, self.addr) pass class TestDebuglink(Harness): def build_command(self): subprocess.check_output('g++ -o dummy dummy.cc'.split()) lines = subprocess.check_output('nm dummy'.split()).splitlines() for line in lines: if b"some_function" in line: self.mangled_name = line.split(b' ')[2] break self.assertTrue(self.mangled_name) def debug_command(self): subprocess.check_output('objcopy --add-gnu-debuglink=dummy.debug dummy' .split()) def tearDown(self): super(TestDebuglink, self).tearDown() subprocess.check_output('rm dummy dummy.debug'.split()) def test_resolve_addr(self): self.resolve_addr() def test_resolve_name(self): self.resolve_name() class TestBuildid(Harness): def build_command(self): subprocess.check_output(('g++ -o dummy -Xlinker ' + \ '--build-id=0x123456789abcdef0123456789abcdef012345678 dummy.cc') .split()) lines = subprocess.check_output('nm dummy'.split()).splitlines() for line in lines: if b"some_function" in line: self.mangled_name = line.split(b' ')[2] break self.assertTrue(self.mangled_name) def debug_command(self): subprocess.check_output('mkdir -p /usr/lib/debug/.build-id/12'.split()) subprocess.check_output(('mv dummy.debug /usr/lib/debug/.build-id' + \ '/12/3456789abcdef0123456789abcdef012345678.debug').split()) def tearDown(self): super(TestBuildid, self).tearDown() subprocess.check_output('rm dummy'.split()) subprocess.check_output(('rm /usr/lib/debug/.build-id/12' + '/3456789abcdef0123456789abcdef012345678.debug').split()) def test_resolve_name(self): self.resolve_addr() def test_resolve_addr(self): self.resolve_name() if __name__ == "__main__": main() bpfcc-0.12.0/tests/python/test_disassembler.py000077500000000000000000000157251357404205000214350ustar00rootroot00000000000000#!/usr/bin/env python # Copyright (c) Clevernet # Licensed under the Apache License, Version 2.0 (the "License") # test program for the 'disassemble_func' and 'decode_table' methods from bcc import BPF from bcc import disassembler import ctypes as ct import random from unittest import main, TestCase class BPFInstr(ct.Structure): _pack_ = 1 _fields_ = [('opcode', ct.c_uint8), ('dst', ct.c_uint8, 4), ('src', ct.c_uint8, 4), ('offset', ct.c_int16), ('imm', ct.c_int32)] class TestDisassembler(TestCase): opcodes = [(0x04, "%dst += %imm"), (0x05, "goto %off <%jmp>"), (0x07, "%dst += %imm"), (0x0c, "%dst += %src"), (0x0f, "%dst += %src"), (0x14, "%dst -= %imm"), (0x15, "if %dst == %imm goto pc%off <%jmp>"), (0x17, "%dst -= %imm"), #(0x18, "lddw"), (0x1c, "%dst -= %src"), (0x1d, "if %dst == %src goto pc%off <%jmp>"), (0x1f, "%dst -= %src"), (0x20, "r0 = *(u32*)skb[%imm]"), (0x24, "%dst *= %imm"), (0x25, "if %dst > %imm goto pc%off <%jmp>"), (0x27, "%dst *= %imm"), (0x28, "r0 = *(u16*)skb[%imm]"), (0x2c, "%dst *= %src"), (0x2d, "if %dst > %src goto pc%off <%jmp>"), (0x2f, "%dst *= %src"), (0x30, "r0 = *(u8*)skb[%imm]"), (0x34, "%dst /= %imm"), (0x35, "if %dst >= %imm goto pc%off <%jmp>"), (0x37, "%dst /= %imm"), (0x38, "r0 = *(u64*)skb[%imm]"), (0x3c, "%dst /= %src"), (0x3d, "if %dst >= %src goto pc%off <%jmp>"), (0x3f, "%dst /= %src"), (0x40, "r0 = *(u32*)skb[%src %sim]"), (0x44, "%dst |= %ibw"), (0x45, "if %dst & %imm goto pc%off <%jmp>"), (0x47, "%dst |= %ibw"), (0x48, "r0 = *(u16*)skb[%src %sim]"), (0x4c, "%dst |= %src"), (0x4d, "if %dst & %src goto pc%off <%jmp>"), (0x4f, "%dst |= %src"), (0x50, "r0 = *(u8*)skb[%src %sim]"), (0x54, "%dst &= %ibw"), (0x55, "if %dst != %imm goto pc%off <%jmp>"), (0x57, "%dst &= %ibw"), (0x58, "r0 = *(u64*)skb[%src %sim]"), (0x5c, "%dst &= %src"), (0x5d, "if %dst != %src goto pc%off <%jmp>"), (0x5f, "%dst &= %src"), (0x61, "%dst = *(u32*)(%src %off)"), (0x62, "*(u32*)(%dst %off) = %imm"), (0x63, "*(u32*)(%dst %off) = %src"), (0x64, "%dst <<= %imm"), (0x65, "if %dst s> %imm goto pc%off <%jmp>"), (0x67, "%dst <<= %imm"), (0x69, "%dst = *(u16*)(%src %off)"), (0x6a, "*(u16*)(%dst %off) = %imm"), (0x6b, "*(u16*)(%dst %off) = %src"), (0x6c, "%dst <<= %src"), (0x6d, "if %dst s> %src goto pc%off <%jmp>"), (0x6f, "%dst <<= %src"), (0x71, "%dst = *(u8*)(%src %off)"), (0x72, "*(u8*)(%dst %off) = %imm"), (0x73, "*(u8*)(%dst %off) = %src"), (0x74, "%dst >>= %imm"), (0x75, "if %dst s>= %imm goto pc%off <%jmp>"), (0x77, "%dst >>= %imm"), (0x79, "%dst = *(u64*)(%src %off)"), (0x7a, "*(u64*)(%dst %off) = %imm"), (0x7b, "*(u64*)(%dst %off) = %src"), (0x7c, "%dst >>= %src"), (0x7d, "if %dst s>= %src goto pc%off <%jmp>"), (0x7f, "%dst >>= %src"), (0x84, "%dst = ~ (u32)%dst"), #(0x85, "call"), (0x87, "%dst = ~ (u64)%dst"), (0x94, "%dst %= %imm"), (0x95, "exit"), (0x97, "%dst %= %imm"), (0x9c, "%dst %= %src"), (0x9f, "%dst %= %src"), (0xa4, "%dst ^= %ibw"), (0xa5, "if %dst < %imm goto pc%off <%jmp>"), (0xa7, "%dst ^= %ibw"), (0xac, "%dst ^= %src"), (0xad, "if %dst < %src goto pc%off <%jmp>"), (0xaf, "%dst ^= %src"), (0xb4, "%dst = %imm"), (0xb5, "if %dst <= %imm goto pc%off <%jmp>"), (0xb7, "%dst = %imm"), (0xbc, "%dst = %src"), (0xbd, "if %dst <= %src goto pc%off <%jmp>"), (0xbf, "%dst = %src"), (0xc4, "%dst s>>= %imm"), (0xc5, "if %dst s< %imm goto pc%off <%jmp>"), (0xc7, "%dst s>>= %imm"), (0xcc, "%dst s>>= %src"), (0xcd, "if %dst s< %src goto pc%off <%jmp>"), (0xcf, "%dst s>>= %src"), (0xd5, "if %dst s<= %imm goto pc%off <%jmp>"), (0xdc, "%dst endian %src"), (0xdd, "if %dst s<= %imm goto pc%off <%jmp>"),] @classmethod def build_instr(cls, op): dst = random.randint(0, 0xf) src = random.randint(0, 0xf) offset = random.randint(0, 0xffff) imm = random.randint(0, 0xffffffff) return BPFInstr(op, dst, src, offset, imm) @classmethod def format_instr(cls, instr, fmt): uimm = ct.c_uint32(instr.imm).value return (fmt.replace("%dst", "r%d" % (instr.dst)) .replace("%src", "r%d" % (instr.src)) .replace("%imm", "%d" % (instr.imm)) .replace("%ibw", "0x%x" % (uimm)) .replace("%sim", "%+d" % (instr.imm)) .replace("%off", "%+d" % (instr.offset)) .replace("%jmp", "%d" % (instr.offset + 1))) def test_func(self): b = BPF(text=""" struct key_t {int a; short b; struct {int c:4; int d:8;} e;} __attribute__((__packed__)); BPF_HASH(test_map, struct key_t); int test_func(void) { return 1; }""") self.assertEqual( """Disassemble of BPF program test_func: 0: (b7) r0 = 1 1: (95) exit""", b.disassemble_func("test_func")) self.assertEqual( """Layout of BPF map test_map (type HASH, FD 3, ID 0): struct { int a; short b; struct { int c:4; int d:8; } e; } key; unsigned long long value;""", b.decode_table("test_map")) def test_bpf_isa(self): for op, instr_fmt in self.opcodes: instr_fmt if instr_fmt is None: continue instr = self.build_instr(op) instr_str = ct.string_at(ct.addressof(instr), ct.sizeof(instr)) target_text = self.format_instr(instr, instr_fmt) self.assertEqual(disassembler.disassemble_str(instr_str)[0], "%4d: (%02x) %s" % (0, op, target_text)) if __name__ == "__main__": main() bpfcc-0.12.0/tests/python/test_dump_func.py000077500000000000000000000011121357404205000207210ustar00rootroot00000000000000#!/usr/bin/env python # Copyright (c) PLUMgrid, Inc. # Licensed under the Apache License, Version 2.0 (the "License") # test program for the 'dump_func' method from bcc import BPF from unittest import main, TestCase class TestDumpFunc(TestCase): def test_return(self): b = BPF(text=""" int entry(void) { return 1; }""") self.assertEqual( b"\xb7\x00\x00\x00\x01\x00\x00\x00" + b"\x95\x00\x00\x00\x00\x00\x00\x00", b.dump_func("entry")) if __name__ == "__main__": main() bpfcc-0.12.0/tests/python/test_flags.py000066400000000000000000000013101357404205000200320ustar00rootroot00000000000000#!/usr/bin/env python # Copyright (c) PLUMgrid, Inc. # Licensed under the Apache License, Version 2.0 (the "License") import unittest from bcc import BPF class TestLru(unittest.TestCase): def test_lru_map_flags(self): test_prog1 = """ BPF_F_TABLE("lru_hash", int, u64, lru, 1024, BPF_F_NO_COMMON_LRU); """ b = BPF(text=test_prog1) t = b["lru"] self.assertEqual(t.flags, 2); def test_hash_map_flags(self): test_prog1 = """ BPF_F_TABLE("hash", int, u64, hash, 1024, BPF_F_NO_PREALLOC); """ b = BPF(text=test_prog1) t = b["hash"] self.assertEqual(t.flags, 1); if __name__ == "__main__": unittest.main() bpfcc-0.12.0/tests/python/test_free_bcc_memory.py000077500000000000000000000034601357404205000220710ustar00rootroot00000000000000#!/usr/bin/env python # # USAGE: test_usdt.py # # Copyright 2018 Facebook, Inc # Licensed under the Apache License, Version 2.0 (the "License") from __future__ import print_function from bcc import BPF from unittest import main, skipUnless, TestCase from subprocess import Popen, PIPE import distutils.version import os def kernel_version_ge(major, minor): # True if running kernel is >= X.Y version = distutils.version.LooseVersion(os.uname()[2]).version if version[0] > major: return True if version[0] < major: return False if minor and version[1] < minor: return False return True class TestFreeLLVMMemory(TestCase): def getRssFile(self): p = Popen(["cat", "/proc/" + str(os.getpid()) + "/status"], stdout=PIPE) rss = None unit = None for line in p.stdout.readlines(): if (line.find(b'RssFile') >= 0): rss = line.split(b' ')[-2] unit = line.split(b' ')[-1].rstrip() break return [rss, unit] @skipUnless(kernel_version_ge(4,5), "requires kernel >= 4.5") def testFreeLLVMMemory(self): text = "int test() { return 0; }" b = BPF(text=text) # get the RssFile before freeing bcc memory [rss1, unit1] = self.getRssFile() self.assertTrue(rss1 != None) # free the bcc memory self.assertTrue(b.free_bcc_memory() == 0) # get the RssFile after freeing bcc memory [rss2, unit2] = self.getRssFile() self.assertTrue(rss2 != None) self.assertTrue(unit1 == unit2) print("Before freeing llvm memory: RssFile: ", rss1, unit1) print("After freeing llvm memory: RssFile: ", rss2, unit2) self.assertTrue(rss1 > rss2) if __name__ == "__main__": main() bpfcc-0.12.0/tests/python/test_histogram.py000077500000000000000000000063341357404205000207510ustar00rootroot00000000000000#!/usr/bin/env python # Copyright (c) PLUMgrid, Inc. # Licensed under the Apache License, Version 2.0 (the "License") from bcc import BPF from ctypes import c_int, c_ulonglong import random import time from unittest import main, TestCase class TestHistogram(TestCase): def test_simple(self): b = BPF(text=""" #include struct bpf_map; BPF_HISTOGRAM(hist1); BPF_HASH(stub); int kprobe__htab_map_delete_elem(struct pt_regs *ctx, struct bpf_map *map, u64 *k) { hist1.increment(bpf_log2l(*k)); return 0; } """) for i in range(0, 32): for j in range(0, random.randint(1, 10)): try: del b["stub"][c_ulonglong(1 << i)] except: pass b["hist1"].print_log2_hist() for i in range(32, 64): for j in range(0, random.randint(1, 10)): try: del b["stub"][c_ulonglong(1 << i)] except: pass b["hist1"].print_log2_hist() b.cleanup() def test_struct(self): b = BPF(text=""" #include struct bpf_map; typedef struct { void *map; u64 slot; } Key; BPF_HISTOGRAM(hist1, Key, 1024); BPF_HASH(stub1); BPF_HASH(stub2); int kprobe__htab_map_delete_elem(struct pt_regs *ctx, struct bpf_map *map, u64 *k) { hist1.increment((Key){map, bpf_log2l(*k)}); return 0; } """) for i in range(0, 64): for j in range(0, random.randint(1, 10)): try: del b["stub1"][c_ulonglong(1 << i)] except: pass try: del b["stub2"][c_ulonglong(1 << i)] except: pass b["hist1"].print_log2_hist() b.cleanup() def test_chars(self): b = BPF(text=""" #include #include typedef struct { char name[TASK_COMM_LEN]; u64 slot; } Key; BPF_HISTOGRAM(hist1, Key, 1024); int kprobe__finish_task_switch(struct pt_regs *ctx, struct task_struct *prev) { Key k = {.slot = bpf_log2l(prev->real_start_time)}; if (!bpf_get_current_comm(&k.name, sizeof(k.name))) hist1.increment(k); return 0; } """) for i in range(0, 100): time.sleep(0.01) b["hist1"].print_log2_hist() b.cleanup() def test_multiple_key(self): b = BPF(text=""" #include #include struct hist_s_key { u64 key_1; u64 key_2; }; struct hist_key { struct hist_s_key s_key; u64 slot; }; BPF_HISTOGRAM(mk_hist, struct hist_key, 1024); int kprobe__vfs_read(struct pt_regs *ctx, struct file *file, char __user *buf, size_t count) { struct hist_key key = {.slot = bpf_log2l(count)}; key.s_key.key_1 = (unsigned long)buf & 0x70; key.s_key.key_2 = (unsigned long)buf & 0x7; mk_hist.increment(key); return 0; } """) def bucket_sort(buckets): buckets.sort() return buckets for i in range(0, 100): time.sleep(0.01) b["mk_hist"].print_log2_hist("size", "k_1 & k_2", section_print_fn=lambda bucket: "%3d %d" % (bucket[0], bucket[1]), bucket_fn=lambda bucket: (bucket.key_1, bucket.key_2), strip_leading_zero=True, bucket_sort_fn=bucket_sort) b.cleanup() if __name__ == "__main__": main() bpfcc-0.12.0/tests/python/test_license.py000077500000000000000000000057021357404205000203740ustar00rootroot00000000000000#!/usr/bin/env python # Copyright (c) 2018 Clevernet, Inc. # Licensed under the Apache License, Version 2.0 (the "License") import unittest from bcc import BPF class TestLicense(unittest.TestCase): gpl_only_text = """ #include struct gpl_s { u64 ts; }; BPF_PERF_OUTPUT(events); int license_program(struct pt_regs *ctx) { struct gpl_s data = {}; data.ts = bpf_ktime_get_ns(); events.perf_submit(ctx, &data, sizeof(data)); return 0; } """ proprietary_text = """ #include struct key_t { u64 ip; u32 pid; u32 uid; char comm[16]; }; BPF_HASH(counts, struct key_t); int license_program(struct pt_regs *ctx) { struct key_t key = {}; u64 zero = 0 , *val; u64 pid = bpf_get_current_pid_tgid(); u32 uid = bpf_get_current_uid_gid(); key.ip = PT_REGS_IP(ctx); key.pid = pid & 0xFFFFFFFF; key.uid = uid & 0xFFFFFFFF; bpf_get_current_comm(&(key.comm), 16); val = counts.lookup_or_try_init(&key, &zero); // update counter if (val) { (*val)++; } return 0; } """ def license(self, lic): return ''' #define BPF_LICENSE %s ''' % (lic) def load_bpf_code(self, bpf_code): event_name = bpf_code.get_syscall_fnname("read") bpf_code.attach_kprobe(event=event_name, fn_name="license_program") bpf_code.detach_kprobe(event=event_name) def test_default(self): b = BPF(text=self.gpl_only_text) self.load_bpf_code(b) def test_gpl_helper_macro(self): b = BPF(text=self.gpl_only_text + self.license('GPL')) self.load_bpf_code(b) def test_proprietary_macro(self): b = BPF(text=self.proprietary_text + self.license('Proprietary')) self.load_bpf_code(b) def test_gpl_compatible_macro(self): b = BPF(text=self.gpl_only_text + self.license('Dual BSD/GPL')) self.load_bpf_code(b) def test_proprietary_words_macro(self): b = BPF(text=self.proprietary_text + self.license('Proprietary license')) self.load_bpf_code(b) @unittest.expectedFailure def test_cflags_fail(self): b = BPF(text=self.gpl_only_text, cflags=["-DBPF_LICENSE=GPL"]) self.load_bpf_code(b) @unittest.expectedFailure def test_cflags_macro_fail(self): b = BPF(text=self.gpl_only_text + self.license('GPL'), cflags=["-DBPF_LICENSE=GPL"]) self.load_bpf_code(b) @unittest.expectedFailure def test_empty_fail_macro(self): b = BPF(text=self.gpl_only_text + self.license('')) self.load_bpf_code(b) @unittest.expectedFailure def test_proprietary_fail_macro(self): b = BPF(text=self.gpl_only_text + self.license('Proprietary license')) self.load_bpf_code(b) @unittest.expectedFailure def test_proprietary_cflags_fail(self): b = BPF(text=self.proprietary_text, cflags=["-DBPF_LICENSE=Proprietary"]) self.load_bpf_code(b) if __name__ == "__main__": unittest.main() bpfcc-0.12.0/tests/python/test_lpm_trie.py000066400000000000000000000036671357404205000205720ustar00rootroot00000000000000#!/usr/bin/env python # Copyright (c) 2017 Facebook, Inc. # Licensed under the Apache License, Version 2.0 (the "License") import ctypes as ct import unittest from bcc import BPF from netaddr import IPAddress class KeyV4(ct.Structure): _fields_ = [("prefixlen", ct.c_uint), ("data", ct.c_ubyte * 4)] class KeyV6(ct.Structure): _fields_ = [("prefixlen", ct.c_uint), ("data", ct.c_ushort * 8)] class TestLpmTrie(unittest.TestCase): def test_lpm_trie_v4(self): test_prog1 = """ BPF_LPM_TRIE(trie, u64, int, 16); """ b = BPF(text=test_prog1) t = b["trie"] k1 = KeyV4(24, (192, 168, 0, 0)) v1 = ct.c_int(24) t[k1] = v1 k2 = KeyV4(28, (192, 168, 0, 0)) v2 = ct.c_int(28) t[k2] = v2 k = KeyV4(32, (192, 168, 0, 15)) self.assertEqual(t[k].value, 28) k = KeyV4(32, (192, 168, 0, 127)) self.assertEqual(t[k].value, 24) with self.assertRaises(KeyError): k = KeyV4(32, (172, 16, 1, 127)) v = t[k] def test_lpm_trie_v6(self): test_prog1 = """ struct key_v6 { u32 prefixlen; u32 data[4]; }; BPF_LPM_TRIE(trie, struct key_v6, int, 16); """ b = BPF(text=test_prog1) t = b["trie"] k1 = KeyV6(64, IPAddress('2a00:1450:4001:814:200e::').words) v1 = ct.c_int(64) t[k1] = v1 k2 = KeyV6(96, IPAddress('2a00:1450:4001:814::200e').words) v2 = ct.c_int(96) t[k2] = v2 k = KeyV6(128, IPAddress('2a00:1450:4001:814::1024').words) self.assertEqual(t[k].value, 96) k = KeyV6(128, IPAddress('2a00:1450:4001:814:2046::').words) self.assertEqual(t[k].value, 64) with self.assertRaises(KeyError): k = KeyV6(128, IPAddress('2a00:ffff::').words) v = t[k] if __name__ == "__main__": unittest.main() bpfcc-0.12.0/tests/python/test_lru.py000066400000000000000000000040571357404205000175530ustar00rootroot00000000000000#!/usr/bin/env python # Copyright (c) PLUMgrid, Inc. # Licensed under the Apache License, Version 2.0 (the "License") import ctypes as ct import os import unittest from bcc import BPF import multiprocessing class TestLru(unittest.TestCase): def test_lru_hash(self): b = BPF(text="""BPF_TABLE("lru_hash", int, u64, lru, 1024);""") t = b["lru"] for i in range(1, 1032): t[ct.c_int(i)] = ct.c_ulonglong(i) for i, v in t.items(): self.assertEqual(v.value, i.value) # BPF_MAP_TYPE_LRU_HASH eviction happens in batch and we expect less # items than specified size. self.assertLess(len(t), 1024); def test_lru_percpu_hash(self): test_prog1 = """ BPF_TABLE("lru_percpu_hash", u32, u32, stats, 1); int hello_world(void *ctx) { u32 key=0; u32 value = 0, *val; val = stats.lookup_or_try_init(&key, &value); if (val) { *val += 1; } return 0; } """ b = BPF(text=test_prog1) stats_map = b.get_table("stats") event_name = b.get_syscall_fnname("clone") b.attach_kprobe(event=event_name, fn_name="hello_world") ini = stats_map.Leaf() for i in range(0, multiprocessing.cpu_count()): ini[i] = 0 # First initialize with key 1 stats_map[ stats_map.Key(1) ] = ini # Then initialize with key 0 stats_map[ stats_map.Key(0) ] = ini # Key 1 should have been evicted with self.assertRaises(KeyError): val = stats_map[ stats_map.Key(1) ] f = os.popen("hostname") f.close() self.assertEqual(len(stats_map),1) val = stats_map[ stats_map.Key(0) ] sum = stats_map.sum(stats_map.Key(0)) avg = stats_map.average(stats_map.Key(0)) max = stats_map.max(stats_map.Key(0)) self.assertGreater(sum.value, 0) self.assertGreater(max.value, 0) b.detach_kprobe(event_name) if __name__ == "__main__": unittest.main() bpfcc-0.12.0/tests/python/test_map_in_map.py000077500000000000000000000077361357404205000210630ustar00rootroot00000000000000#!/usr/bin/env python # # USAGE: test_map_in_map.py # # Copyright 2019 Facebook, Inc # Licensed under the Apache License, Version 2.0 (the "License") from __future__ import print_function from bcc import BPF import distutils.version from unittest import main, skipUnless, TestCase import ctypes as ct import os def kernel_version_ge(major, minor): # True if running kernel is >= X.Y version = distutils.version.LooseVersion(os.uname()[2]).version if version[0] > major: return True if version[0] < major: return False if minor and version[1] < minor: return False return True @skipUnless(kernel_version_ge(4,11), "requires kernel >= 4.11") class TestUDST(TestCase): def test_hash_table(self): bpf_text = """ BPF_ARRAY(cntl, int, 1); BPF_TABLE("hash", int, int, ex1, 1024); BPF_TABLE("hash", int, int, ex2, 1024); BPF_HASH_OF_MAPS(maps_hash, "ex1", 10); int syscall__getuid(void *ctx) { int key = 0, data, *val, cntl_val; void *inner_map; val = cntl.lookup(&key); if (!val || *val == 0) return 0; cntl_val = *val; inner_map = maps_hash.lookup(&cntl_val); if (!inner_map) return 0; val = bpf_map_lookup_elem(inner_map, &key); if (!val) { data = 1; bpf_map_update_elem(inner_map, &key, &data, 0); } else { data = 1 + *val; bpf_map_update_elem(inner_map, &key, &data, 0); } return 0; } """ b = BPF(text=bpf_text) cntl_map = b.get_table("cntl") ex1_map = b.get_table("ex1") ex2_map = b.get_table("ex2") hash_maps = b.get_table("maps_hash") hash_maps[ct.c_int(1)] = ct.c_int(ex1_map.get_fd()) hash_maps[ct.c_int(2)] = ct.c_int(ex2_map.get_fd()) syscall_fnname = b.get_syscall_fnname("getuid") b.attach_kprobe(event=syscall_fnname, fn_name="syscall__getuid") try: ex1_map[ct.c_int(0)] raise Exception("Unexpected success for ex1_map[0]") except KeyError: pass cntl_map[0] = ct.c_int(1) os.getuid() assert(ex1_map[ct.c_int(0)] >= 1) try: ex2_map[ct.c_int(0)] raise Exception("Unexpected success for ex2_map[0]") except KeyError: pass cntl_map[0] = ct.c_int(2) os.getuid() assert(ex2_map[ct.c_int(0)] >= 1) b.detach_kprobe(event=syscall_fnname) del hash_maps[ct.c_int(1)] del hash_maps[ct.c_int(2)] def test_array_table(self): bpf_text = """ BPF_ARRAY(cntl, int, 1); BPF_ARRAY(ex1, int, 1024); BPF_ARRAY(ex2, int, 1024); BPF_ARRAY_OF_MAPS(maps_array, "ex1", 10); int syscall__getuid(void *ctx) { int key = 0, data, *val, cntl_val; void *inner_map; val = cntl.lookup(&key); if (!val || *val == 0) return 0; cntl_val = *val; inner_map = maps_array.lookup(&cntl_val); if (!inner_map) return 0; val = bpf_map_lookup_elem(inner_map, &key); if (val) { data = 1 + *val; bpf_map_update_elem(inner_map, &key, &data, 0); } return 0; } """ b = BPF(text=bpf_text) cntl_map = b.get_table("cntl") ex1_map = b.get_table("ex1") ex2_map = b.get_table("ex2") array_maps = b.get_table("maps_array") array_maps[ct.c_int(1)] = ct.c_int(ex1_map.get_fd()) array_maps[ct.c_int(2)] = ct.c_int(ex2_map.get_fd()) syscall_fnname = b.get_syscall_fnname("getuid") b.attach_kprobe(event=syscall_fnname, fn_name="syscall__getuid") cntl_map[0] = ct.c_int(1) os.getuid() assert(ex1_map[ct.c_int(0)] >= 1) cntl_map[0] = ct.c_int(2) os.getuid() assert(ex2_map[ct.c_int(0)] >= 1) b.detach_kprobe(event=syscall_fnname) if __name__ == "__main__": main() bpfcc-0.12.0/tests/python/test_percpu.py000077500000000000000000000100551357404205000202450ustar00rootroot00000000000000#!/usr/bin/env python # Copyright (c) PLUMgrid, Inc. # Licensed under the Apache License, Version 2.0 (the "License") import os import unittest from bcc import BPF import multiprocessing class TestPercpu(unittest.TestCase): def setUp(self): try: b = BPF(text='BPF_TABLE("percpu_array", u32, u32, stub, 1);') except: raise unittest.SkipTest("PerCpu unsupported on this kernel") def test_helper(self): test_prog1 = """ BPF_PERCPU_ARRAY(stub_default); BPF_PERCPU_ARRAY(stub_type, u64); BPF_PERCPU_ARRAY(stub_full, u64, 1024); """ BPF(text=test_prog1) def test_u64(self): test_prog1 = """ BPF_TABLE("percpu_hash", u32, u64, stats, 1); int hello_world(void *ctx) { u32 key=0; u64 value = 0, *val; val = stats.lookup_or_try_init(&key, &value); if (val) { *val += 1; } return 0; } """ bpf_code = BPF(text=test_prog1) stats_map = bpf_code.get_table("stats") event_name = bpf_code.get_syscall_fnname("clone") bpf_code.attach_kprobe(event=event_name, fn_name="hello_world") ini = stats_map.Leaf() for i in range(0, multiprocessing.cpu_count()): ini[i] = 0 stats_map[ stats_map.Key(0) ] = ini f = os.popen("hostname") f.close() self.assertEqual(len(stats_map),1) val = stats_map[ stats_map.Key(0) ] sum = stats_map.sum(stats_map.Key(0)) avg = stats_map.average(stats_map.Key(0)) max = stats_map.max(stats_map.Key(0)) self.assertGreater(sum.value, int(0)) self.assertGreater(max.value, int(0)) bpf_code.detach_kprobe(event_name) def test_u32(self): test_prog1 = """ BPF_TABLE("percpu_array", u32, u32, stats, 1); int hello_world(void *ctx) { u32 key=0; u32 value = 0, *val; val = stats.lookup_or_try_init(&key, &value); if (val) { *val += 1; } return 0; } """ bpf_code = BPF(text=test_prog1) stats_map = bpf_code.get_table("stats") event_name = bpf_code.get_syscall_fnname("clone") bpf_code.attach_kprobe(event=event_name, fn_name="hello_world") ini = stats_map.Leaf() for i in range(0, multiprocessing.cpu_count()): ini[i] = 0 stats_map[ stats_map.Key(0) ] = ini f = os.popen("hostname") f.close() self.assertEqual(len(stats_map),1) val = stats_map[ stats_map.Key(0) ] sum = stats_map.sum(stats_map.Key(0)) avg = stats_map.average(stats_map.Key(0)) max = stats_map.max(stats_map.Key(0)) self.assertGreater(sum.value, int(0)) self.assertGreater(max.value, int(0)) bpf_code.detach_kprobe(event_name) def test_struct_custom_func(self): test_prog2 = """ typedef struct counter { u32 c1; u32 c2; } counter; BPF_TABLE("percpu_hash", u32, counter, stats, 1); int hello_world(void *ctx) { u32 key=0; counter value = {0,0}, *val; val = stats.lookup_or_try_init(&key, &value); if (val) { val->c1 += 1; val->c2 += 1; } return 0; } """ bpf_code = BPF(text=test_prog2) stats_map = bpf_code.get_table("stats", reducer=lambda x,y: stats_map.sLeaf(x.c1+y.c1)) event_name = bpf_code.get_syscall_fnname("clone") bpf_code.attach_kprobe(event=event_name, fn_name="hello_world") ini = stats_map.Leaf() for i in ini: i = stats_map.sLeaf(0,0) stats_map[ stats_map.Key(0) ] = ini f = os.popen("hostname") f.close() self.assertEqual(len(stats_map),1) k = stats_map[ stats_map.Key(0) ] self.assertGreater(k.c1, int(0)) bpf_code.detach_kprobe(event_name) if __name__ == "__main__": unittest.main() bpfcc-0.12.0/tests/python/test_perf_event.py000077500000000000000000000031401357404205000211010ustar00rootroot00000000000000#!/usr/bin/env python # Copyright (c) 2016 PLUMgrid # Licensed under the Apache License, Version 2.0 (the "License") import bcc import ctypes import multiprocessing import os import time import unittest class TestPerfCounter(unittest.TestCase): def test_cycles(self): text = """ BPF_PERF_ARRAY(cnt1, NUM_CPUS); BPF_ARRAY(prev, u64, NUM_CPUS); BPF_HISTOGRAM(dist); int do_sys_getuid(void *ctx) { u32 cpu = bpf_get_smp_processor_id(); u64 val = cnt1.perf_read(CUR_CPU_IDENTIFIER); if (((s64)val < 0) && ((s64)val > -256)) return 0; prev.update(&cpu, &val); return 0; } int do_ret_sys_getuid(void *ctx) { u32 cpu = bpf_get_smp_processor_id(); u64 val = cnt1.perf_read(CUR_CPU_IDENTIFIER); if (((s64)val < 0) && ((s64)val > -256)) return 0; u64 *prevp = prev.lookup(&cpu); if (prevp) dist.increment(bpf_log2l(val - *prevp)); return 0; } """ b = bcc.BPF(text=text, debug=0, cflags=["-DNUM_CPUS=%d" % multiprocessing.cpu_count()]) event_name = b.get_syscall_fnname("getuid") b.attach_kprobe(event=event_name, fn_name="do_sys_getuid") b.attach_kretprobe(event=event_name, fn_name="do_ret_sys_getuid") cnt1 = b["cnt1"] try: cnt1.open_perf_event(bcc.PerfType.HARDWARE, bcc.PerfHWConfig.CPU_CYCLES) except: if ctypes.get_errno() == 2: raise self.skipTest("hardware events unsupported") raise for i in range(0, 100): os.getuid() b["dist"].print_log2_hist() if __name__ == "__main__": unittest.main() bpfcc-0.12.0/tests/python/test_probe_count.py000077500000000000000000000051451357404205000212720ustar00rootroot00000000000000#!/usr/bin/env python # Copyright (c) Suchakra Sharma # Licensed under the Apache License, Version 2.0 (the "License") from bcc import BPF, _get_num_open_probes, TRACEFS import os import sys from unittest import main, TestCase class TestKprobeCnt(TestCase): def setUp(self): self.b = BPF(text=""" int wololo(void *ctx) { return 0; } """) self.b.attach_kprobe(event_re="^vfs_.*", fn_name="wololo") def test_attach1(self): actual_cnt = 0 with open("%s/available_filter_functions" % TRACEFS, "rb") as f: for line in f: if line.startswith(b"vfs_"): actual_cnt += 1 open_cnt = self.b.num_open_kprobes() self.assertEqual(actual_cnt, open_cnt) def tearDown(self): self.b.cleanup() class TestProbeGlobalCnt(TestCase): def setUp(self): self.b1 = BPF(text="""int count(void *ctx) { return 0; }""") self.b2 = BPF(text="""int count(void *ctx) { return 0; }""") def test_probe_quota(self): self.b1.attach_kprobe(event="schedule", fn_name="count") self.b2.attach_kprobe(event="submit_bio", fn_name="count") self.assertEqual(1, self.b1.num_open_kprobes()) self.assertEqual(1, self.b2.num_open_kprobes()) self.assertEqual(2, _get_num_open_probes()) self.b1.cleanup() self.b2.cleanup() self.assertEqual(0, _get_num_open_probes()) class TestAutoKprobe(TestCase): def setUp(self): self.b = BPF(text=""" int kprobe__schedule(void *ctx) { return 0; } int kretprobe__schedule(void *ctx) { return 0; } """) def test_count(self): self.assertEqual(2, self.b.num_open_kprobes()) def tearDown(self): self.b.cleanup() class TestProbeQuota(TestCase): def setUp(self): self.b = BPF(text="""int count(void *ctx) { return 0; }""") def test_probe_quota(self): with self.assertRaises(Exception): self.b.attach_kprobe(event_re=".*", fn_name="count") def test_uprobe_quota(self): with self.assertRaises(Exception): self.b.attach_uprobe(name="c", sym_re=".*", fn_name="count") def tearDown(self): self.b.cleanup() class TestProbeNotExist(TestCase): def setUp(self): self.b = BPF(text="""int count(void *ctx) { return 0; }""") def test_not_exist(self): with self.assertRaises(Exception): self.b.attach_kprobe(event="___doesnotexist", fn_name="count") def tearDown(self): self.b.cleanup() if __name__ == "__main__": main() bpfcc-0.12.0/tests/python/test_rlimit.py000077500000000000000000000020761357404205000202530ustar00rootroot00000000000000#!/usr/bin/env python # # USAGE: test_usdt.py # # Copyright 2018 Facebook, Inc # Licensed under the Apache License, Version 2.0 (the "License") from __future__ import print_function from bcc import BPF from unittest import main, skipUnless, TestCase import distutils.version import os, resource class TestRlimitMemlock(TestCase): def testRlimitMemlock(self): text = """ BPF_HASH(unused, u64, u64, 65536); int test() { return 0; } """ # save the original memlock limits memlock_limit = resource.getrlimit(resource.RLIMIT_MEMLOCK) # set a small RLIMIT_MEMLOCK limit resource.setrlimit(resource.RLIMIT_MEMLOCK, (4096, 4096)) # below will fail failed = 0 try: b = BPF(text=text, allow_rlimit=False) except: failed = 1 self.assertEqual(failed, 1) # below should succeed b = BPF(text=text, allow_rlimit=True) # reset to the original memlock limits resource.setrlimit(resource.RLIMIT_MEMLOCK, memlock_limit) if __name__ == "__main__": main() bpfcc-0.12.0/tests/python/test_shared_table.py000066400000000000000000000012661357404205000213650ustar00rootroot00000000000000#!/usr/bin/env python # Copyright (c) 2016 Facebook, Inc. # Licensed under the Apache License, Version 2.0 (the "License") import ctypes as ct import unittest from bcc import BPF class TestSharedTable(unittest.TestCase): def test_close_extern(self): b1 = BPF(text="""BPF_TABLE_PUBLIC("array", int, int, table1, 10);""") with BPF(text="""BPF_TABLE("extern", int, int, table1, 10);""") as b2: t2 = b2["table1"] t2[ct.c_int(1)] = ct.c_int(10) self.assertEqual(len(t2), 10) t1 = b1["table1"] self.assertEqual(t1[ct.c_int(1)].value, 10) self.assertEqual(len(t1), 10) if __name__ == "__main__": unittest.main() bpfcc-0.12.0/tests/python/test_stackid.py000077500000000000000000000054061357404205000203750ustar00rootroot00000000000000#!/usr/bin/env python # Copyright (c) PLUMgrid, Inc. # Licensed under the Apache License, Version 2.0 (the "License") import bcc import distutils.version import os import unittest import subprocess def kernel_version_ge(major, minor): # True if running kernel is >= X.Y version = distutils.version.LooseVersion(os.uname()[2]).version if version[0] > major: return True if version[0] < major: return False if minor and version[1] < minor: return False return True @unittest.skipUnless(kernel_version_ge(4,6), "requires kernel >= 4.6") class TestStackid(unittest.TestCase): def test_simple(self): b = bcc.BPF(text=""" #include struct bpf_map; BPF_STACK_TRACE(stack_traces, 10240); BPF_HASH(stack_entries, int, int); BPF_HASH(stub); int kprobe__htab_map_lookup_elem(struct pt_regs *ctx, struct bpf_map *map, u64 *k) { int id = stack_traces.get_stackid(ctx, BPF_F_REUSE_STACKID); if (id < 0) return 0; int key = 1; stack_entries.update(&key, &id); return 0; } """) stub = b["stub"] stack_traces = b["stack_traces"] stack_entries = b["stack_entries"] try: x = stub[stub.Key(1)] except: pass k = stack_entries.Key(1) self.assertIn(k, stack_entries) stackid = stack_entries[k] self.assertIsNotNone(stackid) stack = stack_traces[stackid].ip self.assertEqual(b.ksym(stack[0]), b"htab_map_lookup_elem") def Get_libc_path(): cmd = 'cat /proc/self/maps | grep libc | awk \'{print $6}\' | uniq' output = subprocess.check_output(cmd, shell=True) if not isinstance(output, str): output = output.decode() return output.split('\n')[0] @unittest.skipUnless(kernel_version_ge(4,17), "requires kernel >= 4.17") class TestStackBuildid(unittest.TestCase): def test_simple(self): b = bcc.BPF(text=""" #include struct bpf_map; BPF_STACK_TRACE_BUILDID(stack_traces, 10240); BPF_HASH(stack_entries, int, int); BPF_HASH(stub); int kprobe__sys_getuid(struct pt_regs *ctx, struct bpf_map *map, u64 *k) { int id = stack_traces.get_stackid(ctx, BPF_F_USER_STACK); if (id < 0) return 0; int key = 1; stack_entries.update(&key, &id); return 0; } """) os.getuid() stub = b["stub"] stack_traces = b["stack_traces"] stack_entries = b["stack_entries"] b.add_module(Get_libc_path()) try: x = stub[stub.Key(1)] except: pass k = stack_entries.Key(1) self.assertIn(k, stack_entries) stackid = stack_entries[k] self.assertIsNotNone(stackid) stack = stack_traces[stackid] self.assertTrue(b.sym(stack.trace[0], -1).find(b"getuid")!=-1) if __name__ == "__main__": unittest.main() bpfcc-0.12.0/tests/python/test_stat1.b000066400000000000000000000020201357404205000175620ustar00rootroot00000000000000// Copyright (c) PLUMgrid, Inc. // Licensed under the Apache License, Version 2.0 (the "License") struct IPKey { u32 dip:32; u32 sip:32; }; struct IPLeaf { u32 rx_pkts:64; u32 tx_pkts:64; }; Table stats(1024); struct skbuff { u32 type:32; }; u32 on_packet(struct skbuff *skb) { u32 ret:32 = 0; goto proto::ethernet; state proto::ethernet { } state proto::dot1q { } state proto::ip { u32 rx:32 = 0; u32 tx:32 = 0; u32 IPKey key; if $ip.dst > $ip.src { key.dip = $ip.dst; key.sip = $ip.src; rx = 1; // test arbitrary return stmt if false { return 3; } } else { key.dip = $ip.src; key.sip = $ip.dst; tx = 1; ret = 1; } struct IPLeaf *leaf; leaf = stats[key]; on_valid(leaf) { atomic_add(leaf.rx_pkts, rx); atomic_add(leaf.tx_pkts, tx); } } state proto::udp { } state proto::vxlan { } state proto::gre { } state EOP { return ret; } } bpfcc-0.12.0/tests/python/test_stat1.c000066400000000000000000000023201357404205000175660ustar00rootroot00000000000000// Copyright (c) PLUMgrid, Inc. // Licensed under the Apache License, Version 2.0 (the "License") #include struct IPKey { u32 dip; u32 sip; }; struct IPLeaf { u64 rx_pkts; u64 tx_pkts; }; BPF_HASH(stats, struct IPKey, struct IPLeaf, 256); int on_packet(struct __sk_buff *skb) { u8 *cursor = 0; ethernet: { struct ethernet_t *ethernet = cursor_advance(cursor, sizeof(*ethernet)); switch (ethernet->type) { case ETH_P_IP: goto ip; case ETH_P_8021Q: goto dot1q; default: goto EOP; } } dot1q: { struct dot1q_t *dot1q = cursor_advance(cursor, sizeof(*dot1q)); switch (dot1q->type) { case ETH_P_8021Q: goto ip; default: goto EOP; } } ip: { struct ip_t *ip = cursor_advance(cursor, sizeof(*ip)); int rx = 0, tx = 0; struct IPKey key; if (ip->dst > ip->src) { key.dip = ip->dst; key.sip = ip->src; rx = 1; } else { key.dip = ip->src; key.sip = ip->dst; tx = 1; } struct IPLeaf zleaf = {0}; struct IPLeaf *leaf = stats.lookup_or_try_init(&key, &zleaf); if (leaf) { lock_xadd(&leaf->rx_pkts, rx); lock_xadd(&leaf->tx_pkts, tx); } } EOP: return 0; } bpfcc-0.12.0/tests/python/test_stat1.py000077500000000000000000000051101357404205000177770ustar00rootroot00000000000000#!/usr/bin/env python # Copyright (c) PLUMgrid, Inc. # Licensed under the Apache License, Version 2.0 (the "License") # test program to count the packets sent to a device in a .5 # second period from ctypes import c_uint, c_ulong, Structure from netaddr import IPAddress from bcc import BPF from subprocess import check_call import sys from unittest import main, TestCase arg1 = sys.argv.pop(1) arg2 = "" if len(sys.argv) > 1: arg2 = sys.argv.pop(1) Key = None Leaf = None if arg1.endswith(".b"): class Key(Structure): _fields_ = [("dip", c_uint), ("sip", c_uint)] class Leaf(Structure): _fields_ = [("rx_pkts", c_ulong), ("tx_pkts", c_ulong)] class TestBPFSocket(TestCase): def setUp(self): b = BPF(arg1, arg2, debug=0) fn = b.load_func("on_packet", BPF.SOCKET_FILTER) BPF.attach_raw_socket(fn, "eth0") self.stats = b.get_table("stats", Key, Leaf) def test_ping(self): cmd = ["ping", "-f", "-c", "100", "172.16.1.1"] check_call(cmd) #for key, leaf in self.stats.items(): # print(IPAddress(key.sip), "=>", IPAddress(key.dip), # "rx", leaf.rx_pkts, "tx", leaf.tx_pkts) key = self.stats.Key(IPAddress("172.16.1.2").value, IPAddress("172.16.1.1").value) leaf = self.stats[key] self.assertEqual(leaf.rx_pkts, 100) self.assertEqual(leaf.tx_pkts, 100) del self.stats[key] with self.assertRaises(KeyError): x = self.stats[key] with self.assertRaises(KeyError): del self.stats[key] self.stats.clear() self.assertEqual(len(self.stats), 0) self.stats[key] = leaf self.assertEqual(len(self.stats), 1) self.stats.clear() self.assertEqual(len(self.stats), 0) def test_empty_key(self): # test with a 0 key self.stats.clear() self.stats[self.stats.Key()] = self.stats.Leaf(100, 200) x = self.stats.popitem() self.stats[self.stats.Key(10, 20)] = self.stats.Leaf(300, 400) with self.assertRaises(KeyError): x = self.stats[self.stats.Key()] (_, x) = self.stats.popitem() self.assertEqual(x.rx_pkts, 300) self.assertEqual(x.tx_pkts, 400) self.stats.clear() self.assertEqual(len(self.stats), 0) self.stats[self.stats.Key()] = x self.stats[self.stats.Key(0, 1)] = x self.stats[self.stats.Key(0, 2)] = x self.stats[self.stats.Key(0, 3)] = x self.assertEqual(len(self.stats), 4) if __name__ == "__main__": main() bpfcc-0.12.0/tests/python/test_tools_memleak.py000077500000000000000000000102721357404205000216030ustar00rootroot00000000000000#!/usr/bin/env python from unittest import main, skipUnless, TestCase import distutils.version import os import subprocess import sys import tempfile TOOLS_DIR = "../../tools/" class cfg: cmd_format = "" # Amount of memory to leak. Note, that test application allocates memory # for its own needs in libc, so this amount should be large enough to be # the biggest allocation. leaking_amount = 30000 def kernel_version_ge(major, minor): # True if running kernel is >= X.Y version = distutils.version.LooseVersion(os.uname()[2]).version if version[0] > major: return True if version[0] < major: return False if minor and version[1] < minor: return False return True def setUpModule(): # Build the memory leaking application. c_src = 'test_tools_memleak_leaker_app.c' tmp_dir = tempfile.mkdtemp(prefix='bcc-test-memleak-') c_src_full = os.path.dirname(sys.argv[0]) + os.path.sep + c_src exec_dst = tmp_dir + os.path.sep + 'leaker_app' if subprocess.call(['gcc', '-g', '-O0', '-o', exec_dst, c_src_full]) != 0: print("can't compile the leaking application") raise Exception # Taking two snapshot with one second interval. Getting the largest # allocation. Since attaching to a program happens with a delay, we wait # for the first snapshot, then issue the command to the app. Finally, # second snapshot is used to extract the information. # Helper utilities "timeout" and "setbuf" are used to limit overall running # time, and to disable buffering. cfg.cmd_format = ( 'stdbuf -o 0 -i 0 timeout -s KILL 10s ' + TOOLS_DIR + 'memleak.py -c "{} {{}} {}" -T 1 1 2'.format(exec_dst, cfg.leaking_amount)) @skipUnless(kernel_version_ge(4, 6), "requires kernel >= 4.6") class MemleakToolTests(TestCase): def tearDown(self): if self.p: del(self.p) def run_leaker(self, leak_kind): # Starting memleak.py, which in turn launches the leaking application. self.p = subprocess.Popen(cfg.cmd_format.format(leak_kind), stdin=subprocess.PIPE, stdout=subprocess.PIPE, shell=True) # Waiting for the first report. while True: self.p.poll() if self.p.returncode is not None: break line = self.p.stdout.readline() if b"with outstanding allocations" in line: break # At this point, memleak.py have already launched application and set # probes. Sending command to the leaking application to make its # allocations. out = self.p.communicate(input=b"\n")[0] # If there were memory leaks, they are in the output. Filter the lines # containing "byte" substring. Every interesting line is expected to # start with "N bytes from" x = [x for x in out.split(b'\n') if b'byte' in x] self.assertTrue(len(x) >= 1, msg="At least one line should have 'byte' substring.") # Taking last report. x = x[-1].split() self.assertTrue(len(x) >= 1, msg="There should be at least one word in the line.") # First word is the leak amount in bytes. return int(x[0]) def test_malloc(self): self.assertEqual(cfg.leaking_amount, self.run_leaker("malloc")) def test_calloc(self): self.assertEqual(cfg.leaking_amount, self.run_leaker("calloc")) def test_realloc(self): self.assertEqual(cfg.leaking_amount, self.run_leaker("realloc")) def test_posix_memalign(self): self.assertEqual(cfg.leaking_amount, self.run_leaker("posix_memalign")) def test_valloc(self): self.assertEqual(cfg.leaking_amount, self.run_leaker("valloc")) def test_memalign(self): self.assertEqual(cfg.leaking_amount, self.run_leaker("memalign")) def test_pvalloc(self): self.assertEqual(cfg.leaking_amount, self.run_leaker("pvalloc")) def test_aligned_alloc(self): self.assertEqual(cfg.leaking_amount, self.run_leaker("aligned_alloc")) if __name__ == "__main__": main() bpfcc-0.12.0/tests/python/test_tools_memleak_leaker_app.c000066400000000000000000000036441357404205000235620ustar00rootroot00000000000000// This is a program that leaks memory, used for memory leak detector testing. #include #include #include #include #include #include #include #include static void generate_leak(const char *kind, int amount) { void *ptr = NULL; if (strcmp(kind, "malloc") == 0) { printf("leaking via malloc, %p\n", malloc(amount)); return; } if (strcmp(kind, "calloc") == 0) { printf("leaking via calloc, %p\n", calloc(amount, 1)); return; } if (strcmp(kind, "realloc") == 0) { printf("leaking via realloc, %p\n", realloc(malloc(10), amount)); return; } if (strcmp(kind, "posix_memalign") == 0) { posix_memalign(&ptr, 512, amount); printf("leaking via posix_memalign, %p\n", ptr); return; } if (strcmp(kind, "valloc") == 0) { printf("leaking via valloc, %p\n", valloc(amount)); return; } if (strcmp(kind, "memalign") == 0) { printf("leaking via memalign, %p\n", memalign(512, amount)); return; } if (strcmp(kind, "pvalloc") == 0) { printf("leaking via pvalloc, %p\n", pvalloc(amount)); return; } if (strcmp(kind, "aligned_alloc") == 0) { printf("leaking via aligned_alloc, %p\n", aligned_alloc(512, amount)); return; } if (strcmp(kind, "no_leak") == 0) { void *ptr = malloc(amount); printf("ptr = %p\n", ptr); free(ptr); return; } printf("unknown leak type '%s'\n", kind); } int main(int argc, char *argv[]) { if (argc < 2) { printf("usage: leak-userspace [amount]\n"); return EXIT_SUCCESS; } const char *kind = argv[1]; int amount = 30; if (argc > 2) { amount = atoi(argv[2]); if (amount < 1) amount = 1; } // Wait for something in stdin to give external detector time to attach. char c; read(0, &c, sizeof(c)); // Do the work. generate_leak(kind, amount); return EXIT_SUCCESS; } bpfcc-0.12.0/tests/python/test_tools_smoke.py000077500000000000000000000347271357404205000213210ustar00rootroot00000000000000#!/usr/bin/env python # Copyright (c) Sasha Goldshtein, 2017 # Licensed under the Apache License, Version 2.0 (the "License") import distutils.version import subprocess import os import re from unittest import main, skipUnless, TestCase TOOLS_DIR = "../../tools/" def kernel_version_ge(major, minor): # True if running kernel is >= X.Y version = distutils.version.LooseVersion(os.uname()[2]).version if version[0] > major: return True if version[0] < major: return False if minor and version[1] < minor: return False return True @skipUnless(kernel_version_ge(4,1), "requires kernel >= 4.1") class SmokeTests(TestCase): # Use this for commands that have a built-in timeout, so they only need # to be killed in case of a hard hang. def run_with_duration(self, command, timeout=10): full_command = TOOLS_DIR + command self.assertEqual(0, # clean exit subprocess.call("timeout -s KILL %ds %s > /dev/null" % (timeout, full_command), shell=True)) # Use this for commands that don't have a built-in timeout, so we have # to Ctrl-C out of them by sending SIGINT. If that still doesn't stop # them, send a kill signal 5 seconds later. def run_with_int(self, command, timeout=5, kill_timeout=5, allow_early=False, kill=False): full_command = TOOLS_DIR + command signal = "KILL" if kill else "INT" rc = subprocess.call("timeout -s %s -k %ds %ds %s > /dev/null" % (signal, kill_timeout, timeout, full_command), shell=True) # timeout returns 124 if the program did not terminate prematurely, # and returns 137 if we used KILL instead of INT. So there are three # sensible scenarios: # 1. The script is allowed to return early, and it did, with a # success return code. # 2. The script timed out and was killed by the SIGINT signal. # 3. The script timed out and was killed by the SIGKILL signal, and # this was what we asked for using kill=True. self.assertTrue((rc == 0 and allow_early) or rc == 124 or (rc == 137 and kill), "rc was %d" % rc) def kmod_loaded(self, mod): with open("/proc/modules", "r") as mods: reg = re.compile("^%s\s" % mod) for line in mods: if reg.match(line): return 1 return 0 def setUp(self): pass def tearDown(self): pass def test_argdist(self): self.run_with_duration("argdist.py -v -C 'p::do_sys_open()' -n 1 -i 1") @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4") def test_bashreadline(self): self.run_with_int("bashreadline.py") def test_biolatency(self): self.run_with_duration("biolatency.py 1 1") @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4") def test_biosnoop(self): self.run_with_int("biosnoop.py") def test_biotop(self): self.run_with_duration("biotop.py 1 1") def test_bitesize(self): self.run_with_int("biotop.py") def test_bpflist(self): self.run_with_duration("bpflist.py") def test_btrfsdist(self): # Will attempt to do anything meaningful only when btrfs is installed. self.run_with_duration("btrfsdist.py 1 1") @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4") def test_btrfsslower(self): # Will attempt to do anything meaningful only when btrfs is installed. self.run_with_int("btrfsslower.py", allow_early=True) def test_cachestat(self): self.run_with_duration("cachestat.py 1 1") def test_cachetop(self): # TODO cachetop doesn't like to run without a terminal, disabled # for now. # self.run_with_int("cachetop.py 1") pass @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4") def test_capable(self): self.run_with_int("capable.py") def test_cpudist(self): self.run_with_duration("cpudist.py 1 1") @skipUnless(kernel_version_ge(4,9), "requires kernel >= 4.9") def test_cpuunclaimed(self): self.run_with_duration("cpuunclaimed.py 1 1") @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4") def test_dbslower(self): # Deliberately left empty -- dbslower requires an instance of either # MySQL or PostgreSQL to be running, or it fails to attach. pass @skipUnless(kernel_version_ge(4,3), "requires kernel >= 4.3") def test_dbstat(self): # Deliberately left empty -- dbstat requires an instance of either # MySQL or PostgreSQL to be running, or it fails to attach. pass @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4") def test_dcsnoop(self): self.run_with_int("dcsnoop.py") def test_dcstat(self): self.run_with_duration("dcstat.py 1 1") @skipUnless(kernel_version_ge(4,6), "requires kernel >= 4.6") def test_deadlock(self): # TODO This tool requires a massive BPF stack traces table allocation, # which might fail the run or even trigger the oomkiller to kill some # other processes. Disabling for now. # self.run_with_int("deadlock.py $(pgrep -n bash)", timeout=10) pass @skipUnless(kernel_version_ge(4,7), "requires kernel >= 4.7") def test_drsnoop(self): self.run_with_int("drsnoop.py") @skipUnless(kernel_version_ge(4,8), "requires kernel >= 4.8") def test_execsnoop(self): self.run_with_int("execsnoop.py") def test_ext4dist(self): self.run_with_duration("ext4dist.py 1 1") @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4") def test_ext4slower(self): self.run_with_int("ext4slower.py") @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4") def test_filelife(self): self.run_with_int("filelife.py") @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4") def test_fileslower(self): self.run_with_int("fileslower.py") def test_filetop(self): self.run_with_duration("filetop.py 1 1") def test_funccount(self): self.run_with_int("funccount.py __kmalloc -i 1") @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4") def test_funclatency(self): self.run_with_int("funclatency.py __kmalloc -i 1") @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4") def test_funcslower(self): self.run_with_int("funcslower.py __kmalloc") @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4") def test_gethostlatency(self): self.run_with_int("gethostlatency.py") def test_hardirqs(self): self.run_with_duration("hardirqs.py 1 1") @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4") def test_killsnoop(self): # Because killsnoop intercepts signals, if we send it a SIGINT we we # we likely catch it while it is handling the data packet from the # BPF program, and the exception from the SIGINT will be swallowed by # ctypes. Therefore, we use SIGKILL. # To reproduce the above issue, run killsnoop and in another shell run # `kill -s SIGINT $(pidof python)`. As a result, killsnoop will print # a traceback but will not exit. self.run_with_int("killsnoop.py", kill=True) @skipUnless(kernel_version_ge(4,18), "requires kernel >= 4.18") def test_klockstat(self): self.run_with_int("klockstat.py") @skipUnless(kernel_version_ge(4,9), "requires kernel >= 4.9") def test_llcstat(self): # Requires PMU, which is not available in virtual machines. pass @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4") def test_mdflush(self): self.run_with_int("mdflush.py") @skipUnless(kernel_version_ge(4,6), "requires kernel >= 4.6") def test_memleak(self): self.run_with_duration("memleak.py 1 1") @skipUnless(kernel_version_ge(4,8), "requires kernel >= 4.8") def test_mountsnoop(self): self.run_with_int("mountsnoop.py") @skipUnless(kernel_version_ge(4,3), "requires kernel >= 4.3") def test_mysqld_qslower(self): # Deliberately left empty -- mysqld_qslower requires an instance of # MySQL to be running, or it fails to attach. pass @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4") def test_nfsslower(self): if(self.kmod_loaded("nfs")): self.run_with_int("nfsslower.py") else: pass @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4") def test_nfsdist(self): if(self.kmod_loaded("nfs")): self.run_with_duration("nfsdist.py 1 1") else: pass @skipUnless(kernel_version_ge(4,6), "requires kernel >= 4.6") def test_offcputime(self): self.run_with_duration("offcputime.py 1") @skipUnless(kernel_version_ge(4,6), "requires kernel >= 4.6") def test_offwaketime(self): self.run_with_duration("offwaketime.py 1") @skipUnless(kernel_version_ge(4,9), "requires kernel >= 4.9") def test_oomkill(self): self.run_with_int("oomkill.py") @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4") def test_opensnoop(self): self.run_with_int("opensnoop.py") def test_pidpersec(self): self.run_with_int("pidpersec.py") @skipUnless(kernel_version_ge(4,9), "requires kernel >= 4.9") def test_profile(self): self.run_with_duration("profile.py 1") def test_runqlat(self): self.run_with_duration("runqlat.py 1 1") @skipUnless(kernel_version_ge(4,9), "requires kernel >= 4.9") def test_runqlen(self): self.run_with_duration("runqlen.py 1 1") @skipUnless(kernel_version_ge(4,8), "requires kernel >= 4.8") def test_shmsnoop(self): self.run_with_int("shmsnoop.py") @skipUnless(kernel_version_ge(4,8), "requires kernel >= 4.8") def test_sofdsnoop(self): self.run_with_int("sofdsnoop.py") def test_slabratetop(self): self.run_with_duration("slabratetop.py 1 1") @skipUnless(kernel_version_ge(4,7), "requires kernel >= 4.7") def test_softirqs(self): self.run_with_duration("softirqs.py 1 1") pass @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4") def test_solisten(self): self.run_with_int("solisten.py") @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4") def test_sslsniff(self): self.run_with_int("sslsniff.py") @skipUnless(kernel_version_ge(4,6), "requires kernel >= 4.6") def test_stackcount(self): self.run_with_int("stackcount.py __kmalloc -i 1") @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4") def test_statsnoop(self): self.run_with_int("statsnoop.py") @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4") def test_syncsnoop(self): self.run_with_int("syncsnoop.py") @skipUnless(kernel_version_ge(4,7), "requires kernel >= 4.7") def test_syscount(self): self.run_with_int("syscount.py -i 1") @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4") def test_tcpaccept(self): self.run_with_int("tcpaccept.py") @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4") def test_tcpconnect(self): self.run_with_int("tcpconnect.py") @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4") def test_tcpconnlat(self): self.run_with_int("tcpconnlat.py") @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4") def test_tcplife(self): self.run_with_int("tcplife.py") @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4") def test_tcpretrans(self): self.run_with_int("tcpretrans.py") @skipUnless(kernel_version_ge(4, 7), "requires kernel >= 4.7") def test_tcpdrop(self): self.run_with_int("tcpdrop.py") def test_tcptop(self): self.run_with_duration("tcptop.py 1 1") def test_tplist(self): self.run_with_duration("tplist.py -p %d" % os.getpid()) @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4") def test_trace(self): self.run_with_int("trace.py do_sys_open") @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4") def test_ttysnoop(self): self.run_with_int("ttysnoop.py /dev/console") @skipUnless(kernel_version_ge(4,7), "requires kernel >= 4.7") def test_ucalls(self): self.run_with_int("lib/ucalls.py -l none -S %d" % os.getpid()) @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4") def test_uflow(self): # The Python installed on the Ubuntu buildbot doesn't have USDT # probes, so we can't run uflow. # self.run_with_int("pythonflow.py %d" % os.getpid()) pass @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4") def test_ugc(self): # This requires a runtime that has GC probes to be installed. # Python has them, but only in very recent versions. Skip. pass @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4") def test_uobjnew(self): self.run_with_int("cobjnew.sh %d" % os.getpid()) @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4") def test_ustat(self): self.run_with_duration("lib/ustat.py 1 1") @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4") def test_uthreads(self): self.run_with_int("lib/uthreads.py %d" % os.getpid()) def test_vfscount(self): self.run_with_int("vfscount.py", timeout=15, kill_timeout=15) def test_vfsstat(self): self.run_with_duration("vfsstat.py 1 1") @skipUnless(kernel_version_ge(4,6), "requires kernel >= 4.6") def test_wakeuptime(self): self.run_with_duration("wakeuptime.py 1") def test_xfsdist(self): # Doesn't work on build bot because xfs functions not present in the # kernel image. # self.run_with_duration("xfsdist.py 1 1") pass @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4") def test_xfsslower(self): # Doesn't work on build bot because xfs functions not present in the # kernel image. # self.run_with_int("xfsslower.py") pass def test_zfsdist(self): # Fails to attach the probe if zfs is not installed. pass @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4") def test_zfsslower(self): # Fails to attach the probe if zfs is not installed. pass if __name__ == "__main__": main() bpfcc-0.12.0/tests/python/test_trace1.b000066400000000000000000000017031357404205000177140ustar00rootroot00000000000000// Copyright (c) PLUMgrid, Inc. // Licensed under the Apache License, Version 2.0 (the "License") struct Ptr { u64 ptr:64; }; struct Counters { u64 stat1:64; u64 stat2:64; }; Table stats(1024); // example with on_valid syntax u32 sys_wr (struct proto::pt_regs *ctx) { struct Ptr key = {.ptr=ctx->di}; struct Counters *leaf; leaf = stats[key]; if leaf { atomic_add(leaf->stat2, 1); } log("sys_wr: %p\n", ctx->di); return 0; } // example with smallest available syntax // note: if stats[key] fails, program returns early u32 sys_rd (struct proto::pt_regs *ctx) { struct Ptr key = {.ptr=ctx->di}; atomic_add(stats[key].stat1, 1); } // example with if/else case u32 sys_bpf (struct proto::pt_regs *ctx) { struct Ptr key = {.ptr=ctx->di}; struct Counters *leaf; leaf = stats[key]; if leaf { atomic_add(leaf->stat1, 1); } else { log("update %llx failed\n", ctx->di); } return 0; } bpfcc-0.12.0/tests/python/test_trace1.py000077500000000000000000000025361357404205000201330ustar00rootroot00000000000000#!/usr/bin/env python # Copyright (c) PLUMgrid, Inc. # Licensed under the Apache License, Version 2.0 (the "License") from ctypes import c_uint, c_ulong, Structure from bcc import BPF import os from time import sleep import sys from unittest import main, TestCase arg1 = sys.argv.pop(1) arg2 = "" if len(sys.argv) > 1: arg2 = sys.argv.pop(1) Key = None Leaf = None if arg1.endswith(".b"): class Key(Structure): _fields_ = [("fd", c_ulong)] class Leaf(Structure): _fields_ = [("stat1", c_ulong), ("stat2", c_ulong)] class TestKprobe(TestCase): def setUp(self): b = BPF(arg1, arg2, debug=0) self.stats = b.get_table("stats", Key, Leaf) b.attach_kprobe(event=b.get_syscall_fnname("write"), fn_name="sys_wr") b.attach_kprobe(event=b.get_syscall_fnname("read"), fn_name="sys_rd") b.attach_kprobe(event="htab_map_get_next_key", fn_name="sys_rd") def test_trace1(self): with open("/dev/null", "a") as f: for i in range(0, 100): os.write(f.fileno(), b"") with open("/etc/services", "r") as f: for i in range(0, 200): os.read(f.fileno(), 1) for key, leaf in self.stats.items(): print("fd %x:" % key.fd, "stat1 %d" % leaf.stat1, "stat2 %d" % leaf.stat2) if __name__ == "__main__": main() bpfcc-0.12.0/tests/python/test_trace2.b000066400000000000000000000005421357404205000177150ustar00rootroot00000000000000// Copyright (c) PLUMgrid, Inc. // Licensed under the Apache License, Version 2.0 (the "License") #include "kprobe.b" struct Ptr { u64 ptr:64; }; struct Counters { u64 stat1:64; }; Table stats(1024); u32 count_sched (struct proto::pt_regs *ctx) { struct Ptr key = {.ptr=ctx->bx}; atomic_add(stats[key].stat1, 1); } bpfcc-0.12.0/tests/python/test_trace2.c000066400000000000000000000007221357404205000177160ustar00rootroot00000000000000// Copyright (c) PLUMgrid, Inc. // Licensed under the Apache License, Version 2.0 (the "License") #include struct Ptr { u64 ptr; }; struct Counters { u64 stat1; }; BPF_HASH(stats, struct Ptr, struct Counters, 1024); int count_sched(struct pt_regs *ctx) { struct Ptr key = {.ptr = PT_REGS_PARM1(ctx)}; struct Counters zleaf = {0}; struct Counters *val = stats.lookup_or_try_init(&key, &zleaf); if (val) { val->stat1++; } return 0; } bpfcc-0.12.0/tests/python/test_trace2.py000077500000000000000000000022001357404205000201200ustar00rootroot00000000000000#!/usr/bin/env python # Copyright (c) PLUMgrid, Inc. # Licensed under the Apache License, Version 2.0 (the "License") from ctypes import c_uint, c_ulong, Structure from bcc import BPF from time import sleep import sys from unittest import main, TestCase text = """ #include struct Ptr { u64 ptr; }; struct Counters { char unused; __int128 stat1; }; BPF_HASH(stats, struct Ptr, struct Counters, 1024); int count_sched(struct pt_regs *ctx) { struct Ptr key = {.ptr=PT_REGS_PARM1(ctx)}; struct Counters zleaf; memset(&zleaf, 0, sizeof(zleaf)); struct Counters *val = stats.lookup_or_try_init(&key, &zleaf); if (val) { val->stat1++; } return 0; } """ class TestTracingEvent(TestCase): def setUp(self): b = BPF(text=text, debug=0) self.stats = b.get_table("stats") b.attach_kprobe(event="finish_task_switch", fn_name="count_sched") def test_sched1(self): for i in range(0, 100): sleep(0.01) for key, leaf in self.stats.items(): print("ptr %x:" % key.ptr, "stat1 (%d %d)" % (leaf.stat1[1], leaf.stat1[0])) if __name__ == "__main__": main() bpfcc-0.12.0/tests/python/test_trace3.c000066400000000000000000000025711357404205000177230ustar00rootroot00000000000000// Copyright (c) PLUMgrid, Inc. // Licensed under the Apache License, Version 2.0 (the "License") #include #include struct Request { u64 rq; }; struct Time { u64 start; }; BPF_HASH(requests, struct Request, struct Time, 1024); #define SLOTS 100 BPF_ARRAY(latency, u64, SLOTS); static u32 log2(u32 v) { u32 r, shift; r = (v > 0xFFFF) << 4; v >>= r; shift = (v > 0xFF) << 3; v >>= shift; r |= shift; shift = (v > 0xF) << 2; v >>= shift; r |= shift; shift = (v > 0x3) << 1; v >>= shift; r |= shift; r |= (v >> 1); return r; } static u32 log2l(u64 v) { u32 hi = v >> 32; if (hi) return log2(hi) + 32; else return log2(v); } int probe_blk_start_request(struct pt_regs *ctx) { struct Request rq = {.rq = PT_REGS_PARM1(ctx)}; struct Time tm = {.start = bpf_ktime_get_ns()}; requests.update(&rq, &tm); return 0; } int probe_blk_update_request(struct pt_regs *ctx) { struct Request rq = {.rq = PT_REGS_PARM1(ctx)}; struct Time *tm = requests.lookup(&rq); if (!tm) return 0; u64 delta = bpf_ktime_get_ns() - tm->start; requests.delete(&rq); u64 lg = log2l(delta); u64 base = 1ull << lg; u32 index = (lg * 64 + (delta - base) * 64 / base) * 3 / 64; if (index >= SLOTS) index = SLOTS - 1; u64 zero = 0; u64 *val = latency.lookup_or_try_init(&index, &zero); if (val) { lock_xadd(val, 1); } return 0; } bpfcc-0.12.0/tests/python/test_trace3.py000077500000000000000000000024521357404205000201320ustar00rootroot00000000000000#!/usr/bin/env python # Copyright (c) PLUMgrid, Inc. # Licensed under the Apache License, Version 2.0 (the "License") from ctypes import c_uint, c_ulong, Structure from bcc import BPF from time import sleep import sys from unittest import main, TestCase arg1 = sys.argv.pop(1) arg2 = "" if len(sys.argv) > 1: arg2 = sys.argv.pop(1) class TestBlkRequest(TestCase): def setUp(self): b = BPF(arg1, arg2, debug=0) self.latency = b.get_table("latency", c_uint, c_ulong) b.attach_kprobe(event="blk_start_request", fn_name="probe_blk_start_request") b.attach_kprobe(event="blk_update_request", fn_name="probe_blk_update_request") def test_blk1(self): import subprocess import os # use /opt instead of /tmp so that it hits a real disk for i in range(0, 2): subprocess.call(["dd", "if=/dev/zero", "of=/opt/trace3.txt", "count=1024", "bs=4096"]) subprocess.call(["sync"]) os.unlink("/opt/trace3.txt") for key, leaf in self.latency.items(): print("latency %u:" % key.value, "count %u" % leaf.value) sys.stdout.flush() self.assertEqual(len(list(self.latency.keys())), len(self.latency)) if __name__ == "__main__": main() bpfcc-0.12.0/tests/python/test_trace4.py000077500000000000000000000030011357404205000201220ustar00rootroot00000000000000#!/usr/bin/env python # Copyright (c) PLUMgrid, Inc. # Licensed under the Apache License, Version 2.0 (the "License") from bcc import BPF import os import sys from unittest import main, TestCase class TestKprobeRgx(TestCase): def setUp(self): self.b = BPF(text=b""" typedef struct { int idx; } Key; typedef struct { u64 val; } Val; BPF_HASH(stats, Key, Val, 3); int hello(void *ctx) { Val *val = stats.lookup_or_try_init(&(Key){1}, &(Val){0}); if (val) { val->val++; } return 0; } int goodbye(void *ctx) { Val *val = stats.lookup_or_try_init(&(Key){2}, &(Val){0}); if (val) { val->val++; } return 0; } """) self.b.attach_kprobe(event_re=b"^" + self.b.get_syscall_prefix() + b"bp.*", fn_name=b"hello") self.b.attach_kretprobe(event_re=b"^" + self.b.get_syscall_prefix() + b"bp.*", fn_name=b"goodbye") def test_send1(self): k1 = self.b[b"stats"].Key(1) k2 = self.b[b"stats"].Key(2) self.assertTrue(self.b[b"stats"][k1].val >= 2) self.assertTrue(self.b[b"stats"][k2].val == 1) class TestKprobeReplace(TestCase): def setUp(self): self.b = BPF(text=b"int empty(void *ctx) { return 0; }") def test_periods(self): self.b.attach_kprobe(event_re=b"^tcp_enter_cwr.*", fn_name=b"empty") if __name__ == "__main__": main() bpfcc-0.12.0/tests/python/test_trace_maxactive.py000077500000000000000000000023241357404205000221060ustar00rootroot00000000000000#!/usr/bin/env python # Copyright (c) PLUMgrid, Inc. # Licensed under the Apache License, Version 2.0 (the "License") from bcc import BPF import os import sys from unittest import main, TestCase class TestKprobeMaxactive(TestCase): def setUp(self): self.b = BPF(text=b""" typedef struct { int idx; } Key; typedef struct { u64 val; } Val; BPF_HASH(stats, Key, Val, 3); int hello(void *ctx) { Val *val = stats.lookup_or_init(&(Key){1}, &(Val){0}); val->val++; return 0; } int goodbye(void *ctx) { Val *val = stats.lookup_or_init(&(Key){2}, &(Val){0}); val->val++; return 0; } """) self.b.attach_kprobe(event_re=self.b.get_syscall_prefix() + b"bpf", fn_name=b"hello") self.b.attach_kretprobe(event_re=self.b.get_syscall_prefix() + b"bpf", fn_name=b"goodbye", maxactive=128) def test_send1(self): k1 = self.b[b"stats"].Key(1) k2 = self.b[b"stats"].Key(2) self.assertTrue(self.b[b"stats"][k1].val >= 2) self.assertTrue(self.b[b"stats"][k2].val == 1) if __name__ == "__main__": main() bpfcc-0.12.0/tests/python/test_tracepoint.py000077500000000000000000000041201357404205000211130ustar00rootroot00000000000000#!/usr/bin/env python # Copyright (c) Sasha Goldshtein # Licensed under the Apache License, Version 2.0 (the "License") import bcc import unittest from time import sleep import distutils.version import os import subprocess def kernel_version_ge(major, minor): # True if running kernel is >= X.Y version = distutils.version.LooseVersion(os.uname()[2]).version if version[0] > major: return True if version[0] < major: return False if minor and version[1] < minor: return False return True @unittest.skipUnless(kernel_version_ge(4,7), "requires kernel >= 4.7") class TestTracepoint(unittest.TestCase): def test_tracepoint(self): text = """ BPF_HASH(switches, u32, u64); TRACEPOINT_PROBE(sched, sched_switch) { u64 val = 0; u32 pid = args->next_pid; u64 *existing = switches.lookup_or_init(&pid, &val); (*existing)++; return 0; } """ b = bcc.BPF(text=text) sleep(1) total_switches = 0 for k, v in b["switches"].items(): total_switches += v.value self.assertNotEqual(0, total_switches) @unittest.skipUnless(kernel_version_ge(4,7), "requires kernel >= 4.7") class TestTracepointDataLoc(unittest.TestCase): def test_tracepoint_data_loc(self): text = """ struct value_t { char filename[64]; }; BPF_HASH(execs, u32, struct value_t); TRACEPOINT_PROBE(sched, sched_process_exec) { struct value_t val = {0}; char fn[64]; u32 pid = args->pid; struct value_t *existing = execs.lookup_or_init(&pid, &val); TP_DATA_LOC_READ_CONST(fn, filename, 64); __builtin_memcpy(existing->filename, fn, 64); return 0; } """ b = bcc.BPF(text=text) subprocess.check_output(["/bin/ls"]) sleep(1) self.assertTrue("/bin/ls" in [v.filename.decode() for v in b["execs"].values()]) if __name__ == "__main__": unittest.main() bpfcc-0.12.0/tests/python/test_uprobes.py000077500000000000000000000110101357404205000204160ustar00rootroot00000000000000#!/usr/bin/env python # Copyright (c) PLUMgrid, Inc. # Licensed under the Apache License, Version 2.0 (the "License") import bcc import ctypes import errno import os import subprocess import shutil import time import unittest class TestUprobes(unittest.TestCase): def test_simple_library(self): text = """ #include BPF_ARRAY(stats, u64, 1); static void incr(int idx) { u64 *ptr = stats.lookup(&idx); if (ptr) ++(*ptr); } int count(struct pt_regs *ctx) { bpf_trace_printk("count() uprobe fired"); u32 pid = bpf_get_current_pid_tgid(); if (pid == PID) incr(0); return 0; }""" test_pid = os.getpid() text = text.replace("PID", "%d" % test_pid) b = bcc.BPF(text=text) b.attach_uprobe(name="c", sym="malloc_stats", fn_name="count", pid=test_pid) b.attach_uretprobe(name="c", sym="malloc_stats", fn_name="count", pid=test_pid) libc = ctypes.CDLL("libc.so.6") libc.malloc_stats.restype = None libc.malloc_stats.argtypes = [] libc.malloc_stats() self.assertEqual(b["stats"][ctypes.c_int(0)].value, 2) b.detach_uretprobe(name="c", sym="malloc_stats", pid=test_pid) b.detach_uprobe(name="c", sym="malloc_stats", pid=test_pid) def test_simple_binary(self): text = """ #include BPF_ARRAY(stats, u64, 1); static void incr(int idx) { u64 *ptr = stats.lookup(&idx); if (ptr) ++(*ptr); } int count(struct pt_regs *ctx) { u32 pid = bpf_get_current_pid_tgid(); incr(0); return 0; }""" b = bcc.BPF(text=text) b.attach_uprobe(name="/usr/bin/python", sym="main", fn_name="count") b.attach_uretprobe(name="/usr/bin/python", sym="main", fn_name="count") with os.popen("/usr/bin/python -V") as f: pass self.assertGreater(b["stats"][ctypes.c_int(0)].value, 0) b.detach_uretprobe(name="/usr/bin/python", sym="main") b.detach_uprobe(name="/usr/bin/python", sym="main") def test_mount_namespace(self): text = """ #include BPF_TABLE("array", int, u64, stats, 1); static void incr(int idx) { u64 *ptr = stats.lookup(&idx); if (ptr) ++(*ptr); } int count(struct pt_regs *ctx) { bpf_trace_printk("count() uprobe fired"); u32 pid = bpf_get_current_pid_tgid(); if (pid == PID) incr(0); return 0; }""" # Need to import libc from ctypes to access unshare(2) libc = ctypes.CDLL("libc.so.6", use_errno=True) # Need to find path to libz.so.1 libz_path = None p = subprocess.Popen(["ldconfig", "-p"], stdout=subprocess.PIPE) for l in p.stdout: n = l.split() if n[0] == b"libz.so.1": # if libz was already found, override only if new lib is more # specific (e.g. libc6,x86-64 vs libc6) if not libz_path or len(n[1].split(b",")) > 1: libz_path = n[-1] p.wait() p.stdout.close() p = None self.assertIsNotNone(libz_path) # fork a child that we'll place in a separate mount namespace child_pid = os.fork() if child_pid == 0: # Unshare CLONE_NEWNS if libc.unshare(0x00020000) == -1: e = ctypes.get_errno() raise OSError(e, errno.errorcode[e]) # Remount root MS_REC|MS_PRIVATE if libc.mount(None, b"/", None, (1<<14)|(1<<18) , None) == -1: e = ctypes.get_errno() raise OSError(e, errno.errorcode[e]) if libc.mount(b"tmpfs", b"/tmp", b"tmpfs", 0, None) == -1: e = ctypes.get_errno() raise OSError(e, errno.errorcode[e]) shutil.copy(libz_path, b"/tmp") libz = ctypes.CDLL("/tmp/libz.so.1") time.sleep(1) libz.zlibVersion() time.sleep(5) os._exit(0) libname = "/tmp/libz.so.1" symname = "zlibVersion" text = text.replace("PID", "%d" % child_pid) b = bcc.BPF(text=text) b.attach_uprobe(name=libname, sym=symname, fn_name="count", pid=child_pid) b.attach_uretprobe(name=libname, sym=symname, fn_name="count", pid=child_pid) time.sleep(1) self.assertEqual(b["stats"][ctypes.c_int(0)].value, 2) b.detach_uretprobe(name=libname, sym=symname, pid=child_pid) b.detach_uprobe(name=libname, sym=symname, pid=child_pid) os.wait() if __name__ == "__main__": unittest.main() bpfcc-0.12.0/tests/python/test_usdt.py000077500000000000000000000155571357404205000177420ustar00rootroot00000000000000#!/usr/bin/env python # # USAGE: test_usdt.py # # Copyright 2017 Facebook, Inc # Licensed under the Apache License, Version 2.0 (the "License") from __future__ import print_function from bcc import BPF, USDT from unittest import main, TestCase from subprocess import Popen, PIPE from tempfile import NamedTemporaryFile import ctypes as ct import inspect import os import signal class TestUDST(TestCase): def setUp(self): # Application, minimum, to define three trace points app_text = b""" #include #include #include #include #include "folly/tracing/StaticTracepoint.h" int main() { char s[100]; int i, a = 200, b = 40; for (i = 0; i < 100; i++) s[i] = (i & 7) + (i & 6); uint64_t j = 0; char s1[64]; const char* str = "str"; size_t len = strlen(str); while (1) { FOLLY_SDT(test, probe_point_1, s[7], b); FOLLY_SDT(test, probe_point_3, a, b); FOLLY_SDT(test, probe_point_1, s[4], a); FOLLY_SDT(test, probe_point_2, 5, s[10]); FOLLY_SDT(test, probe_point_3, s[4], s[7]); memset(&s1, '\0', sizeof(s1)); strncpy(s1, str, len); snprintf(s1 + len, sizeof(s1) - len, "%d", j); FOLLY_SDT(test, probe_point_4, j++, &s1); memset(&s1, '\0', sizeof(s1)); strncpy(s1, str, len); snprintf(s1 + len, sizeof(s1) - len, "%d", j); FOLLY_SDT(test, probe_point_5, &s1, j++); sleep(1); } return 1; } """ # BPF program self.bpf_text = """ #include #include struct probe_result_t1 { char v1; int v2; }; struct probe_result_t2 { int v1; char v2; }; struct probe_result_t3 { int v1; int v2; }; struct probe_result_t4 { u64 v1; char v2[8]; }; struct probe_result_t5 { char v1[8]; u64 v2; }; BPF_PERF_OUTPUT(event1); BPF_PERF_OUTPUT(event2); BPF_PERF_OUTPUT(event3); BPF_PERF_OUTPUT(event4); BPF_PERF_OUTPUT(event5); int do_trace1(struct pt_regs *ctx) { struct probe_result_t1 result = {}; bpf_usdt_readarg(1, ctx, &result.v1); bpf_usdt_readarg(2, ctx, &result.v2); event1.perf_submit(ctx, &result, sizeof(result)); return 0; }; int do_trace2(struct pt_regs *ctx) { struct probe_result_t2 result = {}; bpf_usdt_readarg(1, ctx, &result.v1); bpf_usdt_readarg(2, ctx, &result.v2); event2.perf_submit(ctx, &result, sizeof(result)); return 0; } int do_trace3(struct pt_regs *ctx) { struct probe_result_t3 result = {}; bpf_usdt_readarg(1, ctx, &result.v1); bpf_usdt_readarg(2, ctx, &result.v2); event3.perf_submit(ctx, &result, sizeof(result)); return 0; } int do_trace4(struct pt_regs *ctx) { struct probe_result_t4 result = {}; bpf_usdt_readarg(1, ctx, &result.v1); bpf_usdt_readarg_p(2, ctx, &result.v2, sizeof(result.v2)); event4.perf_submit(ctx, &result, sizeof(result)); return 0; } int do_trace5(struct pt_regs *ctx) { struct probe_result_t5 result = {}; bpf_usdt_readarg_p(1, ctx, &result.v1, sizeof(result.v1)); bpf_usdt_readarg(2, ctx, &result.v2); event5.perf_submit(ctx, &result, sizeof(result)); return 0; } """ # Compile and run the application self.ftemp = NamedTemporaryFile(delete=False) self.ftemp.close() comp = Popen(["gcc", "-I", "%s/include" % os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))), "-x", "c", "-o", self.ftemp.name, "-"], stdin=PIPE) comp.stdin.write(app_text) comp.stdin.close() self.assertEqual(comp.wait(), 0) self.app = Popen([self.ftemp.name]) def test_attach1(self): # enable USDT probe from given PID and verifier generated BPF programs u = USDT(pid=int(self.app.pid)) u.enable_probe(probe="probe_point_1", fn_name="do_trace1") u.enable_probe(probe="probe_point_2", fn_name="do_trace2") u.enable_probe(probe="probe_point_3", fn_name="do_trace3") u.enable_probe(probe="probe_point_4", fn_name="do_trace4") u.enable_probe(probe="probe_point_5", fn_name="do_trace5") b = BPF(text=self.bpf_text, usdt_contexts=[u], debug=4) # Event states for each event: # 0 - probe not caught, 1 - probe caught with correct value, # 2 - probe caught with incorrect value self.evt_st_1 = 0 self.evt_st_2 = 0 self.evt_st_3 = 0 # define output data structure in Python class Data1(ct.Structure): _fields_ = [("v1", ct.c_char), ("v2", ct.c_int)] class Data2(ct.Structure): _fields_ = [("v1", ct.c_int), ("v2", ct.c_char)] class Data3(ct.Structure): _fields_ = [("v1", ct.c_int), ("v2", ct.c_int)] class Data4(ct.Structure): _fields_ = [("v1", ct.c_ulonglong), ("v2", ct.c_char * 64)] class Data5(ct.Structure): _fields_ = [("v1", ct.c_char * 64), ("v2", ct.c_ulonglong)] def check_event_val(event, event_state, v1, v2, v3, v4): if ((event.v1 == v1 and event.v2 == v2) or (event.v1 == v3 and event.v2 == v4)): if (event_state == 0 or event_state == 1): return 1 return 2 def print_event1(cpu, data, size): event = ct.cast(data, ct.POINTER(Data1)).contents self.evt_st_1 = check_event_val(event, self.evt_st_1, b'\x0d', 40, b'\x08', 200) def print_event2(cpu, data, size): event = ct.cast(data, ct.POINTER(Data2)).contents # pretend we have two identical probe points to simplify the code self.evt_st_2 = check_event_val(event, self.evt_st_2, 5, b'\x04', 5, b'\x04') def print_event3(cpu, data, size): event = ct.cast(data, ct.POINTER(Data3)).contents self.evt_st_3 = check_event_val(event, self.evt_st_3, 200, 40, 8, 13) def print_event4(cpu, data, size): event = ct.cast(data, ct.POINTER(Data4)).contents print("%s" % event.v2) def print_event5(cpu, data, size): event = ct.cast(data, ct.POINTER(Data5)).contents print("%s" % event.v1) # loop with callback to print_event b["event1"].open_perf_buffer(print_event1) b["event2"].open_perf_buffer(print_event2) b["event3"].open_perf_buffer(print_event3) b["event4"].open_perf_buffer(print_event4) b["event5"].open_perf_buffer(print_event5) # three iterations to make sure we get some probes and have time to process them for i in range(3): b.perf_buffer_poll() self.assertTrue(self.evt_st_1 == 1 and self.evt_st_2 == 1 and self.evt_st_3 == 1) def tearDown(self): # kill the subprocess, clean the environment self.app.kill() self.app.wait() os.unlink(self.ftemp.name) if __name__ == "__main__": main() bpfcc-0.12.0/tests/python/test_usdt2.py000077500000000000000000000133021357404205000200060ustar00rootroot00000000000000#!/usr/bin/env python # # USAGE: test_usdt2.py # # Copyright 2017 Facebook, Inc # Licensed under the Apache License, Version 2.0 (the "License") from __future__ import print_function from bcc import BPF, USDT from unittest import main, TestCase from subprocess import Popen, PIPE from tempfile import NamedTemporaryFile import ctypes as ct import inspect import os import signal class TestUDST(TestCase): def setUp(self): # Application, minimum, to define three trace points app_text = b""" #include #include #include "folly/tracing/StaticTracepoint.h" int main(int argc, char **argv) { int t = atoi(argv[1]); while (1) { FOLLY_SDT(test, probe_point_1, t); FOLLY_SDT(test, probe_point_2, t + 1); FOLLY_SDT(test, probe_point_3, t + 2); sleep(1); } return 1; } """ # BPF program self.bpf_text = """ #include BPF_PERF_OUTPUT(event1); BPF_PERF_OUTPUT(event2); BPF_PERF_OUTPUT(event3); BPF_PERF_OUTPUT(event4); BPF_PERF_OUTPUT(event5); BPF_PERF_OUTPUT(event6); int do_trace1(struct pt_regs *ctx) { u32 pid = bpf_get_current_pid_tgid(); int result = 0; bpf_usdt_readarg(1, ctx, &result); if (FILTER) event1.perf_submit(ctx, &result, sizeof(result)); else event4.perf_submit(ctx, &result, sizeof(result)); return 0; }; int do_trace2(struct pt_regs *ctx) { u32 pid = bpf_get_current_pid_tgid(); int result = 0; bpf_usdt_readarg(1, ctx, &result); if (FILTER) event2.perf_submit(ctx, &result, sizeof(result)); else event5.perf_submit(ctx, &result, sizeof(result)); return 0; } int do_trace3(struct pt_regs *ctx) { u32 pid = bpf_get_current_pid_tgid(); int result = 0; bpf_usdt_readarg(1, ctx, &result); if (FILTER) event3.perf_submit(ctx, &result, sizeof(result)); else event6.perf_submit(ctx, &result, sizeof(result)); return 0; } """ # Compile and run the application self.ftemp = NamedTemporaryFile(delete=False) self.ftemp.close() comp = Popen(["gcc", "-I", "%s/include" % os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))), "-x", "c", "-o", self.ftemp.name, "-"], stdin=PIPE) comp.stdin.write(app_text) comp.stdin.close() self.assertEqual(comp.wait(), 0) # create 3 applications, 2 applications will have usdt attached and # the third one does not, and the third one should not call into # bpf program. self.app = Popen([self.ftemp.name, "1"]) self.app2 = Popen([self.ftemp.name, "11"]) self.app3 = Popen([self.ftemp.name, "21"]) def test_attach1(self): # Enable USDT probe from given PID and verifier generated BPF programs. u = USDT(pid=int(self.app.pid)) u.enable_probe(probe="probe_point_1", fn_name="do_trace1") u.enable_probe(probe="probe_point_2", fn_name="do_trace2") u2 = USDT(pid=int(self.app2.pid)) u2.enable_probe(probe="probe_point_2", fn_name="do_trace2") u2.enable_probe(probe="probe_point_3", fn_name="do_trace3") self.bpf_text = self.bpf_text.replace("FILTER", "pid == %d" % self.app.pid) b = BPF(text=self.bpf_text, usdt_contexts=[u, u2]) # Event states for each event: # 0 - probe not caught, 1 - probe caught with correct value, # 2 - probe caught with incorrect value self.evt_st_1 = 0 self.evt_st_2 = 0 self.evt_st_3 = 0 self.evt_st_4 = 0 self.evt_st_5 = 0 self.evt_st_6 = 0 def check_event_val(data, event_state, expected_val): result = ct.cast(data, ct.POINTER(ct.c_int)).contents if result.value == expected_val: if (event_state == 0 or event_state == 1): return 1 return 2 def print_event1(cpu, data, size): self.evt_st_1 = check_event_val(data, self.evt_st_1, 1) def print_event2(cpu, data, size): self.evt_st_2 = check_event_val(data, self.evt_st_2, 2) def print_event3(cpu, data, size): self.evt_st_3 = check_event_val(data, self.evt_st_3, 3) def print_event4(cpu, data, size): self.evt_st_4 = check_event_val(data, self.evt_st_4, 11) def print_event5(cpu, data, size): self.evt_st_5 = check_event_val(data, self.evt_st_5, 12) def print_event6(cpu, data, size): self.evt_st_6 = check_event_val(data, self.evt_st_6, 13) # loop with callback to print_event b["event1"].open_perf_buffer(print_event1) b["event2"].open_perf_buffer(print_event2) b["event3"].open_perf_buffer(print_event3) b["event4"].open_perf_buffer(print_event4) b["event5"].open_perf_buffer(print_event5) b["event6"].open_perf_buffer(print_event6) # three iterations to make sure we get some probes and have time to process them for i in range(3): b.perf_buffer_poll() # note that event1 and event4 do not really fire, so their state should be 0 # use separate asserts so that if test fails we know which one is the culprit self.assertTrue(self.evt_st_1 == 1) self.assertTrue(self.evt_st_2 == 1) self.assertTrue(self.evt_st_3 == 0) self.assertTrue(self.evt_st_4 == 0) self.assertTrue(self.evt_st_5 == 1) self.assertTrue(self.evt_st_6 == 1) def tearDown(self): # kill the subprocess, clean the environment self.app.kill() self.app.wait() self.app2.kill() self.app2.wait() self.app3.kill() self.app3.wait() os.unlink(self.ftemp.name) if __name__ == "__main__": main() bpfcc-0.12.0/tests/python/test_usdt3.py000077500000000000000000000106511357404205000200130ustar00rootroot00000000000000#!/usr/bin/env python # # USAGE: test_usdt3.py # # Copyright 2018 Facebook, Inc # Licensed under the Apache License, Version 2.0 (the "License") from __future__ import print_function from bcc import BPF, USDT from unittest import main, TestCase from subprocess import Popen, PIPE import ctypes as ct import inspect, os, tempfile class TestUDST(TestCase): def setUp(self): common_h = b""" #include "folly/tracing/StaticTracepoint.h" static inline void record_val(int val) { FOLLY_SDT(test, probe, val); } extern void record_a(int val); extern void record_b(int val); """ a_c = b""" #include #include "common.h" void record_a(int val) { record_val(val); } """ b_c = b""" #include #include "common.h" void record_b(int val) { record_val(val); } """ m_c = b""" #include #include #include "common.h" int main() { while (1) { record_a(1); record_b(2); record_val(3); sleep(1); } return 0; } """ # BPF program self.bpf_text = """ BPF_PERF_OUTPUT(event); int do_trace(struct pt_regs *ctx) { int result = 0; bpf_usdt_readarg(1, ctx, &result); event.perf_submit(ctx, &result, sizeof(result)); return 0; }; """ def _create_file(name, text): text_file = open(name, "wb") text_file.write(text) text_file.close() # Create source files self.tmp_dir = tempfile.mkdtemp() print("temp directory: " + self.tmp_dir) _create_file(self.tmp_dir + "/common.h", common_h) _create_file(self.tmp_dir + "/a.c", a_c) _create_file(self.tmp_dir + "/b.c", b_c) _create_file(self.tmp_dir + "/m.c", m_c) # Compilation # the usdt test:probe exists in liba.so, libb.so and a.out include_path = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) + "/include" a_src = self.tmp_dir + "/a.c" a_obj = self.tmp_dir + "/a.o" a_lib = self.tmp_dir + "/liba.so" b_src = self.tmp_dir + "/b.c" b_obj = self.tmp_dir + "/b.o" b_lib = self.tmp_dir + "/libb.so" m_src = self.tmp_dir + "/m.c" m_bin = self.tmp_dir + "/a.out" m_linker_opt = " -L" + self.tmp_dir + " -la -lb" self.assertEqual(os.system("gcc -I" + include_path + " -fpic -c -o " + a_obj + " " + a_src), 0) self.assertEqual(os.system("gcc -I" + include_path + " -fpic -c -o " + b_obj + " " + b_src), 0) self.assertEqual(os.system("gcc -shared -o " + a_lib + " " + a_obj), 0) self.assertEqual(os.system("gcc -shared -o " + b_lib + " " + b_obj), 0) self.assertEqual(os.system("gcc -I" + include_path + " " + m_src + " -o " + m_bin + m_linker_opt), 0) # Run the application self.app = Popen([m_bin], env=dict(os.environ, LD_LIBRARY_PATH=self.tmp_dir)) os.system("../../tools/tplist.py -vvv -p " + str(self.app.pid)) def test_attach1(self): # enable USDT probe from given PID and verifier generated BPF programs u = USDT(pid=int(self.app.pid)) u.enable_probe(probe="probe", fn_name="do_trace") b = BPF(text=self.bpf_text, usdt_contexts=[u]) # processing events self.probe_value_1 = 0 self.probe_value_2 = 0 self.probe_value_3 = 0 self.probe_value_other = 0 def print_event(cpu, data, size): result = ct.cast(data, ct.POINTER(ct.c_int)).contents if result.value == 1: self.probe_value_1 = 1 elif result.value == 2: self.probe_value_2 = 1 elif result.value == 3: self.probe_value_3 = 1 else: self.probe_value_other = 1 b["event"].open_perf_buffer(print_event) for i in range(100): if (self.probe_value_1 == 0 or self.probe_value_2 == 0 or self.probe_value_3 == 0 or self.probe_value_other != 0): b.perf_buffer_poll() else: break; self.assertTrue(self.probe_value_1 != 0) self.assertTrue(self.probe_value_2 != 0) self.assertTrue(self.probe_value_3 != 0) self.assertTrue(self.probe_value_other == 0) def tearDown(self): # kill the subprocess, clean the environment self.app.kill() self.app.wait() os.system("rm -rf " + self.tmp_dir) if __name__ == "__main__": main() bpfcc-0.12.0/tests/python/test_utils.py000077500000000000000000000013011357404205000201010ustar00rootroot00000000000000#!/usr/bin/env python # Copyright (c) Catalysts GmbH # Licensed under the Apache License, Version 2.0 (the "License") from bcc.utils import get_online_cpus, detect_language import multiprocessing import unittest import os class TestUtils(unittest.TestCase): def test_get_online_cpus(self): online_cpus = get_online_cpus() num_cores = multiprocessing.cpu_count() self.assertEqual(len(online_cpus), num_cores) def test_detect_language(self): candidates = ["c", "java", "perl", "php", "node", "ruby", "python"] language = detect_language(candidates, os.getpid()) self.assertEqual(language, "python") if __name__ == "__main__": unittest.main() bpfcc-0.12.0/tests/python/test_xlate1.b000066400000000000000000000026221357404205000177340ustar00rootroot00000000000000// Copyright (c) PLUMgrid, Inc. // Licensed under the Apache License, Version 2.0 (the "License") // test for packet modification #packed "false" struct IPKey { u32 dip:32; u32 sip:32; }; struct IPLeaf { u32 xdip:32; u32 xsip:32; u64 xlated_pkts:64; }; Table xlate(1024); struct skbuff { u32 type:32; }; u32 on_packet (struct skbuff *skb) { u32 ret:32 = 1; u32 orig_dip:32 = 0; u32 orig_sip:32 = 0; struct IPLeaf *xleaf; goto proto::ethernet; state proto::ethernet { } state proto::dot1q { } state proto::ip { orig_dip = $ip.dst; orig_sip = $ip.src; struct IPKey key = {.dip=orig_dip, .sip=orig_sip}; xlate.lookup(key, xleaf) {}; on_valid(xleaf) { incr_cksum(@ip.hchecksum, orig_dip, xleaf.xdip); incr_cksum(@ip.hchecksum, orig_sip, xleaf.xsip); // the below are equivalent pkt.rewrite_field($ip.dst, xleaf.xdip); $ip.src = xleaf.xsip; atomic_add(xleaf.xlated_pkts, 1); } } state proto::udp { on_valid(xleaf) { incr_cksum(@udp.crc, orig_dip, xleaf.xdip, 1); incr_cksum(@udp.crc, orig_sip, xleaf.xsip, 1); } } state proto::tcp { on_valid(xleaf) { incr_cksum(@tcp.cksum, orig_dip, xleaf.xdip, 1); incr_cksum(@tcp.cksum, orig_sip, xleaf.xsip, 1); } } state proto::vxlan { } state proto::gre { } state EOP { return ret; } } bpfcc-0.12.0/tests/python/test_xlate1.c000066400000000000000000000044731357404205000177430ustar00rootroot00000000000000// Copyright (c) PLUMgrid, Inc. // Licensed under the Apache License, Version 2.0 (the "License") #include struct IPKey { u32 dip; u32 sip; }; struct IPLeaf { u32 xdip; u32 xsip; u64 ip_xlated_pkts; u64 arp_xlated_pkts; }; BPF_HASH(xlate, struct IPKey, struct IPLeaf, 1024); int on_packet(struct __sk_buff *skb) { u8 *cursor = 0; u32 orig_dip = 0; u32 orig_sip = 0; struct IPLeaf xleaf = {}; ethernet: { struct ethernet_t *ethernet = cursor_advance(cursor, sizeof(*ethernet)); switch (ethernet->type) { case ETH_P_IP: goto ip; case ETH_P_ARP: goto arp; case ETH_P_8021Q: goto dot1q; default: goto EOP; } } dot1q: { struct dot1q_t *dot1q = cursor_advance(cursor, sizeof(*dot1q)); switch (dot1q->type) { case ETH_P_IP: goto ip; case ETH_P_ARP: goto arp; default: goto EOP; } } arp: { struct arp_t *arp = cursor_advance(cursor, sizeof(*arp)); orig_dip = arp->tpa; orig_sip = arp->spa; struct IPKey key = {.dip=orig_dip, .sip=orig_sip}; struct IPLeaf *xleafp = xlate.lookup(&key); if (xleafp) { xleaf = *xleafp; arp->tpa = xleaf.xdip; arp->spa = xleaf.xsip; lock_xadd(&xleafp->arp_xlated_pkts, 1); } goto EOP; } ip: { struct ip_t *ip = cursor_advance(cursor, sizeof(*ip)); orig_dip = ip->dst; orig_sip = ip->src; struct IPKey key = {.dip=orig_dip, .sip=orig_sip}; struct IPLeaf *xleafp = xlate.lookup(&key); if (xleafp) { xleaf = *xleafp; ip->dst = xleaf.xdip; incr_cksum_l3(&ip->hchecksum, orig_dip, xleaf.xdip); ip->src = xleaf.xsip; incr_cksum_l3(&ip->hchecksum, orig_sip, xleaf.xsip); lock_xadd(&xleafp->ip_xlated_pkts, 1); } switch (ip->nextp) { case 6: goto tcp; case 17: goto udp; default: goto EOP; } } udp: { struct udp_t *udp = cursor_advance(cursor, sizeof(*udp)); if (xleaf.xdip) { incr_cksum_l4(&udp->crc, orig_dip, xleaf.xdip, 1); incr_cksum_l4(&udp->crc, orig_sip, xleaf.xsip, 1); } goto EOP; } tcp: { struct tcp_t *tcp = cursor_advance(cursor, sizeof(*tcp)); if (xleaf.xdip) { incr_cksum_l4(&tcp->cksum, orig_dip, xleaf.xdip, 1); incr_cksum_l4(&tcp->cksum, orig_sip, xleaf.xsip, 1); } goto EOP; } EOP: return 0; } bpfcc-0.12.0/tests/python/test_xlate1.py000077500000000000000000000046621357404205000201540ustar00rootroot00000000000000#!/usr/bin/env python # Copyright (c) PLUMgrid, Inc. # Licensed under the Apache License, Version 2.0 (the "License") from netaddr import IPAddress from bcc import BPF from pyroute2 import IPRoute, protocols from socket import socket, AF_INET, SOCK_DGRAM from subprocess import call import sys from time import sleep from unittest import main, TestCase arg1 = sys.argv.pop(1) arg2 = "" if len(sys.argv) > 1: arg2 = sys.argv.pop(1) class TestBPFFilter(TestCase): def setUp(self): b = BPF(arg1, arg2, debug=0) fn = b.load_func("on_packet", BPF.SCHED_ACT) ip = IPRoute() ifindex = ip.link_lookup(ifname="eth0")[0] # set up a network to change the flow: # outside | inside # 172.16.1.1 - 172.16.1.2 | 192.168.1.1 - 192.16.1.2 ip.addr("del", index=ifindex, address="172.16.1.2", mask=24) ip.addr("add", index=ifindex, address="192.168.1.2", mask=24) # add an ingress and egress qdisc ip.tc("add", "ingress", ifindex, "ffff:") ip.tc("add", "sfq", ifindex, "1:") # add same program to both ingress/egress, so pkt is translated in both directions action = {"kind": "bpf", "fd": fn.fd, "name": fn.name, "action": "ok"} ip.tc("add-filter", "u32", ifindex, ":1", parent="ffff:", action=[action], protocol=protocols.ETH_P_ALL, classid=1, target=0x10002, keys=['0x0/0x0+0']) ip.tc("add-filter", "u32", ifindex, ":2", parent="1:", action=[action], protocol=protocols.ETH_P_ALL, classid=1, target=0x10002, keys=['0x0/0x0+0']) self.xlate = b.get_table("xlate") def test_xlate(self): key1 = self.xlate.Key(IPAddress("172.16.1.2").value, IPAddress("172.16.1.1").value) leaf1 = self.xlate.Leaf(IPAddress("192.168.1.2").value, IPAddress("192.168.1.1").value, 0, 0) self.xlate[key1] = leaf1 key2 = self.xlate.Key(IPAddress("192.168.1.1").value, IPAddress("192.168.1.2").value) leaf2 = self.xlate.Leaf(IPAddress("172.16.1.1").value, IPAddress("172.16.1.2").value, 0, 0) self.xlate[key2] = leaf2 call(["ping", "-c1", "192.168.1.1"]) leaf = self.xlate[key1] self.assertGreater(leaf.ip_xlated_pkts, 0) self.assertGreater(leaf.arp_xlated_pkts, 0) leaf = self.xlate[key2] self.assertGreater(leaf.ip_xlated_pkts, 0) self.assertGreater(leaf.arp_xlated_pkts, 0) if __name__ == "__main__": main() bpfcc-0.12.0/tests/python/utils.py000066400000000000000000000012461357404205000170470ustar00rootroot00000000000000from pyroute2 import NSPopen from distutils.spawn import find_executable def has_executable(name): path = find_executable(name) if path is None: raise Exception(name + ": command not found") return path class NSPopenWithCheck(NSPopen): """ A wrapper for NSPopen that additionally checks if the program to be executed is available from the system path or not. If found, it proceeds with the usual NSPopen() call. Otherwise, it raises an exception. """ def __init__(self, nsname, *argv, **kwarg): name = list(argv)[0][0] has_executable(name) super(NSPopenWithCheck, self).__init__(nsname, *argv, **kwarg) bpfcc-0.12.0/tests/wrapper.sh.in000077500000000000000000000025731357404205000164440ustar00rootroot00000000000000#!/bin/bash # Copyright (c) PLUMgrid, Inc. # Licensed under the Apache License, Version 2.0 (the "License") #set -x name=$1; shift kind=$1; shift cmd=$1; shift PYTHONPATH=@CMAKE_BINARY_DIR@/src/python/bcc-python LD_LIBRARY_PATH=@CMAKE_BINARY_DIR@:@CMAKE_BINARY_DIR@/src/cc ns=$name function cleanup() { trap - EXIT if [[ "$kind" = "namespace" ]]; then sudo ip netns delete $ns fi } trap cleanup EXIT function ns_run() { sudo ip netns add $ns sudo ip link add $ns.in type veth peer name $ns.out sudo ip link set $ns.in netns $ns sudo ip netns exec $ns ip link set $ns.in name eth0 sudo ip netns exec $ns ip addr add dev eth0 172.16.1.2/24 sudo ip netns exec $ns ip link set eth0 up sudo ip netns exec $ns ethtool -K eth0 tx off sudo ip addr add dev $ns.out 172.16.1.1/24 sudo ip link set $ns.out up sudo bash -c "PYTHONPATH=$PYTHONPATH LD_LIBRARY_PATH=$LD_LIBRARY_PATH ip netns exec $ns $cmd $1 $2" return $? } function sudo_run() { sudo bash -c "PYTHONPATH=$PYTHONPATH LD_LIBRARY_PATH=$LD_LIBRARY_PATH $cmd $1 $2" return $? } function simple_run() { PYTHONPATH=$PYTHONPATH LD_LIBRARY_PATH=$LD_LIBRARY_PATH $cmd $1 $2 return $? } case $kind in namespace) ns_run $@ ;; sudo) sudo_run $@ ;; simple) simple_run $@ ;; *) echo "Invalid kind $kind" exit 1 ;; esac [[ $? -ne 0 ]] && { echo "Failed"; exit 1; } exit 0 bpfcc-0.12.0/tools/000077500000000000000000000000001357404205000140075ustar00rootroot00000000000000bpfcc-0.12.0/tools/CMakeLists.txt000066400000000000000000000021411357404205000165450ustar00rootroot00000000000000file(GLOB C_FILES *.c) file(GLOB PY_FILES *.py) file(GLOB SH_FILES *.sh) file(GLOB TXT_FILES *.txt) list(REMOVE_ITEM TXT_FILES ${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt) foreach(FIL ${PY_FILES}) get_filename_component(FIL_WE ${FIL} NAME_WE) install(PROGRAMS ${FIL} DESTINATION share/bcc/tools RENAME ${FIL_WE}) endforeach() foreach(FIL ${SH_FILES}) if(${FIL} STREQUAL ${CMAKE_CURRENT_SOURCE_DIR}/reset-trace.sh) get_filename_component(FIL_WE ${FIL} NAME_WE) install(PROGRAMS ${FIL} DESTINATION share/bcc/tools RENAME ${FIL_WE}) else() file(READ ${FIL} CONTENT) string(REPLACE ".py -l" " -l" CONTENT_WE ${CONTENT}) string(REPLACE "\"" "\\\"" CONTENT_WE ${CONTENT_WE}) get_filename_component(FIL_WE ${FIL} NAME_WE) install(PROGRAMS ${FIL} DESTINATION share/bcc/tools RENAME ${FIL_WE}) install(CODE "file(WRITE \"\$ENV{DESTDIR}/\${CMAKE_INSTALL_PREFIX}/share/bcc/tools/${FIL_WE}\" \"${CONTENT_WE}\")") endif() endforeach() install(FILES ${C_FILES} DESTINATION share/bcc/tools) install(FILES ${TXT_FILES} DESTINATION share/bcc/tools/doc) add_subdirectory(lib) add_subdirectory(old) bpfcc-0.12.0/tools/argdist.py000077500000000000000000001033461357404205000160300ustar00rootroot00000000000000#!/usr/bin/python # # argdist Trace a function and display a distribution of its # parameter values as a histogram or frequency count. # # USAGE: argdist [-h] [-p PID] [-z STRING_SIZE] [-i INTERVAL] [-n COUNT] [-v] # [-c] [-T TOP] [-C specifier] [-H specifier] [-I header] # # Licensed under the Apache License, Version 2.0 (the "License") # Copyright (C) 2016 Sasha Goldshtein. from bcc import BPF, USDT from time import sleep, strftime import argparse import re import traceback import os import sys class Probe(object): next_probe_index = 0 streq_index = 0 aliases = {"$PID": "(bpf_get_current_pid_tgid() >> 32)"} def _substitute_aliases(self, expr): if expr is None: return expr for alias, subst in Probe.aliases.items(): expr = expr.replace(alias, subst) return expr def _parse_signature(self): params = map(str.strip, self.signature.split(',')) self.param_types = {} for param in params: # If the type is a pointer, the * can be next to the # param name. Other complex types like arrays are not # supported right now. index = param.rfind('*') index = index if index != -1 else param.rfind(' ') param_type = param[0:index + 1].strip() param_name = param[index + 1:].strip() self.param_types[param_name] = param_type def _generate_entry(self): self.entry_probe_func = self.probe_func_name + "_entry" text = """ int PROBENAME(struct pt_regs *ctx SIGNATURE) { u64 __pid_tgid = bpf_get_current_pid_tgid(); u32 __pid = __pid_tgid; // lower 32 bits u32 __tgid = __pid_tgid >> 32; // upper 32 bits PID_FILTER COLLECT return 0; } """ text = text.replace("PROBENAME", self.entry_probe_func) text = text.replace("SIGNATURE", "" if len(self.signature) == 0 else ", " + self.signature) text = text.replace("PID_FILTER", self._generate_pid_filter()) collect = "" for pname in self.args_to_probe: param_hash = self.hashname_prefix + pname if pname == "__latency": collect += """ u64 __time = bpf_ktime_get_ns(); %s.update(&__pid, &__time); """ % param_hash else: collect += "%s.update(&__pid, &%s);\n" % \ (param_hash, pname) text = text.replace("COLLECT", collect) return text def _generate_entry_probe(self): # Any $entry(name) expressions result in saving that argument # when entering the function. self.args_to_probe = set() regex = r"\$entry\((\w+)\)" for expr in self.exprs: for arg in re.finditer(regex, expr): self.args_to_probe.add(arg.group(1)) for arg in re.finditer(regex, self.filter): self.args_to_probe.add(arg.group(1)) if any(map(lambda expr: "$latency" in expr, self.exprs)) or \ "$latency" in self.filter: self.args_to_probe.add("__latency") self.param_types["__latency"] = "u64" # nanoseconds for pname in self.args_to_probe: if pname not in self.param_types: raise ValueError("$entry(%s): no such param" % arg) self.hashname_prefix = "%s_param_" % self.probe_hash_name text = "" for pname in self.args_to_probe: # Each argument is stored in a separate hash that is # keyed by pid. text += "BPF_HASH(%s, u32, %s);\n" % \ (self.hashname_prefix + pname, self.param_types[pname]) text += self._generate_entry() return text def _generate_retprobe_prefix(self): # After we're done here, there are __%s_val variables for each # argument we needed to probe using $entry(name), and they all # have values (which isn't necessarily the case if we missed # the method entry probe). text = "" self.param_val_names = {} for pname in self.args_to_probe: val_name = "__%s_val" % pname text += "%s *%s = %s.lookup(&__pid);\n" % \ (self.param_types[pname], val_name, self.hashname_prefix + pname) text += "if (%s == 0) { return 0 ; }\n" % val_name self.param_val_names[pname] = val_name return text def _replace_entry_exprs(self): for pname, vname in self.param_val_names.items(): if pname == "__latency": entry_expr = "$latency" val_expr = "(bpf_ktime_get_ns() - *%s)" % vname else: entry_expr = "$entry(%s)" % pname val_expr = "(*%s)" % vname for i in range(0, len(self.exprs)): self.exprs[i] = self.exprs[i].replace( entry_expr, val_expr) self.filter = self.filter.replace(entry_expr, val_expr) def _attach_entry_probe(self): if self.is_user: self.bpf.attach_uprobe(name=self.library, sym=self.function, fn_name=self.entry_probe_func, pid=self.pid or -1) else: self.bpf.attach_kprobe(event=self.function, fn_name=self.entry_probe_func) def _bail(self, error): raise ValueError("error parsing probe '%s': %s" % (self.raw_spec, error)) def _validate_specifier(self): # Everything after '#' is the probe label, ignore it spec = self.raw_spec.split('#')[0] parts = spec.strip().split(':') if len(parts) < 3: self._bail("at least the probe type, library, and " + "function signature must be specified") if len(parts) > 6: self._bail("extraneous ':'-separated parts detected") if parts[0] not in ["r", "p", "t", "u"]: self._bail("probe type must be 'p', 'r', 't', or 'u'" + " but got '%s'" % parts[0]) if re.match(r"\S+\(.*\)", parts[2]) is None: self._bail(("function signature '%s' has an invalid " + "format") % parts[2]) def _parse_expr_types(self, expr_types): if len(expr_types) == 0: self._bail("no expr types specified") self.expr_types = expr_types.split(',') def _parse_exprs(self, exprs): if len(exprs) == 0: self._bail("no exprs specified") self.exprs = exprs.split(',') def _make_valid_identifier(self, ident): return re.sub(r'[^A-Za-z0-9_]', '_', ident) def __init__(self, tool, type, specifier): self.usdt_ctx = None self.streq_functions = "" self.pid = tool.args.pid self.cumulative = tool.args.cumulative or False self.raw_spec = specifier self._validate_specifier() spec_and_label = specifier.split('#') self.label = spec_and_label[1] \ if len(spec_and_label) == 2 else None parts = spec_and_label[0].strip().split(':') self.type = type # hist or freq self.probe_type = parts[0] fparts = parts[2].split('(') self.function = fparts[0].strip() if self.probe_type == "t": self.library = "" # kernel self.tp_category = parts[1] self.tp_event = self.function elif self.probe_type == "u": self.library = parts[1] self.probe_func_name = self._make_valid_identifier( "%s_probe%d" % (self.function, Probe.next_probe_index)) self._enable_usdt_probe() else: self.library = parts[1] self.is_user = len(self.library) > 0 self.signature = fparts[1].strip()[:-1] self._parse_signature() # If the user didn't specify an expression to probe, we probe # the retval in a ret probe, or simply the value "1" otherwise. self.is_default_expr = len(parts) < 5 if not self.is_default_expr: self._parse_expr_types(parts[3]) self._parse_exprs(parts[4]) if len(self.exprs) != len(self.expr_types): self._bail("mismatched # of exprs and types") if self.type == "hist" and len(self.expr_types) > 1: self._bail("histograms can only have 1 expr") else: if not self.probe_type == "r" and self.type == "hist": self._bail("histograms must have expr") self.expr_types = \ ["u64" if not self.probe_type == "r" else "int"] self.exprs = \ ["1" if not self.probe_type == "r" else "$retval"] self.filter = "" if len(parts) != 6 else parts[5] self._substitute_exprs() # Do we need to attach an entry probe so that we can collect an # argument that is required for an exit (return) probe? def check(expr): keywords = ["$entry", "$latency"] return any(map(lambda kw: kw in expr, keywords)) self.entry_probe_required = self.probe_type == "r" and \ (any(map(check, self.exprs)) or check(self.filter)) self.probe_func_name = self._make_valid_identifier( "%s_probe%d" % (self.function, Probe.next_probe_index)) self.probe_hash_name = self._make_valid_identifier( "%s_hash%d" % (self.function, Probe.next_probe_index)) Probe.next_probe_index += 1 def _enable_usdt_probe(self): self.usdt_ctx = USDT(path=self.library, pid=self.pid) self.usdt_ctx.enable_probe( self.function, self.probe_func_name) def _generate_streq_function(self, string): fname = "streq_%d" % Probe.streq_index Probe.streq_index += 1 self.streq_functions += """ static inline bool %s(char const *ignored, char const *str) { char needle[] = %s; char haystack[sizeof(needle)]; bpf_probe_read(&haystack, sizeof(haystack), (void *)str); for (int i = 0; i < sizeof(needle) - 1; ++i) { if (needle[i] != haystack[i]) { return false; } } return true; } """ % (fname, string) return fname def _substitute_exprs(self): def repl(expr): expr = self._substitute_aliases(expr) matches = re.finditer('STRCMP\\(("[^"]+\\")', expr) for match in matches: string = match.group(1) fname = self._generate_streq_function(string) expr = expr.replace("STRCMP", fname, 1) return expr.replace("$retval", "PT_REGS_RC(ctx)") for i in range(0, len(self.exprs)): self.exprs[i] = repl(self.exprs[i]) self.filter = repl(self.filter) def _is_string(self, expr_type): return expr_type == "char*" or expr_type == "char *" def _generate_hash_field(self, i): if self._is_string(self.expr_types[i]): return "struct __string_t v%d;\n" % i else: return "%s v%d;\n" % (self.expr_types[i], i) def _generate_usdt_arg_assignment(self, i): expr = self.exprs[i] if self.probe_type == "u" and expr[0:3] == "arg": arg_index = int(expr[3]) arg_ctype = self.usdt_ctx.get_probe_arg_ctype( self.function, arg_index - 1) return (" %s %s = 0;\n" + " bpf_usdt_readarg(%s, ctx, &%s);\n") \ % (arg_ctype, expr, expr[3], expr) else: return "" def _generate_field_assignment(self, i): text = self._generate_usdt_arg_assignment(i) if self._is_string(self.expr_types[i]): return (text + " bpf_probe_read(&__key.v%d.s," + " sizeof(__key.v%d.s), (void *)%s);\n") % \ (i, i, self.exprs[i]) else: return text + " __key.v%d = %s;\n" % \ (i, self.exprs[i]) def _generate_hash_decl(self): if self.type == "hist": return "BPF_HISTOGRAM(%s, %s);" % \ (self.probe_hash_name, self.expr_types[0]) else: text = "struct %s_key_t {\n" % self.probe_hash_name for i in range(0, len(self.expr_types)): text += self._generate_hash_field(i) text += "};\n" text += "BPF_HASH(%s, struct %s_key_t, u64);\n" % \ (self.probe_hash_name, self.probe_hash_name) return text def _generate_key_assignment(self): if self.type == "hist": return self._generate_usdt_arg_assignment(0) + \ ("%s __key = %s;\n" % (self.expr_types[0], self.exprs[0])) else: text = "struct %s_key_t __key = {};\n" % \ self.probe_hash_name for i in range(0, len(self.exprs)): text += self._generate_field_assignment(i) return text def _generate_hash_update(self): if self.type == "hist": return "%s.increment(bpf_log2l(__key));" % \ self.probe_hash_name else: return "%s.increment(__key);" % self.probe_hash_name def _generate_pid_filter(self): # Kernel probes need to explicitly filter pid, because the # attach interface doesn't support pid filtering if self.pid is not None and not self.is_user: return "if (__tgid != %d) { return 0; }" % self.pid else: return "" def generate_text(self): program = "" probe_text = """ DATA_DECL """ + ( "TRACEPOINT_PROBE(%s, %s)" % (self.tp_category, self.tp_event) if self.probe_type == "t" else "int PROBENAME(struct pt_regs *ctx SIGNATURE)") + """ { u64 __pid_tgid = bpf_get_current_pid_tgid(); u32 __pid = __pid_tgid; // lower 32 bits u32 __tgid = __pid_tgid >> 32; // upper 32 bits PID_FILTER PREFIX if (!(FILTER)) return 0; KEY_EXPR COLLECT return 0; } """ prefix = "" signature = "" # If any entry arguments are probed in a ret probe, we need # to generate an entry probe to collect them if self.entry_probe_required: program += self._generate_entry_probe() prefix += self._generate_retprobe_prefix() # Replace $entry(paramname) with a reference to the # value we collected when entering the function: self._replace_entry_exprs() if self.probe_type == "p" and len(self.signature) > 0: # Only entry uprobes/kprobes can have user-specified # signatures. Other probes force it to (). signature = ", " + self.signature program += probe_text.replace("PROBENAME", self.probe_func_name) program = program.replace("SIGNATURE", signature) program = program.replace("PID_FILTER", self._generate_pid_filter()) decl = self._generate_hash_decl() key_expr = self._generate_key_assignment() collect = self._generate_hash_update() program = program.replace("DATA_DECL", decl) program = program.replace("KEY_EXPR", key_expr) program = program.replace("FILTER", "1" if len(self.filter) == 0 else self.filter) program = program.replace("COLLECT", collect) program = program.replace("PREFIX", prefix) return self.streq_functions + program def _attach_u(self): libpath = BPF.find_library(self.library) if libpath is None: libpath = BPF.find_exe(self.library) if libpath is None or len(libpath) == 0: self._bail("unable to find library %s" % self.library) if self.probe_type == "r": self.bpf.attach_uretprobe(name=libpath, sym=self.function, fn_name=self.probe_func_name, pid=self.pid or -1) else: self.bpf.attach_uprobe(name=libpath, sym=self.function, fn_name=self.probe_func_name, pid=self.pid or -1) def _attach_k(self): if self.probe_type == "t": pass # Nothing to do for tracepoints elif self.probe_type == "r": self.bpf.attach_kretprobe(event=self.function, fn_name=self.probe_func_name) else: self.bpf.attach_kprobe(event=self.function, fn_name=self.probe_func_name) def attach(self, bpf): self.bpf = bpf if self.probe_type == "u": return if self.is_user: self._attach_u() else: self._attach_k() if self.entry_probe_required: self._attach_entry_probe() def _v2s(self, v): # Most fields can be converted with plain str(), but strings # are wrapped in a __string_t which has an .s field if "__string_t" in type(v).__name__: return str(v.s) return str(v) def _display_expr(self, i): # Replace ugly latency calculation with $latency expr = self.exprs[i].replace( "(bpf_ktime_get_ns() - *____latency_val)", "$latency") # Replace alias values back with the alias name for alias, subst in Probe.aliases.items(): expr = expr.replace(subst, alias) # Replace retval expression with $retval expr = expr.replace("PT_REGS_RC(ctx)", "$retval") # Replace ugly (*__param_val) expressions with param name return re.sub(r"\(\*__(\w+)_val\)", r"\1", expr) def _display_key(self, key): if self.is_default_expr: if not self.probe_type == "r": return "total calls" else: return "retval = %s" % str(key.v0) else: # The key object has v0, ..., vk fields containing # the values of the expressions from self.exprs def str_i(i): key_i = self._v2s(getattr(key, "v%d" % i)) return "%s = %s" % \ (self._display_expr(i), key_i) return ", ".join(map(str_i, range(0, len(self.exprs)))) def display(self, top): data = self.bpf.get_table(self.probe_hash_name) if self.type == "freq": print(self.label or self.raw_spec) print("\t%-10s %s" % ("COUNT", "EVENT")) sdata = sorted(data.items(), key=lambda p: p[1].value) if top is not None: sdata = sdata[-top:] for key, value in sdata: # Print some nice values if the user didn't # specify an expression to probe if self.is_default_expr: if not self.probe_type == "r": key_str = "total calls" else: key_str = "retval = %s" % \ self._v2s(key.v0) else: key_str = self._display_key(key) print("\t%-10s %s" % (str(value.value), key_str)) elif self.type == "hist": label = self.label or (self._display_expr(0) if not self.is_default_expr else "retval") data.print_log2_hist(val_type=label) if not self.cumulative: data.clear() def __str__(self): return self.label or self.raw_spec class Tool(object): examples = """ Probe specifier syntax: {p,r,t,u}:{[library],category}:function(signature)[:type[,type...]:expr[,expr...][:filter]][#label] Where: p,r,t,u -- probe at function entry, function exit, kernel tracepoint, or USDT probe in exit probes: can use $retval, $entry(param), $latency library -- the library that contains the function (leave empty for kernel functions) category -- the category of the kernel tracepoint (e.g. net, sched) function -- the function name to trace (or tracepoint name) signature -- the function's parameters, as in the C header type -- the type of the expression to collect (supports multiple) expr -- the expression to collect (supports multiple) filter -- the filter that is applied to collected values label -- the label for this probe in the resulting output EXAMPLES: argdist -H 'p::__kmalloc(u64 size):u64:size' Print a histogram of allocation sizes passed to kmalloc argdist -p 1005 -C 'p:c:malloc(size_t size):size_t:size:size==16' Print a frequency count of how many times process 1005 called malloc with an allocation size of 16 bytes argdist -C 'r:c:gets():char*:(char*)$retval#snooped strings' Snoop on all strings returned by gets() argdist -H 'r::__kmalloc(size_t size):u64:$latency/$entry(size)#ns per byte' Print a histogram of nanoseconds per byte from kmalloc allocations argdist -C 'p::__kmalloc(size_t sz, gfp_t flags):size_t:sz:flags&GFP_ATOMIC' Print frequency count of kmalloc allocation sizes that have GFP_ATOMIC argdist -p 1005 -C 'p:c:write(int fd):int:fd' -T 5 Print frequency counts of how many times writes were issued to a particular file descriptor number, in process 1005, but only show the top 5 busiest fds argdist -p 1005 -H 'r:c:read()' Print a histogram of results (sizes) returned by read() in process 1005 argdist -C 'r::__vfs_read():u32:$PID:$latency > 100000' Print frequency of reads by process where the latency was >0.1ms argdist -H 'r::__vfs_read(void *file, void *buf, size_t count):size_t: $entry(count):$latency > 1000000' Print a histogram of read sizes that were longer than 1ms argdist -H \\ 'p:c:write(int fd, const void *buf, size_t count):size_t:count:fd==1' Print a histogram of buffer sizes passed to write() across all processes, where the file descriptor was 1 (STDOUT) argdist -C 'p:c:fork()#fork calls' Count fork() calls in libc across all processes Can also use funccount.py, which is easier and more flexible argdist -H 't:block:block_rq_complete():u32:args->nr_sector' Print histogram of number of sectors in completing block I/O requests argdist -C 't:irq:irq_handler_entry():int:args->irq' Aggregate interrupts by interrupt request (IRQ) argdist -C 'u:pthread:pthread_start():u64:arg2' -p 1337 Print frequency of function addresses used as a pthread start function, relying on the USDT pthread_start probe in process 1337 argdist -H 'p:c:sleep(u32 seconds):u32:seconds' \\ -H 'p:c:nanosleep(struct timespec *req):long:req->tv_nsec' Print histograms of sleep() and nanosleep() parameter values argdist -p 2780 -z 120 \\ -C 'p:c:write(int fd, char* buf, size_t len):char*:buf:fd==1' Spy on writes to STDOUT performed by process 2780, up to a string size of 120 characters argdist -I 'kernel/sched/sched.h' \\ -C 'p::__account_cfs_rq_runtime(struct cfs_rq *cfs_rq):s64:cfs_rq->runtime_remaining' Trace on the cfs scheduling runqueue remaining runtime. The struct cfs_rq is defined in kernel/sched/sched.h which is in kernel source tree and not in kernel-devel package. So this command needs to run at the kernel source tree root directory so that the added header file can be found by the compiler. """ def __init__(self): parser = argparse.ArgumentParser(description="Trace a " + "function and display a summary of its parameter values.", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=Tool.examples) parser.add_argument("-p", "--pid", type=int, help="id of the process to trace (optional)") parser.add_argument("-z", "--string-size", default=80, type=int, help="maximum string size to read from char* arguments") parser.add_argument("-i", "--interval", default=1, type=int, help="output interval, in seconds (default 1 second)") parser.add_argument("-d", "--duration", type=int, help="total duration of trace, in seconds") parser.add_argument("-n", "--number", type=int, dest="count", help="number of outputs") parser.add_argument("-v", "--verbose", action="store_true", help="print resulting BPF program code before executing") parser.add_argument("-c", "--cumulative", action="store_true", help="do not clear histograms and freq counts at " + "each interval") parser.add_argument("-T", "--top", type=int, help="number of top results to show (not applicable to " + "histograms)") parser.add_argument("-H", "--histogram", action="append", dest="histspecifier", metavar="specifier", help="probe specifier to capture histogram of " + "(see examples below)") parser.add_argument("-C", "--count", action="append", dest="countspecifier", metavar="specifier", help="probe specifier to capture count of " + "(see examples below)") parser.add_argument("-I", "--include", action="append", metavar="header", help="additional header files to include in the BPF program " "as either full path, " "or relative to relative to current working directory, " "or relative to default kernel header search path") self.args = parser.parse_args() self.usdt_ctx = None def _create_probes(self): self.probes = [] for specifier in (self.args.countspecifier or []): self.probes.append(Probe(self, "freq", specifier)) for histspecifier in (self.args.histspecifier or []): self.probes.append(Probe(self, "hist", histspecifier)) if len(self.probes) == 0: print("at least one specifier is required") exit(1) def _generate_program(self): bpf_source = """ struct __string_t { char s[%d]; }; #include """ % self.args.string_size for include in (self.args.include or []): if include.startswith((".", "/")): include = os.path.abspath(include) bpf_source += "#include \"%s\"\n" % include else: bpf_source += "#include <%s>\n" % include bpf_source += BPF.generate_auto_includes( map(lambda p: p.raw_spec, self.probes)) for probe in self.probes: bpf_source += probe.generate_text() if self.args.verbose: for text in [probe.usdt_ctx.get_text() for probe in self.probes if probe.usdt_ctx]: print(text) print(bpf_source) usdt_contexts = [probe.usdt_ctx for probe in self.probes if probe.usdt_ctx] self.bpf = BPF(text=bpf_source, usdt_contexts=usdt_contexts) def _attach(self): for probe in self.probes: probe.attach(self.bpf) if self.args.verbose: print("open uprobes: %s" % list(self.bpf.uprobe_fds.keys())) print("open kprobes: %s" % list(self.bpf.kprobe_fds.keys())) def _main_loop(self): count_so_far = 0 seconds = 0 while True: try: sleep(self.args.interval) seconds += self.args.interval except KeyboardInterrupt: exit() print("[%s]" % strftime("%H:%M:%S")) for probe in self.probes: probe.display(self.args.top) count_so_far += 1 if self.args.count is not None and \ count_so_far >= self.args.count: exit() if self.args.duration and \ seconds >= self.args.duration: exit() def run(self): try: self._create_probes() self._generate_program() self._attach() self._main_loop() except: exc_info = sys.exc_info() sys_exit = exc_info[0] is SystemExit if self.args.verbose: traceback.print_exc() elif not sys_exit: print(exc_info[1]) exit(0 if sys_exit else 1) if __name__ == "__main__": Tool().run() bpfcc-0.12.0/tools/argdist_example.txt000066400000000000000000000521771357404205000177340ustar00rootroot00000000000000Demonstrations of argdist. argdist probes functions you specify and collects parameter values into a histogram or a frequency count. This can be used to understand the distribution of values a certain parameter takes, filter and print interesting parameters without attaching a debugger, and obtain general execution statistics on various functions. For example, suppose you want to find what allocation sizes are common in your application: # ./argdist -p 2420 -c -C 'p:c:malloc(size_t size):size_t:size' [01:42:29] p:c:malloc(size_t size):size_t:size COUNT EVENT [01:42:30] p:c:malloc(size_t size):size_t:size COUNT EVENT [01:42:31] p:c:malloc(size_t size):size_t:size COUNT EVENT 1 size = 16 [01:42:32] p:c:malloc(size_t size):size_t:size COUNT EVENT 2 size = 16 [01:42:33] p:c:malloc(size_t size):size_t:size COUNT EVENT 3 size = 16 [01:42:34] p:c:malloc(size_t size):size_t:size COUNT EVENT 4 size = 16 ^C It seems that the application is allocating blocks of size 16. The COUNT column contains the number of occurrences of a particular event, and the EVENT column describes the event. In this case, the "size" parameter was probed and its value was 16, repeatedly. Now, suppose you wanted a histogram of buffer sizes passed to the write() function across the system: # ./argdist -c -H 'p:c:write(int fd, void *buf, size_t len):size_t:len' [01:45:22] p:c:write(int fd, void *buf, size_t len):size_t:len len : count distribution 0 -> 1 : 0 | | 2 -> 3 : 2 |************* | 4 -> 7 : 0 | | 8 -> 15 : 2 |************* | 16 -> 31 : 0 | | 32 -> 63 : 6 |****************************************| [01:45:23] p:c:write(int fd, void *buf, size_t len):size_t:len len : count distribution 0 -> 1 : 0 | | 2 -> 3 : 11 |*************** | 4 -> 7 : 0 | | 8 -> 15 : 4 |***** | 16 -> 31 : 0 | | 32 -> 63 : 28 |****************************************| 64 -> 127 : 12 |***************** | [01:45:24] p:c:write(int fd, void *buf, size_t len):size_t:len len : count distribution 0 -> 1 : 0 | | 2 -> 3 : 21 |**************** | 4 -> 7 : 0 | | 8 -> 15 : 6 |**** | 16 -> 31 : 0 | | 32 -> 63 : 52 |****************************************| 64 -> 127 : 26 |******************** | ^C It seems that most writes fall into three buckets: very small writes of 2-3 bytes, medium writes of 32-63 bytes, and larger writes of 64-127 bytes. But these are writes across the board -- what if you wanted to focus on writes to STDOUT? # ./argdist -c -H 'p:c:write(int fd, void *buf, size_t len):size_t:len:fd==1' [01:47:17] p:c:write(int fd, void *buf, size_t len):size_t:len:fd==1 len : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 1 |****************************************| 16 -> 31 : 0 | | 32 -> 63 : 1 |****************************************| [01:47:18] p:c:write(int fd, void *buf, size_t len):size_t:len:fd==1 len : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 2 |************* | 16 -> 31 : 0 | | 32 -> 63 : 3 |******************** | 64 -> 127 : 6 |****************************************| [01:47:19] p:c:write(int fd, void *buf, size_t len):size_t:len:fd==1 len : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 3 |********* | 16 -> 31 : 0 | | 32 -> 63 : 5 |*************** | 64 -> 127 : 13 |****************************************| ^C The "fd==1" part is a filter that is applied to every invocation of write(). Only if the filter condition is true, the value is recorded. You can also use argdist to trace kernel functions. For example, suppose you wanted a histogram of kernel allocation (kmalloc) sizes across the system, printed twice with 3 second intervals: # ./argdist -i 3 -n 2 -H 'p::__kmalloc(size_t size):size_t:size' [01:50:00] p::__kmalloc(size_t size):size_t:size size : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 6 |****************************************| [01:50:03] p::__kmalloc(size_t size):size_t:size size : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 22 |****************************************| 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 5 |********* | 128 -> 255 : 2 |*** | Occasionally, numeric information isn't enough and you want to capture strings. What are the strings printed by puts() across the system? # ./argdist -i 10 -n 1 -C 'p:c:puts(char *str):char*:str' [01:53:54] p:c:puts(char *str):char*:str COUNT EVENT 2 str = Press ENTER to start. It looks like the message "Press ENTER to start." was printed twice during the 10 seconds we were tracing. What about reads? You could trace gets() across the system and print the strings input by the user (note how "r" is used instead of "p" to attach a probe to the function's return): # ./argdist -i 10 -n 1 -C 'r:c:gets():char*:(char*)$retval:$retval!=0' [02:12:23] r:c:gets():char*:$retval:$retval!=0 COUNT EVENT 1 (char*)$retval = hi there 3 (char*)$retval = sasha 8 (char*)$retval = hello Similarly, we could get a histogram of the error codes returned by read(): # ./argdist -i 10 -c 1 -H 'r:c:read()' [02:15:36] r:c:read() retval : count distribution 0 -> 1 : 29 |****************************************| 2 -> 3 : 11 |*************** | 4 -> 7 : 0 | | 8 -> 15 : 3 |**** | 16 -> 31 : 2 |** | 32 -> 63 : 22 |****************************** | 64 -> 127 : 5 |****** | 128 -> 255 : 0 | | 256 -> 511 : 1 |* | 512 -> 1023 : 1 |* | 1024 -> 2047 : 0 | | 2048 -> 4095 : 2 |** | In return probes, you can also trace the latency of the function (unless it is recursive) and the parameters it had on entry. For example, we can identify which processes are performing slow synchronous filesystem reads -- say, longer than 0.1ms (100,000ns): # ./argdist -C 'r::__vfs_read():u32:$PID:$latency > 100000' [01:08:48] r::__vfs_read():u32:$PID:$latency > 100000 COUNT EVENT 1 $PID = 10457 21 $PID = 2780 [01:08:49] r::__vfs_read():u32:$PID:$latency > 100000 COUNT EVENT 1 $PID = 10457 21 $PID = 2780 ^C It looks like process 2780 performed 21 slow reads. Occasionally, entry parameter values are also interesting. For example, you might be curious how long it takes malloc() to allocate memory -- nanoseconds per byte allocated. Let's go: # ./argdist -H 'r:c:malloc(size_t size):u64:$latency/$entry(size);ns per byte' -n 1 -i 10 [01:11:13] ns per byte : count distribution 0 -> 1 : 0 | | 2 -> 3 : 4 |***************** | 4 -> 7 : 3 |************* | 8 -> 15 : 2 |******** | 16 -> 31 : 1 |**** | 32 -> 63 : 0 | | 64 -> 127 : 7 |******************************* | 128 -> 255 : 1 |**** | 256 -> 511 : 0 | | 512 -> 1023 : 1 |**** | 1024 -> 2047 : 1 |**** | 2048 -> 4095 : 9 |****************************************| 4096 -> 8191 : 1 |**** | It looks like a tri-modal distribution. Some allocations are extremely cheap, and take 2-15 nanoseconds per byte. Other allocations are slower, and take 64-127 nanoseconds per byte. And some allocations are slower still, and take multiple microseconds per byte. You could also group results by more than one field. For example, __kmalloc takes an additional flags parameter that describes how to allocate memory: # ./argdist -c -C 'p::__kmalloc(size_t size, gfp_t flags):gfp_t,size_t:flags,size' [03:42:29] p::__kmalloc(size_t size, gfp_t flags):gfp_t,size_t:flags,size COUNT EVENT 1 flags = 16, size = 152 2 flags = 131280, size = 8 7 flags = 131280, size = 16 [03:42:30] p::__kmalloc(size_t size, gfp_t flags):gfp_t,size_t:flags,size COUNT EVENT 1 flags = 16, size = 152 6 flags = 131280, size = 8 19 flags = 131280, size = 16 [03:42:31] p::__kmalloc(size_t size, gfp_t flags):gfp_t,size_t:flags,size COUNT EVENT 2 flags = 16, size = 152 10 flags = 131280, size = 8 31 flags = 131280, size = 16 [03:42:32] p::__kmalloc(size_t size, gfp_t flags):gfp_t,size_t:flags,size COUNT EVENT 2 flags = 16, size = 152 14 flags = 131280, size = 8 43 flags = 131280, size = 16 ^C The flags value must be expanded by hand, but it's still helpful to eliminate certain kinds of allocations or visually group them together. argdist also has basic support for kernel tracepoints. It is sometimes more convenient to use tracepoints because they are documented and don't vary a lot between kernel versions. For example, let's trace the net:net_dev_start_xmit tracepoint and print out the protocol field from the tracepoint structure: # argdist -C 't:net:net_dev_start_xmit():u16:args->protocol' [13:01:49] t:net:net_dev_start_xmit():u16:args->protocol COUNT EVENT 8 args->protocol = 2048 ^C Note that to discover the format of the net:net_dev_start_xmit tracepoint, you use the tplist tool (tplist -v net:net_dev_start_xmit). Occasionally, it is useful to filter certain expressions by string. This is not trivially supported by BPF, but argdist provides a STRCMP helper you can use in filter expressions. For example, to get a histogram of latencies opening a specific file, run this: # argdist -c -H 'r:c:open(char *file):u64:$latency/1000:STRCMP("test.txt",$entry(file))' [02:16:38] [02:16:39] [02:16:40] $latency/1000 : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 2 |****************************************| [02:16:41] $latency/1000 : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 1 |********** | 16 -> 31 : 4 |****************************************| [02:16:42] $latency/1000 : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 1 |******** | 16 -> 31 : 5 |****************************************| [02:16:43] $latency/1000 : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 1 |******** | 16 -> 31 : 5 |****************************************| Here's a final example that finds how many write() system calls are performed by each process on the system: # argdist -c -C 'p:c:write():int:$PID;write per process' -n 2 [06:47:18] write by process COUNT EVENT 3 $PID = 8889 7 $PID = 7615 7 $PID = 2480 [06:47:19] write by process COUNT EVENT 9 $PID = 8889 23 $PID = 7615 23 $PID = 2480 USAGE message: # argdist -h usage: argdist [-h] [-p PID] [-z STRING_SIZE] [-i INTERVAL] [-n COUNT] [-v] [-c] [-T TOP] [-H specifier] [-C[specifier] [-I header] Trace a function and display a summary of its parameter values. optional arguments: -h, --help show this help message and exit -p PID, --pid PID id of the process to trace (optional) -z STRING_SIZE, --string-size STRING_SIZE maximum string size to read from char* arguments -i INTERVAL, --interval INTERVAL output interval, in seconds (default 1 second) -d DURATION, --duration DURATION total duration of trace, in seconds -n COUNT, --number COUNT number of outputs -v, --verbose print resulting BPF program code before executing -c, --cumulative do not clear histograms and freq counts at each interval -T TOP, --top TOP number of top results to show (not applicable to histograms) -H specifier, --histogram specifier probe specifier to capture histogram of (see examples below) -C specifier, --count specifier probe specifier to capture count of (see examples below) -I header, --include header additional header files to include in the BPF program as either full path, or relative to current working directory, or relative to default kernel header search path Probe specifier syntax: {p,r,t,u}:{[library],category}:function(signature)[:type[,type...]:expr[,expr...][:filter]][#label] Where: p,r,t,u -- probe at function entry, function exit, kernel tracepoint, or USDT probe in exit probes: can use $retval, $entry(param), $latency library -- the library that contains the function (leave empty for kernel functions) category -- the category of the kernel tracepoint (e.g. net, sched) signature -- the function's parameters, as in the C header type -- the type of the expression to collect (supports multiple) expr -- the expression to collect (supports multiple) filter -- the filter that is applied to collected values label -- the label for this probe in the resulting output EXAMPLES: argdist -H 'p::__kmalloc(u64 size):u64:size' Print a histogram of allocation sizes passed to kmalloc argdist -p 1005 -C 'p:c:malloc(size_t size):size_t:size:size==16' Print a frequency count of how many times process 1005 called malloc with an allocation size of 16 bytes argdist -C 'r:c:gets():char*:$retval#snooped strings' Snoop on all strings returned by gets() argdist -H 'r::__kmalloc(size_t size):u64:$latency/$entry(size)#ns per byte' Print a histogram of nanoseconds per byte from kmalloc allocations argdist -C 'p::__kmalloc(size_t size, gfp_t flags):size_t:size:flags&GFP_ATOMIC' Print frequency count of kmalloc allocation sizes that have GFP_ATOMIC argdist -p 1005 -C 'p:c:write(int fd):int:fd' -T 5 Print frequency counts of how many times writes were issued to a particular file descriptor number, in process 1005, but only show the top 5 busiest fds argdist -p 1005 -H 'r:c:read()' Print a histogram of error codes returned by read() in process 1005 argdist -C 'r::__vfs_read():u32:$PID:$latency > 100000' Print frequency of reads by process where the latency was >0.1ms argdist -H 'r::__vfs_read(void *file, void *buf, size_t count):size_t:$entry(count):$latency > 1000000' Print a histogram of read sizes that were longer than 1ms argdist -H \ 'p:c:write(int fd, const void *buf, size_t count):size_t:count:fd==1' Print a histogram of buffer sizes passed to write() across all processes, where the file descriptor was 1 (STDOUT) argdist -C 'p:c:fork()#fork calls' Count fork() calls in libc across all processes Can also use funccount.py, which is easier and more flexible argdist -H 't:block:block_rq_complete():u32:args->nr_sector' Print histogram of number of sectors in completing block I/O requests argdist -C 't:irq:irq_handler_entry():int:args->irq' Aggregate interrupts by interrupt request (IRQ) argdist -C 'u:pthread:pthread_start():u64:arg2' -p 1337 Print frequency of function addresses used as a pthread start function, relying on the USDT pthread_start probe in process 1337 argdist -H 'p:c:sleep(u32 seconds):u32:seconds' \ -H 'p:c:nanosleep(struct timespec *req):long:req->tv_nsec' Print histograms of sleep() and nanosleep() parameter values argdist -p 2780 -z 120 \ -C 'p:c:write(int fd, char* buf, size_t len):char*:buf:fd==1' Spy on writes to STDOUT performed by process 2780, up to a string size of 120 characters argdist -I 'kernel/sched/sched.h' \ -C 'p::__account_cfs_rq_runtime(struct cfs_rq *cfs_rq):s64:cfs_rq->runtime_remaining' Trace on the cfs scheduling runqueue remaining runtime. The struct cfs_rq is defined in kernel/sched/sched.h which is in kernel source tree and not in kernel-devel package. So this command needs to run at the kernel source tree root directory so that the added header file can be found by the compiler. bpfcc-0.12.0/tools/bashreadline.py000077500000000000000000000045331357404205000170120ustar00rootroot00000000000000#!/usr/bin/python # # bashreadline Print entered bash commands from all running shells. # For Linux, uses BCC, eBPF. Embedded C. # # USAGE: bashreadline [-s SHARED] # This works by tracing the readline() function using a uretprobe (uprobes). # When you failed to run the script directly with error: # `Exception: could not determine address of symbol b'readline'`, # you may need specify the location of libreadline.so library # with `-s` option. # # Copyright 2016 Netflix, Inc. # Licensed under the Apache License, Version 2.0 (the "License") # # 28-Jan-2016 Brendan Gregg Created this. # 12-Feb-2016 Allan McAleavy migrated to BPF_PERF_OUTPUT from __future__ import print_function from bcc import BPF from time import strftime import argparse parser = argparse.ArgumentParser( description="Print entered bash commands from all running shells", formatter_class=argparse.RawDescriptionHelpFormatter) parser.add_argument("-s", "--shared", nargs="?", const="/lib/libreadline.so", type=str, help="specify the location of libreadline.so library.\ Default is /lib/libreadline.so") args = parser.parse_args() name = args.shared if args.shared else "/bin/bash" # load BPF program bpf_text = """ #include #include struct str_t { u64 pid; char str[80]; }; BPF_PERF_OUTPUT(events); int printret(struct pt_regs *ctx) { struct str_t data = {}; char comm[TASK_COMM_LEN] = {}; u32 pid; if (!PT_REGS_RC(ctx)) return 0; pid = bpf_get_current_pid_tgid(); data.pid = pid; bpf_probe_read(&data.str, sizeof(data.str), (void *)PT_REGS_RC(ctx)); bpf_get_current_comm(&comm, sizeof(comm)); if (comm[0] == 'b' && comm[1] == 'a' && comm[2] == 's' && comm[3] == 'h' && comm[4] == 0 ) { events.perf_submit(ctx,&data,sizeof(data)); } return 0; }; """ b = BPF(text=bpf_text) b.attach_uretprobe(name=name, sym="readline", fn_name="printret") # header print("%-9s %-6s %s" % ("TIME", "PID", "COMMAND")) def print_event(cpu, data, size): event = b["events"].event(data) print("%-9s %-6d %s" % (strftime("%H:%M:%S"), event.pid, event.str.decode('utf-8', 'replace'))) b["events"].open_perf_buffer(print_event) while 1: try: b.perf_buffer_poll() except KeyboardInterrupt: exit() bpfcc-0.12.0/tools/bashreadline_example.txt000066400000000000000000000015621357404205000207100ustar00rootroot00000000000000Demonstrations of bashreadline, the Linux eBPF/bcc version. This prints bash commands from all running bash shells on the system. For example: # ./bashreadline TIME PID COMMAND 05:28:25 21176 ls -l 05:28:28 21176 date 05:28:35 21176 echo hello world 05:28:43 21176 foo this command failed 05:28:45 21176 df -h 05:29:04 3059 echo another shell 05:29:13 21176 echo first shell again When running the script on Arch Linux, you may need to specify the location of libreadline.so library: # ./bashreadline -s /lib/libreadline.so TIME PID COMMAND 11:17:34 28796 whoami 11:17:41 28796 ps -ef 11:17:51 28796 echo "Hello eBPF!" 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). bpfcc-0.12.0/tools/biolatency.py000077500000000000000000000141251357404205000165200ustar00rootroot00000000000000#!/usr/bin/python # @lint-avoid-python-3-compatibility-imports # # biolatency Summarize block device I/O latency as a histogram. # For Linux, uses BCC, eBPF. # # USAGE: biolatency [-h] [-T] [-Q] [-m] [-D] [interval] [count] # # Copyright (c) 2015 Brendan Gregg. # Licensed under the Apache License, Version 2.0 (the "License") # # 20-Sep-2015 Brendan Gregg Created this. from __future__ import print_function from bcc import BPF from time import sleep, strftime import argparse # arguments examples = """examples: ./biolatency # summarize block I/O latency as a histogram ./biolatency 1 10 # print 1 second summaries, 10 times ./biolatency -mT 1 # 1s summaries, milliseconds, and timestamps ./biolatency -Q # include OS queued time in I/O time ./biolatency -D # show each disk device separately ./biolatency -F # show I/O flags separately """ parser = argparse.ArgumentParser( description="Summarize block device I/O latency as a histogram", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("-T", "--timestamp", action="store_true", help="include timestamp on output") parser.add_argument("-Q", "--queued", action="store_true", help="include OS queued time in I/O time") parser.add_argument("-m", "--milliseconds", action="store_true", help="millisecond histogram") parser.add_argument("-D", "--disks", action="store_true", help="print a histogram per disk device") parser.add_argument("-F", "--flags", action="store_true", help="print a histogram per set of I/O flags") parser.add_argument("interval", nargs="?", default=99999999, help="output interval, in seconds") parser.add_argument("count", nargs="?", default=99999999, help="number of outputs") parser.add_argument("--ebpf", action="store_true", help=argparse.SUPPRESS) args = parser.parse_args() countdown = int(args.count) debug = 0 if args.flags and args.disks: print("ERROR: can only use -D or -F. Exiting.") exit() # define BPF program bpf_text = """ #include #include typedef struct disk_key { char disk[DISK_NAME_LEN]; u64 slot; } disk_key_t; typedef struct flag_key { u64 flags; u64 slot; } flag_key_t; BPF_HASH(start, struct request *); STORAGE // time block I/O int trace_req_start(struct pt_regs *ctx, struct request *req) { u64 ts = bpf_ktime_get_ns(); start.update(&req, &ts); return 0; } // output int trace_req_done(struct pt_regs *ctx, struct request *req) { u64 *tsp, delta; // fetch timestamp and calculate delta tsp = start.lookup(&req); if (tsp == 0) { return 0; // missed issue } delta = bpf_ktime_get_ns() - *tsp; FACTOR // store as histogram STORE start.delete(&req); return 0; } """ # code substitutions if args.milliseconds: bpf_text = bpf_text.replace('FACTOR', 'delta /= 1000000;') label = "msecs" else: bpf_text = bpf_text.replace('FACTOR', 'delta /= 1000;') label = "usecs" if args.disks: bpf_text = bpf_text.replace('STORAGE', 'BPF_HISTOGRAM(dist, disk_key_t);') bpf_text = bpf_text.replace('STORE', 'disk_key_t key = {.slot = bpf_log2l(delta)}; ' + 'void *__tmp = (void *)req->rq_disk->disk_name; ' + 'bpf_probe_read(&key.disk, sizeof(key.disk), __tmp); ' + 'dist.increment(key);') elif args.flags: bpf_text = bpf_text.replace('STORAGE', 'BPF_HISTOGRAM(dist, flag_key_t);') bpf_text = bpf_text.replace('STORE', 'flag_key_t key = {.slot = bpf_log2l(delta)}; ' + 'key.flags = req->cmd_flags; ' + 'dist.increment(key);') else: bpf_text = bpf_text.replace('STORAGE', 'BPF_HISTOGRAM(dist);') bpf_text = bpf_text.replace('STORE', 'dist.increment(bpf_log2l(delta));') if debug or args.ebpf: print(bpf_text) if args.ebpf: exit() # load BPF program b = BPF(text=bpf_text) if args.queued: b.attach_kprobe(event="blk_account_io_start", fn_name="trace_req_start") else: if BPF.get_kprobe_functions(b'blk_start_request'): b.attach_kprobe(event="blk_start_request", fn_name="trace_req_start") b.attach_kprobe(event="blk_mq_start_request", fn_name="trace_req_start") b.attach_kprobe(event="blk_account_io_done", fn_name="trace_req_done") print("Tracing block device I/O... Hit Ctrl-C to end.") # see blk_fill_rwbs(): req_opf = { 0: "Read", 1: "Write", 2: "Flush", 3: "Discard", 5: "SecureErase", 6: "ZoneReset", 7: "WriteSame", 9: "WriteZeros" } REQ_OP_BITS = 8 REQ_OP_MASK = ((1 << REQ_OP_BITS) - 1) REQ_SYNC = 1 << (REQ_OP_BITS + 3) REQ_META = 1 << (REQ_OP_BITS + 4) REQ_PRIO = 1 << (REQ_OP_BITS + 5) REQ_NOMERGE = 1 << (REQ_OP_BITS + 6) REQ_IDLE = 1 << (REQ_OP_BITS + 7) REQ_FUA = 1 << (REQ_OP_BITS + 9) REQ_RAHEAD = 1 << (REQ_OP_BITS + 11) REQ_BACKGROUND = 1 << (REQ_OP_BITS + 12) REQ_NOWAIT = 1 << (REQ_OP_BITS + 13) def flags_print(flags): desc = "" # operation if flags & REQ_OP_MASK in req_opf: desc = req_opf[flags & REQ_OP_MASK] else: desc = "Unknown" # flags if flags & REQ_SYNC: desc = "Sync-" + desc if flags & REQ_META: desc = "Metadata-" + desc if flags & REQ_FUA: desc = "ForcedUnitAccess-" + desc if flags & REQ_PRIO: desc = "Priority-" + desc if flags & REQ_NOMERGE: desc = "NoMerge-" + desc if flags & REQ_IDLE: desc = "Idle-" + desc if flags & REQ_RAHEAD: desc = "ReadAhead-" + desc if flags & REQ_BACKGROUND: desc = "Background-" + desc if flags & REQ_NOWAIT: desc = "NoWait-" + desc return desc # output exiting = 0 if args.interval else 1 dist = b.get_table("dist") while (1): try: sleep(int(args.interval)) except KeyboardInterrupt: exiting = 1 print() if args.timestamp: print("%-8s\n" % strftime("%H:%M:%S"), end="") if args.flags: dist.print_log2_hist(label, "flags", flags_print) else: dist.print_log2_hist(label, "disk") dist.clear() countdown -= 1 if exiting or countdown == 0: exit() bpfcc-0.12.0/tools/biolatency_example.txt000066400000000000000000000415021357404205000204160ustar00rootroot00000000000000Demonstrations of biolatency, the Linux eBPF/bcc version. biolatency traces block device I/O (disk I/O), and records the distribution of I/O latency (time), printing this as a histogram when Ctrl-C is hit. For example: # ./biolatency Tracing block device I/O... Hit Ctrl-C to end. ^C usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 1 | | 128 -> 255 : 12 |******** | 256 -> 511 : 15 |********** | 512 -> 1023 : 43 |******************************* | 1024 -> 2047 : 52 |**************************************| 2048 -> 4095 : 47 |********************************** | 4096 -> 8191 : 52 |**************************************| 8192 -> 16383 : 36 |************************** | 16384 -> 32767 : 15 |********** | 32768 -> 65535 : 2 |* | 65536 -> 131071 : 2 |* | The latency of the disk I/O is measured from the issue to the device to its completion. A -Q option can be used to include time queued in the kernel. This example output shows a large mode of latency from about 128 microseconds to about 32767 microseconds (33 milliseconds). The bulk of the I/O was between 1 and 8 ms, which is the expected block device latency for rotational storage devices. The highest latency seen while tracing was between 65 and 131 milliseconds: the last row printed, for which there were 2 I/O. For efficiency, biolatency uses an in-kernel eBPF map to store timestamps with requests, and another in-kernel map to store the histogram (the "count") column, which is copied to user-space only when output is printed. These methods lower the performance overhead when tracing is performed. In the following example, the -m option is used to print a histogram using milliseconds as the units (which eliminates the first several rows), -T to print timestamps with the output, and to print 1 second summaries 5 times: # ./biolatency -mT 1 5 Tracing block device I/O... Hit Ctrl-C to end. 06:20:16 msecs : count distribution 0 -> 1 : 36 |**************************************| 2 -> 3 : 1 |* | 4 -> 7 : 3 |*** | 8 -> 15 : 17 |***************** | 16 -> 31 : 33 |********************************** | 32 -> 63 : 7 |******* | 64 -> 127 : 6 |****** | 06:20:17 msecs : count distribution 0 -> 1 : 96 |************************************ | 2 -> 3 : 25 |********* | 4 -> 7 : 29 |*********** | 8 -> 15 : 62 |*********************** | 16 -> 31 : 100 |**************************************| 32 -> 63 : 62 |*********************** | 64 -> 127 : 18 |****** | 06:20:18 msecs : count distribution 0 -> 1 : 68 |************************* | 2 -> 3 : 76 |**************************** | 4 -> 7 : 20 |******* | 8 -> 15 : 48 |***************** | 16 -> 31 : 103 |**************************************| 32 -> 63 : 49 |****************** | 64 -> 127 : 17 |****** | 06:20:19 msecs : count distribution 0 -> 1 : 522 |*************************************+| 2 -> 3 : 225 |**************** | 4 -> 7 : 38 |** | 8 -> 15 : 8 | | 16 -> 31 : 1 | | 06:20:20 msecs : count distribution 0 -> 1 : 436 |**************************************| 2 -> 3 : 106 |********* | 4 -> 7 : 34 |** | 8 -> 15 : 19 |* | 16 -> 31 : 1 | | How the I/O latency distribution changes over time can be seen. The -Q option begins measuring I/O latency from when the request was first queued in the kernel, and includes queuing latency: # ./biolatency -Q Tracing block device I/O... Hit Ctrl-C to end. ^C usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 0 | | 128 -> 255 : 3 |* | 256 -> 511 : 37 |************** | 512 -> 1023 : 30 |*********** | 1024 -> 2047 : 18 |******* | 2048 -> 4095 : 22 |******** | 4096 -> 8191 : 14 |***** | 8192 -> 16383 : 48 |******************* | 16384 -> 32767 : 96 |**************************************| 32768 -> 65535 : 31 |************ | 65536 -> 131071 : 26 |********** | 131072 -> 262143 : 12 |**** | This better reflects the latency suffered by the application (if it is synchronous I/O), whereas the default mode without kernel queueing better reflects the performance of the device. Note that the storage device (and storage device controller) usually have queues of their own, which are always included in the latency, with or without -Q. The -D option will print a histogram per disk. Eg: # ./biolatency -D Tracing block device I/O... Hit Ctrl-C to end. ^C Bucket disk = 'xvdb' usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 0 | | 128 -> 255 : 1 | | 256 -> 511 : 33 |********************** | 512 -> 1023 : 36 |************************ | 1024 -> 2047 : 58 |****************************************| 2048 -> 4095 : 51 |*********************************** | 4096 -> 8191 : 21 |************** | 8192 -> 16383 : 2 |* | Bucket disk = 'xvdc' usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 0 | | 128 -> 255 : 1 | | 256 -> 511 : 38 |*********************** | 512 -> 1023 : 42 |************************* | 1024 -> 2047 : 66 |****************************************| 2048 -> 4095 : 40 |************************ | 4096 -> 8191 : 14 |******** | Bucket disk = 'xvda1' usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 0 | | 128 -> 255 : 0 | | 256 -> 511 : 18 |********** | 512 -> 1023 : 67 |************************************* | 1024 -> 2047 : 35 |******************* | 2048 -> 4095 : 71 |****************************************| 4096 -> 8191 : 65 |************************************ | 8192 -> 16383 : 65 |************************************ | 16384 -> 32767 : 20 |*********** | 32768 -> 65535 : 7 |*** | This output sows that xvda1 has much higher latency, usually between 0.5 ms and 32 ms, whereas xvdc is usually between 0.2 ms and 4 ms. The -F option prints a separate histogram for each unique set of request flags. For example: ./biolatency.py -Fm Tracing block device I/O... Hit Ctrl-C to end. ^C flags = Read msecs : count distribution 0 -> 1 : 180 |************* | 2 -> 3 : 519 |****************************************| 4 -> 7 : 60 |**** | 8 -> 15 : 123 |********* | 16 -> 31 : 68 |***** | 32 -> 63 : 0 | | 64 -> 127 : 2 | | 128 -> 255 : 12 | | 256 -> 511 : 0 | | 512 -> 1023 : 1 | | flags = Sync-Write msecs : count distribution 0 -> 1 : 5 |****************************************| flags = Flush msecs : count distribution 0 -> 1 : 2 |****************************************| flags = Metadata-Read msecs : count distribution 0 -> 1 : 3 |****************************************| 2 -> 3 : 2 |************************** | 4 -> 7 : 0 | | 8 -> 15 : 1 |************* | 16 -> 31 : 1 |************* | flags = Write msecs : count distribution 0 -> 1 : 103 |******************************* | 2 -> 3 : 106 |******************************** | 4 -> 7 : 130 |****************************************| 8 -> 15 : 79 |************************ | 16 -> 31 : 5 |* | 32 -> 63 : 0 | | 64 -> 127 : 0 | | 128 -> 255 : 0 | | 256 -> 511 : 1 | | flags = NoMerge-Read msecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 5 |****************************************| 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 1 |******** | flags = NoMerge-Write msecs : count distribution 0 -> 1 : 30 |** | 2 -> 3 : 293 |******************** | 4 -> 7 : 564 |****************************************| 8 -> 15 : 463 |******************************** | 16 -> 31 : 21 |* | 32 -> 63 : 0 | | 64 -> 127 : 0 | | 128 -> 255 : 0 | | 256 -> 511 : 5 | | flags = Priority-Metadata-Read msecs : count distribution 0 -> 1 : 1 |****************************************| 2 -> 3 : 0 | | 4 -> 7 : 1 |****************************************| 8 -> 15 : 1 |****************************************| flags = ForcedUnitAccess-Metadata-Sync-Write msecs : count distribution 0 -> 1 : 2 |****************************************| flags = ReadAhead-Read msecs : count distribution 0 -> 1 : 15 |*************************** | 2 -> 3 : 22 |****************************************| 4 -> 7 : 14 |************************* | 8 -> 15 : 8 |************** | 16 -> 31 : 1 |* | flags = Priority-Metadata-Write msecs : count distribution 0 -> 1 : 9 |****************************************| These can be handled differently by the storage device, and this mode lets us examine their performance in isolation. USAGE message: # ./biolatency -h usage: biolatency [-h] [-T] [-Q] [-m] [-D] [-F] [interval] [count] Summarize block device I/O latency as a histogram positional arguments: interval output interval, in seconds count number of outputs optional arguments: -h, --help show this help message and exit -T, --timestamp include timestamp on output -Q, --queued include OS queued time in I/O time -m, --milliseconds millisecond histogram -D, --disks print a histogram per disk device -F, --flags print a histogram per set of I/O flags examples: ./biolatency # summarize block I/O latency as a histogram ./biolatency 1 10 # print 1 second summaries, 10 times ./biolatency -mT 1 # 1s summaries, milliseconds, and timestamps ./biolatency -Q # include OS queued time in I/O time ./biolatency -D # show each disk device separately ./biolatency -F # show I/O flags separately bpfcc-0.12.0/tools/biosnoop.lua000077500000000000000000000116111357404205000163450ustar00rootroot00000000000000#!/usr/bin/env bcc-lua --[[ Copyright 2016 GitHub, Inc 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. --]] local program = [[ #include #include struct val_t { u32 pid; char name[TASK_COMM_LEN]; }; struct data_t { u32 pid; u64 rwflag; u64 delta; u64 sector; u64 len; u64 ts; char disk_name[DISK_NAME_LEN]; char name[TASK_COMM_LEN]; }; BPF_HASH(start, struct request *); BPF_HASH(infobyreq, struct request *, struct val_t); BPF_PERF_OUTPUT(events); // cache PID and comm by-req int trace_pid_start(struct pt_regs *ctx, struct request *req) { struct val_t val = {}; if (bpf_get_current_comm(&val.name, sizeof(val.name)) == 0) { val.pid = bpf_get_current_pid_tgid(); infobyreq.update(&req, &val); } return 0; } // time block I/O int trace_req_start(struct pt_regs *ctx, struct request *req) { u64 ts; ts = bpf_ktime_get_ns(); start.update(&req, &ts); return 0; } // output int trace_req_completion(struct pt_regs *ctx, struct request *req) { u64 *tsp, delta; u32 *pidp = 0; struct val_t *valp; struct data_t data ={}; u64 ts; // fetch timestamp and calculate delta tsp = start.lookup(&req); if (tsp == 0) { // missed tracing issue return 0; } ts = bpf_ktime_get_ns(); data.delta = ts - *tsp; data.ts = ts / 1000; valp = infobyreq.lookup(&req); if (valp == 0) { data.len = req->__data_len; strcpy(data.name,"?"); } else { data.pid = valp->pid; data.len = req->__data_len; data.sector = req->__sector; bpf_probe_read(&data.name, sizeof(data.name), valp->name); bpf_probe_read(&data.disk_name, sizeof(data.disk_name), req->rq_disk->disk_name); } /* * The following deals with a kernel version change (in mainline 4.7, although * it may be backported to earlier kernels) with how block request write flags * are tested. We handle both pre- and post-change versions here. Please avoid * kernel version tests like this as much as possible: they inflate the code, * test, and maintenance burden. */ #ifdef REQ_WRITE data.rwflag = !!(req->cmd_flags & REQ_WRITE); #elif defined(REQ_OP_SHIFT) data.rwflag = !!((req->cmd_flags >> REQ_OP_SHIFT) == REQ_OP_WRITE); #else data.rwflag = !!((req->cmd_flags & REQ_OP_MASK) == REQ_OP_WRITE); #endif events.perf_submit(ctx,&data,sizeof(data)); start.delete(&req); infobyreq.delete(&req); return 0; } ]] local ffi = require("ffi") return function(BPF, utils) local bpf = BPF:new{text=program} bpf:attach_kprobe{event="blk_account_io_start", fn_name="trace_pid_start"} bpf:attach_kprobe{event="blk_start_request", fn_name="trace_req_start"} bpf:attach_kprobe{event="blk_mq_start_request", fn_name="trace_req_start"} bpf:attach_kprobe{event="blk_account_io_completion", fn_name="trace_req_completion"} print("%-14s %-14s %-6s %-7s %-2s %-9s %-7s %7s" % {"TIME(s)", "COMM", "PID", "DISK", "T", "SECTOR", "BYTES", "LAT(ms)"}) local rwflg = "" local start_ts = 0 local prev_ts = 0 local delta = 0 local function print_event(cpu, event) local val = -1 local event_pid = event.pid local event_delta = tonumber(event.delta) local event_sector = tonumber(event.sector) local event_len = tonumber(event.len) local event_ts = tonumber(event.ts) local event_disk_name = ffi.string(event.disk_name) local event_name = ffi.string(event.name) if event.rwflag == 1 then rwflg = "W" end if event.rwflag == 0 then rwflg = "R" end if not event_name:match("%?") then val = event_sector end if start_ts == 0 then prev_ts = start_ts end if start_ts == 1 then delta = delta + (event_ts - prev_ts) end print("%-14.9f %-14.14s %-6s %-7s %-2s %-9s %-7s %7.2f" % { delta / 1000000, event_name, event_pid, event_disk_name, rwflg, val, event_len, event_delta / 1000000}) prev_ts = event_ts start_ts = 1 end local TASK_COMM_LEN = 16 -- linux/sched.h local DISK_NAME_LEN = 32 -- linux/genhd.h bpf:get_table("events"):open_perf_buffer(print_event, [[ struct { uint32_t pid; uint64_t rwflag; uint64_t delta; uint64_t sector; uint64_t len; uint64_t ts; char disk_name[$]; char name[$]; } ]], {DISK_NAME_LEN, TASK_COMM_LEN}, 64) bpf:perf_buffer_poll_loop() end bpfcc-0.12.0/tools/biosnoop.py000077500000000000000000000126221357404205000162170ustar00rootroot00000000000000#!/usr/bin/python # @lint-avoid-python-3-compatibility-imports # # biosnoop Trace block device I/O and print details including issuing PID. # For Linux, uses BCC, eBPF. # # This uses in-kernel eBPF maps to cache process details (PID and comm) by I/O # request, as well as a starting timestamp for calculating I/O latency. # # Copyright (c) 2015 Brendan Gregg. # Licensed under the Apache License, Version 2.0 (the "License") # # 16-Sep-2015 Brendan Gregg Created this. # 11-Feb-2016 Allan McAleavy updated for BPF_PERF_OUTPUT from __future__ import print_function from bcc import BPF import re import argparse # arguments examples = """examples: ./biosnoop # trace all block I/O ./biosnoop -Q # include OS queued time """ parser = argparse.ArgumentParser( description="Trace block I/O", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("-Q", "--queue", action="store_true", help="include OS queued time") parser.add_argument("--ebpf", action="store_true", help=argparse.SUPPRESS) args = parser.parse_args() debug = 0 # define BPF program bpf_text=""" #include #include struct val_t { u64 ts; u32 pid; char name[TASK_COMM_LEN]; }; struct data_t { u32 pid; u64 rwflag; u64 delta; u64 qdelta; u64 sector; u64 len; u64 ts; char disk_name[DISK_NAME_LEN]; char name[TASK_COMM_LEN]; }; BPF_HASH(start, struct request *); BPF_HASH(infobyreq, struct request *, struct val_t); BPF_PERF_OUTPUT(events); // cache PID and comm by-req int trace_pid_start(struct pt_regs *ctx, struct request *req) { struct val_t val = {}; u64 ts; if (bpf_get_current_comm(&val.name, sizeof(val.name)) == 0) { val.pid = bpf_get_current_pid_tgid() >> 32; if (##QUEUE##) { val.ts = bpf_ktime_get_ns(); } infobyreq.update(&req, &val); } return 0; } // time block I/O int trace_req_start(struct pt_regs *ctx, struct request *req) { u64 ts; ts = bpf_ktime_get_ns(); start.update(&req, &ts); return 0; } // output int trace_req_completion(struct pt_regs *ctx, struct request *req) { u64 *tsp; struct val_t *valp; struct data_t data = {}; u64 ts; // fetch timestamp and calculate delta tsp = start.lookup(&req); if (tsp == 0) { // missed tracing issue return 0; } ts = bpf_ktime_get_ns(); data.delta = ts - *tsp; data.ts = ts / 1000; data.qdelta = 0; valp = infobyreq.lookup(&req); if (valp == 0) { data.len = req->__data_len; strcpy(data.name, "?"); } else { if (##QUEUE##) { data.qdelta = *tsp - valp->ts; } data.pid = valp->pid; data.len = req->__data_len; data.sector = req->__sector; bpf_probe_read(&data.name, sizeof(data.name), valp->name); struct gendisk *rq_disk = req->rq_disk; bpf_probe_read(&data.disk_name, sizeof(data.disk_name), rq_disk->disk_name); } /* * The following deals with a kernel version change (in mainline 4.7, although * it may be backported to earlier kernels) with how block request write flags * are tested. We handle both pre- and post-change versions here. Please avoid * kernel version tests like this as much as possible: they inflate the code, * test, and maintenance burden. */ #ifdef REQ_WRITE data.rwflag = !!(req->cmd_flags & REQ_WRITE); #elif defined(REQ_OP_SHIFT) data.rwflag = !!((req->cmd_flags >> REQ_OP_SHIFT) == REQ_OP_WRITE); #else data.rwflag = !!((req->cmd_flags & REQ_OP_MASK) == REQ_OP_WRITE); #endif events.perf_submit(ctx, &data, sizeof(data)); start.delete(&req); infobyreq.delete(&req); return 0; } """ if args.queue: bpf_text = bpf_text.replace('##QUEUE##', '1') else: bpf_text = bpf_text.replace('##QUEUE##', '0') if debug or args.ebpf: print(bpf_text) if args.ebpf: exit() # initialize BPF b = BPF(text=bpf_text) b.attach_kprobe(event="blk_account_io_start", fn_name="trace_pid_start") if BPF.get_kprobe_functions(b'blk_start_request'): b.attach_kprobe(event="blk_start_request", fn_name="trace_req_start") b.attach_kprobe(event="blk_mq_start_request", fn_name="trace_req_start") b.attach_kprobe(event="blk_account_io_completion", fn_name="trace_req_completion") # header print("%-11s %-14s %-6s %-7s %-1s %-10s %-7s" % ("TIME(s)", "COMM", "PID", "DISK", "T", "SECTOR", "BYTES"), end="") if args.queue: print("%7s " % ("QUE(ms)"), end="") print("%7s" % "LAT(ms)") rwflg = "" start_ts = 0 prev_ts = 0 delta = 0 # process event def print_event(cpu, data, size): event = b["events"].event(data) global start_ts if start_ts == 0: start_ts = event.ts if event.rwflag == 1: rwflg = "W" else: rwflg = "R" delta = float(event.ts) - start_ts print("%-11.6f %-14.14s %-6s %-7s %-1s %-10s %-7s" % ( delta / 1000000, event.name.decode('utf-8', 'replace'), event.pid, event.disk_name.decode('utf-8', 'replace'), rwflg, event.sector, event.len), end="") if args.queue: print("%7.2f " % (float(event.qdelta) / 1000000), end="") print("%7.2f" % (float(event.delta) / 1000000)) # loop with callback to print_event b["events"].open_perf_buffer(print_event, page_cnt=64) while 1: try: b.perf_buffer_poll() except KeyboardInterrupt: exit() bpfcc-0.12.0/tools/biosnoop_example.txt000066400000000000000000000063471357404205000201250ustar00rootroot00000000000000Demonstrations of biosnoop, the Linux eBPF/bcc version. biosnoop traces block device I/O (disk I/O), and prints a line of output per I/O. Example: # ./biosnoop TIME(s) COMM PID DISK T SECTOR BYTES LAT(ms) 0.000004 supervise 1950 xvda1 W 13092560 4096 0.74 0.000178 supervise 1950 xvda1 W 13092432 4096 0.61 0.001469 supervise 1956 xvda1 W 13092440 4096 1.24 0.001588 supervise 1956 xvda1 W 13115128 4096 1.09 1.022346 supervise 1950 xvda1 W 13115272 4096 0.98 1.022568 supervise 1950 xvda1 W 13188496 4096 0.93 1.023534 supervise 1956 xvda1 W 13188520 4096 0.79 1.023585 supervise 1956 xvda1 W 13189512 4096 0.60 2.003920 xfsaild/md0 456 xvdc W 62901512 8192 0.23 2.003931 xfsaild/md0 456 xvdb W 62901513 512 0.25 2.004034 xfsaild/md0 456 xvdb W 62901520 8192 0.35 2.004042 xfsaild/md0 456 xvdb W 63542016 4096 0.36 2.004204 kworker/0:3 26040 xvdb W 41950344 65536 0.34 2.044352 supervise 1950 xvda1 W 13192672 4096 0.65 2.044574 supervise 1950 xvda1 W 13189072 4096 0.58 This includes the PID and comm (process name) that were on-CPU at the time of issue (which usually means the process responsible). The latency of the disk I/O, measured from the issue to the device to its completion, is included as the last column. This example output is from what should be an idle system, however, the following is visible in iostat: $ iostat -x 1 [...] avg-cpu: %user %nice %system %iowait %steal %idle 0.12 0.00 0.12 0.00 0.00 99.75 Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s await svctm %util xvda 0.00 0.00 0.00 4.00 0.00 16.00 0.00 0.00 0.00 xvdb 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 xvdc 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 md0 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 There are 4 write IOPS. The output of biosnoop identifies the reason: multiple supervise processes are issuing writes to the xvda1 disk. I can now drill down on supervise using other tools to understand its file system workload. The -Q option includes a column to show the time spent queued in the OS: # biosnoop.py -Q TIME(s) COMM PID DISK T SECTOR BYTES QUE(ms) LAT(ms) 0.000000 kworker/u72:1 13379 nvme1n1 W 1142400 4096 0.01 0.55 0.000771 sync 22177 nvme1n1 W 41963894 3072 0.11 0.47 5.332998 xfsaild/nvme1n 1061 nvme1n1 W 545728 16384 0.01 0.61 5.333044 xfsaild/nvme1n 1061 nvme1n1 W 2349728 16384 0.02 0.64 5.333065 xfsaild/nvme1n 1061 nvme1n1 W 20971521 512 0.02 0.65 5.333067 xfsaild/nvme1n 1061 nvme1n1 W 20971528 8192 0.00 0.65 [...] USAGE message: usage: biosnoop.py [-h] [-Q] Trace block I/O optional arguments: -h, --help show this help message and exit -Q, --queue include OS queued time examples: ./biosnoop # trace all block I/O ./biosnoop -Q # include OS queued time bpfcc-0.12.0/tools/biotop.py000077500000000000000000000144451357404205000156700ustar00rootroot00000000000000#!/usr/bin/python # @lint-avoid-python-3-compatibility-imports # # biotop block device (disk) I/O by process. # For Linux, uses BCC, eBPF. # # USAGE: biotop.py [-h] [-C] [-r MAXROWS] [interval] [count] # # This uses in-kernel eBPF maps to cache process details (PID and comm) by I/O # request, as well as a starting timestamp for calculating I/O latency. # # Copyright 2016 Netflix, Inc. # Licensed under the Apache License, Version 2.0 (the "License") # # 06-Feb-2016 Brendan Gregg Created this. from __future__ import print_function from bcc import BPF from time import sleep, strftime import argparse import signal from subprocess import call # arguments examples = """examples: ./biotop # block device I/O top, 1 second refresh ./biotop -C # don't clear the screen ./biotop 5 # 5 second summaries ./biotop 5 10 # 5 second summaries, 10 times only """ parser = argparse.ArgumentParser( description="Block device (disk) I/O by process", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("-C", "--noclear", action="store_true", help="don't clear the screen") parser.add_argument("-r", "--maxrows", default=20, help="maximum rows to print, default 20") parser.add_argument("interval", nargs="?", default=1, help="output interval, in seconds") parser.add_argument("count", nargs="?", default=99999999, help="number of outputs") parser.add_argument("--ebpf", action="store_true", help=argparse.SUPPRESS) args = parser.parse_args() interval = int(args.interval) countdown = int(args.count) maxrows = int(args.maxrows) clear = not int(args.noclear) # linux stats loadavg = "/proc/loadavg" diskstats = "/proc/diskstats" # signal handler def signal_ignore(signal_value, frame): print() # load BPF program bpf_text = """ #include #include // for saving process info by request struct who_t { u32 pid; char name[TASK_COMM_LEN]; }; // the key for the output summary struct info_t { u32 pid; int rwflag; int major; int minor; char name[TASK_COMM_LEN]; }; // the value of the output summary struct val_t { u64 bytes; u64 us; u32 io; }; BPF_HASH(start, struct request *); BPF_HASH(whobyreq, struct request *, struct who_t); BPF_HASH(counts, struct info_t, struct val_t); // cache PID and comm by-req int trace_pid_start(struct pt_regs *ctx, struct request *req) { struct who_t who = {}; if (bpf_get_current_comm(&who.name, sizeof(who.name)) == 0) { who.pid = bpf_get_current_pid_tgid() >> 32; whobyreq.update(&req, &who); } return 0; } // time block I/O int trace_req_start(struct pt_regs *ctx, struct request *req) { u64 ts; ts = bpf_ktime_get_ns(); start.update(&req, &ts); return 0; } // output int trace_req_completion(struct pt_regs *ctx, struct request *req) { u64 *tsp; // fetch timestamp and calculate delta tsp = start.lookup(&req); if (tsp == 0) { return 0; // missed tracing issue } struct who_t *whop; struct val_t *valp, zero = {}; u64 delta_us = (bpf_ktime_get_ns() - *tsp) / 1000; // setup info_t key struct info_t info = {}; info.major = req->rq_disk->major; info.minor = req->rq_disk->first_minor; /* * The following deals with a kernel version change (in mainline 4.7, although * it may be backported to earlier kernels) with how block request write flags * are tested. We handle both pre- and post-change versions here. Please avoid * kernel version tests like this as much as possible: they inflate the code, * test, and maintenance burden. */ #ifdef REQ_WRITE info.rwflag = !!(req->cmd_flags & REQ_WRITE); #elif defined(REQ_OP_SHIFT) info.rwflag = !!((req->cmd_flags >> REQ_OP_SHIFT) == REQ_OP_WRITE); #else info.rwflag = !!((req->cmd_flags & REQ_OP_MASK) == REQ_OP_WRITE); #endif whop = whobyreq.lookup(&req); if (whop == 0) { // missed pid who, save stats as pid 0 valp = counts.lookup_or_try_init(&info, &zero); } else { info.pid = whop->pid; __builtin_memcpy(&info.name, whop->name, sizeof(info.name)); valp = counts.lookup_or_try_init(&info, &zero); } if (valp) { // save stats valp->us += delta_us; valp->bytes += req->__data_len; valp->io++; } start.delete(&req); whobyreq.delete(&req); return 0; } """ if args.ebpf: print(bpf_text) exit() b = BPF(text=bpf_text) b.attach_kprobe(event="blk_account_io_start", fn_name="trace_pid_start") if BPF.get_kprobe_functions(b'blk_start_request'): b.attach_kprobe(event="blk_start_request", fn_name="trace_req_start") b.attach_kprobe(event="blk_mq_start_request", fn_name="trace_req_start") b.attach_kprobe(event="blk_account_io_completion", fn_name="trace_req_completion") print('Tracing... Output every %d secs. Hit Ctrl-C to end' % interval) # cache disk major,minor -> diskname disklookup = {} with open(diskstats) as stats: for line in stats: a = line.split() disklookup[a[0] + "," + a[1]] = a[2] # output exiting = 0 while 1: try: sleep(interval) except KeyboardInterrupt: exiting = 1 # header if clear: call("clear") else: print() with open(loadavg) as stats: print("%-8s loadavg: %s" % (strftime("%H:%M:%S"), stats.read())) print("%-6s %-16s %1s %-3s %-3s %-8s %5s %7s %6s" % ("PID", "COMM", "D", "MAJ", "MIN", "DISK", "I/O", "Kbytes", "AVGms")) # by-PID output counts = b.get_table("counts") line = 0 for k, v in reversed(sorted(counts.items(), key=lambda counts: counts[1].bytes)): # lookup disk disk = str(k.major) + "," + str(k.minor) if disk in disklookup: diskname = disklookup[disk] else: diskname = "?" # print line avg_ms = (float(v.us) / 1000) / v.io print("%-6d %-16s %1s %-3d %-3d %-8s %5s %7s %6.2f" % (k.pid, k.name.decode('utf-8', 'replace'), "W" if k.rwflag else "R", k.major, k.minor, diskname, v.io, v.bytes / 1024, avg_ms)) line += 1 if line >= maxrows: break counts.clear() countdown -= 1 if exiting or countdown == 0: print("Detaching...") exit() bpfcc-0.12.0/tools/biotop_example.txt000066400000000000000000000221561357404205000175650ustar00rootroot00000000000000Demonstrations of biotop, the Linux eBPF/bcc version. Short for block device I/O top, biotop summarizes which processes are performing disk I/O. It's top for disks. Sample output: # ./biotop Tracing... Output every 1 secs. Hit Ctrl-C to end 08:04:11 loadavg: 1.48 0.87 0.45 1/287 14547 PID COMM D MAJ MIN DISK I/O Kbytes AVGms 14501 cksum R 202 1 xvda1 361 28832 3.39 6961 dd R 202 1 xvda1 1628 13024 0.59 13855 dd R 202 1 xvda1 1627 13016 0.59 326 jbd2/xvda1-8 W 202 1 xvda1 3 168 3.00 1880 supervise W 202 1 xvda1 2 8 6.71 1873 supervise W 202 1 xvda1 2 8 2.51 1871 supervise W 202 1 xvda1 2 8 1.57 1876 supervise W 202 1 xvda1 2 8 1.22 1892 supervise W 202 1 xvda1 2 8 0.62 1878 supervise W 202 1 xvda1 2 8 0.78 1886 supervise W 202 1 xvda1 2 8 1.30 1894 supervise W 202 1 xvda1 2 8 3.46 1869 supervise W 202 1 xvda1 2 8 0.73 1888 supervise W 202 1 xvda1 2 8 1.48 By default the screen refreshes every 1 second, and shows the top 20 disk consumers, sorted on total Kbytes. The first line printed is the header, which has the time and then the contents of /proc/loadavg. For the interval summarized by the output above, the "cksum" command performed 361 disk reads to the "xvda1" device, for a total of 28832 Kbytes, with an average I/O time of 3.39 ms. Two "dd" processes were also reading from the same disk, which a higher I/O rate and lower latency. While the average I/O size is not printed, it can be determined by dividing the Kbytes column by the I/O column. The columns through to Kbytes show the workload applied. The final column, AVGms, shows resulting performance. Other bcc tools can be used to get more details when needed: biolatency and biosnoop. Many years ago I created the original "iotop", and later regretted not calling it diskiotop or blockiotop, as "io" alone is ambiguous. This time it is biotop. The -C option can be used to prevent the screen from clearing (my preference). Here's using it with a 5 second interval: # ./biotop -C 5 Tracing... Output every 5 secs. Hit Ctrl-C to end 08:09:44 loadavg: 0.42 0.44 0.39 2/282 22115 PID COMM D MAJ MIN DISK I/O Kbytes AVGms 22069 dd R 202 1 xvda1 5993 47976 0.33 326 jbd2/xvda1-8 W 202 1 xvda1 3 168 2.67 1866 svscan R 202 1 xvda1 33 132 1.24 1880 supervise W 202 1 xvda1 10 40 0.56 1873 supervise W 202 1 xvda1 10 40 0.79 1871 supervise W 202 1 xvda1 10 40 0.78 1876 supervise W 202 1 xvda1 10 40 0.68 1892 supervise W 202 1 xvda1 10 40 0.71 1878 supervise W 202 1 xvda1 10 40 0.65 1886 supervise W 202 1 xvda1 10 40 0.78 1894 supervise W 202 1 xvda1 10 40 0.80 1869 supervise W 202 1 xvda1 10 40 0.91 1888 supervise W 202 1 xvda1 10 40 0.63 22069 bash R 202 1 xvda1 1 16 19.94 9251 kworker/u16:2 W 202 16 xvdb 2 8 0.13 08:09:49 loadavg: 0.47 0.44 0.39 1/282 22231 PID COMM D MAJ MIN DISK I/O Kbytes AVGms 22069 dd R 202 1 xvda1 13450 107600 0.35 22199 cksum R 202 1 xvda1 941 45548 4.63 326 jbd2/xvda1-8 W 202 1 xvda1 3 168 2.93 24467 kworker/0:2 W 202 16 xvdb 1 64 0.28 1880 supervise W 202 1 xvda1 10 40 0.81 1873 supervise W 202 1 xvda1 10 40 0.81 1871 supervise W 202 1 xvda1 10 40 1.03 1876 supervise W 202 1 xvda1 10 40 0.76 1892 supervise W 202 1 xvda1 10 40 0.74 1878 supervise W 202 1 xvda1 10 40 0.94 1886 supervise W 202 1 xvda1 10 40 0.76 1894 supervise W 202 1 xvda1 10 40 0.69 1869 supervise W 202 1 xvda1 10 40 0.72 1888 supervise W 202 1 xvda1 10 40 1.70 22199 bash R 202 1 xvda1 2 20 0.35 482 xfsaild/md0 W 202 16 xvdb 5 13 0.27 482 xfsaild/md0 W 202 32 xvdc 2 8 0.33 31331 pickup R 202 1 xvda1 1 4 0.31 08:09:54 loadavg: 0.51 0.45 0.39 2/282 22346 PID COMM D MAJ MIN DISK I/O Kbytes AVGms 22069 dd R 202 1 xvda1 14689 117512 0.32 326 jbd2/xvda1-8 W 202 1 xvda1 3 168 2.33 1880 supervise W 202 1 xvda1 10 40 0.65 1873 supervise W 202 1 xvda1 10 40 1.08 1871 supervise W 202 1 xvda1 10 40 0.66 1876 supervise W 202 1 xvda1 10 40 0.79 1892 supervise W 202 1 xvda1 10 40 0.67 1878 supervise W 202 1 xvda1 10 40 0.66 1886 supervise W 202 1 xvda1 10 40 1.02 1894 supervise W 202 1 xvda1 10 40 0.88 1869 supervise W 202 1 xvda1 10 40 0.89 1888 supervise W 202 1 xvda1 10 40 1.25 08:09:59 loadavg: 0.55 0.46 0.40 2/282 22461 PID COMM D MAJ MIN DISK I/O Kbytes AVGms 22069 dd R 202 1 xvda1 14442 115536 0.33 326 jbd2/xvda1-8 W 202 1 xvda1 3 168 3.46 1880 supervise W 202 1 xvda1 10 40 0.87 1873 supervise W 202 1 xvda1 10 40 0.87 1871 supervise W 202 1 xvda1 10 40 0.78 1876 supervise W 202 1 xvda1 10 40 0.86 1892 supervise W 202 1 xvda1 10 40 0.89 1878 supervise W 202 1 xvda1 10 40 0.87 1886 supervise W 202 1 xvda1 10 40 0.86 1894 supervise W 202 1 xvda1 10 40 1.06 1869 supervise W 202 1 xvda1 10 40 1.12 1888 supervise W 202 1 xvda1 10 40 0.98 08:10:04 loadavg: 0.59 0.47 0.40 3/282 22576 PID COMM D MAJ MIN DISK I/O Kbytes AVGms 22069 dd R 202 1 xvda1 14179 113432 0.34 326 jbd2/xvda1-8 W 202 1 xvda1 3 168 2.39 1880 supervise W 202 1 xvda1 10 40 0.81 1873 supervise W 202 1 xvda1 10 40 1.02 1871 supervise W 202 1 xvda1 10 40 1.15 1876 supervise W 202 1 xvda1 10 40 1.10 1892 supervise W 202 1 xvda1 10 40 0.77 1878 supervise W 202 1 xvda1 10 40 0.72 1886 supervise W 202 1 xvda1 10 40 0.81 1894 supervise W 202 1 xvda1 10 40 0.86 1869 supervise W 202 1 xvda1 10 40 0.83 1888 supervise W 202 1 xvda1 10 40 0.79 24467 kworker/0:2 R 202 32 xvdc 3 12 0.26 1056 cron R 202 1 xvda1 2 8 0.30 24467 kworker/0:2 R 202 16 xvdb 1 4 0.23 08:10:09 loadavg: 0.54 0.46 0.40 2/281 22668 PID COMM D MAJ MIN DISK I/O Kbytes AVGms 22069 dd R 202 1 xvda1 250 2000 0.34 326 jbd2/xvda1-8 W 202 1 xvda1 3 168 2.40 1880 supervise W 202 1 xvda1 8 32 0.93 1873 supervise W 202 1 xvda1 8 32 0.76 1871 supervise W 202 1 xvda1 8 32 0.60 1876 supervise W 202 1 xvda1 8 32 0.61 1892 supervise W 202 1 xvda1 8 32 0.68 1878 supervise W 202 1 xvda1 8 32 0.90 1886 supervise W 202 1 xvda1 8 32 0.57 1894 supervise W 202 1 xvda1 8 32 0.97 1869 supervise W 202 1 xvda1 8 32 0.69 1888 supervise W 202 1 xvda1 8 32 0.67 This shows another "dd" command reading from xvda1. On this system, various "supervise" processes do 8 disk writes per second, every second (they are creating and updating "status" files). USAGE message: # ./biotop.py -h usage: biotop.py [-h] [-C] [-r MAXROWS] [interval] [count] Block device (disk) I/O by process positional arguments: interval output interval, in seconds count number of outputs optional arguments: -h, --help show this help message and exit -C, --noclear don't clear the screen -r MAXROWS, --maxrows MAXROWS maximum rows to print, default 20 examples: ./biotop # block device I/O top, 1 second refresh ./biotop -C # don't clear the screen ./biotop 5 # 5 second summaries ./biotop 5 10 # 5 second summaries, 10 times only bpfcc-0.12.0/tools/bitesize.py000077500000000000000000000021761357404205000162100ustar00rootroot00000000000000#!/usr/bin/python # # bitehist.py Block I/O size histogram. # For Linux, uses BCC, eBPF. See .c file. # # USAGE: bitesize # # Ctrl-C will print the partially gathered histogram then exit. # # Copyright (c) 2016 Allan McAleavy # Licensed under the Apache License, Version 2.0 (the "License") # # 05-Feb-2016 Allan McAleavy ran pep8 against file # 19-Mar-2019 Brendan Gregg Switched to use tracepoints. from bcc import BPF from time import sleep bpf_text = """ #include #include struct proc_key_t { char name[TASK_COMM_LEN]; u64 slot; }; BPF_HISTOGRAM(dist, struct proc_key_t); TRACEPOINT_PROBE(block, block_rq_issue) { struct proc_key_t key = {.slot = bpf_log2l(args->bytes / 1024)}; bpf_probe_read(&key.name, sizeof(key.name), args->comm); dist.increment(key); return 0; } """ # load BPF program b = BPF(text=bpf_text) print("Tracing block I/O... Hit Ctrl-C to end.") # trace until Ctrl-C dist = b.get_table("dist") try: sleep(99999999) except KeyboardInterrupt: dist.print_log2_hist("Kbytes", "Process Name", section_print_fn=bytes.decode) bpfcc-0.12.0/tools/bitesize_example.txt000066400000000000000000000117541357404205000201110ustar00rootroot00000000000000Examples of bitesize.py, the Linux bcc/eBPF version. The aim of this tool is to show I/O distribution for requested block sizes, by process name. # ./bitesize.py Tracing... Hit Ctrl-C to end. ^C Process Name = 'kworker/u128:1' Kbytes : count distribution 0 -> 1 : 1 |******************** | 2 -> 3 : 0 | | 4 -> 7 : 2 |****************************************| Process Name = 'bitesize.py' Kbytes : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 0 | | 128 -> 255 : 1 |****************************************| Process Name = 'dd' Kbytes : count distribution 0 -> 1 : 3 | | 2 -> 3 : 0 | | 4 -> 7 : 6 | | 8 -> 15 : 0 | | 16 -> 31 : 1 | | 32 -> 63 : 1 | | 64 -> 127 : 0 | | 128 -> 255 : 0 | | 256 -> 511 : 1 | | 512 -> 1023 : 0 | | 1024 -> 2047 : 488 |****************************************| Process Name = 'jbd2/dm-1-8' Kbytes : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 1 |****************************************| Process Name = 'cat' Kbytes : count distribution 0 -> 1 : 1 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 1 | | 64 -> 127 : 0 | | 128 -> 255 : 0 | | 256 -> 511 : 1924 |****************************************| Process Name = 'ntpd' Kbytes : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 104 |****************************************| Process Name = 'vmtoolsd' Kbytes : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 1 |****************************************| Process Name = 'bash' Kbytes : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 2 |****************************************| Process Name = 'jbd2/sdb-8' Kbytes : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 1 |****************************************| 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 1 |****************************************| We can see from above that there was a dd command being run which generated 488 IOPS between 1MB and 2MB, we can also see the cat command generating 1924 IOPS between 256Kb and 512Kb. bpfcc-0.12.0/tools/bpflist.py000077500000000000000000000046231357404205000160340ustar00rootroot00000000000000#!/usr/bin/python # # bpflist Display processes currently using BPF programs and maps, # pinned BPF programs and maps, and enabled probes. # # USAGE: bpflist [-v] # # Idea by Brendan Gregg. # # Copyright 2017, Sasha Goldshtein # Licensed under the Apache License, Version 2.0 # # 09-Mar-2017 Sasha Goldshtein Created this. from bcc import BPF, USDT import argparse import re import os import subprocess examples = """examples: bpflist # display all processes currently using BPF bpflist -v # also count kprobes/uprobes bpflist -vv # display kprobes/uprobes and count them """ parser = argparse.ArgumentParser( description="Display processes currently using BPF programs and maps", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("-v", "--verbosity", action="count", default=0, help="count and display kprobes/uprobes as well") args = parser.parse_args() def comm_for_pid(pid): try: return open("/proc/%d/comm" % pid).read().strip() except: return "[unknown]" counts = {} def parse_probes(typ): if args.verbosity > 1: print("open %ss:" % typ) for probe in open("/sys/kernel/debug/tracing/%s_events" % typ): # Probes opened by bcc have a specific pattern that includes the pid # of the requesting process. match = re.search('_bcc_(\\d+)\\s', probe) if match: pid = int(match.group(1)) counts[(pid, typ)] = counts.get((pid, typ), 0) + 1 if args.verbosity > 1: print(probe.strip()) if args.verbosity > 1: print("") if args.verbosity > 0: parse_probes("kprobe") parse_probes("uprobe") def find_bpf_fds(pid): root = '/proc/%d/fd' % pid for fd in os.listdir(root): try: link = os.readlink(os.path.join(root, fd)) except OSError: continue match = re.match('.*bpf-(\\w+)', link) if match: tup = (pid, match.group(1)) counts[tup] = counts.get(tup, 0) + 1 for pdir in os.listdir('/proc'): if re.match('\\d+', pdir): try: find_bpf_fds(int(pdir)) except OSError: continue print("%-6s %-16s %-8s %s" % ("PID", "COMM", "TYPE", "COUNT")) for (pid, typ), count in sorted(counts.items(), key=lambda t: t[0][0]): comm = comm_for_pid(pid) print("%-6d %-16s %-8s %-4d" % (pid, comm, typ, count)) bpfcc-0.12.0/tools/bpflist_example.txt000066400000000000000000000042031357404205000177250ustar00rootroot00000000000000Demonstrations of bpflist. bpflist displays information on running BPF programs and optionally also prints open kprobes and uprobes. It is used to understand which BPF programs are currently running on the system. For example: # bpflist PID COMM TYPE COUNT 4058 fileslower prog 4 4058 fileslower map 2 4106 bashreadline map 1 4106 bashreadline prog 1 From the output above, the fileslower and bashreadline tools are running. fileslower has installed 4 BPF programs (functions) and has opened 2 BPF maps (such as hashes, histograms, stack trace tables, and so on). In verbose mode, bpflist also counts the number of kprobes and uprobes opened by the process. This information is obtained heuristically: bcc-based tools include the process id in the name of the probe. For example: # bpflist -v PID COMM TYPE COUNT 4058 fileslower prog 4 4058 fileslower kprobe 4 4058 fileslower map 2 4106 bashreadline uprobe 1 4106 bashreadline prog 1 4106 bashreadline map 1 In double-verbose mode, the probe definitions are also displayed: # bpflist -vv open kprobes: p:kprobes/p___vfs_read_bcc_4058 __vfs_read r:kprobes/r___vfs_read_bcc_4058 __vfs_read p:kprobes/p___vfs_write_bcc_4058 __vfs_write r:kprobes/r___vfs_write_bcc_4058 __vfs_write open uprobes: r:uprobes/r__bin_bash_0xa4dd0_bcc_4106 /bin/bash:0x00000000000a4dd0 PID COMM TYPE COUNT 4058 fileslower prog 4 4058 fileslower kprobe 4 4058 fileslower map 2 4106 bashreadline uprobe 1 4106 bashreadline prog 1 4106 bashreadline map 1 USAGE: # bpflist -h usage: bpflist.py [-h] [-v] Display processes currently using BPF programs and maps optional arguments: -h, --help show this help message and exit -v, --verbosity count and display kprobes/uprobes as well examples: bpflist # display all processes currently using BPF bpflist -v # also count kprobes/uprobes bpflist -vv # display kprobes/uprobes and count them bpfcc-0.12.0/tools/btrfsdist.py000077500000000000000000000143011357404205000163670ustar00rootroot00000000000000#!/usr/bin/python # @lint-avoid-python-3-compatibility-imports # # btrfsdist Summarize btrfs operation latency. # For Linux, uses BCC, eBPF. # # USAGE: btrfsdist [-h] [-T] [-m] [-p PID] [interval] [count] # # Copyright 2016 Netflix, Inc. # Licensed under the Apache License, Version 2.0 (the "License") # # 15-Feb-2016 Brendan Gregg Created this. from __future__ import print_function from bcc import BPF from time import sleep, strftime import argparse # symbols kallsyms = "/proc/kallsyms" # arguments examples = """examples: ./btrfsdist # show operation latency as a histogram ./btrfsdist -p 181 # trace PID 181 only ./btrfsdist 1 10 # print 1 second summaries, 10 times ./btrfsdist -m 5 # 5s summaries, milliseconds """ parser = argparse.ArgumentParser( description="Summarize btrfs operation latency", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("-T", "--notimestamp", action="store_true", help="don't include timestamp on interval output") parser.add_argument("-m", "--milliseconds", action="store_true", help="output in milliseconds") parser.add_argument("-p", "--pid", help="trace this PID only") parser.add_argument("interval", nargs="?", help="output interval, in seconds") parser.add_argument("count", nargs="?", default=99999999, help="number of outputs") parser.add_argument("--ebpf", action="store_true", help=argparse.SUPPRESS) args = parser.parse_args() pid = args.pid countdown = int(args.count) if args.milliseconds: factor = 1000000 label = "msecs" else: factor = 1000 label = "usecs" if args.interval and int(args.interval) == 0: print("ERROR: interval 0. Exiting.") exit() debug = 0 # define BPF program bpf_text = """ #include #include #include #define OP_NAME_LEN 8 typedef struct dist_key { char op[OP_NAME_LEN]; u64 slot; } dist_key_t; BPF_HASH(start, u32); BPF_HISTOGRAM(dist, dist_key_t); // time operation int trace_entry(struct pt_regs *ctx) { u32 pid = bpf_get_current_pid_tgid(); if (FILTER_PID) return 0; u64 ts = bpf_ktime_get_ns(); start.update(&pid, &ts); return 0; } // The current btrfs (Linux 4.5) uses generic_file_read_iter() instead of it's // own read function. So we need to trace that and then filter on btrfs, which // I do by checking file->f_op. int trace_read_entry(struct pt_regs *ctx, struct kiocb *iocb) { u32 pid = bpf_get_current_pid_tgid(); if (FILTER_PID) return 0; // btrfs filter on file->f_op == btrfs_file_operations struct file *fp = iocb->ki_filp; if ((u64)fp->f_op != BTRFS_FILE_OPERATIONS) return 0; u64 ts = bpf_ktime_get_ns(); start.update(&pid, &ts); return 0; } // The current btrfs (Linux 4.5) uses generic_file_open(), instead of it's own // function. Same as with reads. Trace the generic path and filter: int trace_open_entry(struct pt_regs *ctx, struct inode *inode, struct file *file) { u32 pid; pid = bpf_get_current_pid_tgid(); if (FILTER_PID) return 0; // btrfs filter on file->f_op == btrfs_file_operations if ((u64)file->f_op != BTRFS_FILE_OPERATIONS) return 0; u64 ts = bpf_ktime_get_ns(); start.update(&pid, &ts); return 0; } static int trace_return(struct pt_regs *ctx, const char *op) { u64 *tsp; u32 pid = bpf_get_current_pid_tgid(); // fetch timestamp and calculate delta tsp = start.lookup(&pid); if (tsp == 0) { return 0; // missed start or filtered } u64 delta = (bpf_ktime_get_ns() - *tsp) / FACTOR; // store as histogram dist_key_t key = {.slot = bpf_log2l(delta)}; __builtin_memcpy(&key.op, op, sizeof(key.op)); dist.increment(key); start.delete(&pid); return 0; } int trace_read_return(struct pt_regs *ctx) { char *op = "read"; return trace_return(ctx, op); } int trace_write_return(struct pt_regs *ctx) { char *op = "write"; return trace_return(ctx, op); } int trace_open_return(struct pt_regs *ctx) { char *op = "open"; return trace_return(ctx, op); } int trace_fsync_return(struct pt_regs *ctx) { char *op = "fsync"; return trace_return(ctx, op); } """ # code replacements with open(kallsyms) as syms: ops = '' for line in syms: a = line.rstrip().split() (addr, name) = (a[0], a[2]) name = name.split("\t")[0] if name == "btrfs_file_operations": ops = "0x" + addr break if ops == '': print("ERROR: no btrfs_file_operations in /proc/kallsyms. Exiting.") print("HINT: the kernel should be built with CONFIG_KALLSYMS_ALL.") exit() bpf_text = bpf_text.replace('BTRFS_FILE_OPERATIONS', ops) bpf_text = bpf_text.replace('FACTOR', str(factor)) if args.pid: bpf_text = bpf_text.replace('FILTER_PID', 'pid != %s' % pid) else: bpf_text = bpf_text.replace('FILTER_PID', '0') if debug or args.ebpf: print(bpf_text) if args.ebpf: exit() # load BPF program b = BPF(text=bpf_text) # Common file functions. See earlier comment about generic_file_read_iter(). b.attach_kprobe(event="generic_file_read_iter", fn_name="trace_read_entry") b.attach_kprobe(event="btrfs_file_write_iter", fn_name="trace_entry") b.attach_kprobe(event="generic_file_open", fn_name="trace_open_entry") b.attach_kprobe(event="btrfs_sync_file", fn_name="trace_entry") b.attach_kretprobe(event="generic_file_read_iter", fn_name="trace_read_return") b.attach_kretprobe(event="btrfs_file_write_iter", fn_name="trace_write_return") b.attach_kretprobe(event="generic_file_open", fn_name="trace_open_return") b.attach_kretprobe(event="btrfs_sync_file", fn_name="trace_fsync_return") print("Tracing btrfs operation latency... Hit Ctrl-C to end.") # output exiting = 0 dist = b.get_table("dist") while (1): try: if args.interval: sleep(int(args.interval)) else: sleep(99999999) except KeyboardInterrupt: exiting = 1 print() if args.interval and (not args.notimestamp): print(strftime("%H:%M:%S:")) dist.print_log2_hist(label, "operation") dist.clear() countdown -= 1 if exiting or countdown == 0: exit() bpfcc-0.12.0/tools/btrfsdist_example.txt000066400000000000000000000225111357404205000202700ustar00rootroot00000000000000Demonstrations of btrfsdist, the Linux eBPF/bcc version. btrfsdist traces btrfs reads, writes, opens, and fsyncs, and summarizes their latency as a power-of-2 histogram. For example: # ./btrfsdist Tracing btrfs operation latency... Hit Ctrl-C to end. ^C operation = 'read' usecs : count distribution 0 -> 1 : 15 | | 2 -> 3 : 1308 |******* | 4 -> 7 : 198 |* | 8 -> 15 : 0 | | 16 -> 31 : 11 | | 32 -> 63 : 361 |* | 64 -> 127 : 55 | | 128 -> 255 : 104 | | 256 -> 511 : 7312 |****************************************| 512 -> 1023 : 387 |** | 1024 -> 2047 : 10 | | 2048 -> 4095 : 4 | | operation = 'write' usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 4 |****************************************| operation = 'open' usecs : count distribution 0 -> 1 : 1 |********** | 2 -> 3 : 4 |****************************************| This output shows a bi-modal distribution for read latency, with a faster mode of 1,308 reads that took between 2 and 3 microseconds, and a slower mode of over 7,312 reads that took between 256 and 511 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. An optional interval and a count can be provided, as well as -m to show the distributions in milliseconds. For example, two second summaries, five times: # ./btrfsdist 2 5 Tracing btrfs operation latency... Hit Ctrl-C to end. 03:40:49: operation = 'read' usecs : count distribution 0 -> 1 : 15 | | 2 -> 3 : 833 |******** | 4 -> 7 : 127 |* | 8 -> 15 : 0 | | 16 -> 31 : 8 | | 32 -> 63 : 907 |******** | 64 -> 127 : 91 | | 128 -> 255 : 246 |** | 256 -> 511 : 4164 |****************************************| 512 -> 1023 : 193 |* | 1024 -> 2047 : 4 | | 2048 -> 4095 : 6 | | 4096 -> 8191 : 2 | | 03:40:51: operation = 'read' usecs : count distribution 0 -> 1 : 25 | | 2 -> 3 : 1491 |*************** | 4 -> 7 : 218 |** | 8 -> 15 : 0 | | 16 -> 31 : 16 | | 32 -> 63 : 1527 |*************** | 64 -> 127 : 319 |*** | 128 -> 255 : 429 |**** | 256 -> 511 : 3841 |****************************************| 512 -> 1023 : 232 |** | 1024 -> 2047 : 3 | | 2048 -> 4095 : 6 | | 4096 -> 8191 : 1 | | 8192 -> 16383 : 1 | | 03:40:53: operation = 'read' usecs : count distribution 0 -> 1 : 27 | | 2 -> 3 : 2999 |********************************* | 4 -> 7 : 407 |**** | 8 -> 15 : 0 | | 16 -> 31 : 46 | | 32 -> 63 : 3538 |****************************************| 64 -> 127 : 595 |****** | 128 -> 255 : 621 |******* | 256 -> 511 : 3532 |*************************************** | 512 -> 1023 : 212 |** | 1024 -> 2047 : 1 | | 2048 -> 4095 : 0 | | 4096 -> 8191 : 0 | | 8192 -> 16383 : 0 | | 16384 -> 32767 : 1 | | 03:40:55: operation = 'read' usecs : count distribution 0 -> 1 : 221 | | 2 -> 3 : 12580 |****************************************| 4 -> 7 : 1366 |**** | 8 -> 15 : 0 | | 16 -> 31 : 289 | | 32 -> 63 : 10782 |********************************** | 64 -> 127 : 1232 |*** | 128 -> 255 : 807 |** | 256 -> 511 : 2299 |******* | 512 -> 1023 : 135 | | 1024 -> 2047 : 5 | | 2048 -> 4095 : 2 | | 03:40:57: operation = 'read' usecs : count distribution 0 -> 1 : 73951 |************************* | 2 -> 3 : 117639 |****************************************| 4 -> 7 : 7943 |** | 8 -> 15 : 1841 | | 16 -> 31 : 1143 | | 32 -> 63 : 5006 |* | 64 -> 127 : 483 | | 128 -> 255 : 242 | | 256 -> 511 : 253 | | 512 -> 1023 : 84 | | 1024 -> 2047 : 23 | | This shows a read workload that begins bimodal, and eventually the second mode disappears. The reason for this is that the workload cached during tracing. Note that the rate also increased, with over 200k reads for the final two second sample. USAGE message: # ./btrfsdist -h usage: btrfsdist [-h] [-T] [-m] [-p PID] [interval] [count] Summarize btrfs 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: ./btrfsdist # show operation latency as a histogram ./btrfsdist -p 181 # trace PID 181 only ./btrfsdist 1 10 # print 1 second summaries, 10 times ./btrfsdist -m 5 # 5s summaries, milliseconds bpfcc-0.12.0/tools/btrfsslower.py000077500000000000000000000233631357404205000167470ustar00rootroot00000000000000#!/usr/bin/python # @lint-avoid-python-3-compatibility-imports # # btrfsslower Trace slow btrfs operations. # For Linux, uses BCC, eBPF. # # USAGE: btrfsslower [-h] [-j] [-p PID] [min_ms] [-d DURATION] # # This script traces common btrfs file operations: reads, writes, opens, and # syncs. It measures the time spent in these operations, and prints details # for each that exceeded a threshold. # # WARNING: This adds low-overhead instrumentation to these btrfs 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 (even if it prints no "slower" events) can # begin to become significant. # # By default, a minimum millisecond threshold of 10 is used. # # Copyright 2016 Netflix, Inc. # Licensed under the Apache License, Version 2.0 (the "License") # # 15-Feb-2016 Brendan Gregg Created this. # 16-Oct-2016 Dina Goldshtein -p to filter by process ID. from __future__ import print_function from bcc import BPF import argparse from datetime import datetime, timedelta from time import strftime # symbols kallsyms = "/proc/kallsyms" # arguments examples = """examples: ./btrfsslower # trace operations slower than 10 ms (default) ./btrfsslower 1 # trace operations slower than 1 ms ./btrfsslower -j 1 # ... 1 ms, parsable output (csv) ./btrfsslower 0 # trace all operations (warning: verbose) ./btrfsslower -p 185 # trace PID 185 only ./btrfsslower -d 10 # trace for 10 seconds only """ parser = argparse.ArgumentParser( description="Trace common btrfs file operations slower than a threshold", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("-j", "--csv", action="store_true", help="just print fields: comma-separated values") parser.add_argument("-p", "--pid", help="trace this PID only") parser.add_argument("min_ms", nargs="?", default='10', help="minimum I/O duration to trace, in ms (default 10)") parser.add_argument("--ebpf", action="store_true", help=argparse.SUPPRESS) parser.add_argument("-d", "--duration", help="total duration of trace in seconds") args = parser.parse_args() min_ms = int(args.min_ms) pid = args.pid csv = args.csv debug = 0 if args.duration: args.duration = timedelta(seconds=int(args.duration)) # define BPF program bpf_text = """ #include #include #include #include // XXX: switch these to char's when supported #define TRACE_READ 0 #define TRACE_WRITE 1 #define TRACE_OPEN 2 #define TRACE_FSYNC 3 struct val_t { u64 ts; u64 offset; struct file *fp; }; struct data_t { // XXX: switch some to u32's when supported u64 ts_us; u64 type; u64 size; u64 offset; u64 delta_us; u64 pid; char task[TASK_COMM_LEN]; char file[DNAME_INLINE_LEN]; }; BPF_HASH(entryinfo, u64, struct val_t); BPF_PERF_OUTPUT(events); // // Store timestamp and size on entry // // The current btrfs (Linux 4.5) uses generic_file_read_iter() instead of it's // own read function. So we need to trace that and then filter on btrfs, which // I do by checking file->f_op. int trace_read_entry(struct pt_regs *ctx, struct kiocb *iocb) { u64 id = bpf_get_current_pid_tgid(); u32 pid = id >> 32; // PID is higher part if (FILTER_PID) return 0; // btrfs filter on file->f_op == btrfs_file_operations struct file *fp = iocb->ki_filp; if ((u64)fp->f_op != BTRFS_FILE_OPERATIONS) return 0; // store filep and timestamp by pid struct val_t val = {}; val.ts = bpf_ktime_get_ns(); val.fp = fp; val.offset = iocb->ki_pos; if (val.fp) entryinfo.update(&id, &val); return 0; } // btrfs_file_write_iter(): int trace_write_entry(struct pt_regs *ctx, struct kiocb *iocb) { u64 id = bpf_get_current_pid_tgid(); u32 pid = id >> 32; // PID is higher part if (FILTER_PID) return 0; // store filep and timestamp by id struct val_t val = {}; val.ts = bpf_ktime_get_ns(); val.fp = iocb->ki_filp; val.offset = iocb->ki_pos; if (val.fp) entryinfo.update(&id, &val); return 0; } // The current btrfs (Linux 4.5) uses generic_file_open(), instead of it's own // function. Same as with reads. Trace the generic path and filter: int trace_open_entry(struct pt_regs *ctx, struct inode *inode, struct file *file) { u64 id = bpf_get_current_pid_tgid(); u32 pid = id >> 32; // PID is higher part if (FILTER_PID) return 0; // btrfs filter on file->f_op == btrfs_file_operations if ((u64)file->f_op != BTRFS_FILE_OPERATIONS) return 0; // store filep and timestamp by id struct val_t val = {}; val.ts = bpf_ktime_get_ns(); val.fp = file; val.offset = 0; if (val.fp) entryinfo.update(&id, &val); return 0; } // btrfs_sync_file(): int trace_fsync_entry(struct pt_regs *ctx, struct file *file) { u64 id = bpf_get_current_pid_tgid(); u32 pid = id >> 32; // PID is higher part if (FILTER_PID) return 0; // store filep and timestamp by id struct val_t val = {}; val.ts = bpf_ktime_get_ns(); val.fp = file; val.offset = 0; if (val.fp) entryinfo.update(&id, &val); return 0; } // // Output // static int trace_return(struct pt_regs *ctx, int type) { struct val_t *valp; u64 id = bpf_get_current_pid_tgid(); u32 pid = id >> 32; // PID is higher part valp = entryinfo.lookup(&id); if (valp == 0) { // missed tracing issue or filtered return 0; } // calculate delta u64 ts = bpf_ktime_get_ns(); u64 delta_us = (ts - valp->ts) / 1000; entryinfo.delete(&id); if (FILTER_US) return 0; // populate output struct u32 size = PT_REGS_RC(ctx); struct data_t data = {.type = type, .size = size, .delta_us = delta_us, .pid = pid}; data.ts_us = ts / 1000; data.offset = valp->offset; bpf_get_current_comm(&data.task, sizeof(data.task)); // workaround (rewriter should handle file to d_name in one step): struct dentry *de = NULL; struct qstr qs = {}; de = valp->fp->f_path.dentry; qs = de->d_name; if (qs.len == 0) return 0; bpf_probe_read(&data.file, sizeof(data.file), (void *)qs.name); // output events.perf_submit(ctx, &data, sizeof(data)); return 0; } int trace_read_return(struct pt_regs *ctx) { return trace_return(ctx, TRACE_READ); } int trace_write_return(struct pt_regs *ctx) { return trace_return(ctx, TRACE_WRITE); } int trace_open_return(struct pt_regs *ctx) { return trace_return(ctx, TRACE_OPEN); } int trace_fsync_return(struct pt_regs *ctx) { return trace_return(ctx, TRACE_FSYNC); } """ # code replacements with open(kallsyms) as syms: ops = '' for line in syms: a = line.rstrip().split() (addr, name) = (a[0], a[2]) name = name.split("\t")[0] if name == "btrfs_file_operations": ops = "0x" + addr break if ops == '': print("ERROR: no btrfs_file_operations in /proc/kallsyms. Exiting.") print("HINT: the kernel should be built with CONFIG_KALLSYMS_ALL.") exit() bpf_text = bpf_text.replace('BTRFS_FILE_OPERATIONS', ops) if min_ms == 0: bpf_text = bpf_text.replace('FILTER_US', '0') else: bpf_text = bpf_text.replace('FILTER_US', 'delta_us <= %s' % str(min_ms * 1000)) if args.pid: bpf_text = bpf_text.replace('FILTER_PID', 'pid != %s' % pid) else: bpf_text = bpf_text.replace('FILTER_PID', '0') if debug or args.ebpf: print(bpf_text) if args.ebpf: exit() # process event def print_event(cpu, data, size): event = b["events"].event(data) type = 'R' if event.type == 1: type = 'W' elif event.type == 2: type = 'O' elif event.type == 3: type = 'S' if (csv): print("%d,%s,%d,%s,%d,%d,%d,%s" % ( event.ts_us, event.task.decode('utf-8', 'replace'), event.pid, type, event.size, event.offset, event.delta_us, event.file.decode('utf-8', 'replace'))) return print("%-8s %-14.14s %-6s %1s %-7s %-8d %7.2f %s" % (strftime("%H:%M:%S"), event.task.decode('utf-8', 'replace'), event.pid, type, event.size, event.offset / 1024, float(event.delta_us) / 1000, event.file.decode('utf-8', 'replace'))) # initialize BPF b = BPF(text=bpf_text) # Common file functions. See earlier comment about generic_*(). b.attach_kprobe(event="generic_file_read_iter", fn_name="trace_read_entry") b.attach_kprobe(event="btrfs_file_write_iter", fn_name="trace_write_entry") b.attach_kprobe(event="generic_file_open", fn_name="trace_open_entry") b.attach_kprobe(event="btrfs_sync_file", fn_name="trace_fsync_entry") b.attach_kretprobe(event="generic_file_read_iter", fn_name="trace_read_return") b.attach_kretprobe(event="btrfs_file_write_iter", fn_name="trace_write_return") b.attach_kretprobe(event="generic_file_open", fn_name="trace_open_return") b.attach_kretprobe(event="btrfs_sync_file", fn_name="trace_fsync_return") # header if (csv): print("ENDTIME_us,TASK,PID,TYPE,BYTES,OFFSET_b,LATENCY_us,FILE") else: if min_ms == 0: print("Tracing btrfs operations") else: print("Tracing btrfs operations slower than %d ms" % min_ms) print("%-8s %-14s %-6s %1s %-7s %-8s %7s %s" % ("TIME", "COMM", "PID", "T", "BYTES", "OFF_KB", "LAT(ms)", "FILENAME")) # read events b["events"].open_perf_buffer(print_event, page_cnt=64) start_time = datetime.now() while not args.duration or datetime.now() - start_time < args.duration: try: b.perf_buffer_poll(timeout=1000) except KeyboardInterrupt: exit() bpfcc-0.12.0/tools/btrfsslower_example.txt000066400000000000000000000152341357404205000206440ustar00rootroot00000000000000Demonstrations of btrfsslower, the Linux eBPF/bcc version. btrfsslower shows btrfs reads, writes, opens, and fsyncs, slower than a threshold. For example: # ./btrfsslower Tracing btrfs operations slower than 10 ms TIME COMM PID T BYTES OFF_KB LAT(ms) FILENAME 01:22:03 randread.pl 13602 R 8192 391384 10.40 data1 01:22:03 randread.pl 13602 R 8192 92632 10.41 data1 01:22:06 randread.pl 13602 R 8192 199800 17.33 data1 01:22:06 randread.pl 13602 R 8192 415160 17.21 data1 01:22:07 randread.pl 13602 R 8192 729984 11.93 data1 01:22:09 randread.pl 13602 R 8192 342784 11.90 data1 [...] This shows several reads from a "randread.pl" program, each 8 Kbytes in size, and from a "data1" file. These all had over 10 ms latency. 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. The threshold can be provided as an argument. Eg, I/O slower than 1 ms: # ./btrfsslower 1 Tracing btrfs operations slower than 1 ms TIME COMM PID T BYTES OFF_KB LAT(ms) FILENAME 03:26:54 randread.pl 30578 R 8192 214864 1.87 data1 03:26:54 randread.pl 30578 R 8192 267600 1.48 data1 03:26:54 randread.pl 30578 R 8192 704200 1.30 data1 03:26:54 randread.pl 30578 R 8192 492352 3.09 data1 03:26:55 randread.pl 30578 R 8192 319448 1.34 data1 03:26:55 randread.pl 30578 R 8192 676032 1.88 data1 03:26:55 randread.pl 30578 R 8192 646712 2.24 data1 03:26:55 randread.pl 30578 R 8192 124376 1.02 data1 03:26:55 randread.pl 30578 R 8192 223064 2.64 data1 03:26:55 randread.pl 30578 R 8192 521280 1.55 data1 03:26:55 randread.pl 30578 R 8192 272992 2.48 data1 03:26:55 randread.pl 30578 R 8192 450112 2.67 data1 03:26:55 randread.pl 30578 R 8192 361808 1.78 data1 03:26:55 randread.pl 30578 R 8192 41088 1.46 data1 03:26:55 randread.pl 30578 R 8192 756576 1.67 data1 03:26:55 randread.pl 30578 R 8192 711776 2.74 data1 03:26:55 randread.pl 30578 R 8192 129472 1.34 data1 03:26:55 randread.pl 30578 R 8192 526928 1.82 data1 03:26:56 randread.pl 30578 R 8192 312768 1.44 data1 03:26:56 randread.pl 30578 R 8192 34720 1.14 data1 03:26:56 randread.pl 30578 R 8192 258376 1.13 data1 03:26:56 randread.pl 30578 R 8192 308456 1.44 data1 03:26:56 randread.pl 30578 R 8192 759656 1.27 data1 03:26:56 randread.pl 30578 R 8192 387424 3.24 data1 03:26:56 randread.pl 30578 R 8192 168864 3.38 data1 03:26:56 randread.pl 30578 R 8192 699296 1.38 data1 03:26:56 randread.pl 30578 R 8192 405688 2.37 data1 03:26:56 randread.pl 30578 R 8192 559064 1.18 data1 03:26:56 randread.pl 30578 R 8192 264808 1.13 data1 03:26:56 randread.pl 30578 R 8192 369240 2.20 data1 [...] There's now much more output (this spans less than 3 seconds, the previous output spanned 6 seconds), as the lower threshold is catching more I/O. A threshold of 0 will trace all operations. Warning: the output will be verbose, as it will include all file system cache hits. # ./btrfsslower 0 Tracing btrfs operations TIME COMM PID T BYTES OFF_KB LAT(ms) FILENAME 03:28:17 bash 32597 O 0 0 0.00 date.txt 03:28:17 date 32597 W 29 0 0.02 date.txt 03:28:23 cksum 32743 O 0 0 0.00 date.txt 03:28:23 cksum 32743 R 29 0 0.01 date.txt 03:28:23 cksum 32743 R 0 0 0.00 date.txt While tracing, the following commands were run in another window: # date > date.txt # cksum date.txt The output of btrfsslower now includes open operations ("O"), and writes ("W"). The first read from cksum(1) returned 29 bytes, and the second returned 0: causing cksum(1) to stop reading. A -j option will print just the fields (parsable output, csv): # ./btrfsslower -j 1 ENDTIME_us,TASK,PID,TYPE,BYTES,OFFSET_b,LATENCY_us,FILE 8930665366,randread.pl,2717,R,8192,230391808,4312,data1 8930670746,randread.pl,2717,R,8192,347832320,1296,data1 8930675995,randread.pl,2717,R,8192,409812992,4207,data1 8930680213,randread.pl,2717,R,8192,498204672,3104,data1 8930685970,randread.pl,2717,R,8192,553164800,1843,data1 8930687568,randread.pl,2717,R,8192,339492864,1475,data1 8930694108,randread.pl,2717,R,8192,500711424,6276,data1 8930697139,randread.pl,2717,R,8192,485801984,2180,data1 8930705755,randread.pl,2717,R,8192,376922112,7535,data1 8930711340,randread.pl,2717,R,8192,380084224,3314,data1 8930740964,randread.pl,2717,R,8192,226091008,24762,data1 8930743169,randread.pl,2717,R,8192,361570304,1809,data1 8930748789,randread.pl,2717,R,8192,346931200,1530,data1 8930763514,randread.pl,2717,R,8192,59719680,13938,data1 8930764870,randread.pl,2717,R,8192,406511616,1313,data1 8930774327,randread.pl,2717,R,8192,661430272,7361,data1 8930780360,randread.pl,2717,R,8192,406904832,2220,data1 8930785736,randread.pl,2717,R,8192,523419648,2005,data1 8930794560,randread.pl,2717,R,8192,342974464,8388,data1 [...] This may be useful for visualizing with another tool, for example, for producing a scatter plot of ENDTIME vs LATENCY, to look for time-based patterns. USAGE message: # ./btrfsslower -h usage: btrfsslower [-h] [-j] [-p PID] [min_ms] [-d DURATION] Trace common btrfs file operations slower than a threshold positional arguments: min_ms minimum I/O duration to trace, in ms (default 10) optional arguments: -h, --help show this help message and exit -j, --csv just print fields: comma-separated values -p PID, --pid PID trace this PID only -d DURATION, --duration DURATION total duration of trace in seconds examples: ./btrfsslower # trace operations slower than 10 ms (default) ./btrfsslower 1 # trace operations slower than 1 ms ./btrfsslower -j 1 # ... 1 ms, parsable output (csv) ./btrfsslower 0 # trace all operations (warning: verbose) ./btrfsslower -p 185 # trace PID 185 only ./btrfsslower -d 10 # trace for 10 seconds only bpfcc-0.12.0/tools/cachestat.py000077500000000000000000000111531357404205000163240ustar00rootroot00000000000000#!/usr/bin/python # # cachestat Count cache kernel function calls. # For Linux, uses BCC, eBPF. See .c file. # # USAGE: cachestat # Taken from funccount by Brendan Gregg # This is a rewrite of cachestat from perf to bcc # https://github.com/brendangregg/perf-tools/blob/master/fs/cachestat # # Copyright (c) 2016 Allan McAleavy. # Copyright (c) 2015 Brendan Gregg. # Licensed under the Apache License, Version 2.0 (the "License") # # 09-Sep-2015 Brendan Gregg Created this. # 06-Nov-2015 Allan McAleavy # 13-Jan-2016 Allan McAleavy run pep8 against program # 02-Feb-2019 Brendan Gregg Column shuffle, bring back %ratio from __future__ import print_function from bcc import BPF from time import sleep, strftime import argparse import signal import re from sys import argv # signal handler def signal_ignore(signal, frame): print() # Function to gather data from /proc/meminfo # return dictionary for quicker lookup of both values def get_meminfo(): result = dict() for line in open('/proc/meminfo'): k = line.split(':', 3) v = k[1].split() result[k[0]] = int(v[0]) return result # set global variables mpa = 0 mbd = 0 apcl = 0 apd = 0 total = 0 misses = 0 hits = 0 debug = 0 # arguments parser = argparse.ArgumentParser( description="Count cache kernel function calls", formatter_class=argparse.RawDescriptionHelpFormatter) parser.add_argument("-T", "--timestamp", action="store_true", help="include timestamp on output") parser.add_argument("interval", nargs="?", default=1, help="output interval, in seconds") parser.add_argument("count", nargs="?", default=-1, help="number of outputs") parser.add_argument("--ebpf", action="store_true", help=argparse.SUPPRESS) args = parser.parse_args() count = int(args.count) tstamp = args.timestamp interval = int(args.interval) # define BPF program bpf_text = """ #include struct key_t { u64 ip; }; BPF_HASH(counts, struct key_t); int do_count(struct pt_regs *ctx) { struct key_t key = {}; u64 ip; key.ip = PT_REGS_IP(ctx); counts.increment(key); // update counter return 0; } """ if debug or args.ebpf: print(bpf_text) if args.ebpf: exit() # load BPF program b = BPF(text=bpf_text) b.attach_kprobe(event="add_to_page_cache_lru", fn_name="do_count") b.attach_kprobe(event="mark_page_accessed", fn_name="do_count") b.attach_kprobe(event="account_page_dirtied", fn_name="do_count") b.attach_kprobe(event="mark_buffer_dirty", fn_name="do_count") # header if tstamp: print("%-8s " % "TIME", end="") print("%8s %8s %8s %8s %12s %10s" % ("HITS", "MISSES", "DIRTIES", "HITRATIO", "BUFFERS_MB", "CACHED_MB")) loop = 0 exiting = 0 while 1: if count > 0: loop += 1 if loop > count: exit() try: sleep(interval) except KeyboardInterrupt: exiting = 1 # as cleanup can take many seconds, trap Ctrl-C: signal.signal(signal.SIGINT, signal_ignore) counts = b["counts"] for k, v in sorted(counts.items(), key=lambda counts: counts[1].value): func = b.ksym(k.ip) # partial string matches in case of .isra (necessary?) if func.find(b"mark_page_accessed") == 0: mpa = max(0, v.value) if func.find(b"mark_buffer_dirty") == 0: mbd = max(0, v.value) if func.find(b"add_to_page_cache_lru") == 0: apcl = max(0, v.value) if func.find(b"account_page_dirtied") == 0: apd = max(0, v.value) # total = total cache accesses without counting dirties # misses = total of add to lru because of read misses total = mpa - mbd misses = apcl - apd if misses < 0: misses = 0 if total < 0: total = 0 hits = total - misses # If hits are < 0, then its possible misses are overestimated # due to possibly page cache read ahead adding more pages than # needed. In this case just assume misses as total and reset hits. if hits < 0: misses = total hits = 0 ratio = 0 if total > 0: ratio = float(hits) / total if debug: print("%d %d %d %d %d %d %d\n" % (mpa, mbd, apcl, apd, total, misses, hits)) counts.clear() # Get memory info mem = get_meminfo() cached = int(mem["Cached"]) / 1024 buff = int(mem["Buffers"]) / 1024 if tstamp: print("%-8s " % strftime("%H:%M:%S"), end="") print("%8d %8d %8d %7.2f%% %12.0f %10.0f" % (hits, misses, mbd, 100 * ratio, buff, cached)) mpa = mbd = apcl = apd = total = misses = hits = cached = buff = 0 if exiting: print("Detaching...") exit() bpfcc-0.12.0/tools/cachestat_example.txt000066400000000000000000000076541357404205000202360ustar00rootroot00000000000000Demonstrations of cachestat, the Linux eBPF/bcc version. cachestat shows hits and misses to the file system page cache. For example: # cachestat HITS MISSES DIRTIES HITRATIO BUFFERS_MB CACHED_MB 1132 0 4 100.00% 277 4367 161 0 36 100.00% 277 4372 16 0 28 100.00% 277 4372 17154 13750 15 55.51% 277 4422 19 0 1 100.00% 277 4422 83 0 83 100.00% 277 4421 16 0 1 100.00% 277 4423 ^C 0 -19 360 0.00% 277 4423 Detaching... While tracing, there was a burst of misses in the fourth second, bringing the hit ration down to 55%. This shows a 1 Gbyte uncached file that is read twice: (root) ~ # ./cachestat.py HITS MISSES DIRTIES HITRATIO BUFFERS_MB CACHED_MB 1 0 0 100.00% 5 191 198 12136 0 1.61% 5 238 1 11007 3 0.01% 5 281 0 6384 0 0.00% 5 306 1 14464 0 0.01% 5 363 0 11776 0 0.00% 5 409 1 11712 0 0.01% 5 454 32 13184 0 0.24% 5 506 0 11232 0 0.00% 5 550 1 13056 0 0.01% 5 601 16 14720 0 0.11% 5 658 33 9920 0 0.33% 5 697 0 13248 0 0.00% 5 749 4 14144 0 0.03% 5 804 0 9728 0 0.00% 5 842 1 10816 0 0.01% 5 885 808 13504 1 5.65% 5 938 0 11409 0 0.00% 5 982 0 11520 0 0.00% 5 1027 0 15616 0 0.00% 5 1088 1 9792 0 0.01% 5 1126 0 8256 0 0.00% 5 1158 1 9600 0 0.01% 5 1196 599 4804 0 11.09% 5 1215 1 0 0 100.00% 5 1215 0 0 0 0.00% 5 1215 3 1 0 75.00% 5 1215 79536 34 0 99.96% 5 1215 87693 274 4 99.69% 6 1214 89018 3546 0 96.17% 7 1227 33531 201 4 99.40% 7 1228 22 44 0 33.33% 8 1228 0 0 0 0.00% 8 1228 73 21 2 77.66% 8 1228 It took 24 seconds to read the 1 Gbyte file the first time, shown in the output by the high MISSES rate and low HITRATIO. The second time it took 4 seconds, and the HITRATIO was around 99%. This output shows a 1 Gbyte file being created and added to the page cache: (root) ~ # ./cachestat.py HITS MISSES DIRTIES HITRATIO BUFFERS_MB CACHED_MB 1 0 0 100.00% 8 209 0 0 165584 0.00% 8 856 0 0 96505 0.00% 8 1233 0 0 0 0.00% 8 1233 Note the high rate of DIRTIES, and the CACHED_MD size increases by 1024 Mbytes. USAGE message: # cachestat -h usage: cachestat.py [-h] [-T] [interval] [count] Count cache kernel function calls positional arguments: interval output interval, in seconds count number of outputs optional arguments: -h, --help show this help message and exit -T, --timestamp include timestamp on output bpfcc-0.12.0/tools/cachetop.py000077500000000000000000000162041357404205000161550ustar00rootroot00000000000000#!/usr/bin/python # @lint-avoid-python-3-compatibility-imports # # cachetop Count cache kernel function calls per processes # For Linux, uses BCC, eBPF. # # USAGE: cachetop # Taken from cachestat by Brendan Gregg # # Copyright (c) 2016-present, Facebook, Inc. # Licensed under the Apache License, Version 2.0 (the "License") # # 13-Jul-2016 Emmanuel Bretelle first version from __future__ import absolute_import from __future__ import division # Do not import unicode_literals until #623 is fixed # from __future__ import unicode_literals from __future__ import print_function from bcc import BPF from collections import defaultdict from time import strftime import argparse import curses import pwd import re import signal from time import sleep FIELDS = ( "PID", "UID", "CMD", "HITS", "MISSES", "DIRTIES", "READ_HIT%", "WRITE_HIT%" ) DEFAULT_FIELD = "HITS" DEFAULT_SORT_FIELD = FIELDS.index(DEFAULT_FIELD) # signal handler def signal_ignore(signal, frame): print() # Function to gather data from /proc/meminfo # return dictionary for quicker lookup of both values def get_meminfo(): result = {} for line in open('/proc/meminfo'): k = line.split(':', 3) v = k[1].split() result[k[0]] = int(v[0]) return result def get_processes_stats( bpf, sort_field=DEFAULT_SORT_FIELD, sort_reverse=False): ''' Return a tuple containing: buffer cached list of tuple with per process cache stats ''' counts = bpf.get_table("counts") stats = defaultdict(lambda: defaultdict(int)) for k, v in counts.items(): stats["%d-%d-%s" % (k.pid, k.uid, k.comm.decode('utf-8', 'replace'))][k.ip] = v.value stats_list = [] for pid, count in sorted(stats.items(), key=lambda stat: stat[0]): rtaccess = 0 wtaccess = 0 mpa = 0 mbd = 0 apcl = 0 apd = 0 access = 0 misses = 0 rhits = 0 whits = 0 for k, v in count.items(): if re.match(b'mark_page_accessed', bpf.ksym(k)) is not None: mpa = max(0, v) if re.match(b'mark_buffer_dirty', bpf.ksym(k)) is not None: mbd = max(0, v) if re.match(b'add_to_page_cache_lru', bpf.ksym(k)) is not None: apcl = max(0, v) if re.match(b'account_page_dirtied', bpf.ksym(k)) is not None: apd = max(0, v) # access = total cache access incl. reads(mpa) and writes(mbd) # misses = total of add to lru which we do when we write(mbd) # and also the mark the page dirty(same as mbd) access = (mpa + mbd) misses = (apcl + apd) # rtaccess is the read hit % during the sample period. # wtaccess is the write hit % during the smaple period. if mpa > 0: rtaccess = float(mpa) / (access + misses) if apcl > 0: wtaccess = float(apcl) / (access + misses) if wtaccess != 0: whits = 100 * wtaccess if rtaccess != 0: rhits = 100 * rtaccess _pid, uid, comm = pid.split('-', 2) stats_list.append( (int(_pid), uid, comm, access, misses, mbd, rhits, whits)) stats_list = sorted( stats_list, key=lambda stat: stat[sort_field], reverse=sort_reverse ) counts.clear() return stats_list def handle_loop(stdscr, args): # don't wait on key press stdscr.nodelay(1) # set default sorting field sort_field = FIELDS.index(DEFAULT_FIELD) sort_reverse = False # load BPF program bpf_text = """ #include struct key_t { u64 ip; u32 pid; u32 uid; char comm[16]; }; BPF_HASH(counts, struct key_t); int do_count(struct pt_regs *ctx) { struct key_t key = {}; u64 pid = bpf_get_current_pid_tgid(); u32 uid = bpf_get_current_uid_gid(); key.ip = PT_REGS_IP(ctx); key.pid = pid & 0xFFFFFFFF; key.uid = uid & 0xFFFFFFFF; bpf_get_current_comm(&(key.comm), 16); counts.increment(key); return 0; } """ b = BPF(text=bpf_text) b.attach_kprobe(event="add_to_page_cache_lru", fn_name="do_count") b.attach_kprobe(event="mark_page_accessed", fn_name="do_count") b.attach_kprobe(event="account_page_dirtied", fn_name="do_count") b.attach_kprobe(event="mark_buffer_dirty", fn_name="do_count") exiting = 0 while 1: s = stdscr.getch() if s == ord('q'): exiting = 1 elif s == ord('r'): sort_reverse = not sort_reverse elif s == ord('<'): sort_field = max(0, sort_field - 1) elif s == ord('>'): sort_field = min(len(FIELDS) - 1, sort_field + 1) try: sleep(args.interval) except KeyboardInterrupt: exiting = 1 # as cleanup can take many seconds, trap Ctrl-C: signal.signal(signal.SIGINT, signal_ignore) # Get memory info mem = get_meminfo() cached = int(mem["Cached"]) / 1024 buff = int(mem["Buffers"]) / 1024 process_stats = get_processes_stats( b, sort_field=sort_field, sort_reverse=sort_reverse) stdscr.clear() stdscr.addstr( 0, 0, "%-8s Buffers MB: %.0f / Cached MB: %.0f " "/ Sort: %s / Order: %s" % ( strftime("%H:%M:%S"), buff, cached, FIELDS[sort_field], sort_reverse and "descending" or "ascending" ) ) # header stdscr.addstr( 1, 0, "{0:8} {1:8} {2:16} {3:8} {4:8} {5:8} {6:10} {7:10}".format( *FIELDS ), curses.A_REVERSE ) (height, width) = stdscr.getmaxyx() for i, stat in enumerate(process_stats): uid = int(stat[1]) try: username = pwd.getpwuid(uid)[0] except KeyError: # `pwd` throws a KeyError if the user cannot be found. This can # happen e.g. when the process is running in a cgroup that has # different users from the host. username = 'UNKNOWN({})'.format(uid) stdscr.addstr( i + 2, 0, "{0:8} {username:8.8} {2:16} {3:8} {4:8} " "{5:8} {6:9.1f}% {7:9.1f}%".format( *stat, username=username ) ) if i > height - 4: break stdscr.refresh() if exiting: print("Detaching...") return def parse_arguments(): parser = argparse.ArgumentParser( description='show Linux page cache hit/miss statistics including read ' 'and write hit % per processes in a UI like top.' ) parser.add_argument( 'interval', type=int, default=5, nargs='?', help='Interval between probes.' ) args = parser.parse_args() return args args = parse_arguments() curses.wrapper(handle_loop, args) bpfcc-0.12.0/tools/cachetop_example.txt000066400000000000000000000075161357404205000200620ustar00rootroot00000000000000# ./cachetop -h usage: cachetop.py [-h] [interval] show Linux page cache hit/miss statistics including read and write hit % per processes in a UI like top. positional arguments: interval Interval between probes. optional arguments: -h, --help show this help message and exit examples: ./cachetop # run with default option of 5 seconds delay ./cachetop 1 # print every second hit/miss stats # ./cachetop 5 13:01:01 Buffers MB: 76 / Cached MB: 114 / Sort: HITS / Order: ascending PID UID CMD HITS MISSES DIRTIES READ_HIT% WRITE_HIT% 1 root systemd 2 0 0 100.0% 0.0% 680 root vminfo 3 4 2 14.3% 42.9% 567 syslog rs:main Q:Reg 10 4 2 57.1% 21.4% 986 root kworker/u2:2 10 2457 4 0.2% 99.5% 988 root kworker/u2:2 10 9 4 31.6% 36.8% 877 vagrant systemd 18 4 2 72.7% 13.6% 983 root python 148 3 143 3.3% 1.3% 981 root strace 419 3 143 65.4% 0.5% 544 messageb dbus-daemon 455 371 454 0.1% 0.4% 243 root jbd2/dm-0-8 457 371 454 0.4% 0.4% 985 root (mount) 560 2457 4 18.4% 81.4% 987 root systemd-udevd 566 9 4 97.7% 1.2% 988 root systemd-cgroups 569 9 4 97.8% 1.2% 986 root modprobe 578 9 4 97.8% 1.2% 287 root systemd-journal 598 371 454 14.9% 0.3% 985 root mount 692 2457 4 21.8% 78.0% 984 vagrant find 9529 2457 4 79.5% 20.5% Above shows the run of `find /` on a newly booted system. Command used to generate the activity # find / Below shows the hit rate increases as we run find a second time and it gets it its pages from the cache. # ./cachetop.py 13:01:01 Buffers MB: 76 / Cached MB: 115 / Sort: HITS / Order: ascending PID UID CMD HITS MISSES DIRTIES READ_HIT% WRITE_HIT% 544 messageb dbus-daemon 2 2 1 25.0% 50.0% 680 root vminfo 2 2 1 25.0% 50.0% 243 root jbd2/dm-0-8 3 2 1 40.0% 40.0% 1068 root python 5 0 0 100.0% 0.0% 1071 vagrant bash 350 0 0 100.0% 0.0% 1071 vagrant find 12959 0 0 100.0% 0.0% Below shows that the dirty pages increases as a file of 80M is created running # dd if=/dev/urandom of=/tmp/c bs=8192 count=10000 # ./cachetop.py 10 13:01:01 Buffers MB: 77 / Cached MB: 193 / Sort: HITS / Order: ascending PID UID CMD HITS MISSES DIRTIES READ_HIT% WRITE_HIT% 544 messageb dbus-daemon 9 10 7 10.5% 15.8% 680 root vminfo 9 10 7 10.5% 15.8% 1109 root python 22 0 0 100.0% 0.0% 243 root jbd2/dm-0-8 25 10 7 51.4% 8.6% 1070 root kworker/u2:2 85 0 0 100.0% 0.0% 1110 vagrant bash 366 0 0 100.0% 0.0% 1110 vagrant dd 42183 40000 20000 27.0% 24.3% The file copied into page cache was named /tmp/c with a size of 81920000 (81920000/4096) = 20000 bpfcc-0.12.0/tools/capable.py000077500000000000000000000153111357404205000157540ustar00rootroot00000000000000#!/usr/bin/python # @lint-avoid-python-3-compatibility-imports # # capable Trace security capabilitiy checks (cap_capable()). # For Linux, uses BCC, eBPF. Embedded C. # # USAGE: capable [-h] [-v] [-p PID] [-K] [-U] # # Copyright 2016 Netflix, Inc. # Licensed under the Apache License, Version 2.0 (the "License") # # 13-Sep-2016 Brendan Gregg Created this. from __future__ import print_function from os import getpid from functools import partial from bcc import BPF import errno import argparse from time import strftime # arguments examples = """examples: ./capable # trace capability checks ./capable -v # verbose: include non-audit checks ./capable -p 181 # only trace PID 181 ./capable -K # add kernel stacks to trace ./capable -U # add user-space stacks to trace ./capable -x # extra fields: show TID and INSETID columns """ parser = argparse.ArgumentParser( description="Trace security capability checks", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("-v", "--verbose", action="store_true", help="include non-audit checks") parser.add_argument("-p", "--pid", help="trace this PID only") parser.add_argument("-K", "--kernel-stack", action="store_true", help="output kernel stack trace") parser.add_argument("-U", "--user-stack", action="store_true", help="output user stack trace") parser.add_argument("-x", "--extra", action="store_true", help="show extra fields in TID and INSETID columns") args = parser.parse_args() debug = 0 # capabilities to names, generated from (and will need updating): # awk '/^#define.CAP_.*[0-9]$/ { print " " $3 ": \"" $2 "\"," }' \ # include/uapi/linux/capability.h capabilities = { 0: "CAP_CHOWN", 1: "CAP_DAC_OVERRIDE", 2: "CAP_DAC_READ_SEARCH", 3: "CAP_FOWNER", 4: "CAP_FSETID", 5: "CAP_KILL", 6: "CAP_SETGID", 7: "CAP_SETUID", 8: "CAP_SETPCAP", 9: "CAP_LINUX_IMMUTABLE", 10: "CAP_NET_BIND_SERVICE", 11: "CAP_NET_BROADCAST", 12: "CAP_NET_ADMIN", 13: "CAP_NET_RAW", 14: "CAP_IPC_LOCK", 15: "CAP_IPC_OWNER", 16: "CAP_SYS_MODULE", 17: "CAP_SYS_RAWIO", 18: "CAP_SYS_CHROOT", 19: "CAP_SYS_PTRACE", 20: "CAP_SYS_PACCT", 21: "CAP_SYS_ADMIN", 22: "CAP_SYS_BOOT", 23: "CAP_SYS_NICE", 24: "CAP_SYS_RESOURCE", 25: "CAP_SYS_TIME", 26: "CAP_SYS_TTY_CONFIG", 27: "CAP_MKNOD", 28: "CAP_LEASE", 29: "CAP_AUDIT_WRITE", 30: "CAP_AUDIT_CONTROL", 31: "CAP_SETFCAP", 32: "CAP_MAC_OVERRIDE", 33: "CAP_MAC_ADMIN", 34: "CAP_SYSLOG", 35: "CAP_WAKE_ALARM", 36: "CAP_BLOCK_SUSPEND", 37: "CAP_AUDIT_READ", } class Enum(set): def __getattr__(self, name): if name in self: return name raise AttributeError # Stack trace types StackType = Enum(("Kernel", "User",)) # define BPF program bpf_text = """ #include #include #include struct data_t { u32 tgid; u32 pid; u32 uid; int cap; int audit; int insetid; char comm[TASK_COMM_LEN]; #ifdef KERNEL_STACKS int kernel_stack_id; #endif #ifdef USER_STACKS int user_stack_id; #endif }; BPF_PERF_OUTPUT(events); #if defined(USER_STACKS) || defined(KERNEL_STACKS) BPF_STACK_TRACE(stacks, 2048); #endif int kprobe__cap_capable(struct pt_regs *ctx, const struct cred *cred, struct user_namespace *targ_ns, int cap, int cap_opt) { u64 __pid_tgid = bpf_get_current_pid_tgid(); u32 tgid = __pid_tgid >> 32; u32 pid = __pid_tgid; int audit; int insetid; #ifdef CAP_OPT_NONE audit = (cap_opt & 0b10) == 0; insetid = (cap_opt & 0b100) != 0; #else audit = cap_opt; insetid = -1; #endif FILTER1 FILTER2 FILTER3 u32 uid = bpf_get_current_uid_gid(); struct data_t data = {.tgid = tgid, .pid = pid, .uid = uid, .cap = cap, .audit = audit, .insetid = insetid}; #ifdef KERNEL_STACKS data.kernel_stack_id = stacks.get_stackid(ctx, 0); #endif #ifdef USER_STACKS data.user_stack_id = stacks.get_stackid(ctx, BPF_F_USER_STACK); #endif bpf_get_current_comm(&data.comm, sizeof(data.comm)); events.perf_submit(ctx, &data, sizeof(data)); return 0; }; """ if args.pid: bpf_text = bpf_text.replace('FILTER1', 'if (pid != %s) { return 0; }' % args.pid) if not args.verbose: bpf_text = bpf_text.replace('FILTER2', 'if (audit == 0) { return 0; }') if args.kernel_stack: bpf_text = "#define KERNEL_STACKS\n" + bpf_text if args.user_stack: bpf_text = "#define USER_STACKS\n" + bpf_text bpf_text = bpf_text.replace('FILTER1', '') bpf_text = bpf_text.replace('FILTER2', '') bpf_text = bpf_text.replace('FILTER3', 'if (pid == %s) { return 0; }' % getpid()) if debug: print(bpf_text) # initialize BPF b = BPF(text=bpf_text) # header if args.extra: print("%-9s %-6s %-6s %-6s %-16s %-4s %-20s %-6s %s" % ( "TIME", "UID", "PID", "TID", "COMM", "CAP", "NAME", "AUDIT", "INSETID")) else: print("%-9s %-6s %-6s %-16s %-4s %-20s %-6s" % ( "TIME", "UID", "PID", "COMM", "CAP", "NAME", "AUDIT")) def stack_id_err(stack_id): # -EFAULT in get_stackid normally means the stack-trace is not availible, # Such as getting kernel stack trace in userspace code return (stack_id < 0) and (stack_id != -errno.EFAULT) def print_stack(bpf, stack_id, stack_type, tgid): if stack_id_err(stack_id): print(" [Missed %s Stack]" % stack_type) return stack = list(bpf.get_table("stacks").walk(stack_id)) for addr in stack: print(" ", end="") print("%s" % (bpf.sym(addr, tgid, show_module=True, show_offset=True))) # process event def print_event(bpf, cpu, data, size): event = b["events"].event(data) if event.cap in capabilities: name = capabilities[event.cap] else: name = "?" if args.extra: print("%-9s %-6d %-6d %-6d %-16s %-4d %-20s %-6d %s" % (strftime("%H:%M:%S"), event.uid, event.pid, event.tgid, event.comm.decode('utf-8', 'replace'), event.cap, name, event.audit, str(event.insetid) if event.insetid != -1 else "N/A")) else: print("%-9s %-6d %-6d %-16s %-4d %-20s %-6d" % (strftime("%H:%M:%S"), event.uid, event.pid, event.comm.decode('utf-8', 'replace'), event.cap, name, event.audit)) if args.kernel_stack: print_stack(bpf, event.kernel_stack_id, StackType.Kernel, -1) if args.user_stack: print_stack(bpf, event.user_stack_id, StackType.User, event.tgid) # loop with callback to print_event callback = partial(print_event, b) b["events"].open_perf_buffer(callback) while 1: try: b.perf_buffer_poll() except KeyboardInterrupt: exit() bpfcc-0.12.0/tools/capable_example.txt000066400000000000000000000125471357404205000176630ustar00rootroot00000000000000Demonstrations of capable, the Linux eBPF/bcc version. capable traces calls to the kernel cap_capable() function, which does security capability checks, and prints details for each call. For example: # ./capable.py 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 Checks where AUDIT is 0 are ignored by default, which can be changed with -v but is more verbose. We can show the TID and INSETID columns with -x. Since only a recent kernel version >= 5.1 reports the INSETID bit to cap_capable(), the fallback value "N/A" will be displayed on older kernels. # ./capable.py -x TIME UID PID TID COMM CAP NAME AUDIT INSETID 08:22:36 0 12869 12869 chown 0 CAP_CHOWN 1 0 08:22:36 0 12869 12869 chown 0 CAP_CHOWN 1 0 08:22:36 0 12869 12869 chown 0 CAP_CHOWN 1 0 08:23:02 0 13036 13036 setuidgid 6 CAP_SETGID 1 0 08:23:02 0 13036 13036 setuidgid 6 CAP_SETGID 1 0 08:23:02 0 13036 13036 setuidgid 7 CAP_SETUID 1 1 08:23:13 0 13085 13085 chmod 3 CAP_FOWNER 1 0 08:23:13 0 13085 13085 chmod 4 CAP_FSETID 1 0 08:23:13 0 13085 13085 chmod 3 CAP_FOWNER 1 0 08:23:13 0 13085 13085 chmod 4 CAP_FSETID 1 0 08:23:13 0 13085 13085 chmod 4 CAP_FSETID 1 0 08:24:27 0 13522 13522 ping 13 CAP_NET_RAW 1 0 [...] 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. It is possible to include a kernel stack trace to the capable events by passing -K to the command: # ./capable.py -K TIME UID PID COMM CAP NAME AUDIT 15:32:21 1000 10708 fetchmail 7 CAP_SETUID 1 cap_capable+0x1 [kernel] ns_capable_common+0x7a [kernel] __sys_setresuid+0xc8 [kernel] do_syscall_64+0x56 [kernel] entry_SYSCALL_64_after_hwframe+0x49 [kernel] 15:32:21 1000 30047 procmail 6 CAP_SETGID 1 cap_capable+0x1 [kernel] ns_capable_common+0x7a [kernel] may_setgroups+0x2f [kernel] __x64_sys_setgroups+0x18 [kernel] do_syscall_64+0x56 [kernel] entry_SYSCALL_64_after_hwframe+0x49 [kernel] Similarly, it is possible to include user-space stack with -U (or they can be used both at the same time to include user and kernel stack). USAGE: # ./capable.py -h usage: capable.py [-h] [-v] [-p PID] [-K] [-U] Trace security capability checks optional arguments: -h, --help show this help message and exit -v, --verbose include non-audit checks -p PID, --pid PID trace this PID only -K, --kernel-stack output kernel stack trace -U, --user-stack output user stack trace examples: ./capable # trace capability checks ./capable -v # verbose: include non-audit checks ./capable -p 181 # only trace PID 181 ./capable -K # add kernel stacks to trace ./capable -U # add user-space stacks to trace bpfcc-0.12.0/tools/cobjnew.sh000077500000000000000000000000741357404205000157760ustar00rootroot00000000000000#!/bin/bash lib=$(dirname $0)/lib $lib/uobjnew.py -l c "$@" bpfcc-0.12.0/tools/cobjnew_example.txt000077700000000000000000000000001357404205000244002lib/uobjnew_example.txtustar00rootroot00000000000000bpfcc-0.12.0/tools/cpudist.py000077500000000000000000000121271357404205000160420ustar00rootroot00000000000000#!/usr/bin/python # @lint-avoid-python-3-compatibility-imports # # cpudist Summarize on- and off-CPU time per task as a histogram. # # USAGE: cpudist [-h] [-O] [-T] [-m] [-P] [-L] [-p PID] [interval] [count] # # This measures the time a task spends on or off the CPU, and shows this time # as a histogram, optionally per-process. # # Copyright 2016 Sasha Goldshtein # Licensed under the Apache License, Version 2.0 (the "License") from __future__ import print_function from bcc import BPF from time import sleep, strftime import argparse examples = """examples: cpudist # summarize on-CPU time as a histogram cpudist -O # summarize off-CPU time as a histogram cpudist 1 10 # print 1 second summaries, 10 times cpudist -mT 1 # 1s summaries, milliseconds, and timestamps cpudist -P # show each PID separately cpudist -p 185 # trace PID 185 only """ parser = argparse.ArgumentParser( description="Summarize on-CPU time per task as a histogram.", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("-O", "--offcpu", action="store_true", help="measure off-CPU time") parser.add_argument("-T", "--timestamp", action="store_true", help="include timestamp on output") parser.add_argument("-m", "--milliseconds", action="store_true", help="millisecond histogram") parser.add_argument("-P", "--pids", action="store_true", help="print a histogram per process ID") parser.add_argument("-L", "--tids", action="store_true", help="print a histogram per thread ID") parser.add_argument("-p", "--pid", help="trace this PID only") parser.add_argument("interval", nargs="?", default=99999999, help="output interval, in seconds") parser.add_argument("count", nargs="?", default=99999999, help="number of outputs") parser.add_argument("--ebpf", action="store_true", help=argparse.SUPPRESS) args = parser.parse_args() countdown = int(args.count) debug = 0 bpf_text = """#include #include """ if not args.offcpu: bpf_text += "#define ONCPU\n" bpf_text += """ typedef struct pid_key { u64 id; u64 slot; } pid_key_t; BPF_HASH(start, u32, u64, MAX_PID); STORAGE static inline void store_start(u32 tgid, u32 pid, u64 ts) { if (FILTER) return; start.update(&pid, &ts); } static inline void update_hist(u32 tgid, u32 pid, u64 ts) { if (FILTER) return; u64 *tsp = start.lookup(&pid); if (tsp == 0) return; if (ts < *tsp) { // Probably a clock issue where the recorded on-CPU event had a // timestamp later than the recorded off-CPU event, or vice versa. return; } u64 delta = ts - *tsp; FACTOR STORE } int sched_switch(struct pt_regs *ctx, struct task_struct *prev) { u64 ts = bpf_ktime_get_ns(); u64 pid_tgid = bpf_get_current_pid_tgid(); u32 tgid = pid_tgid >> 32, pid = pid_tgid; #ifdef ONCPU if (prev->state == TASK_RUNNING) { #else if (1) { #endif u32 prev_pid = prev->pid; u32 prev_tgid = prev->tgid; #ifdef ONCPU update_hist(prev_tgid, prev_pid, ts); #else store_start(prev_tgid, prev_pid, ts); #endif } BAIL: #ifdef ONCPU store_start(tgid, pid, ts); #else update_hist(tgid, pid, ts); #endif return 0; } """ if args.pid: bpf_text = bpf_text.replace('FILTER', 'tgid != %s' % args.pid) else: bpf_text = bpf_text.replace('FILTER', '0') if args.milliseconds: bpf_text = bpf_text.replace('FACTOR', 'delta /= 1000000;') label = "msecs" else: bpf_text = bpf_text.replace('FACTOR', 'delta /= 1000;') label = "usecs" if args.pids or args.tids: section = "pid" pid = "tgid" if args.tids: pid = "pid" section = "tid" bpf_text = bpf_text.replace('STORAGE', 'BPF_HISTOGRAM(dist, pid_key_t, MAX_PID);') bpf_text = bpf_text.replace('STORE', 'pid_key_t key = {.id = ' + pid + ', .slot = bpf_log2l(delta)}; ' + 'dist.increment(key);') else: section = "" bpf_text = bpf_text.replace('STORAGE', 'BPF_HISTOGRAM(dist);') bpf_text = bpf_text.replace('STORE', 'dist.increment(bpf_log2l(delta));') if debug or args.ebpf: print(bpf_text) if args.ebpf: exit() max_pid = int(open("/proc/sys/kernel/pid_max").read()) b = BPF(text=bpf_text, cflags=["-DMAX_PID=%d" % max_pid]) b.attach_kprobe(event="finish_task_switch", fn_name="sched_switch") print("Tracing %s-CPU time... Hit Ctrl-C to end." % ("off" if args.offcpu else "on")) exiting = 0 if args.interval else 1 dist = b.get_table("dist") while (1): try: sleep(int(args.interval)) except KeyboardInterrupt: exiting = 1 print() if args.timestamp: print("%-8s\n" % strftime("%H:%M:%S"), end="") def pid_to_comm(pid): try: comm = open("/proc/%d/comm" % pid, "r").read() return "%d %s" % (pid, comm) except IOError: return str(pid) dist.print_log2_hist(label, section, section_print_fn=pid_to_comm) dist.clear() countdown -= 1 if exiting or countdown == 0: exit() bpfcc-0.12.0/tools/cpudist_example.txt000066400000000000000000000402341357404205000177410ustar00rootroot00000000000000Demonstrations of cpudist. This program summarizes task on-CPU time as a histogram, showing how long tasks spent on the CPU before being descheduled. This provides valuable information that can indicate oversubscription (too many tasks for too few processors), overhead due to excessive context switching (e.g. a common shared lock for multiple threads), uneven workload distribution, too-granular tasks, and more. Alternatively, the same options are available for summarizing task off-CPU time, which helps understand how often threads are being descheduled and how long they spend waiting for I/O, locks, timers, and other causes of suspension. # ./cpudist.py Tracing on-CPU time... Hit Ctrl-C to end. ^C usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 1 | | 4 -> 7 : 1 | | 8 -> 15 : 13 |** | 16 -> 31 : 187 |****************************************| 32 -> 63 : 89 |******************* | 64 -> 127 : 26 |***** | 128 -> 255 : 0 | | 256 -> 511 : 1 | | This is from a mostly idle system. Tasks wake up occasionally and run for only a few dozen microseconds, and then get descheduled. Here's some output from a system that is heavily loaded by threads that perform computation but also compete for a lock: # ./cpudist.py Tracing on-CPU time... Hit Ctrl-C to end. ^C usecs : count distribution 0 -> 1 : 51 |* | 2 -> 3 : 395 |*********** | 4 -> 7 : 259 |******* | 8 -> 15 : 61 |* | 16 -> 31 : 75 |** | 32 -> 63 : 31 | | 64 -> 127 : 7 | | 128 -> 255 : 5 | | 256 -> 511 : 3 | | 512 -> 1023 : 5 | | 1024 -> 2047 : 6 | | 2048 -> 4095 : 4 | | 4096 -> 8191 : 1361 |****************************************| 8192 -> 16383 : 523 |*************** | 16384 -> 32767 : 3 | | A bimodal distribution is now clearly visible. Most of the time, tasks were able to run for 4-16ms before being descheduled (this is likely the quantum length). Occasionally, tasks had to be descheduled a lot earlier -- possibly because they competed for a shared lock. If necessary, you can restrict the output to include only threads from a particular process -- this helps reduce noise: # ./cpudist.py -p $(pidof parprimes) Tracing on-CPU time... Hit Ctrl-C to end. ^C usecs : count distribution 0 -> 1 : 3 | | 2 -> 3 : 17 | | 4 -> 7 : 39 | | 8 -> 15 : 52 |* | 16 -> 31 : 43 | | 32 -> 63 : 12 | | 64 -> 127 : 13 | | 128 -> 255 : 0 | | 256 -> 511 : 1 | | 512 -> 1023 : 11 | | 1024 -> 2047 : 15 | | 2048 -> 4095 : 41 | | 4096 -> 8191 : 1134 |************************ | 8192 -> 16383 : 1883 |****************************************| 16384 -> 32767 : 65 |* | You can also ask for output at predefined intervals, and include timestamps for easier interpretation. While we're at it, the -P switch will print a histogram separately for each process: # ./cpudist.py -TP 5 3 Tracing on-CPU time... Hit Ctrl-C to end. 03:46:51 pid = 0 usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 1 |** | 4 -> 7 : 17 |********************************** | 8 -> 15 : 11 |********************** | 16 -> 31 : 20 |****************************************| 32 -> 63 : 15 |****************************** | 64 -> 127 : 9 |****************** | 128 -> 255 : 6 |************ | 256 -> 511 : 0 | | 512 -> 1023 : 0 | | 1024 -> 2047 : 0 | | 2048 -> 4095 : 1 |** | pid = 5068 usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 1 |************* | 4 -> 7 : 3 |****************************************| 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 1 |************* | 03:46:56 pid = 0 usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 1 |** | 4 -> 7 : 19 |****************************************| 8 -> 15 : 11 |*********************** | 16 -> 31 : 9 |****************** | 32 -> 63 : 3 |****** | 64 -> 127 : 1 |** | 128 -> 255 : 3 |****** | 256 -> 511 : 0 | | 512 -> 1023 : 1 |** | pid = 5068 usecs : count distribution 0 -> 1 : 1 |******************** | 2 -> 3 : 0 | | 4 -> 7 : 2 |****************************************| 03:47:01 pid = 0 usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 12 |******************************** | 8 -> 15 : 15 |****************************************| 16 -> 31 : 15 |****************************************| 32 -> 63 : 0 | | 64 -> 127 : 3 |******** | 128 -> 255 : 1 |** | 256 -> 511 : 0 | | 512 -> 1023 : 1 |** | pid = 5068 usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 1 |****** | 4 -> 7 : 6 |****************************************| 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 2 |************* | This histogram was obtained while executing `dd if=/dev/zero of=/dev/null` with fairly large block sizes. You could also ask for an off-CPU report using the -O switch. Here's a histogram of task block times while the system is heavily loaded: # ./cpudist -O -p $(parprimes) Tracing off-CPU time... Hit Ctrl-C to end. ^C usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 1 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 3 | | 64 -> 127 : 1 | | 128 -> 255 : 1 | | 256 -> 511 : 0 | | 512 -> 1023 : 2 | | 1024 -> 2047 : 4 | | 2048 -> 4095 : 3 | | 4096 -> 8191 : 70 |*** | 8192 -> 16383 : 867 |****************************************| 16384 -> 32767 : 141 |****** | 32768 -> 65535 : 8 | | 65536 -> 131071 : 0 | | 131072 -> 262143 : 1 | | 262144 -> 524287 : 2 | | 524288 -> 1048575 : 3 | | As you can see, threads are switching out for relatively long intervals, even though we know the workload doesn't have any significant blocking. This can be a result of over-subscription -- too many threads contending over too few CPUs. Indeed, there are four available CPUs and more than four runnable threads: # nproc 4 # cat /proc/loadavg 0.04 0.11 0.06 9/147 7494 (This shows we have 9 threads runnable out of 147 total. This is more than 4, the number of available CPUs.) Finally, let's ask for a per-thread report and values in milliseconds instead of microseconds: # ./cpudist.py -p $(pidof parprimes) -mL Tracing on-CPU time... Hit Ctrl-C to end. tid = 5092 msecs : count distribution 0 -> 1 : 3 | | 2 -> 3 : 4 | | 4 -> 7 : 4 | | 8 -> 15 : 535 |****************************************| 16 -> 31 : 14 |* | tid = 5093 msecs : count distribution 0 -> 1 : 8 | | 2 -> 3 : 6 | | 4 -> 7 : 4 | | 8 -> 15 : 534 |****************************************| 16 -> 31 : 12 | | tid = 5094 msecs : count distribution 0 -> 1 : 38 |*** | 2 -> 3 : 5 | | 4 -> 7 : 5 | | 8 -> 15 : 476 |****************************************| 16 -> 31 : 25 |** | tid = 5095 msecs : count distribution 0 -> 1 : 31 |** | 2 -> 3 : 6 | | 4 -> 7 : 10 | | 8 -> 15 : 478 |****************************************| 16 -> 31 : 20 |* | tid = 5096 msecs : count distribution 0 -> 1 : 21 |* | 2 -> 3 : 5 | | 4 -> 7 : 4 | | 8 -> 15 : 523 |****************************************| 16 -> 31 : 16 |* | tid = 5097 msecs : count distribution 0 -> 1 : 11 | | 2 -> 3 : 7 | | 4 -> 7 : 7 | | 8 -> 15 : 502 |****************************************| 16 -> 31 : 23 |* | tid = 5098 msecs : count distribution 0 -> 1 : 21 |* | 2 -> 3 : 5 | | 4 -> 7 : 3 | | 8 -> 15 : 494 |****************************************| 16 -> 31 : 28 |** | tid = 5099 msecs : count distribution 0 -> 1 : 15 |* | 2 -> 3 : 4 | | 4 -> 7 : 6 | | 8 -> 15 : 521 |****************************************| 16 -> 31 : 12 | | It looks like all threads are more-or-less equally busy, and are typically switched out after running for 8-15 milliseconds (again, this is the typical quantum length). USAGE message: # ./cpudist.py -h usage: cpudist.py [-h] [-O] [-T] [-m] [-P] [-L] [-p PID] [interval] [count] Summarize on-CPU time per task as a histogram. positional arguments: interval output interval, in seconds count number of outputs optional arguments: -h, --help show this help message and exit -O, --offcpu measure off-CPU time -T, --timestamp include timestamp on output -m, --milliseconds millisecond histogram -P, --pids print a histogram per process ID -L, --tids print a histogram per thread ID -p PID, --pid PID trace this PID only examples: cpudist # summarize on-CPU time as a histogram cpudist -O # summarize off-CPU time as a histogram cpudist 1 10 # print 1 second summaries, 10 times cpudist -mT 1 # 1s summaries, milliseconds, and timestamps cpudist -P # show each PID separately cpudist -p 185 # trace PID 185 only bpfcc-0.12.0/tools/cpuunclaimed.py000077500000000000000000000344031357404205000170410ustar00rootroot00000000000000#!/usr/bin/python # @lint-avoid-python-3-compatibility-imports # # cpuunclaimed Sample CPU run queues and calculate unclaimed idle CPU. # For Linux, uses BCC, eBPF. # # This samples the length of the run queues and determine when there are idle # CPUs, yet queued threads waiting their turn. Report the amount of idle # (yet unclaimed by waiting threads) CPU as a system-wide percentage. # # This situation can happen for a number of reasons: # # - An application has been bound to some, but not all, CPUs, and has runnable # threads that cannot migrate to other CPUs due to this configuration. # - CPU affinity: an optimization that leaves threads on CPUs where the CPU # caches are warm, even if this means short periods of waiting while other # CPUs are idle. The wait period is tunale (see sysctl, kernel.sched*). # - Scheduler bugs. # # An unclaimed idle of < 1% is likely to be CPU affinity, and not usually a # cause for concern. By leaving the CPU idle, overall throughput of the system # may be improved. This tool is best for identifying larger issues, > 2%, due # to the coarseness of its 99 Hertz samples. # # This is an experimental tool that currently works by use of sampling to # keep overheads low. Tool assumptions: # # - CPU samples consistently fire around the same offset. There will sometimes # be a lag as a sample is delayed by higher-priority interrupts, but it is # assumed the subsequent samples will catch up to the expected offsets (as # is seen in practice). You can use -J to inspect sample offsets. Some # systems can power down CPUs when idle, and when they wake up again they # may begin firing at a skewed offset: this tool will detect the skew, print # an error, and exit. # - All CPUs are online (see ncpu). # # If this identifies unclaimed CPU, you can double check it by dumping raw # samples (-j), as well as using other tracing tools to instrument scheduler # events (although this latter approach has much higher overhead). # # This tool passes all sampled events to user space for post processing. # I originally wrote this to do the calculations entirerly in kernel context, # and only pass a summary. That involves a number of challenges, and the # overhead savings may not outweigh the caveats. You can see my WIP here: # https://gist.github.com/brendangregg/731cf2ce54bf1f9a19d4ccd397625ad9 # # USAGE: cpuunclaimed [-h] [-j] [-J] [-T] [interval] [count] # # If you see "Lost 1881 samples" warnings, try increasing wakeup_hz. # # REQUIRES: Linux 4.9+ (BPF_PROG_TYPE_PERF_EVENT support). Under tools/old is # a version of this tool that may work on Linux 4.6 - 4.8. # # Copyright 2016 Netflix, Inc. # Licensed under the Apache License, Version 2.0 (the "License") # # 20-Dec-2016 Brendan Gregg Created this. from __future__ import print_function from bcc import BPF, PerfType, PerfSWConfig from time import sleep, strftime import argparse import multiprocessing from os import getpid, system, open, close, dup, unlink, O_WRONLY from tempfile import NamedTemporaryFile # arguments examples = """examples: ./cpuunclaimed # sample and calculate unclaimed idle CPUs, # output every 1 second (default) ./cpuunclaimed 5 10 # print 5 second summaries, 10 times ./cpuunclaimed -T 1 # 1s summaries and timestamps ./cpuunclaimed -j # raw dump of all samples (verbose), CSV """ parser = argparse.ArgumentParser( description="Sample CPU run queues and calculate unclaimed idle CPU", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("-j", "--csv", action="store_true", help="print sample summaries (verbose) as comma-separated values") parser.add_argument("-J", "--fullcsv", action="store_true", help="print sample summaries with extra fields: CPU sample offsets") parser.add_argument("-T", "--timestamp", action="store_true", help="include timestamp on output") parser.add_argument("interval", nargs="?", default=-1, help="output interval, in seconds") parser.add_argument("count", nargs="?", default=99999999, help="number of outputs") parser.add_argument("--ebpf", action="store_true", help=argparse.SUPPRESS) args = parser.parse_args() countdown = int(args.count) frequency = 99 dobind = 1 wakeup_hz = 10 # frequency to read buffers wakeup_s = float(1) / wakeup_hz ncpu = multiprocessing.cpu_count() # assume all are online debug = 0 # Linux 4.15 introduced a new field runnable_weight # in linux_src:kernel/sched/sched.h as # struct cfs_rq { # struct load_weight load; # unsigned long runnable_weight; # unsigned int nr_running, h_nr_running; # ...... # } # and this tool requires to access nr_running to get # runqueue len information. # # The commit which introduces cfs_rq->runnable_weight # field also introduces the field sched_entity->runnable_weight # where sched_entity is defined in linux_src:include/linux/sched.h. # # To cope with pre-4.15 and 4.15/post-4.15 releases, # we run a simple BPF program to detect whether # field sched_entity->runnable_weight exists. The existence of # this field should infer the existence of cfs_rq->runnable_weight. # # This will need maintenance as the relationship between these # two fields may change in the future. # def check_runnable_weight_field(): # Define the bpf program for checking purpose bpf_check_text = """ #include unsigned long dummy(struct sched_entity *entity) { return entity->runnable_weight; } """ # Get a temporary file name tmp_file = NamedTemporaryFile(delete=False) tmp_file.close(); # Duplicate and close stderr (fd = 2) old_stderr = dup(2) close(2) # Open a new file, should get fd number 2 # This will avoid printing llvm errors on the screen fd = open(tmp_file.name, O_WRONLY) try: t = BPF(text=bpf_check_text) success_compile = True except: success_compile = False # Release the fd 2, and next dup should restore old stderr close(fd) dup(old_stderr) close(old_stderr) # remove the temporary file and return unlink(tmp_file.name) return success_compile # process arguments if args.fullcsv: args.csv = True if args.csv: interval = 0.2 if args.interval != -1 and (args.fullcsv or args.csv): print("ERROR: cannot use interval with either -j or -J. Exiting.") exit() if args.interval == -1: args.interval = "1" interval = float(args.interval) # define BPF program bpf_text = """ #include #include #include struct data_t { u64 ts; u64 cpu; u64 len; }; BPF_PERF_OUTPUT(events); // Declare enough of cfs_rq to find nr_running, since we can't #import the // header. This will need maintenance. It is from kernel/sched/sched.h: struct cfs_rq_partial { struct load_weight load; RUNNABLE_WEIGHT_FIELD unsigned int nr_running, h_nr_running; }; int do_perf_event(struct bpf_perf_event_data *ctx) { int cpu = bpf_get_smp_processor_id(); u64 now = bpf_ktime_get_ns(); /* * Fetch the run queue length from task->se.cfs_rq->nr_running. This is an * unstable interface and may need maintenance. Perhaps a future version * of BPF will support task_rq(p) or something similar as a more reliable * interface. */ unsigned int len = 0; struct task_struct *task = NULL; struct cfs_rq_partial *my_q = NULL; task = (struct task_struct *)bpf_get_current_task(); my_q = (struct cfs_rq_partial *)task->se.cfs_rq; len = my_q->nr_running; struct data_t data = {.ts = now, .cpu = cpu, .len = len}; events.perf_submit(ctx, &data, sizeof(data)); return 0; } """ if check_runnable_weight_field(): bpf_text = bpf_text.replace('RUNNABLE_WEIGHT_FIELD', 'unsigned long runnable_weight;') else: bpf_text = bpf_text.replace('RUNNABLE_WEIGHT_FIELD', '') # code substitutions if debug or args.ebpf: print(bpf_text) if args.ebpf: exit() # initialize BPF & perf_events b = BPF(text=bpf_text) # TODO: check for HW counters first and use if more accurate b.attach_perf_event(ev_type=PerfType.SOFTWARE, ev_config=PerfSWConfig.TASK_CLOCK, fn_name="do_perf_event", sample_period=0, sample_freq=frequency) if args.csv: if args.timestamp: print("TIME", end=",") print("TIMESTAMP_ns", end=",") print(",".join("CPU" + str(c) for c in range(ncpu)), end="") if args.fullcsv: print(",", end="") print(",".join("OFFSET_ns_CPU" + str(c) for c in range(ncpu)), end="") print() else: print(("Sampling run queues... Output every %s seconds. " + "Hit Ctrl-C to end.") % args.interval) samples = {} group = {} last = 0 # process event def print_event(cpu, data, size): event = b["events"].event(data) samples[event.ts] = {} samples[event.ts]['cpu'] = event.cpu samples[event.ts]['len'] = event.len exiting = 0 if args.interval else 1 slept = float(0) # Choose the elapsed time from one sample group to the next that identifies a # new sample group (a group being a set of samples from all CPUs). The # earliest timestamp is compared in each group. This trigger is also used # for sanity testing, if a group's samples exceed half this value. trigger = int(0.8 * (1000000000 / frequency)) # read events b["events"].open_perf_buffer(print_event, page_cnt=64) while 1: # allow some buffering by calling sleep(), to reduce the context switch # rate and lower overhead. try: if not exiting: sleep(wakeup_s) except KeyboardInterrupt: exiting = 1 b.perf_buffer_poll() slept += wakeup_s if slept < 0.999 * interval: # floating point workaround continue slept = 0 positive = 0 # number of samples where an idle CPU could have run work running = 0 idle = 0 if debug >= 2: print("DEBUG: begin samples loop, count %d" % len(samples)) for e in sorted(samples): if debug >= 2: print("DEBUG: ts %d cpu %d len %d delta %d trig %d" % (e, samples[e]['cpu'], samples[e]['len'], e - last, e - last > trigger)) # look for time jumps to identify a new sample group if e - last > trigger: # first first group timestamp, and sanity test g_time = 0 g_max = 0 for ge in sorted(group): if g_time == 0: g_time = ge g_max = ge # process previous sample group if args.csv: lens = [0] * ncpu offs = [0] * ncpu for ge in sorted(group): lens[samples[ge]['cpu']] = samples[ge]['len'] if args.fullcsv: offs[samples[ge]['cpu']] = ge - g_time if g_time > 0: # else first sample if args.timestamp: print("%-8s" % strftime("%H:%M:%S"), end=",") print("%d" % g_time, end=",") print(",".join(str(lens[c]) for c in range(ncpu)), end="") if args.fullcsv: print(",", end="") print(",".join(str(offs[c]) for c in range(ncpu))) else: print() else: # calculate stats g_running = 0 g_queued = 0 for ge in group: if samples[ge]['len'] > 0: g_running += 1 if samples[ge]['len'] > 1: g_queued += samples[ge]['len'] - 1 g_idle = ncpu - g_running # calculate the number of threads that could have run as the # minimum of idle and queued if g_idle > 0 and g_queued > 0: if g_queued > g_idle: i = g_idle else: i = g_queued positive += i running += g_running idle += g_idle # now sanity test, after -J output g_range = g_max - g_time if g_range > trigger / 2: # if a sample group exceeds half the interval, we can no # longer draw conclusions about some CPUs idle while others # have queued work. Error and exit. This can happen when # CPUs power down, then start again on different offsets. # TODO: Since this is a sampling tool, an error margin should # be anticipated, so an improvement may be to bump a counter # instead of exiting, and only exit if this counter shows # a skewed sample rate of over, say, 1%. Such an approach # would allow a small rate of outliers (sampling error), # and, we could tighten the trigger to be, say, trigger / 5. # In the case of a power down, if it's detectable, perhaps # the tool could reinitialize the timers (although exiting # is simple and works). print(("ERROR: CPU samples arrived at skewed offsets " + "(CPUs may have powered down when idle), " + "spanning %d ns (expected < %d ns). Debug with -J, " + "and see the man page. As output may begin to be " + "unreliable, exiting.") % (g_range, trigger / 2)) exit() # these are done, remove for ge in sorted(group): del samples[ge] # begin next group group = {} last = e # stash this timestamp in a sample group dict group[e] = 1 if not args.csv: total = running + idle unclaimed = util = 0 if debug: print("DEBUG: hit %d running %d idle %d total %d buffered %d" % ( positive, running, idle, total, len(samples))) if args.timestamp: print("%-8s " % strftime("%H:%M:%S"), end="") # output if total: unclaimed = float(positive) / total util = float(running) / total print("%%CPU %6.2f%%, unclaimed idle %0.2f%%" % (100 * util, 100 * unclaimed)) countdown -= 1 if exiting or countdown == 0: exit() bpfcc-0.12.0/tools/cpuunclaimed_example.txt000066400000000000000000000363201357404205000207400ustar00rootroot00000000000000Demonstrations of cpuunclaimed, the Linux eBPF/bcc version. This tool samples the length of the CPU run queues and determine when there are idle CPUs, yet queued threads waiting their turn. It reports the amount of idle (yet unclaimed by waiting threads) CPU as a system-wide percentage. For example: # ./cpuunclaimed.py Sampling run queues... Output every 1 seconds. Hit Ctrl-C to end. %CPU 83.00%, unclaimed idle 0.12% %CPU 87.25%, unclaimed idle 0.38% %CPU 85.00%, unclaimed idle 0.25% %CPU 85.00%, unclaimed idle 0.25% %CPU 80.88%, unclaimed idle 0.00% %CPU 82.25%, unclaimed idle 0.00% %CPU 83.50%, unclaimed idle 0.12% %CPU 81.50%, unclaimed idle 0.00% %CPU 81.38%, unclaimed idle 0.00% [...] This shows a system running at over 80% CPU utilization, and with less than 0.5% unclaimed idle CPUs. Unclaimed idle CPUs can happen for a number of reasons: - An application has been bound to some, but not all, CPUs, and has runnable threads that cannot migrate to other CPUs due to this configuration. - CPU affinity: an optimization that leaves threads on CPUs where the CPU caches are warm, even if this means short periods of waiting while other CPUs are idle. The wait period is tunale (see sysctl, kernel.sched*). - Scheduler bugs. An unclaimed idle of < 1% is likely to be CPU affinity, and not usually a cause for concern. By leaving the CPU idle, overall throughput of the system may be improved. This tool is best for identifying larger issues, > 2%, due to the coarseness of its 99 Hertz samples. This is an 8 CPU system, with an 8 CPU-bound threaded application running that has been bound to one CPU (via taskset): # ./cpuunclaimed.py Sampling run queues... Output every 1 seconds. Hit Ctrl-C to end. %CPU 12.63%, unclaimed idle 86.36% %CPU 12.50%, unclaimed idle 87.50% %CPU 12.63%, unclaimed idle 87.37% %CPU 12.75%, unclaimed idle 87.25% %CPU 12.50%, unclaimed idle 87.50% %CPU 12.63%, unclaimed idle 87.37% %CPU 12.50%, unclaimed idle 87.50% %CPU 12.50%, unclaimed idle 87.50% [...] It shows that 7 of the 8 CPUs (87.5%) are idle at the same time there are queued threads waiting to run on CPU. This is an artificial situation caused by binding threads to the same CPU, to demonstrate how the tool works. This is an 8 CPU system running a Linux kernel build with "make -j8", and -T to print timestamps: # ./cpuunclaimed.py -T Sampling run queues... Output every 1 seconds. Hit Ctrl-C to end. 22:25:55 %CPU 98.88%, unclaimed idle 0.12% 22:25:56 %CPU 99.75%, unclaimed idle 0.25% 22:25:57 %CPU 99.50%, unclaimed idle 0.50% 22:25:58 %CPU 99.25%, unclaimed idle 0.75% 22:25:59 %CPU 99.75%, unclaimed idle 0.25% 22:26:00 %CPU 99.50%, unclaimed idle 0.50% 22:26:01 %CPU 99.25%, unclaimed idle 0.75% 22:26:02 %CPU 99.25%, unclaimed idle 0.75% 22:26:03 %CPU 99.01%, unclaimed idle 0.87% 22:26:04 %CPU 99.88%, unclaimed idle 0.12% 22:26:05 %CPU 99.38%, unclaimed idle 0.62% There's now a consistent, yet small, amount of unclaimed idle CPU. This is expected to be deliberate: CPU affinity, as mentioned earlier. The -j option will print raw samples: around one hundred lines of output every second. For the same system with a Linux kernel build of "make -j8": # ./cpuunclaimed.py -j TIMESTAMP_ns,CPU0,CPU1,CPU2,CPU3,CPU4,CPU5,CPU6,CPU7 514606928954752,1,1,1,1,1,1,1,1 514606939054312,1,1,1,1,1,1,1,2 514606949156518,1,1,1,1,1,1,1,1 514606959256596,2,2,1,1,1,1,1,1 514606969357989,1,1,1,1,1,2,1,1 514606979459700,1,2,1,1,1,2,1,1 514606989560481,1,1,1,1,1,1,1,1 514606999661396,1,1,1,1,1,1,2,1 514607009795601,1,1,1,1,1,1,1,2 514607019862711,1,1,1,1,1,1,1,1 514607029963734,1,1,1,1,1,1,1,1 514607040062372,1,1,1,1,1,1,1,1 514607050197735,1,1,1,2,1,1,1,1 514607060266464,1,1,1,1,1,1,1,2 514607070368025,1,1,1,1,1,2,1,1 514607080468375,1,1,1,1,1,1,1,2 514607090570292,3,2,1,1,1,1,1,1 514607100670725,1,1,1,1,1,2,1,1 514607110771946,1,2,1,1,1,1,1,1 514607120873489,1,1,1,1,2,1,2,1 514607130973857,2,1,1,1,3,1,1,1 514607141080056,0,1,1,1,1,2,1,3 514607151176312,1,1,1,2,1,1,1,1 514607161277753,1,1,1,1,1,1,2,1 514607171379095,1,1,1,1,1,1,1,1 514607181479262,1,1,1,1,1,1,1,1 514607191580794,3,1,1,1,1,1,1,1 514607201680952,1,1,1,1,1,1,2,1 514607211783683,1,1,1,1,1,1,1,1 514607221883274,1,1,1,1,1,1,0,1 514607231984244,1,1,1,1,1,1,1,1 514607242085698,1,1,1,1,1,1,1,1 514607252216898,1,2,1,1,1,1,1,1 514607262289420,1,1,1,1,1,2,1,1 514607272389922,1,1,1,1,1,1,1,1 514607282489413,1,1,1,1,1,1,1,1 514607292589950,1,3,1,1,1,1,1,1 514607302693367,1,1,1,1,2,1,1,1 514607312793792,1,1,1,1,1,1,1,1 514607322895249,1,1,1,3,1,1,3,1 514607332994278,1,0,1,1,1,2,1,2 514607343095836,1,1,1,1,1,2,1,1 514607353196533,1,1,1,1,2,1,1,1 514607363297749,1,1,1,1,1,1,1,2 514607373399011,1,1,1,1,1,1,1,2 514607383499730,1,1,1,1,1,1,1,2 514607393601510,1,1,1,1,1,1,1,2 514607403704117,2,1,1,1,1,1,1,2 514607413802700,1,1,1,1,2,1,0,1 514607423904559,1,1,1,1,1,1,1,1 [...] The output is verbose: printing out a timestamp, and then the length of each CPU's run queue. The second last line, of timestamp 514607413802700, is an example of what this tool detects: CPU 4 has a run queue length of 4, which means one thread running and one thread queued, while CPU 6 has a run queue length of 0: idle. The very next sample shows all CPUs busy. The -J option prints raw samples with time offsets showing when the samples were collected on each CPU. It's mostly useful for debugging the tool itself. For example, during a Linux kernel build: # ./cpuunclaimed.py -J TIMESTAMP_ns,CPU0,CPU1,CPU2,CPU3,CPU4,CPU5,CPU6,CPU7,OFFSET_ns_CPU0,OFFSET_ns_CPU1,OFFSET_ns_CPU2,OFFSET_ns_CPU3,OFFSET_ns_CPU4,OFFSET_ns_CPU5,OFFSET_ns_CPU6,OFFSET_ns_CPU7 514722625198188,1,1,1,1,1,1,1,2,0,28321,51655,73396,89654,111172,132803,159792 514722635299034,1,1,1,1,1,2,1,1,0,28809,51999,74183,89552,110011,131995,153519 514722645400274,1,1,1,1,1,1,1,2,0,28024,51333,73652,88964,110075,131973,153568 514722655501816,1,2,1,1,1,1,1,1,0,28893,51671,75233,89496,109430,131945,153694 514722665602594,1,1,2,1,1,2,1,1,0,28623,50988,73866,89383,109186,131786,154555 514722675703498,1,1,1,1,1,1,1,1,0,27379,51031,73175,89625,110380,131482,104811 514722685804942,1,1,1,1,1,2,1,1,0,27213,50501,72183,88797,108780,130659,152153 514722695906294,1,1,1,1,1,1,1,1,0,27036,51182,73420,87861,109585,130364,155089 514722706005778,1,1,1,1,1,1,1,1,0,28492,51851,74138,89744,110208,132462,154060 514722716060705,1,1,1,1,1,1,1,1,0,154499,152528,155232,155046,154502,178746,200001 514722726209615,1,1,1,1,1,1,1,1,0,28170,49580,72605,87741,108144,130723,152138 514722736309475,1,2,1,1,1,1,1,1,0,27421,51386,73061,89358,109457,131273,153005 514722746410845,1,2,1,1,1,2,1,1,0,27788,50840,72720,88920,109111,131143,152979 514722756511363,1,1,1,1,1,1,2,1,0,28280,50977,73559,89848,109659,131579,152693 514722766613044,1,1,1,1,1,1,1,1,0,28046,50812,72754,89160,110108,130735,152948 514722776712932,1,1,1,2,1,1,1,1,0,28586,51177,73974,89588,109947,132376,154162 514722786815477,1,1,1,1,1,1,1,1,0,27973,71104,72539,88302,108896,130414,152236 514722796914955,1,1,1,1,1,1,1,1,0,29054,52354,74214,89592,110615,132586,153925 514722807044060,1,1,1,1,1,1,1,1,1587130,0,24079,46633,61787,82325,104706,125278 514722817117432,2,1,2,1,1,1,1,1,0,27628,51038,75138,89724,109340,132426,155348 514722827218254,1,1,1,1,1,1,2,1,0,29111,51868,74347,88904,109911,132764,153851 514722837340158,1,1,1,1,1,1,1,1,0,7366,30760,53528,68622,89317,111095,132319 514722847421305,1,1,1,1,1,1,1,1,0,28257,51105,73841,89037,110820,131605,153368 514722857521112,1,1,1,1,1,1,1,1,0,28544,51441,73857,89530,110497,131915,153513 514722867626129,0,2,1,1,1,1,1,1,0,24621,47917,70568,85391,106670,128081,150329 514722877727183,2,1,1,1,1,1,1,1,0,24869,47630,71547,84761,106048,128444,149285 514722887824589,1,1,1,1,1,1,2,1,0,28793,51212,73863,89584,109773,132348,153194 514722897925481,1,1,1,1,1,1,2,1,0,29278,51163,73961,89774,109592,132029,153715 514722908026097,1,1,1,1,1,1,1,1,0,30630,35595,36210,188001,190815,190072,190732 514722918127439,1,1,1,1,1,1,1,1,0,28544,51885,73948,89987,109763,132632,154083 514722928227399,1,1,1,1,1,1,1,1,0,31882,51574,74769,89939,110578,132951,154356 514722938329471,1,1,1,1,1,1,1,1,0,28498,51304,74101,89670,110278,132653,153176 514722948430589,1,1,1,1,1,1,1,1,0,27868,50925,73477,89676,109583,132360,153014 514722958531802,1,1,1,1,1,1,1,1,0,28505,50886,73729,89919,109618,131988,152896 514722968632181,1,1,1,1,1,1,1,1,0,28492,51749,73977,90334,109816,132897,152890 514722978733584,1,1,1,1,1,1,1,1,0,28847,50957,74121,90014,110019,132377,152978 514722988834321,1,1,1,1,1,1,1,1,0,28601,51437,74021,89968,110252,132233,153623 514722998937170,1,1,2,1,1,1,1,1,0,27007,50044,73259,87725,108663,132194,152459 514723009036821,1,2,1,2,1,1,1,1,0,28226,50937,73983,89110,110476,131740,153663 514723019137577,1,1,1,1,1,1,1,1,0,30261,52357,75657,87803,61823,131850,153585 514723029238745,1,1,1,1,1,1,1,1,0,28030,50752,74452,89240,110791,132187,153327 514723039339069,1,1,1,1,1,1,1,1,0,29791,52636,75996,90475,110414,132232,154714 514723049439822,1,1,1,1,2,1,1,1,0,29133,56662,74153,89520,110683,132740,154708 514723059541617,1,1,1,1,1,1,1,1,0,27932,51480,74644,89656,109176,131499,153732 514723069642500,1,1,2,1,1,1,2,1,0,27678,51509,73984,90136,110124,131554,153459 514723079743525,2,1,1,1,1,1,1,1,0,28029,51424,74394,90056,110087,132383,152963 514723089844091,2,1,1,2,1,1,1,1,0,28944,51692,74440,90339,110402,132722,154083 514723099945957,1,1,2,1,1,1,1,1,0,28425,51267,73164,89322,115048,114630,115187 514723110047020,1,1,2,0,1,1,1,2,0,28192,50811,76814,89835,109370,131265,153511 514723120216662,1,1,2,1,1,2,1,1,29,34,0,4514,19268,40293,62674,84009 [...] This is a Xen guest system, and it shows that CPU 0 usually completes first (an offset of 0), followed by CPU 1 around 28000 nanoseconds later, and so on. The spread of offsets is triggered by the bcc Python library that initializes the timers, which steps through the CPUs in sequence, with a small delay between them merely from executing its own loop code. Here's more output during a Linux kernel build: # ./cpuunclaimed.py -J TIMESTAMP_ns,CPU0,CPU1,CPU2,CPU3,CPU4,CPU5,CPU6,CPU7,OFFSET_ns_CPU0,OFFSET_ns_CPU1,OFFSET_ns_CPU2,OFFSET_ns_CPU3,OFFSET_ns_CPU4,OFFSET_ns_CPU5,OFFSET_ns_CPU6,OFFSET_ns_CPU7 514722625198188,1,1,1,1,1,1,1,2,0,28321,51655,73396,89654,111172,132803,159792 515700745758947,2,1,1,1,1,1,1,1,0,19835,34891,49397,59364,71988,87571,102769 515700755860451,2,1,1,1,1,1,1,2,0,19946,34323,49855,59844,72741,87925,102891 515700765960560,1,1,1,1,1,1,1,1,0,20805,35339,50436,59677,73557,88661,104796 515700776061744,1,1,1,1,1,1,1,1,1626,77,0,190,153452,154665,178218,154116 515700786162017,1,1,1,1,1,1,1,1,0,20497,35361,51552,59787,74451,147789,104545 515700796262811,1,1,1,1,1,1,1,2,0,20910,35657,50805,60175,73953,88492,103527 515700806364951,1,1,1,1,1,1,1,1,0,20140,35023,50074,59726,72757,88040,102421 515700816465253,1,1,1,1,1,1,2,1,0,20952,34899,50262,60048,72890,88067,103545 515700826566573,1,1,1,1,1,1,1,1,0,20898,35490,50609,59805,74060,88550,103354 515700836667480,1,1,1,1,1,1,2,1,0,20548,34760,50959,59490,73059,87820,103006 515700846768182,1,1,1,1,1,1,2,1,0,20571,35113,50777,59962,74139,88543,103192 515700856869468,1,1,2,1,1,2,2,1,0,20932,35382,50510,60106,73739,91818,103684 515700866971905,1,1,1,2,1,1,1,1,0,19780,33018,49075,58375,71949,86537,102136 515700877073459,2,1,1,1,1,1,1,1,0,20065,73966,48989,58832,71408,85714,101067 515700887172772,1,1,1,1,1,1,1,1,0,20909,34608,51493,59890,73564,88668,103454 515700897273292,1,2,1,1,1,1,1,1,0,20353,35292,50114,59773,73948,88615,103383 515700907374341,1,1,2,1,1,1,1,1,0,20816,35206,50915,60062,73878,88857,103794 515700917475331,1,1,6,1,1,2,1,1,0,20752,34931,50280,59764,73781,88329,103234 515700927576958,1,1,1,1,1,1,1,1,0,19929,34703,50181,59364,73004,88053,103127 515700937677298,1,1,2,2,1,1,1,1,0,21178,34724,50740,61193,73452,89030,103390 515700947778409,2,1,1,1,1,1,1,1,0,21059,35604,50853,60098,73919,88675,103506 515700957879196,2,1,1,1,1,1,1,1,0,21326,35939,51492,60083,74249,89474,103761 [...] Notice the tighter range of offsets? I began executing cpuunclaimed when the system was idle, and it initialized the CPU timers more quickly, and then I began the Linux kernel build. Here's some different output, this time from a physical system with 4 CPUs, also doing a kernel build, # ./cpuunclaimed.py -J TIMESTAMP_ns,CPU0,CPU1,CPU2,CPU3,OFFSET_ns_CPU0,OFFSET_ns_CPU1,OFFSET_ns_CPU2,OFFSET_ns_CPU3 4429382557480,1,1,1,1,0,6011,10895,16018 4429392655042,2,1,1,1,0,8217,13661,19378 4429402757604,1,1,1,1,0,6879,12433,18000 4429412857809,1,1,1,1,0,8303,13190,18719 4429422960709,2,1,1,1,0,6095,11234,17079 4429433060391,1,1,1,2,0,6747,12480,18070 4429443161699,1,1,1,1,0,6560,12264,17945 4429453262002,1,2,1,1,0,6992,12644,18341 4429463363706,1,2,1,1,0,6211,12071,17853 4429473465571,1,1,1,1,0,5766,11495,17638 4429483566920,1,1,1,1,0,5223,11736,16358 4429493666279,1,1,1,1,0,6964,12653,18410 4429503769113,1,1,1,1,0,5161,11399,16612 4429513870744,1,1,1,1,0,5943,10583,15768 4429523969826,1,1,1,1,0,6533,12336,18189 4429534070311,1,1,1,1,0,6834,12816,18488 4429544170456,1,1,1,1,0,7284,13401,19129 4429554274467,1,2,1,1,0,5941,11160,16594 4429564372365,1,2,1,1,0,7514,13618,19190 4429574474406,1,2,1,1,0,6687,12650,18248 4429584574220,1,2,1,1,0,7912,13705,19136 [...] If the offset range becomes too great, we can no longer conclude about when some CPUs were idle and others had queued work. The tool will detect this, and print an error message and exit. Some systems can power down CPUs when idle, and when they wake up again the timed samples may resume from different offsets. If this happens, this tool can no longer draw conclusions about when some CPUs were idle and others had queued work, so it prints an error, and exits. Eg: # ./cpuunclaimed.py 1 Sampling run queues... Output every 1 seconds. Hit Ctrl-C to end. %CPU 0.25%, unclaimed idle 0.00% %CPU 0.75%, unclaimed idle 0.00% %CPU 0.25%, unclaimed idle 0.00% %CPU 0.00%, unclaimed idle 0.00% %CPU 0.00%, unclaimed idle 0.00% %CPU 0.12%, unclaimed idle 0.00% %CPU 0.00%, unclaimed idle 0.00% %CPU 0.25%, unclaimed idle 0.00% %CPU 0.00%, unclaimed idle 0.00% %CPU 0.12%, unclaimed idle 0.00% %CPU 0.13%, unclaimed idle 0.00% %CPU 0.12%, unclaimed idle 0.00% %CPU 0.00%, unclaimed idle 0.00% %CPU 0.00%, unclaimed idle 0.00% %CPU 0.00%, unclaimed idle 0.00% %CPU 0.00%, unclaimed idle 0.00% ERROR: CPU samples arrived at skewed offsets (CPUs may have powered down when idle), spanning 4328176 ns (expected < 4040404 ns). Debug with -J, and see the man page. As output may begin to be unreliable, exiting. It's expected that this will only really occur on idle systems. USAGE: # ./cpuunclaimed.py -h usage: cpuunclaimed.py [-h] [-j] [-J] [-T] [interval] [count] Sample CPU run queues and calculate unclaimed idle CPU positional arguments: interval output interval, in seconds count number of outputs optional arguments: -h, --help show this help message and exit -j, --csv print sample summaries (verbose) as comma-separated values -J, --fullcsv print sample summaries with extra fields: CPU sample offsets -T, --timestamp include timestamp on output examples: ./cpuunclaimed # sample and calculate unclaimed idle CPUs, # output every 1 second (default) ./cpuunclaimed 5 10 # print 5 second summaries, 10 times ./cpuunclaimed -T 1 # 1s summaries and timestamps ./cpuunclaimed -j # raw dump of all samples (verbose), CSV bpfcc-0.12.0/tools/criticalstat.py000077500000000000000000000204661357404205000170620ustar00rootroot00000000000000#!/usr/bin/python # @lint-avoid-python-3-compatibility-imports # # criticalstat Trace long critical sections (IRQs or preemption disabled) # For Linux, uses BCC, eBPF. Requires kernel built with # CONFIG_DEBUG_PREEMPT and CONFIG_PREEMPTIRQ_EVENTS # # USAGE: criticalstat [-h] [-p] [-i] [-d DURATION] # # Copyright (c) 2018, Google, Inc. # Licensed under the Apache License, Version 2.0 (the "License") # # By Joel Fernandes from __future__ import print_function from bcc import BPF import argparse import sys import subprocess import os.path examples="" parser = argparse.ArgumentParser( description="Trace long critical sections", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("-p", "--preemptoff", action="store_true", help="Find long sections where preemption was off") parser.add_argument("-i", "--irqoff", action="store_true", help="Find long sections where IRQ was off") parser.add_argument("-d", "--duration", default=100, help="Duration in uS (microseconds) below which we filter") args = parser.parse_args() preemptoff = False irqoff = False if args.irqoff: preemptoff = False irqoff = True elif args.preemptoff: preemptoff = True irqoff = False debugfs_path = subprocess.Popen ("cat /proc/mounts | grep -w debugfs" + " | awk '{print $2}'", shell=True, stdout=subprocess.PIPE).stdout.read().split(b"\n")[0] if debugfs_path == "": print("ERROR: Unable to find debugfs mount point"); sys.exit(0); trace_path = debugfs_path + b"/tracing/events/preemptirq/"; if (not os.path.exists(trace_path + b"irq_disable") or not os.path.exists(trace_path + b"irq_enable") or not os.path.exists(trace_path + b"preempt_disable") or not os.path.exists(trace_path + b"preempt_enable")): print("ERROR: required tracing events are not available\n" + "Make sure the kernel is built with CONFIG_DEBUG_PREEMPT " + "and CONFIG_PREEMPTIRQ_EVENTS enabled. Also please disable " + "CONFIG_PROVE_LOCKING and CONFIG_LOCKDEP on older kernels.") sys.exit(0) bpf_text = """ #include #include enum addr_offs { START_CALLER_OFF, START_PARENT_OFF, END_CALLER_OFF, END_PARENT_OFF }; struct start_data { u32 addr_offs[2]; u64 ts; int idle_skip; int active; }; struct data_t { u64 time; s64 stack_id; u32 cpu; u64 id; u32 addrs[4]; /* indexed by addr_offs */ char comm[TASK_COMM_LEN]; }; BPF_STACK_TRACE(stack_traces, 16384); BPF_PERCPU_ARRAY(sts, struct start_data, 1); BPF_PERCPU_ARRAY(isidle, u64, 1); BPF_PERF_OUTPUT(events); /* * In the below code we install tracepoint probes on preempt or * IRQ disable/enable critical sections and idle events, the cases * are combinations of 4 different states. * The states are defined as: * CSenter: A critical section has been entered. Either due to * preempt disable or irq disable. * CSexit: A critical section has been exited. Either due to * preempt enable or irq enable. * Ienter: The CPU has entered an idle state. * Iexit: The CPU has exited an idle state. * * The scenario we are trying to detect is if there is an overlap * between Critical sections and idle entry/exit. If there are any * such cases, we avoid recording those critical sections since they * are not worth while to record and just add noise. */ TRACEPOINT_PROBE(power, cpu_idle) { int idx = 0; u64 val; struct start_data *stdp, std; // Mark active sections as that they should be skipped // Handle the case CSenter, Ienter, CSexit, Iexit // Handle the case CSenter, Ienter, Iexit, CSexit stdp = sts.lookup(&idx); if (stdp && stdp->active) { /* * Due to verifier issues, we have to copy contents * of stdp onto the stack before the update. * Fix it to directly update once kernel patch d71962f * becomes more widespread. */ std = *stdp; std.idle_skip = 1; sts.update(&idx, &std); } // Mark CPU as actively within idle or not. if (args->state < 100) { val = 1; isidle.update(&idx, &val); } else { val = 0; isidle.update(&idx, &val); } return 0; } static int in_idle(void) { u64 *idlep; int idx = 0; // Skip event if we're in idle loop idlep = isidle.lookup(&idx); if (idlep && *idlep) return 1; return 0; } static void reset_state(void) { int idx = 0; struct start_data s = {}; sts.update(&idx, &s); } TRACEPOINT_PROBE(preemptirq, TYPE_disable) { int idx = 0; struct start_data s; // Handle the case Ienter, CSenter, CSexit, Iexit // Handle the case Ienter, CSenter, Iexit, CSexit if (in_idle()) { reset_state(); return 0; } u64 ts = bpf_ktime_get_ns(); s.idle_skip = 0; s.addr_offs[START_CALLER_OFF] = args->caller_offs; s.addr_offs[START_PARENT_OFF] = args->parent_offs; s.ts = ts; s.active = 1; sts.update(&idx, &s); return 0; } TRACEPOINT_PROBE(preemptirq, TYPE_enable) { int idx = 0; u64 start_ts, end_ts, diff; struct start_data *stdp; // Handle the case CSenter, Ienter, CSexit, Iexit // Handle the case Ienter, CSenter, CSexit, Iexit if (in_idle()) { reset_state(); return 0; } stdp = sts.lookup(&idx); if (!stdp) { reset_state(); return 0; } // Handle the case Ienter, Csenter, Iexit, Csexit if (!stdp->active) { reset_state(); return 0; } // Handle the case CSenter, Ienter, Iexit, CSexit if (stdp->idle_skip) { reset_state(); return 0; } end_ts = bpf_ktime_get_ns(); start_ts = stdp->ts; if (start_ts > end_ts) { reset_state(); return 0; } diff = end_ts - start_ts; if (diff < DURATION) { reset_state(); return 0; } u64 id = bpf_get_current_pid_tgid(); struct data_t data = {}; if (bpf_get_current_comm(&data.comm, sizeof(data.comm)) == 0) { data.addrs[START_CALLER_OFF] = stdp->addr_offs[START_CALLER_OFF]; data.addrs[START_PARENT_OFF] = stdp->addr_offs[START_PARENT_OFF]; data.addrs[END_CALLER_OFF] = args->caller_offs; data.addrs[END_PARENT_OFF] = args->parent_offs; data.id = id; data.stack_id = stack_traces.get_stackid(args, 0); data.time = diff; data.cpu = bpf_get_smp_processor_id(); events.perf_submit(args, &data, sizeof(data)); } reset_state(); return 0; } """ bpf_text = bpf_text.replace('DURATION', '{}'.format(int(args.duration) * 1000)) if preemptoff: bpf_text = bpf_text.replace('TYPE', 'preempt') else: bpf_text = bpf_text.replace('TYPE', 'irq') b = BPF(text=bpf_text) def get_syms(kstack): syms = [] for addr in kstack: s = b.ksym(addr, show_offset=True) syms.append(s) return syms # process event def print_event(cpu, data, size): try: global b event = b["events"].event(data) stack_traces = b['stack_traces'] stext = b.ksymname('_stext') print("===================================") print("TASK: %s (pid %5d tid %5d) Total Time: %-9.3fus\n\n" % (event.comm, \ (event.id >> 32), (event.id & 0xffffffff), float(event.time) / 1000), end="") print("Section start: {} -> {}".format(b.ksym(stext + event.addrs[0]), b.ksym(stext + event.addrs[1]))) print("Section end: {} -> {}".format(b.ksym(stext + event.addrs[2]), b.ksym(stext + event.addrs[3]))) if event.stack_id >= 0: kstack = stack_traces.walk(event.stack_id) syms = get_syms(kstack) if not syms: return for s in syms: print(" ", end="") print("%s" % s) else: print("NO STACK FOUND DUE TO COLLISION") print("===================================") print("") except Exception: sys.exit(0) b["events"].open_perf_buffer(print_event, page_cnt=256) print("Finding critical section with {} disabled for > {}us".format( ('preempt' if preemptoff else 'IRQ'), args.duration)) while 1: try: b.perf_buffer_poll() except KeyboardInterrupt: exit() bpfcc-0.12.0/tools/criticalstat_example.txt000066400000000000000000000113341357404205000207530ustar00rootroot00000000000000Demonstrations of criticalstat: Find long atomic critical sections in the kernel. criticalstat traces and reports occurences of atomic critical sections in the kernel with useful stacktraces showing the origin of them. Such critical sections frequently occur due to use of spinlocks, or if interrupts or preemption were explicity disabled by a driver. IRQ routines in Linux are also executed with interrupts disabled. There are many reasons. Such critical sections are a source of long latency/responsive issues for real-time systems. This works by probing the preempt/irq and cpuidle tracepoints in the kernel. Since this uses BPF, only the root user can use this tool. Further, the kernel has to be built with certain CONFIG options enabled inorder for it to work: CONFIG_PREEMPTIRQ_EVENTS CONFIG_DEBUG_PREEMPT Additionally, the following options should be turned off on older kernels: CONFIG_PROVE_LOCKING CONFIG_LOCKDEP USAGE: # ./criticalstat -h usage: criticalstat [-h] [-p] [-i] [-d DURATION] Trace long critical sections optional arguments: -h, --help Show this help message and exit -p, --preemptoff Find long sections where preemption was off -i, --irqoff Find long sections where IRQ was off -d DURATION, --duration DURATION Duration in uS (microseconds) below which we filter examples: ./criticalstat # run with default options: irq off for more than 100 uS ./criticalstat -p # find sections with preemption disabled for more than 100 uS ./criticalstat -d 500 # find sections with IRQs disabled for more than 500 uS ./criticalstat -p -d 500 # find sections with preemption disabled for more than 500 uS The tool runs continuously until interrupted by Ctrl-C By default, criticalstat finds IRQ disable sections for > 100us. # ./criticalstat Finding critical section with IRQ disabled for > 100us =================================== TASK: kworker/u16:5 (pid 5903 tid 5903) Total Time: 194.427 us Section start: __schedule -> schedule Section end: _raw_spin_unlock_irq -> finish_task_switch trace_hardirqs_on+0xdc trace_hardirqs_on+0xdc _raw_spin_unlock_irq+0x18 finish_task_switch+0xf0 __schedule+0x8c8 preempt_schedule_irq+0x38 el1_preempt+0x8 =================================== If too many sections are showing up, the user can raise the threshold to only show critical sections that are > 500us by passing "-d" option: # ./criticalstat -d 500 Finding critical section with IRQ disabled for > 500us =================================== TASK: crtc_commit:111 (pid 246 tid 246) Total Time: 580.730 us Section start: clk_enable_lock -> clk_enable Section end: _raw_spin_unlock_irqrestore -> clk_enable trace_hardirqs_on+0xdc trace_hardirqs_on+0xdc _raw_spin_unlock_irqrestore+0x24 clk_enable+0x80 msm_dss_enable_clk+0x7c sde_power_resource_enable+0x578 _sde_crtc_vblank_enable_no_lock+0x68 sde_crtc_vblank+0x8c sde_kms_enable_vblank+0x18 vblank_ctrl_worker+0xd0 kthread_worker_fn+0xf8 kthread+0x114 ret_from_fork+0x10 =================================== If instead of irq disabled sections, we want to see preempt disabled sections, then pass the "-p" option. Below we try to find preempt-disabled critical sections that are > 500us. # ./criticalstat -p -d 500 Finding critical section with preempt disabled for > 500us =================================== TASK: swapper/1 (pid 0 tid 0) Total Time: 618.437 us Section start: preempt_count_add -> preempt_count_add Section end: preempt_count_sub -> preempt_count_sub trace_preempt_on+0x98 trace_preempt_on+0x98 preempt_latency_stop+0x164 preempt_count_sub+0x50 schedule+0x74 schedule_preempt_disabled+0x14 cpu_startup_entry+0x84 secondary_start_kernel+0x1c8 [unknown] =================================== criticalstat -p can also reflect kernel scheduler issues sometimes. These may show up as long preempt-off sections if the functions in the scheduler take a long time to run (such as pick_next_task_fair which selects the CPU for a task Follow is a report showing a preempt-off latency of 700us during the schedule loop's execution: =================================== TASK: irq/296-cs35l36 (pid 666 tid 666) Total Time: 732.657 us Section start: schedule -> schedule Section end: schedule -> schedule trace_preempt_on+0x98 trace_preempt_on+0x98 preempt_count_sub+0xa4 schedule+0x78 schedule_timeout+0x80 wait_for_common+0xb4 wait_for_completion_timeout+0x28 geni_i2c_xfer+0x298 __i2c_transfer+0x4e0 i2c_transfer+0x8 irq_thread_fn+0x2c irq_thread+0x160 kthread+0x118 ret_from_fork+0x10 =================================== See Also: Linux kernel's preemptoff and irqoff tracers which provide similar tracing but with some limitations. bpfcc-0.12.0/tools/cthreads_example.txt000077700000000000000000000000001357404205000247142lib/uthreads_example.txtustar00rootroot00000000000000bpfcc-0.12.0/tools/dbslower.py000077500000000000000000000156651357404205000162220ustar00rootroot00000000000000#!/usr/bin/python # # dbslower Trace MySQL and PostgreSQL queries slower than a threshold. # # USAGE: dbslower [-v] [-p PID [PID ...]] [-b PATH_TO_BINARY] [-m THRESHOLD] # {mysql,postgres} # # By default, a threshold of 1ms is used. Set the threshold to 0 to trace all # queries (verbose). # # Script works in two different modes: # 1) USDT probes, which means it needs MySQL and PostgreSQL built with # USDT (DTrace) support. # 2) uprobe and uretprobe on exported function of binary specified by # PATH_TO_BINARY parameter. (At the moment only MySQL support) # # If no PID or PATH_TO_BINARY is provided, the script attempts to discover # all MySQL or PostgreSQL database processes and uses USDT probes. # # Strongly inspired by Brendan Gregg's work on the mysqld_qslower script. # # Copyright 2017, Sasha Goldshtein # Licensed under the Apache License, Version 2.0 # # 15-Feb-2017 Sasha Goldshtein Created this. from bcc import BPF, USDT import argparse import re import subprocess examples = """examples: dbslower postgres # trace PostgreSQL queries slower than 1ms dbslower postgres -p 188 322 # trace specific PostgreSQL processes dbslower mysql -p 480 -m 30 # trace MySQL queries slower than 30ms dbslower mysql -p 480 -v # trace MySQL queries & print the BPF program dbslower mysql -x $(which mysqld) # trace MySQL queries with uprobes """ parser = argparse.ArgumentParser( description="", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("-v", "--verbose", action="store_true", help="print the BPF program") parser.add_argument("db", choices=["mysql", "postgres"], help="the database engine to use") parser.add_argument("-p", "--pid", type=int, nargs='*', dest="pids", metavar="PID", help="the pid(s) to trace") parser.add_argument("-x", "--exe", type=str, dest="path", metavar="PATH", help="path to binary") parser.add_argument("-m", "--threshold", type=int, default=1, help="trace queries slower than this threshold (ms)") parser.add_argument("--ebpf", action="store_true", help=argparse.SUPPRESS) args = parser.parse_args() threshold_ns = args.threshold * 1000000 mode = "USDT" if args.path and not args.pids: if args.db == "mysql": regex = "\\w+dispatch_command\\w+" symbols = BPF.get_user_functions_and_addresses(args.path, regex) if len(symbols) == 0: print("Can't find function 'dispatch_command' in %s" % (args.path)) exit(1) (mysql_func_name, addr) = symbols[0] if mysql_func_name.find(b'COM_DATA') >= 0: mode = "MYSQL57" else: mode = "MYSQL56" else: # Placeholder for PostrgeSQL # Look on functions initStringInfo, pgstat_report_activity, EndCommand, # NullCommand print("Sorry at the moment PostgreSQL supports only USDT") exit(1) program = """ #include DEFINE_THRESHOLD DEFINE_USDT DEFINE_MYSQL56 DEFINE_MYSQL57 struct temp_t { u64 timestamp; #ifdef USDT char *query; #else /* MySQL clears query packet before uretprobe call - so copy query in advance */ char query[256]; #endif //USDT }; struct data_t { u64 pid; u64 timestamp; u64 duration; char query[256]; }; BPF_HASH(temp, u64, struct temp_t); BPF_PERF_OUTPUT(events); int query_start(struct pt_regs *ctx) { #if defined(MYSQL56) || defined(MYSQL57) /* Trace only packets with enum_server_command == COM_QUERY */ #ifdef MYSQL56 u64 command = (u64) PT_REGS_PARM1(ctx); #else //MYSQL57 u64 command = (u64) PT_REGS_PARM3(ctx); #endif if (command != 3) return 0; #endif struct temp_t tmp = {}; tmp.timestamp = bpf_ktime_get_ns(); #if defined(MYSQL56) bpf_probe_read(&tmp.query, sizeof(tmp.query), (void*) PT_REGS_PARM3(ctx)); #elif defined(MYSQL57) void* st = (void*) PT_REGS_PARM2(ctx); char* query; bpf_probe_read(&query, sizeof(query), st); bpf_probe_read(&tmp.query, sizeof(tmp.query), query); #else //USDT bpf_usdt_readarg(1, ctx, &tmp.query); #endif u64 pid = bpf_get_current_pid_tgid(); temp.update(&pid, &tmp); return 0; } int query_end(struct pt_regs *ctx) { struct temp_t *tempp; u64 pid = bpf_get_current_pid_tgid(); tempp = temp.lookup(&pid); if (!tempp) return 0; u64 delta = bpf_ktime_get_ns() - tempp->timestamp; #ifdef THRESHOLD if (delta >= THRESHOLD) { #endif //THRESHOLD struct data_t data = {}; data.pid = pid >> 32; // only process id data.timestamp = tempp->timestamp; data.duration = delta; bpf_probe_read(&data.query, sizeof(data.query), tempp->query); events.perf_submit(ctx, &data, sizeof(data)); #ifdef THRESHOLD } #endif //THRESHOLD temp.delete(&pid); return 0; }; """.replace("DEFINE_USDT", "#define USDT" if mode == "USDT" else "") \ .replace("DEFINE_MYSQL56", "#define MYSQL56" if mode == "MYSQL56" else "") \ .replace("DEFINE_MYSQL57", "#define MYSQL57" if mode == "MYSQL57" else "") \ .replace("DEFINE_THRESHOLD", "#define THRESHOLD %d" % threshold_ns if threshold_ns > 0 else "") if mode.startswith("MYSQL"): # Uprobes mode bpf = BPF(text=program) bpf.attach_uprobe(name=args.path, sym=mysql_func_name, fn_name="query_start") bpf.attach_uretprobe(name=args.path, sym=mysql_func_name, fn_name="query_end") else: # USDT mode if not args.pids or len(args.pids) == 0: if args.db == "mysql": args.pids = map(int, subprocess.check_output( "pidof mysqld".split()).split()) elif args.db == "postgres": args.pids = map(int, subprocess.check_output( "pidof postgres".split()).split()) usdts = map(lambda pid: USDT(pid=pid), args.pids) for usdt in usdts: usdt.enable_probe("query__start", "query_start") usdt.enable_probe("query__done", "query_end") if args.verbose: print('\n'.join(map(lambda u: u.get_text(), usdts))) bpf = BPF(text=program, usdt_contexts=usdts) if args.verbose or args.ebpf: print(program) if args.ebpf: exit() start = BPF.monotonic_time() def print_event(cpu, data, size): event = bpf["events"].event(data) print("%-14.6f %-6d %8.3f %s" % ( float(event.timestamp - start) / 1000000000, event.pid, float(event.duration) / 1000000, event.query)) if mode.startswith("MYSQL"): print("Tracing database queries for application %s slower than %d ms..." % (args.path, args.threshold)) else: print("Tracing database queries for pids %s slower than %d ms..." % (', '.join(map(str, args.pids)), args.threshold)) print("%-14s %-6s %8s %s" % ("TIME(s)", "PID", "MS", "QUERY")) bpf["events"].open_perf_buffer(print_event, page_cnt=64) while True: try: bpf.perf_buffer_poll() except KeyboardInterrupt: exit() bpfcc-0.12.0/tools/dbslower_example.txt000066400000000000000000000076151357404205000201150ustar00rootroot00000000000000Demonstrations of dbslower, the Linux eBPF/bcc version. dbslower traces queries served by a MySQL or PostgreSQL server, and prints those that exceed a latency (query time) threshold. By default a threshold of 1 ms is used. For example: # dbslower mysql Tracing database queries for pids 25776 slower than 1 ms... TIME(s) PID MS QUERY 1.315800 25776 2000.999 call getproduct(97) 3.360380 25776 3.226 call getproduct(6) ^C This traced two queries slower than 1ms, one of which is very slow: over 2 seconds. We can filter out the shorter ones and keep only the really slow ones: # dbslower mysql -m 1000 Tracing database queries for pids 25776 slower than 1000 ms... TIME(s) PID MS QUERY 1.421264 25776 2002.183 call getproduct(97) 3.572617 25776 2001.381 call getproduct(97) 5.661411 25776 2001.867 call getproduct(97) 7.748296 25776 2001.329 call getproduct(97) ^C This looks like a pattern -- we keep making this slow query every 2 seconds or so, and it takes approximately 2 seconds to run. By default, dbslower will try to detect mysqld and postgres processes, but if necessary, you can specify the process ids with the -p switch: # dbslower mysql -p $(pidof mysql) Tracing database queries for pids 25776 slower than 1 ms... TIME(s) PID MS QUERY 2.002125 25776 3.340 call getproduct(7) 2.045006 25776 2001.558 call getproduct(97) 4.131863 25776 2002.275 call getproduct(97) 6.190513 25776 3.248 call getproduct(33) ^C Specifying 0 as the threshold will print all the queries: # dbslower mysql -m 0 Tracing database queries for pids 25776 slower than 0 ms... TIME(s) PID MS QUERY 6.003720 25776 2.363 /* mysql-connector-java-5.1.40 ( Revision: 402933ef52cad9aa82624e80acbea46e3a701ce6 ) */SELECT @@session.auto_increment_increment AS auto_increment_increment, @@character_set_client AS character_set_client, @@character_set_connection AS character_set_conn 6.599219 25776 0.068 SET NAMES latin1 6.613944 25776 0.057 SET character_set_results = NULL 6.645228 25776 0.059 SET autocommit=1 6.653798 25776 0.059 SET sql_mode='NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES' 6.682184 25776 2.526 select * from users where id = 0 6.767888 25776 0.288 select id from products where userid = 0 6.790642 25776 2.255 call getproduct(0) 6.809865 25776 0.218 call getproduct(1) 6.846878 25776 0.248 select * from users where id = 1 6.847623 25776 0.166 select id from products where userid = 1 6.867363 25776 0.244 call getproduct(2) 6.868162 25776 0.107 call getproduct(3) 6.874726 25776 0.208 select * from users where id = 2 6.881722 25776 0.260 select id from products where userid = 2 ^C Here we can see the MySQL connector initialization and connection establishment, before the actual queries start coming in. USAGE: # dbslower -h usage: dbslower.py [-h] [-v] [-p [PIDS [PIDS ...]]] [-x PATH] [-m THRESHOLD] {mysql,postgres} positional arguments: {mysql,postgres} the database engine to use optional arguments: -h, --help show this help message and exit -v, --verbose print the BPF program -p [PID [PID ...]], --pid [PID [PID ...]] the pid(s) to trace -x PATH, --exe PATH path to binary -m THRESHOLD, --threshold THRESHOLD trace queries slower than this threshold (ms) examples: dbslower postgres # trace PostgreSQL queries slower than 1ms dbslower postgres -p 188 322 # trace specific PostgreSQL processes dbslower mysql -p 480 -m 30 # trace MySQL queries slower than 30ms dbslower mysql -p 480 -v # trace MySQL queries and print the BPF program dbslower mysql -x $(which mysqld) # trace MySQL queries with uprobes bpfcc-0.12.0/tools/dbstat.py000077500000000000000000000073021357404205000156470ustar00rootroot00000000000000#!/usr/bin/python # # dbstat Display a histogram of MySQL and PostgreSQL query latencies. # # USAGE: dbstat [-v] [-p PID [PID ...]] [-m THRESHOLD] [-u] # [-i INTERVAL] {mysql,postgres} # # This tool uses USDT probes, which means it needs MySQL and PostgreSQL built # with USDT (DTrace) support. # # Copyright 2017, Sasha Goldshtein # Licensed under the Apache License, Version 2.0 # # 15-Feb-2017 Sasha Goldshtein Created this. from bcc import BPF, USDT import argparse import subprocess from time import sleep, strftime examples = """ dbstat postgres # display a histogram of PostgreSQL query latencies dbstat mysql -v # display MySQL latencies and print the BPF program dbstat mysql -u # display query latencies in microseconds (default: ms) dbstat mysql -m 5 # trace only queries slower than 5ms dbstat mysql -p 408 # trace queries in a specific process """ parser = argparse.ArgumentParser( description="", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("-v", "--verbose", action="store_true", help="print the BPF program") parser.add_argument("db", choices=["mysql", "postgres"], help="the database engine to use") parser.add_argument("-p", "--pid", type=int, nargs='*', dest="pids", metavar="PID", help="the pid(s) to trace") parser.add_argument("-m", "--threshold", type=int, default=0, help="trace queries slower than this threshold (ms)") parser.add_argument("-u", "--microseconds", action="store_true", help="display query latencies in microseconds (default: milliseconds)") parser.add_argument("-i", "--interval", type=int, default=99999999999, help="print summary at this interval (seconds)") args = parser.parse_args() if not args.pids or len(args.pids) == 0: if args.db == "mysql": args.pids = map(int, subprocess.check_output( "pidof mysqld".split()).split()) elif args.db == "postgres": args.pids = map(int, subprocess.check_output( "pidof postgres".split()).split()) program = """ #include BPF_HASH(temp, u64, u64); BPF_HISTOGRAM(latency); int probe_start(struct pt_regs *ctx) { u64 timestamp = bpf_ktime_get_ns(); u64 pid = bpf_get_current_pid_tgid(); temp.update(&pid, ×tamp); return 0; } int probe_end(struct pt_regs *ctx) { u64 *timestampp; u64 pid = bpf_get_current_pid_tgid(); timestampp = temp.lookup(&pid); if (!timestampp) return 0; u64 delta = bpf_ktime_get_ns() - *timestampp; FILTER delta /= SCALE; latency.increment(bpf_log2l(delta)); temp.delete(&pid); return 0; } """ program = program.replace("SCALE", str(1000 if args.microseconds else 1000000)) program = program.replace("FILTER", "" if args.threshold == 0 else "if (delta / 1000000 < %d) { return 0; }" % args.threshold) usdts = map(lambda pid: USDT(pid=pid), args.pids) for usdt in usdts: usdt.enable_probe("query__start", "probe_start") usdt.enable_probe("query__done", "probe_end") if args.verbose: print('\n'.join(map(lambda u: u.get_text(), usdts))) print(program) bpf = BPF(text=program, usdt_contexts=usdts) print("Tracing database queries for pids %s slower than %d ms..." % (', '.join(map(str, args.pids)), args.threshold)) latencies = bpf["latency"] def print_hist(): print("[%s]" % strftime("%H:%M:%S")) latencies.print_log2_hist("query latency (%s)" % ("us" if args.microseconds else "ms")) print("") latencies.clear() while True: try: sleep(args.interval) print_hist() except KeyboardInterrupt: print_hist() break bpfcc-0.12.0/tools/dbstat_example.txt000066400000000000000000000150001357404205000175400ustar00rootroot00000000000000Demonstrations of dbstat, the Linux eBPF/bcc version. dbstat traces queries performed by a MySQL or PostgreSQL database process, and displays a histogram of query latencies. For example: # dbstat mysql Tracing database queries for pids 25776 slower than 0 ms... query latency (ms) : count distribution 0 -> 1 : 990 |****************************************| 2 -> 3 : 7 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 0 | | 128 -> 255 : 0 | | 256 -> 511 : 0 | | 512 -> 1023 : 0 | | 1024 -> 2047 : 2 | | ^C It's immediately evident that the vast majority of queries finish very quickly, in under 1ms, but there are some super-slow queries occasionally, in the 1-2 seconds bucket. We can filter out the shorter queries with the -m switch: # dbstat mysql -m 1000 Tracing database queries for pids 25776 slower than 1000 ms... query latency (ms) : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 0 | | 128 -> 255 : 0 | | 256 -> 511 : 0 | | 512 -> 1023 : 0 | | 1024 -> 2047 : 8 |****************************************| ^C By default, dbstat will try to detect mysqld and postgres processes, but if necessary, you can specify the process ids with the -p switch. Here, the -i switch is also used to request histograms at 3 second intervals: # dbstat mysql -p $(pidof mysql) -i 3 Tracing database queries for pids 25776 slower than 0 ms... [06:14:36] query latency (ms) : count distribution 0 -> 1 : 758 |****************************************| 2 -> 3 : 1 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 0 | | 128 -> 255 : 0 | | 256 -> 511 : 0 | | 512 -> 1023 : 0 | | 1024 -> 2047 : 1 | | [06:14:39] query latency (ms) : count distribution 0 -> 1 : 436 |****************************************| 2 -> 3 : 2 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 0 | | 128 -> 255 : 0 | | 256 -> 511 : 0 | | 512 -> 1023 : 0 | | 1024 -> 2047 : 1 | | [06:14:42] query latency (ms) : count distribution 0 -> 1 : 399 |****************************************| 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 0 | | 128 -> 255 : 0 | | 256 -> 511 : 0 | | 512 -> 1023 : 0 | | 1024 -> 2047 : 1 | | ^C USAGE: # dbstat -h usage: dbstat.py [-h] [-v] [-p [PID [PID ...]]] [-m THRESHOLD] [-u] [-i INTERVAL] {mysql,postgres} positional arguments: {mysql,postgres} the database engine to use optional arguments: -h, --help show this help message and exit -v, --verbose print the BPF program -p [PID [PID ...]], --pid [PID [PID ...]] the pid(s) to trace -m THRESHOLD, --threshold THRESHOLD trace queries slower than this threshold (ms) -u, --microseconds display query latencies in microseconds (default: milliseconds) -i INTERVAL, --interval INTERVAL print summary at this interval (seconds) dbstat postgres # display a histogram of PostgreSQL query latencies dbstat mysql -v # display MySQL latencies and print the BPF program dbstat mysql -u # display query latencies in microseconds (default: ms) dbstat mysql -m 5 # trace only queries slower than 5ms dbstat mysql -p 408 # trace queries in a specific process bpfcc-0.12.0/tools/dcsnoop.py000077500000000000000000000075401357404205000160370ustar00rootroot00000000000000#!/usr/bin/python # @lint-avoid-python-3-compatibility-imports # # dcsnoop Trace directory entry cache (dcache) lookups. # For Linux, uses BCC, eBPF. Embedded C. # # USAGE: dcsnoop [-h] [-a] # # By default, this traces every failed dcache lookup, and shows the process # performing the lookup and the filename requested. A -a option can be used # to show all lookups, not just failed ones. # # This uses kernel dynamic tracing of the d_lookup() function, and will need # to be modified to match kernel changes. # # Also see dcstat(8), for per-second summaries. # # Copyright 2016 Netflix, Inc. # Licensed under the Apache License, Version 2.0 (the "License") # # 09-Feb-2016 Brendan Gregg Created this. from __future__ import print_function from bcc import BPF import argparse import re import time # arguments examples = """examples: ./dcsnoop # trace failed dcache lookups ./dcsnoop -a # trace all dcache lookups """ parser = argparse.ArgumentParser( description="Trace directory entry cache (dcache) lookups", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("-a", "--all", action="store_true", help="trace all lookups (default is fails only)") parser.add_argument("--ebpf", action="store_true", help=argparse.SUPPRESS) args = parser.parse_args() # define BPF program bpf_text = """ #include #include #include #define MAX_FILE_LEN 64 enum lookup_type { LOOKUP_MISS, LOOKUP_REFERENCE, }; struct entry_t { char name[MAX_FILE_LEN]; }; BPF_HASH(entrybypid, u32, struct entry_t); struct data_t { u32 pid; enum lookup_type type; char comm[TASK_COMM_LEN]; char filename[MAX_FILE_LEN]; }; BPF_PERF_OUTPUT(events); /* from fs/namei.c: */ struct nameidata { struct path path; struct qstr last; // [...] }; static inline void submit_event(struct pt_regs *ctx, void *name, int type, u32 pid) { struct data_t data = { .pid = pid, .type = type, }; bpf_get_current_comm(&data.comm, sizeof(data.comm)); bpf_probe_read(&data.filename, sizeof(data.filename), name); events.perf_submit(ctx, &data, sizeof(data)); } int trace_fast(struct pt_regs *ctx, struct nameidata *nd, struct path *path) { u32 pid = bpf_get_current_pid_tgid(); submit_event(ctx, (void *)nd->last.name, LOOKUP_REFERENCE, pid); return 1; } int kprobe__d_lookup(struct pt_regs *ctx, const struct dentry *parent, const struct qstr *name) { u32 pid = bpf_get_current_pid_tgid(); struct entry_t entry = {}; const char *fname = name->name; if (fname) { bpf_probe_read(&entry.name, sizeof(entry.name), (void *)fname); } entrybypid.update(&pid, &entry); return 0; } int kretprobe__d_lookup(struct pt_regs *ctx) { u32 pid = bpf_get_current_pid_tgid(); struct entry_t *ep; ep = entrybypid.lookup(&pid); if (ep == 0 || PT_REGS_RC(ctx) != 0) { return 0; // missed entry or lookup didn't fail } submit_event(ctx, (void *)ep->name, LOOKUP_MISS, pid); entrybypid.delete(&pid); return 0; } """ if args.ebpf: print(bpf_text) exit() # initialize BPF b = BPF(text=bpf_text) if args.all: b.attach_kprobe(event="lookup_fast", fn_name="trace_fast") mode_s = { 0: 'M', 1: 'R', } start_ts = time.time() def print_event(cpu, data, size): event = b["events"].event(data) print("%-11.6f %-6d %-16s %1s %s" % ( time.time() - start_ts, event.pid, event.comm.decode('utf-8', 'replace'), mode_s[event.type], event.filename.decode('utf-8', 'replace'))) # header print("%-11s %-6s %-16s %1s %s" % ("TIME(s)", "PID", "COMM", "T", "FILE")) b["events"].open_perf_buffer(print_event, page_cnt=64) while 1: try: b.perf_buffer_poll() except KeyboardInterrupt: exit() bpfcc-0.12.0/tools/dcsnoop_example.txt000066400000000000000000000104231357404205000177300ustar00rootroot00000000000000Demonstrations of dcsnoop, the Linux eBPF/bcc 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. By default, only failed lookups are shown. For example: # ./dcsnoop.py TIME(s) PID COMM T FILE 0.002837 1643 snmpd M net/dev 0.002852 1643 snmpd M 1643 0.002856 1643 snmpd M net 0.002863 1643 snmpd M dev 0.002952 1643 snmpd M net/if_inet6 0.002964 1643 snmpd M if_inet6 0.003180 1643 snmpd M net/ipv4/neigh/eth0/retrans_time_ms 0.003192 1643 snmpd M ipv4/neigh/eth0/retrans_time_ms 0.003197 1643 snmpd M neigh/eth0/retrans_time_ms 0.003203 1643 snmpd M eth0/retrans_time_ms 0.003206 1643 snmpd M retrans_time_ms 0.003245 1643 snmpd M ipv6/neigh/eth0/retrans_time_ms 0.003249 1643 snmpd M neigh/eth0/retrans_time_ms 0.003252 1643 snmpd M eth0/retrans_time_ms 0.003255 1643 snmpd M retrans_time_ms 0.003287 1643 snmpd M conf/eth0/forwarding 0.003292 1643 snmpd M eth0/forwarding 0.003295 1643 snmpd M forwarding 0.003326 1643 snmpd M base_reachable_time_ms [...] I ran a drop caches at the same time as executing this tool. The output shows the processes, the type of event ("T" column: M == miss, R == reference), and the filename for the dcache lookup. The way the dcache is currently implemented, each component of a path is checked in turn. The first line, showing "net/dev" from snmp, will be a lookup for "net" in a directory (that isn't shown here). If it finds "net", it will then lookup "dev" inside net. You can see this sequence a little later, starting at time 0.003180, where a pathname is being searched directory by directory. The -a option will show all lookups, although be warned, the output will be very verbose. For example: # ./dcsnoop TIME(s) PID COMM T FILE 0.000000 20279 dcsnoop.py M p_lookup_fast 0.000010 20279 dcsnoop.py M enable 0.000013 20279 dcsnoop.py M id 0.000015 20279 dcsnoop.py M filter 0.000017 20279 dcsnoop.py M trigger 0.000019 20279 dcsnoop.py M format 0.006148 20279 dcsnoop.py R sys/kernel/debug/tracing/trace_pipe 0.006158 20279 dcsnoop.py R kernel/debug/tracing/trace_pipe 0.006161 20279 dcsnoop.py R debug/tracing/trace_pipe 0.006164 20279 dcsnoop.py R tracing/trace_pipe 0.006166 20279 dcsnoop.py R trace_pipe 0.015900 1643 snmpd R proc/sys/net/ipv6/conf/lo/forwarding 0.015901 1643 snmpd R sys/net/ipv6/conf/lo/forwarding 0.015901 1643 snmpd R net/ipv6/conf/lo/forwarding 0.015902 1643 snmpd R ipv6/conf/lo/forwarding 0.015903 1643 snmpd R conf/lo/forwarding 0.015904 1643 snmpd R lo/forwarding 0.015905 1643 snmpd M lo/forwarding 0.015908 1643 snmpd R forwarding 0.015909 1643 snmpd M forwarding 0.015937 1643 snmpd R proc/sys/net/ipv6/neigh/lo/base_reachable_time_ms 0.015937 1643 snmpd R sys/net/ipv6/neigh/lo/base_reachable_time_ms 0.015938 1643 snmpd R net/ipv6/neigh/lo/base_reachable_time_ms 0.015939 1643 snmpd R ipv6/neigh/lo/base_reachable_time_ms 0.015940 1643 snmpd R neigh/lo/base_reachable_time_ms 0.015941 1643 snmpd R lo/base_reachable_time_ms 0.015941 1643 snmpd R base_reachable_time_ms 0.015943 1643 snmpd M base_reachable_time_ms 0.043569 1876 supervise M 20281 0.043573 1886 supervise M 20280 0.043582 1886 supervise R supervise/status.new [...] USAGE message: # ./dcsnoop.py -h usage: dcsnoop.py [-h] [-a] Trace directory entry cache (dcache) lookups optional arguments: -h, --help show this help message and exit -a, --all trace all lookups (default is fails only) examples: ./dcsnoop # trace failed dcache lookups ./dcsnoop -a # trace all dcache lookups bpfcc-0.12.0/tools/dcstat.py000077500000000000000000000075161357404205000156570ustar00rootroot00000000000000#!/usr/bin/python # @lint-avoid-python-3-compatibility-imports # # dcstat Directory entry cache (dcache) stats. # For Linux, uses BCC, eBPF. # # USAGE: dcstat [interval [count]] # # 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. # # Copyright 2016 Netflix, Inc. # Licensed under the Apache License, Version 2.0 (the "License") # # 09-Feb-2016 Brendan Gregg Created this. from __future__ import print_function from bcc import BPF from ctypes import c_int from time import sleep, strftime from sys import argv def usage(): print("USAGE: %s [interval [count]]" % argv[0]) exit() # arguments interval = 1 count = -1 if len(argv) > 1: try: interval = int(argv[1]) if interval == 0: raise if len(argv) > 2: count = int(argv[2]) except: # also catches -h, --help usage() # define BPF program bpf_text = """ #include enum stats { S_REFS = 1, S_SLOW, S_MISS, S_MAXSTAT }; BPF_ARRAY(stats, u64, S_MAXSTAT); /* * How this is instrumented, and how to interpret the statistics, is very much * tied to the current kernel implementation (this was written on Linux 4.4). * This will need maintenance to keep working as the implementation changes. To * aid future adventurers, this is is what the current code does, and why. * * First problem: the current implementation takes a path and then does a * lookup of each component. So how do we count a reference? Once for the path * lookup, or once for every component lookup? I've chosen the latter * since it seems to map more closely to actual dcache lookups (via * __d_lookup_rcu()). It's counted via calls to lookup_fast(). * * The implementation tries different, progressively slower, approaches to * lookup a file. At what point do we call it a dcache miss? I've chosen when * a d_lookup() (which is called during lookup_slow()) returns zero. * * I've also included a "SLOW" statistic to show how often the fast lookup * failed. Whether this exists or is interesting is an implementation detail, * and the "SLOW" statistic may be removed in future versions. */ void count_fast(struct pt_regs *ctx) { int key = S_REFS; u64 *leaf = stats.lookup(&key); if (leaf) (*leaf)++; } void count_lookup(struct pt_regs *ctx) { int key = S_SLOW; u64 *leaf = stats.lookup(&key); if (leaf) (*leaf)++; if (PT_REGS_RC(ctx) == 0) { key = S_MISS; leaf = stats.lookup(&key); if (leaf) (*leaf)++; } } """ # load BPF program b = BPF(text=bpf_text) b.attach_kprobe(event="lookup_fast", fn_name="count_fast") b.attach_kretprobe(event="d_lookup", fn_name="count_lookup") # stat column labels and indexes stats = { "REFS": 1, "SLOW": 2, "MISS": 3 } # header print("%-8s " % "TIME", end="") for stype, idx in sorted(stats.items(), key=lambda k_v: (k_v[1], k_v[0])): print(" %8s" % (stype + "/s"), end="") print(" %8s" % "HIT%") # output i = 0 while (1): if count > 0: i += 1 if i > count: exit() try: sleep(interval) except KeyboardInterrupt: pass exit() print("%-8s: " % strftime("%H:%M:%S"), end="") # print each statistic as a column for stype, idx in sorted(stats.items(), key=lambda k_v: (k_v[1], k_v[0])): try: val = b["stats"][c_int(idx)].value / interval print(" %8d" % val, end="") except: print(" %8d" % 0, end="") # print hit ratio percentage try: ref = b["stats"][c_int(stats["REFS"])].value miss = b["stats"][c_int(stats["MISS"])].value hit = ref - miss pct = float(100) * hit / ref print(" %8.2f" % pct) except: print(" %7s%%" % "-") b["stats"].clear() bpfcc-0.12.0/tools/dcstat_example.txt000066400000000000000000000064171357404205000175550ustar00rootroot00000000000000Demonstrations of dcstat, the Linux eBPF/bcc version. dcstat shows directory entry cache (dcache) statistics. For example: # ./dcstat TIME REFS/s SLOW/s MISS/s HIT% 08:11:47: 2059 141 97 95.29 08:11:48: 79974 151 106 99.87 08:11:49: 192874 146 102 99.95 08:11:50: 2051 144 100 95.12 08:11:51: 73373 17239 17194 76.57 08:11:52: 54685 25431 25387 53.58 08:11:53: 18127 8182 8137 55.12 08:11:54: 22517 10345 10301 54.25 08:11:55: 7524 2881 2836 62.31 08:11:56: 2067 141 97 95.31 08:11:57: 2115 145 101 95.22 The output shows the total references per second ("REFS/s"), the number that took a slower code path to be processed ("SLOW/s"), the number of dcache misses ("MISS/s"), and the hit ratio as a percentage. By default, an interval of 1 second is used. At 08:11:49, there were 192 thousand references, which almost entirely hit from the dcache, with a hit ration of 99.95%. A little later, starting at 08:11:51, a workload began that walked many uncached files, reducing the hit ratio to 53%, and more importantly, a miss rate of over 10 thousand per second. Here's an interesting workload: # ./dcstat TIME REFS/s SLOW/s MISS/s HIT% 08:15:53: 250683 141 97 99.96 08:15:54: 266115 145 101 99.96 08:15:55: 268428 141 97 99.96 08:15:56: 260389 143 99 99.96 It's a 99.96% hit ratio, and these are all negative hits: accessing a file that does not exist. Here's the C program that generated the workload: # cat -n badopen.c 1 #include 2 #include 3 #include 4 5 int 6 main(int argc, char *argv[]) 7 { 8 int fd; 9 while (1) { 10 fd = open("bad", O_RDONLY); 11 } 12 return 0; 13 } This is a simple workload generator than tries to open a missing file ("bad") as quickly as possible. Lets see what happens if the workload attempts to open a different filename each time (which is also a missing file), using the following C code: # cat -n badopen2.c 1 #include 2 #include 3 #include 4 #include 5 6 int 7 main(int argc, char *argv[]) 8 { 9 int fd, i = 0; 10 char buf[128] = {}; 11 12 while (1) { 13 sprintf(buf, "bad%d", i++); 14 fd = open(buf, O_RDONLY); 15 } 16 return 0; 17 } Here's dcstat: # ./dcstat TIME REFS/s SLOW/s MISS/s HIT% 08:18:52: 241131 237544 237505 1.51 08:18:53: 238210 236323 236278 0.82 08:18:54: 235259 233307 233261 0.85 08:18:55: 233144 231256 231214 0.83 08:18:56: 231981 230097 230053 0.83 dcstat also supports an optional interval and optional count. For example, printing 5 second summaries 3 times: # ./dcstat 5 3 TIME REFS/s SLOW/s MISS/s HIT% 08:20:03: 2085 143 99 95.23 08:20:08: 2077 143 98 95.24 08:20:14: 2071 144 100 95.15 USAGE message: # ./dcstat -h USAGE: ./dcstat [interval [count]] bpfcc-0.12.0/tools/deadlock.c000066400000000000000000000156131357404205000157270ustar00rootroot00000000000000/* * deadlock.c Detects potential deadlocks in a running process. * For Linux, uses BCC, eBPF. See .py file. * * Copyright 2017 Facebook, Inc. * Licensed under the Apache License, Version 2.0 (the "License") * * 1-Feb-2016 Kenny Yu Created this. */ #include #include // Maximum number of mutexes a single thread can hold at once. // If the number is too big, the unrolled loops wil cause the stack // to be too big, and the bpf verifier will fail. #define MAX_HELD_MUTEXES 16 // Info about held mutexes. `mutex` will be 0 if not held. struct held_mutex_t { u64 mutex; u64 stack_id; }; // List of mutexes that a thread is holding. Whenever we loop over this array, // we need to force the compiler to unroll the loop, otherwise the bcc verifier // will fail because the loop will create a backwards edge. struct thread_to_held_mutex_leaf_t { struct held_mutex_t held_mutexes[MAX_HELD_MUTEXES]; }; // Map of thread ID -> array of (mutex addresses, stack id) BPF_HASH(thread_to_held_mutexes, u32, struct thread_to_held_mutex_leaf_t, 2097152); // Key type for edges. Represents an edge from mutex1 => mutex2. struct edges_key_t { u64 mutex1; u64 mutex2; }; // Leaf type for edges. Holds information about where each mutex was acquired. struct edges_leaf_t { u64 mutex1_stack_id; u64 mutex2_stack_id; u32 thread_pid; char comm[TASK_COMM_LEN]; }; // Represents all edges currently in the mutex wait graph. BPF_HASH(edges, struct edges_key_t, struct edges_leaf_t, 2097152); // Info about parent thread when a child thread is created. struct thread_created_leaf_t { u64 stack_id; u32 parent_pid; char comm[TASK_COMM_LEN]; }; // Map of child thread pid -> info about parent thread. BPF_HASH(thread_to_parent, u32, struct thread_created_leaf_t); // Stack traces when threads are created and when mutexes are locked/unlocked. BPF_STACK_TRACE(stack_traces, 655360); // The first argument to the user space function we are tracing // is a pointer to the mutex M held by thread T. // // For all mutexes N held by mutexes_held[T] // add edge N => M (held by T) // mutexes_held[T].add(M) int trace_mutex_acquire(struct pt_regs *ctx, void *mutex_addr) { // Higher 32 bits is process ID, Lower 32 bits is thread ID u32 pid = bpf_get_current_pid_tgid(); u64 mutex = (u64)mutex_addr; struct thread_to_held_mutex_leaf_t empty_leaf = {}; struct thread_to_held_mutex_leaf_t *leaf = thread_to_held_mutexes.lookup_or_try_init(&pid, &empty_leaf); if (!leaf) { bpf_trace_printk( "could not add thread_to_held_mutex key, thread: %d, mutex: %p\n", pid, mutex); return 1; // Could not insert, no more memory } // Recursive mutexes lock the same mutex multiple times. We cannot tell if // the mutex is recursive after the mutex is already created. To avoid noisy // reports, disallow self edges. Do one pass to check if we are already // holding the mutex, and if we are, do nothing. #pragma unroll for (int i = 0; i < MAX_HELD_MUTEXES; ++i) { if (leaf->held_mutexes[i].mutex == mutex) { return 1; // Disallow self edges } } u64 stack_id = stack_traces.get_stackid(ctx, BPF_F_USER_STACK); int added_mutex = 0; #pragma unroll for (int i = 0; i < MAX_HELD_MUTEXES; ++i) { // If this is a free slot, see if we can insert. if (!leaf->held_mutexes[i].mutex) { if (!added_mutex) { leaf->held_mutexes[i].mutex = mutex; leaf->held_mutexes[i].stack_id = stack_id; added_mutex = 1; } continue; // Nothing to do for a free slot } // Add edges from held mutex => current mutex struct edges_key_t edge_key = {}; edge_key.mutex1 = leaf->held_mutexes[i].mutex; edge_key.mutex2 = mutex; struct edges_leaf_t edge_leaf = {}; edge_leaf.mutex1_stack_id = leaf->held_mutexes[i].stack_id; edge_leaf.mutex2_stack_id = stack_id; edge_leaf.thread_pid = pid; bpf_get_current_comm(&edge_leaf.comm, sizeof(edge_leaf.comm)); // Returns non-zero on error int result = edges.update(&edge_key, &edge_leaf); if (result) { bpf_trace_printk("could not add edge key %p, %p, error: %d\n", edge_key.mutex1, edge_key.mutex2, result); continue; // Could not insert, no more memory } } // There were no free slots for this mutex. if (!added_mutex) { bpf_trace_printk("could not add mutex %p, added_mutex: %d\n", mutex, added_mutex); return 1; } return 0; } // The first argument to the user space function we are tracing // is a pointer to the mutex M held by thread T. // // mutexes_held[T].remove(M) int trace_mutex_release(struct pt_regs *ctx, void *mutex_addr) { // Higher 32 bits is process ID, Lower 32 bits is thread ID u32 pid = bpf_get_current_pid_tgid(); u64 mutex = (u64)mutex_addr; struct thread_to_held_mutex_leaf_t *leaf = thread_to_held_mutexes.lookup(&pid); if (!leaf) { // If the leaf does not exist for the pid, then it means we either missed // the acquire event, or we had no more memory and could not add it. bpf_trace_printk( "could not find thread_to_held_mutex, thread: %d, mutex: %p\n", pid, mutex); return 1; } // For older kernels without "Bpf: allow access into map value arrays" // (https://lkml.org/lkml/2016/8/30/287) the bpf verifier will fail with an // invalid memory access on `leaf->held_mutexes[i]` below. On newer kernels, // we can avoid making this extra copy in `value` and use `leaf` directly. struct thread_to_held_mutex_leaf_t value = {}; bpf_probe_read(&value, sizeof(struct thread_to_held_mutex_leaf_t), leaf); #pragma unroll for (int i = 0; i < MAX_HELD_MUTEXES; ++i) { // Find the current mutex (if it exists), and clear it. // Note: Can't use `leaf->` in this if condition, see comment above. if (value.held_mutexes[i].mutex == mutex) { leaf->held_mutexes[i].mutex = 0; leaf->held_mutexes[i].stack_id = 0; } } return 0; } // Trace return from clone() syscall in the child thread (return value > 0). int trace_clone(struct pt_regs *ctx, unsigned long flags, void *child_stack, void *ptid, void *ctid, struct pt_regs *regs) { u32 child_pid = PT_REGS_RC(ctx); if (child_pid <= 0) { return 1; } struct thread_created_leaf_t thread_created_leaf = {}; thread_created_leaf.parent_pid = bpf_get_current_pid_tgid(); thread_created_leaf.stack_id = stack_traces.get_stackid(ctx, BPF_F_USER_STACK); bpf_get_current_comm(&thread_created_leaf.comm, sizeof(thread_created_leaf.comm)); struct thread_created_leaf_t *insert_result = thread_to_parent.lookup_or_try_init(&child_pid, &thread_created_leaf); if (!insert_result) { bpf_trace_printk( "could not add thread_created_key, child: %d, parent: %d\n", child_pid, thread_created_leaf.parent_pid); return 1; // Could not insert, no more memory } return 0; } bpfcc-0.12.0/tools/deadlock.py000077500000000000000000000467301357404205000161440ustar00rootroot00000000000000#!/usr/bin/python # # deadlock Detects potential deadlocks (lock order inversions) # on a running process. For Linux, uses BCC, eBPF. # # USAGE: deadlock.py [-h] [--binary BINARY] [--dump-graph DUMP_GRAPH] # [--verbose] [--lock-symbols LOCK_SYMBOLS] # [--unlock-symbols UNLOCK_SYMBOLS] # pid # # This traces pthread mutex lock and unlock calls to build a directed graph # representing the mutex wait graph: # # - Nodes in the graph represent mutexes. # - Edge (A, B) exists if there exists some thread T where lock(A) was called # and lock(B) was called before unlock(A) was called. # # If the program finds a potential lock order inversion, the program will dump # the cycle of mutexes and the stack traces where each mutex was acquired, and # then exit. # # This program can only find potential deadlocks that occur while the program # is tracing the process. It cannot find deadlocks that may have occurred # before the program was attached to the process. # # Since this traces all mutex lock and unlock events and all thread creation # events on the traced process, the overhead of this bpf program can be very # high if the process has many threads and mutexes. You should only run this on # a process where the slowdown is acceptable. # # Note: This tool does not work for shared mutexes or recursive mutexes. # # For shared (read-write) mutexes, a deadlock requires a cycle in the wait # graph where at least one of the mutexes in the cycle is acquiring exclusive # (write) ownership. # # For recursive mutexes, lock() is called multiple times on the same mutex. # However, there is no way to determine if a mutex is a recursive mutex # after the mutex has been created. As a result, this tool will not find # potential deadlocks that involve only one mutex. # # Copyright 2017 Facebook, Inc. # Licensed under the Apache License, Version 2.0 (the "License") # # 01-Feb-2017 Kenny Yu Created this. from __future__ import ( absolute_import, division, unicode_literals, print_function ) from bcc import BPF from collections import defaultdict import argparse import json import os import subprocess import sys import time class DiGraph(object): ''' Adapted from networkx: http://networkx.github.io/ Represents a directed graph. Edges can store (key, value) attributes. ''' def __init__(self): # Map of node -> set of nodes self.adjacency_map = {} # Map of (node1, node2) -> map string -> arbitrary attribute # This will not be copied in subgraph() self.attributes_map = {} def neighbors(self, node): return self.adjacency_map.get(node, set()) def edges(self): edges = [] for node, neighbors in self.adjacency_map.items(): for neighbor in neighbors: edges.append((node, neighbor)) return edges def nodes(self): return self.adjacency_map.keys() def attributes(self, node1, node2): return self.attributes_map[(node1, node2)] def add_edge(self, node1, node2, **kwargs): if node1 not in self.adjacency_map: self.adjacency_map[node1] = set() if node2 not in self.adjacency_map: self.adjacency_map[node2] = set() self.adjacency_map[node1].add(node2) self.attributes_map[(node1, node2)] = kwargs def remove_node(self, node): self.adjacency_map.pop(node, None) for _, neighbors in self.adjacency_map.items(): neighbors.discard(node) def subgraph(self, nodes): graph = DiGraph() for node in nodes: for neighbor in self.neighbors(node): if neighbor in nodes: graph.add_edge(node, neighbor) return graph def node_link_data(self): ''' Returns the graph as a dictionary in a format that can be serialized. ''' data = { 'directed': True, 'multigraph': False, 'graph': {}, 'links': [], 'nodes': [], } # Do one pass to build a map of node -> position in nodes node_to_number = {} for node in self.adjacency_map.keys(): node_to_number[node] = len(data['nodes']) data['nodes'].append({'id': node}) # Do another pass to build the link information for node, neighbors in self.adjacency_map.items(): for neighbor in neighbors: link = self.attributes_map[(node, neighbor)].copy() link['source'] = node_to_number[node] link['target'] = node_to_number[neighbor] data['links'].append(link) return data def strongly_connected_components(G): ''' Adapted from networkx: http://networkx.github.io/ Parameters ---------- G : DiGraph Returns ------- comp : generator of sets A generator of sets of nodes, one for each strongly connected component of G. ''' preorder = {} lowlink = {} scc_found = {} scc_queue = [] i = 0 # Preorder counter for source in G.nodes(): if source not in scc_found: queue = [source] while queue: v = queue[-1] if v not in preorder: i = i + 1 preorder[v] = i done = 1 v_nbrs = G.neighbors(v) for w in v_nbrs: if w not in preorder: queue.append(w) done = 0 break if done == 1: lowlink[v] = preorder[v] for w in v_nbrs: if w not in scc_found: if preorder[w] > preorder[v]: lowlink[v] = min([lowlink[v], lowlink[w]]) else: lowlink[v] = min([lowlink[v], preorder[w]]) queue.pop() if lowlink[v] == preorder[v]: scc_found[v] = True scc = {v} while ( scc_queue and preorder[scc_queue[-1]] > preorder[v] ): k = scc_queue.pop() scc_found[k] = True scc.add(k) yield scc else: scc_queue.append(v) def simple_cycles(G): ''' Adapted from networkx: http://networkx.github.io/ Parameters ---------- G : DiGraph Returns ------- cycle_generator: generator A generator that produces elementary cycles of the graph. Each cycle is represented by a list of nodes along the cycle. ''' def _unblock(thisnode, blocked, B): stack = set([thisnode]) while stack: node = stack.pop() if node in blocked: blocked.remove(node) stack.update(B[node]) B[node].clear() # Johnson's algorithm requires some ordering of the nodes. # We assign the arbitrary ordering given by the strongly connected comps # There is no need to track the ordering as each node removed as processed. # save the actual graph so we can mutate it here # We only take the edges because we do not want to # copy edge and node attributes here. subG = G.subgraph(G.nodes()) sccs = list(strongly_connected_components(subG)) while sccs: scc = sccs.pop() # order of scc determines ordering of nodes startnode = scc.pop() # Processing node runs 'circuit' routine from recursive version path = [startnode] blocked = set() # vertex: blocked from search? closed = set() # nodes involved in a cycle blocked.add(startnode) B = defaultdict(set) # graph portions that yield no elementary circuit stack = [(startnode, list(subG.neighbors(startnode)))] while stack: thisnode, nbrs = stack[-1] if nbrs: nextnode = nbrs.pop() if nextnode == startnode: yield path[:] closed.update(path) elif nextnode not in blocked: path.append(nextnode) stack.append((nextnode, list(subG.neighbors(nextnode)))) closed.discard(nextnode) blocked.add(nextnode) continue # done with nextnode... look for more neighbors if not nbrs: # no more nbrs if thisnode in closed: _unblock(thisnode, blocked, B) else: for nbr in subG.neighbors(thisnode): if thisnode not in B[nbr]: B[nbr].add(thisnode) stack.pop() path.pop() # done processing this node subG.remove_node(startnode) H = subG.subgraph(scc) # make smaller to avoid work in SCC routine sccs.extend(list(strongly_connected_components(H))) def find_cycle(graph): ''' Looks for a cycle in the graph. If found, returns the first cycle. If nodes a1, a2, ..., an are in a cycle, then this returns: [(a1,a2), (a2,a3), ... (an-1,an), (an, a1)] Otherwise returns an empty list. ''' cycles = list(simple_cycles(graph)) if cycles: nodes = cycles[0] nodes.append(nodes[0]) edges = [] prev = nodes[0] for node in nodes[1:]: edges.append((prev, node)) prev = node return edges else: return [] def print_cycle(binary, graph, edges, thread_info, print_stack_trace_fn): ''' Prints the cycle in the mutex graph in the following format: Potential Deadlock Detected! Cycle in lock order graph: M0 => M1 => M2 => M0 for (m, n) in cycle: Mutex n acquired here while holding Mutex m in thread T: [ stack trace ] Mutex m previously acquired by thread T here: [ stack trace ] for T in all threads: Thread T was created here: [ stack trace ] ''' # List of mutexes in the cycle, first and last repeated nodes_in_order = [] # Map mutex address -> readable alias node_addr_to_name = {} for counter, (m, n) in enumerate(edges): nodes_in_order.append(m) # For global or static variables, try to symbolize the mutex address. symbol = symbolize_with_objdump(binary, m) if symbol: symbol += ' ' node_addr_to_name[m] = 'Mutex M%d (%s0x%016x)' % (counter, symbol, m) nodes_in_order.append(nodes_in_order[0]) print('----------------\nPotential Deadlock Detected!\n') print( 'Cycle in lock order graph: %s\n' % (' => '.join([node_addr_to_name[n] for n in nodes_in_order])) ) # Set of threads involved in the lock inversion thread_pids = set() # For each edge in the cycle, print where the two mutexes were held for (m, n) in edges: thread_pid = graph.attributes(m, n)['thread_pid'] thread_comm = graph.attributes(m, n)['thread_comm'] first_mutex_stack_id = graph.attributes(m, n)['first_mutex_stack_id'] second_mutex_stack_id = graph.attributes(m, n)['second_mutex_stack_id'] thread_pids.add(thread_pid) print( '%s acquired here while holding %s in Thread %d (%s):' % ( node_addr_to_name[n], node_addr_to_name[m], thread_pid, thread_comm ) ) print_stack_trace_fn(second_mutex_stack_id) print('') print( '%s previously acquired by the same Thread %d (%s) here:' % (node_addr_to_name[m], thread_pid, thread_comm) ) print_stack_trace_fn(first_mutex_stack_id) print('') # Print where the threads were created, if available for thread_pid in thread_pids: parent_pid, stack_id, parent_comm = thread_info.get( thread_pid, (None, None, None) ) if parent_pid: print( 'Thread %d created by Thread %d (%s) here: ' % (thread_pid, parent_pid, parent_comm) ) print_stack_trace_fn(stack_id) else: print( 'Could not find stack trace where Thread %d was created' % thread_pid ) print('') def symbolize_with_objdump(binary, addr): ''' Searches the binary for the address using objdump. Returns the symbol if it is found, otherwise returns empty string. ''' try: command = ( 'objdump -tT %s | grep %x | awk {\'print $NF\'} | c++filt' % (binary, addr) ) output = subprocess.check_output(command, shell=True) return output.decode('utf-8').strip() except subprocess.CalledProcessError: return '' def strlist(s): '''Given a comma-separated string, returns a list of substrings''' return s.strip().split(',') def main(): examples = '''Examples: deadlock 181 # Analyze PID 181 deadlock 181 --binary /lib/x86_64-linux-gnu/libpthread.so.0 # Analyze PID 181 and locks from this binary. # If tracing a process that is running from # a dynamically-linked binary, this argument # is required and should be the path to the # pthread library. deadlock 181 --verbose # Analyze PID 181 and print statistics about # the mutex wait graph. deadlock 181 --lock-symbols my_mutex_lock1,my_mutex_lock2 \\ --unlock-symbols my_mutex_unlock1,my_mutex_unlock2 # Analyze PID 181 and trace custom mutex # symbols instead of pthread mutexes. deadlock 181 --dump-graph graph.json # Analyze PID 181 and dump the mutex wait # graph to graph.json. ''' parser = argparse.ArgumentParser( description=( 'Detect potential deadlocks (lock inversions) in a running binary.' '\nMust be run as root.' ), formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples, ) parser.add_argument('pid', type=int, help='Pid to trace') # Binaries with `:` in the path will fail to attach uprobes on kernels # running without this patch: https://lkml.org/lkml/2017/1/13/585. # Symlinks to the binary without `:` in the path can get around this issue. parser.add_argument( '--binary', type=str, default='', help='If set, trace the mutexes from the binary at this path. ' 'For statically-linked binaries, this argument is not required. ' 'For dynamically-linked binaries, this argument is required and ' 'should be the path of the pthread library the binary is using. ' 'Example: /lib/x86_64-linux-gnu/libpthread.so.0', ) parser.add_argument( '--dump-graph', type=str, default='', help='If set, this will dump the mutex graph to the specified file.', ) parser.add_argument( '--verbose', action='store_true', help='Print statistics about the mutex wait graph.', ) parser.add_argument( '--lock-symbols', type=strlist, default=['pthread_mutex_lock'], help='Comma-separated list of lock symbols to trace. Default is ' 'pthread_mutex_lock. These symbols cannot be inlined in the binary.', ) parser.add_argument( '--unlock-symbols', type=strlist, default=['pthread_mutex_unlock'], help='Comma-separated list of unlock symbols to trace. Default is ' 'pthread_mutex_unlock. These symbols cannot be inlined in the binary.', ) args = parser.parse_args() if not args.binary: try: args.binary = os.readlink('/proc/%d/exe' % args.pid) except OSError as e: print('%s. Is the process (pid=%d) running?' % (str(e), args.pid)) sys.exit(1) bpf = BPF(src_file=b'deadlock.c') # Trace where threads are created bpf.attach_kretprobe(event=bpf.get_syscall_fnname('clone'), fn_name='trace_clone') # We must trace unlock first, otherwise in the time we attached the probe # on lock() and have not yet attached the probe on unlock(), a thread can # acquire mutexes and release them, but the release events will not be # traced, resulting in noisy reports. for symbol in args.unlock_symbols: try: bpf.attach_uprobe( name=args.binary, sym=symbol, fn_name='trace_mutex_release', pid=args.pid, ) except Exception as e: print('%s. Failed to attach to symbol: %s' % (str(e), symbol)) sys.exit(1) for symbol in args.lock_symbols: try: bpf.attach_uprobe( name=args.binary, sym=symbol, fn_name='trace_mutex_acquire', pid=args.pid, ) except Exception as e: print('%s. Failed to attach to symbol: %s' % (str(e), symbol)) sys.exit(1) def print_stack_trace(stack_id): '''Closure that prints the symbolized stack trace.''' for addr in bpf.get_table('stack_traces').walk(stack_id): line = bpf.sym(addr, args.pid) # Try to symbolize with objdump if we cannot with bpf. if line == '[unknown]': symbol = symbolize_with_objdump(args.binary, addr) if symbol: line = symbol print('@ %016x %s' % (addr, line)) print('Tracing... Hit Ctrl-C to end.') while True: try: # Map of child thread pid -> parent info thread_info = { child.value: (parent.parent_pid, parent.stack_id, parent.comm) for child, parent in bpf.get_table('thread_to_parent').items() } # Mutex wait directed graph. Nodes are mutexes. Edge (A,B) exists # if there exists some thread T where lock(A) was called and # lock(B) was called before unlock(A) was called. graph = DiGraph() for key, leaf in bpf.get_table('edges').items(): graph.add_edge( key.mutex1, key.mutex2, thread_pid=leaf.thread_pid, thread_comm=leaf.comm.decode('utf-8'), first_mutex_stack_id=leaf.mutex1_stack_id, second_mutex_stack_id=leaf.mutex2_stack_id, ) if args.verbose: print( 'Mutexes: %d, Edges: %d' % (len(graph.nodes()), len(graph.edges())) ) if args.dump_graph: with open(args.dump_graph, 'w') as f: data = graph.node_link_data() f.write(json.dumps(data, indent=2)) cycle = find_cycle(graph) if cycle: print_cycle( args.binary, graph, cycle, thread_info, print_stack_trace ) sys.exit(1) time.sleep(1) except KeyboardInterrupt: break if __name__ == '__main__': main() bpfcc-0.12.0/tools/deadlock_example.txt000066400000000000000000000376371357404205000200510ustar00rootroot00000000000000Demonstrations of deadlock. This program detects potential deadlocks on a running process. The program attaches uprobes on `pthread_mutex_lock` and `pthread_mutex_unlock` to build a mutex wait directed graph, and then looks for a cycle in this graph. This graph has the following properties: - Nodes in the graph represent mutexes. - Edge (A, B) exists if there exists some thread T where lock(A) was called and lock(B) was called before unlock(A) was called. If there is a cycle in this graph, this indicates that there is a lock order inversion (potential deadlock). If the program finds a lock order inversion, the program will dump the cycle of mutexes, dump the stack traces where each mutex was acquired, and then exit. This program can only find potential deadlocks that occur while the program is tracing the process. It cannot find deadlocks that may have occurred before the program was attached to the process. Since this traces all mutex lock and unlock events and all thread creation events on the traced process, the overhead of this bpf program can be very high if the process has many threads and mutexes. You should only run this on a process where the slowdown is acceptable. Note: This tool does not work for shared mutexes or recursive mutexes. For shared (read-write) mutexes, a deadlock requires a cycle in the wait graph where at least one of the mutexes in the cycle is acquiring exclusive (write) ownership. For recursive mutexes, lock() is called multiple times on the same mutex. However, there is no way to determine if a mutex is a recursive mutex after the mutex has been created. As a result, this tool will not find potential deadlocks that involve only one mutex. # ./deadlock.py 181 Tracing... Hit Ctrl-C to end. ---------------- Potential Deadlock Detected! Cycle in lock order graph: Mutex M0 (main::static_mutex3 0x0000000000473c60) => Mutex M1 (0x00007fff6d738400) => Mutex M2 (global_mutex1 0x0000000000473be0) => Mutex M3 (global_mutex2 0x0000000000473c20) => Mutex M0 (main::static_mutex3 0x0000000000473c60) Mutex M1 (0x00007fff6d738400) acquired here while holding Mutex M0 (main::static_mutex3 0x0000000000473c60) in Thread 357250 (lockinversion): @ 00000000004024d0 pthread_mutex_lock @ 0000000000406dd0 std::mutex::lock() @ 00000000004070d2 std::lock_guard::lock_guard(std::mutex&) @ 0000000000402e38 main::{lambda()#3}::operator()() const @ 0000000000406ba8 void std::_Bind_simple::_M_invoke<>(std::_Index_tuple<>) @ 0000000000406951 std::_Bind_simple::operator()() @ 000000000040673a std::thread::_Impl >::_M_run() @ 00007fd4496564e1 execute_native_thread_routine @ 00007fd449dd57f1 start_thread @ 00007fd44909746d __clone Mutex M0 (main::static_mutex3 0x0000000000473c60) previously acquired by the same Thread 357250 (lockinversion) here: @ 00000000004024d0 pthread_mutex_lock @ 0000000000406dd0 std::mutex::lock() @ 00000000004070d2 std::lock_guard::lock_guard(std::mutex&) @ 0000000000402e22 main::{lambda()#3}::operator()() const @ 0000000000406ba8 void std::_Bind_simple::_M_invoke<>(std::_Index_tuple<>) @ 0000000000406951 std::_Bind_simple::operator()() @ 000000000040673a std::thread::_Impl >::_M_run() @ 00007fd4496564e1 execute_native_thread_routine @ 00007fd449dd57f1 start_thread @ 00007fd44909746d __clone Mutex M2 (global_mutex1 0x0000000000473be0) acquired here while holding Mutex M1 (0x00007fff6d738400) in Thread 357251 (lockinversion): @ 00000000004024d0 pthread_mutex_lock @ 0000000000406dd0 std::mutex::lock() @ 00000000004070d2 std::lock_guard::lock_guard(std::mutex&) @ 0000000000402ea8 main::{lambda()#4}::operator()() const @ 0000000000406b46 void std::_Bind_simple::_M_invoke<>(std::_Index_tuple<>) @ 000000000040692d std::_Bind_simple::operator()() @ 000000000040671c std::thread::_Impl >::_M_run() @ 00007fd4496564e1 execute_native_thread_routine @ 00007fd449dd57f1 start_thread @ 00007fd44909746d __clone Mutex M1 (0x00007fff6d738400) previously acquired by the same Thread 357251 (lockinversion) here: @ 00000000004024d0 pthread_mutex_lock @ 0000000000406dd0 std::mutex::lock() @ 00000000004070d2 std::lock_guard::lock_guard(std::mutex&) @ 0000000000402e97 main::{lambda()#4}::operator()() const @ 0000000000406b46 void std::_Bind_simple::_M_invoke<>(std::_Index_tuple<>) @ 000000000040692d std::_Bind_simple::operator()() @ 000000000040671c std::thread::_Impl >::_M_run() @ 00007fd4496564e1 execute_native_thread_routine @ 00007fd449dd57f1 start_thread @ 00007fd44909746d __clone Mutex M3 (global_mutex2 0x0000000000473c20) acquired here while holding Mutex M2 (global_mutex1 0x0000000000473be0) in Thread 357247 (lockinversion): @ 00000000004024d0 pthread_mutex_lock @ 0000000000406dd0 std::mutex::lock() @ 00000000004070d2 std::lock_guard::lock_guard(std::mutex&) @ 0000000000402d5f main::{lambda()#1}::operator()() const @ 0000000000406c6c void std::_Bind_simple::_M_invoke<>(std::_Index_tuple<>) @ 0000000000406999 std::_Bind_simple::operator()() @ 0000000000406776 std::thread::_Impl >::_M_run() @ 00007fd4496564e1 execute_native_thread_routine @ 00007fd449dd57f1 start_thread @ 00007fd44909746d __clone Mutex M2 (global_mutex1 0x0000000000473be0) previously acquired by the same Thread 357247 (lockinversion) here: @ 00000000004024d0 pthread_mutex_lock @ 0000000000406dd0 std::mutex::lock() @ 00000000004070d2 std::lock_guard::lock_guard(std::mutex&) @ 0000000000402d4e main::{lambda()#1}::operator()() const @ 0000000000406c6c void std::_Bind_simple::_M_invoke<>(std::_Index_tuple<>) @ 0000000000406999 std::_Bind_simple::operator()() @ 0000000000406776 std::thread::_Impl >::_M_run() @ 00007fd4496564e1 execute_native_thread_routine @ 00007fd449dd57f1 start_thread @ 00007fd44909746d __clone Mutex M0 (main::static_mutex3 0x0000000000473c60) acquired here while holding Mutex M3 (global_mutex2 0x0000000000473c20) in Thread 357248 (lockinversion): @ 00000000004024d0 pthread_mutex_lock @ 0000000000406dd0 std::mutex::lock() @ 00000000004070d2 std::lock_guard::lock_guard(std::mutex&) @ 0000000000402dc9 main::{lambda()#2}::operator()() const @ 0000000000406c0a void std::_Bind_simple::_M_invoke<>(std::_Index_tuple<>) @ 0000000000406975 std::_Bind_simple::operator()() @ 0000000000406758 std::thread::_Impl >::_M_run() @ 00007fd4496564e1 execute_native_thread_routine @ 00007fd449dd57f1 start_thread @ 00007fd44909746d __clone Mutex M3 (global_mutex2 0x0000000000473c20) previously acquired by the same Thread 357248 (lockinversion) here: @ 00000000004024d0 pthread_mutex_lock @ 0000000000406dd0 std::mutex::lock() @ 00000000004070d2 std::lock_guard::lock_guard(std::mutex&) @ 0000000000402db8 main::{lambda()#2}::operator()() const @ 0000000000406c0a void std::_Bind_simple::_M_invoke<>(std::_Index_tuple<>) @ 0000000000406975 std::_Bind_simple::operator()() @ 0000000000406758 std::thread::_Impl >::_M_run() @ 00007fd4496564e1 execute_native_thread_routine @ 00007fd449dd57f1 start_thread @ 00007fd44909746d __clone Thread 357248 created by Thread 350692 (lockinversion) here: @ 00007fd449097431 __clone @ 00007fd449dd5ef5 pthread_create @ 00007fd449658440 std::thread::_M_start_thread(std::shared_ptr) @ 00000000004033ac std::thread::thread(main::{lambda()#2}&&) @ 000000000040308f main @ 00007fd448faa0f6 __libc_start_main @ 0000000000402ad8 [unknown] Thread 357250 created by Thread 350692 (lockinversion) here: @ 00007fd449097431 __clone @ 00007fd449dd5ef5 pthread_create @ 00007fd449658440 std::thread::_M_start_thread(std::shared_ptr) @ 00000000004034b2 std::thread::thread(main::{lambda()#3}&&) @ 00000000004030b9 main @ 00007fd448faa0f6 __libc_start_main @ 0000000000402ad8 [unknown] Thread 357251 created by Thread 350692 (lockinversion) here: @ 00007fd449097431 __clone @ 00007fd449dd5ef5 pthread_create @ 00007fd449658440 std::thread::_M_start_thread(std::shared_ptr) @ 00000000004035b8 std::thread::thread(main::{lambda()#4}&&) @ 00000000004030e6 main @ 00007fd448faa0f6 __libc_start_main @ 0000000000402ad8 [unknown] Thread 357247 created by Thread 350692 (lockinversion) here: @ 00007fd449097431 __clone @ 00007fd449dd5ef5 pthread_create @ 00007fd449658440 std::thread::_M_start_thread(std::shared_ptr) @ 00000000004032a6 std::thread::thread(main::{lambda()#1}&&) @ 0000000000403070 main @ 00007fd448faa0f6 __libc_start_main @ 0000000000402ad8 [unknown] This is output from a process that has a potential deadlock involving 4 mutexes and 4 threads: - Thread 357250 acquired M1 while holding M0 (edge M0 -> M1) - Thread 357251 acquired M2 while holding M1 (edge M1 -> M2) - Thread 357247 acquired M3 while holding M2 (edge M2 -> M3) - Thread 357248 acquired M0 while holding M3 (edge M3 -> M0) This is the C++ program that generated the output above: ```c++ #include #include #include #include std::mutex global_mutex1; std::mutex global_mutex2; int main(void) { static std::mutex static_mutex3; std::mutex local_mutex4; std::cout << "sleeping for a bit to allow trace to attach..." << std::endl; std::this_thread::sleep_for(std::chrono::seconds(10)); std::cout << "starting program..." << std::endl; auto t1 = std::thread([] { std::lock_guard g1(global_mutex1); std::lock_guard g2(global_mutex2); }); t1.join(); auto t2 = std::thread([] { std::lock_guard g2(global_mutex2); std::lock_guard g3(static_mutex3); }); t2.join(); auto t3 = std::thread([&local_mutex4] { std::lock_guard g3(static_mutex3); std::lock_guard g4(local_mutex4); }); t3.join(); auto t4 = std::thread([&local_mutex4] { std::lock_guard g4(local_mutex4); std::lock_guard g1(global_mutex1); }); t4.join(); std::cout << "sleeping to allow trace to collect data..." << std::endl; std::this_thread::sleep_for(std::chrono::seconds(5)); std::cout << "done!" << std::endl; } ``` Note that an actual deadlock did not occur, although this mutex lock ordering creates the possibility of a deadlock, and this is a hint to the programmer to reconsider the lock ordering. If the mutexes are global or static and debug symbols are enabled, the output will contain the mutex symbol name. The output uses a similar format as ThreadSanitizer (https://github.com/google/sanitizers/wiki/ThreadSanitizerDeadlockDetector). # ./deadlock.py 181 --binary /usr/local/bin/lockinversion Tracing... Hit Ctrl-C to end. ^C If the traced process is instantiated from a statically-linked executable, this argument is optional, and the program will determine the path of the executable from the pid. However, on older kernels without this patch ("uprobe: Find last occurrence of ':' when parsing uprobe PATH:OFFSET", https://lkml.org/lkml/2017/1/13/585), binaries that contain `:` in the path cannot be attached with uprobes. As a workaround, we can create a symlink to the binary, and provide the symlink name instead to the `--binary` option. # ./deadlock.py 181 --binary /lib/x86_64-linux-gnu/libpthread.so.0 Tracing... Hit Ctrl-C to end. ^C If the traced process is instantiated from a dynamically-linked executable, this argument is required and needs to be the path to the pthread shared library used by the executable. # ./deadlock.py 181 --dump-graph graph.json --verbose Tracing... Hit Ctrl-C to end. Mutexes: 0, Edges: 0 Mutexes: 532, Edges: 411 Mutexes: 735, Edges: 675 Mutexes: 1118, Edges: 1278 Mutexes: 1666, Edges: 2185 Mutexes: 2056, Edges: 2694 Mutexes: 2245, Edges: 2906 Mutexes: 2656, Edges: 3479 Mutexes: 2813, Edges: 3785 ^C If the program does not find a deadlock, it will keep running until you hit Ctrl-C. If you pass the `--verbose` flag, the program will also dump statistics about the number of mutexes and edges in the mutex wait graph. If you want to serialize the graph to analyze it later, you can pass the `--dump-graph FILE` flag, and the program will serialize the graph in json. # ./deadlock.py 181 --lock-symbols custom_mutex1_lock,custom_mutex2_lock --unlock_symbols custom_mutex1_unlock,custom_mutex2_unlock --verbose Tracing... Hit Ctrl-C to end. Mutexes: 0, Edges: 0 Mutexes: 532, Edges: 411 Mutexes: 735, Edges: 675 Mutexes: 1118, Edges: 1278 Mutexes: 1666, Edges: 2185 Mutexes: 2056, Edges: 2694 Mutexes: 2245, Edges: 2906 Mutexes: 2656, Edges: 3479 Mutexes: 2813, Edges: 3785 ^C If your program is using custom mutexes and not pthread mutexes, you can use the `--lock-symbols` and `--unlock-symbols` flags to specify different mutex symbols to trace. The flags take a comma-separated string of symbol names. Note that if the symbols are inlined in the binary, then this program can result in false positives. USAGE message: # ./deadlock.py -h usage: deadlock.py [-h] [--binary BINARY] [--dump-graph DUMP_GRAPH] [--verbose] [--lock-symbols LOCK_SYMBOLS] [--unlock-symbols UNLOCK_SYMBOLS] pid Detect potential deadlocks (lock inversions) in a running binary. Must be run as root. positional arguments: pid Pid to trace optional arguments: -h, --help show this help message and exit --binary BINARY If set, trace the mutexes from the binary at this path. For statically-linked binaries, this argument is not required. For dynamically-linked binaries, this argument is required and should be the path of the pthread library the binary is using. Example: /lib/x86_64-linux-gnu/libpthread.so.0 --dump-graph DUMP_GRAPH If set, this will dump the mutex graph to the specified file. --verbose Print statistics about the mutex wait graph. --lock-symbols LOCK_SYMBOLS Comma-separated list of lock symbols to trace. Default is pthread_mutex_lock. These symbols cannot be inlined in the binary. --unlock-symbols UNLOCK_SYMBOLS Comma-separated list of unlock symbols to trace. Default is pthread_mutex_unlock. These symbols cannot be inlined in the binary. Examples: deadlock 181 # Analyze PID 181 deadlock 181 --binary /lib/x86_64-linux-gnu/libpthread.so.0 # Analyze PID 181 and locks from this binary. # If tracing a process that is running from # a dynamically-linked binary, this argument # is required and should be the path to the # pthread library. deadlock 181 --verbose # Analyze PID 181 and print statistics about # the mutex wait graph. deadlock 181 --lock-symbols my_mutex_lock1,my_mutex_lock2 \ --unlock-symbols my_mutex_unlock1,my_mutex_unlock2 # Analyze PID 181 and trace custom mutex # symbols instead of pthread mutexes. deadlock 181 --dump-graph graph.json # Analyze PID 181 and dump the mutex wait # graph to graph.json. bpfcc-0.12.0/tools/drsnoop.py000077500000000000000000000152541357404205000160570ustar00rootroot00000000000000#!/usr/bin/python # @lint-avoid-python-3-compatibility-imports # # drsnoop Trace direct reclaim and print details including issuing PID. # For Linux, uses BCC, eBPF. # # This uses in-kernel eBPF maps to cache process details (PID and comm) by # direct reclaim begin, as well as a starting timestamp for calculating # latency. # # Copyright (c) 2019 Ethercflow # Licensed under the Apache License, Version 2.0 (the "License") # # 20-Feb-2019 Ethercflow Created this. # 09-Mar-2019 Ethercflow Updated for show sys mem info. from __future__ import print_function from bcc import ArgString, BPF import argparse from datetime import datetime, timedelta import os import math # symbols kallsyms = "/proc/kallsyms" # arguments examples = """examples: ./drsnoop # trace all direct reclaim ./drsnoop -T # include timestamps ./drsnoop -U # include UID ./drsnoop -P 181 # only trace PID 181 ./drsnoop -t 123 # only trace TID 123 ./drsnoop -u 1000 # only trace UID 1000 ./drsnoop -d 10 # trace for 10 seconds only ./drsnoop -n main # only print process names containing "main" """ parser = argparse.ArgumentParser( description="Trace direct reclaim", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("-T", "--timestamp", action="store_true", help="include timestamp on output") parser.add_argument("-U", "--print-uid", action="store_true", help="print UID column") parser.add_argument("-p", "--pid", help="trace this PID only") parser.add_argument("-t", "--tid", help="trace this TID only") parser.add_argument("-u", "--uid", help="trace this UID only") parser.add_argument("-d", "--duration", help="total duration of trace in seconds") parser.add_argument("-n", "--name", type=ArgString, help="only print process names containing this name") parser.add_argument("-v", "--verbose", action="store_true", help="show system memory state") parser.add_argument("--ebpf", action="store_true", help=argparse.SUPPRESS) args = parser.parse_args() debug = 0 if args.duration: args.duration = timedelta(seconds=int(args.duration)) # vm_stat vm_stat_addr = '' with open(kallsyms) as syms: for line in syms: (addr, size, name) = line.rstrip().split(" ", 2) name = name.split("\t")[0] if name == "vm_stat": vm_stat_addr = "0x" + addr break if name == "vm_zone_stat": vm_stat_addr = "0x" + addr break if vm_stat_addr == '': print("ERROR: no vm_stat or vm_zone_stat in /proc/kallsyms. Exiting.") print("HINT: the kernel should be built with CONFIG_KALLSYMS_ALL.") exit() NR_FREE_PAGES = 0 PAGE_SIZE = os.sysconf("SC_PAGE_SIZE") PAGE_SHIFT = int(math.log(PAGE_SIZE) / math.log(2)) def K(x): return x << (PAGE_SHIFT - 10) # load BPF program bpf_text = """ #include #include #include struct val_t { u64 id; u64 ts; // start time char name[TASK_COMM_LEN]; u64 vm_stat[NR_VM_ZONE_STAT_ITEMS]; }; struct data_t { u64 id; u32 uid; u64 nr_reclaimed; u64 delta; u64 ts; // end time char name[TASK_COMM_LEN]; u64 vm_stat[NR_VM_ZONE_STAT_ITEMS]; }; BPF_HASH(start, u64, struct val_t); BPF_PERF_OUTPUT(events); TRACEPOINT_PROBE(vmscan, mm_vmscan_direct_reclaim_begin) { struct val_t val = {}; u64 id = bpf_get_current_pid_tgid(); u32 pid = id >> 32; // PID is higher part u32 tid = id; // Cast and get the lower part u32 uid = bpf_get_current_uid_gid(); u64 ts; PID_TID_FILTER UID_FILTER if (bpf_get_current_comm(&val.name, sizeof(val.name)) == 0) { val.id = id; val.ts = bpf_ktime_get_ns(); bpf_probe_read(&val.vm_stat, sizeof(val.vm_stat), (const void *)%s); start.update(&id, &val); } return 0; } TRACEPOINT_PROBE(vmscan, mm_vmscan_direct_reclaim_end) { u64 id = bpf_get_current_pid_tgid(); struct val_t *valp; struct data_t data = {}; u64 ts = bpf_ktime_get_ns(); valp = start.lookup(&id); if (valp == NULL) { // missed entry return 0; } data.delta = ts - valp->ts; data.ts = ts / 1000; data.id = valp->id; data.uid = bpf_get_current_uid_gid(); bpf_probe_read(&data.name, sizeof(data.name), valp->name); bpf_probe_read(&data.vm_stat, sizeof(data.vm_stat), valp->vm_stat); data.nr_reclaimed = args->nr_reclaimed; events.perf_submit(args, &data, sizeof(data)); start.delete(&id); return 0; } """ % vm_stat_addr if args.tid: # TID trumps PID bpf_text = bpf_text.replace('PID_TID_FILTER', 'if (tid != %s) { return 0; }' % args.tid) elif args.pid: bpf_text = bpf_text.replace('PID_TID_FILTER', 'if (pid != %s) { return 0; }' % args.pid) else: bpf_text = bpf_text.replace('PID_TID_FILTER', '') if args.uid: bpf_text = bpf_text.replace('UID_FILTER', 'if (uid != %s) { return 0; }' % args.uid) else: bpf_text = bpf_text.replace('UID_FILTER', '') if debug or args.ebpf: print(bpf_text) if args.ebpf: exit() # initialize BPF b = BPF(text=bpf_text) initial_ts = 0 # header if args.timestamp: print("%-14s" % ("TIME(s)"), end="") if args.print_uid: print("%-6s" % ("UID"), end="") print("%-14s %-6s %8s %5s" % ("COMM", "TID" if args.tid else "PID", "LAT(ms)", "PAGES"), end="") if args.verbose: print("%10s" % ("FREE(KB)")) else: print("") # process event def print_event(cpu, data, size): event = b["events"].event(data) global initial_ts if not initial_ts: initial_ts = event.ts if args.name and bytes(args.name) not in event.name: return if args.timestamp: delta = event.ts - initial_ts print("%-14.9f" % (float(delta) / 1000000), end="") if args.print_uid: print("%-6d" % event.uid, end="") print("%-14.14s %-6s %8.2f %5d" % (event.name.decode('utf-8', 'replace'), event.id & 0xffffffff if args.tid else event.id >> 32, float(event.delta) / 1000000, event.nr_reclaimed), end="") if args.verbose: print("%10d" % K(event.vm_stat[NR_FREE_PAGES])) else: print("") # loop with callback to print_event b["events"].open_perf_buffer(print_event, page_cnt=64) start_time = datetime.now() while not args.duration or datetime.now() - start_time < args.duration: try: b.perf_buffer_poll() except KeyboardInterrupt: exit() bpfcc-0.12.0/tools/drsnoop_example.txt000066400000000000000000000120001357404205000177400ustar00rootroot00000000000000Demonstrations of drsnoop, the Linux eBPF/bcc version. drsnoop traces the direct reclaim system-wide, and prints various details. Example output: # ./drsnoop COMM PID LAT(ms) PAGES summond 17678 0.19 143 summond 17669 0.55 313 summond 17669 0.15 145 summond 17669 0.27 237 summond 17669 0.48 111 summond 17669 0.16 75 head 17821 0.29 339 head 17825 0.17 109 summond 17669 0.14 73 summond 17496 104.84 40 summond 17678 0.32 167 summond 17678 0.14 106 summond 17678 0.16 67 summond 17678 0.29 267 summond 17678 0.27 69 summond 17678 0.32 46 base64 17816 0.16 85 summond 17678 0.43 283 summond 17678 0.14 182 head 17736 0.57 135 ^C While tracing, the processes alloc pages,due to insufficient memory available in the system, direct reclaim events happened, which will increase the waiting delay of the processes. drsnoop can be useful for discovering when allocstall(/proc/vmstat) continues to increase, whether it is caused by some critical processes or not. The -p option can be used to filter on a PID, which is filtered in-kernel. Here I've used it with -T to print timestamps: # ./drsnoop -Tp 17491 TIME(s) COMM PID LAT(ms) PAGES 107.364115000 summond 17491 0.24 50 107.364550000 summond 17491 0.26 38 107.365266000 summond 17491 0.36 72 107.365753000 summond 17491 0.22 49 ^C This shows the summond process allocs pages, and direct reclaim events happening, and the delays are not affected much. The -U option include UID on output: # ./drsnoop -U UID COMM PID LAT(ms) PAGES 1000 summond 17678 0.32 46 0 base64 17816 0.16 85 1000 summond 17678 0.43 283 1000 summond 17678 0.14 182 0 head 17821 0.29 339 0 head 17825 0.17 109 ^C The -u option filtering UID: # ./drsnoop -Uu 1000 UID COMM PID LAT(ms) PAGES 1000 summond 17678 0.19 143 1000 summond 17669 0.55 313 1000 summond 17669 0.15 145 1000 summond 17669 0.27 237 1000 summond 17669 0.48 111 1000 summond 17669 0.16 75 1000 summond 17669 0.14 73 1000 summond 17678 0.32 167 ^C A maximum tracing duration can be set with the -d option. For example, to trace for 2 seconds: # ./drsnoop -d 2 COMM PID LAT(ms) PAGES head 21715 0.15 195 The -n option can be used to filter on process name using partial matches: # ./drsnoop -n mond COMM PID LAT(ms) PAGES summond 10271 0.03 51 summond 10271 0.03 51 summond 10259 0.05 51 summond 10269 319.41 37 summond 10270 111.73 35 summond 10270 0.11 78 summond 10270 0.12 71 summond 10270 0.03 35 summond 10277 111.62 41 summond 10277 0.08 45 summond 10277 0.06 32 ^C This caught the 'summond' command because it partially matches 'mond' that's passed to the '-n' option. The -v option can be used to show system memory state (now only free mem) at the beginning of direct reclaiming: # ./drsnoop.py -v COMM PID LAT(ms) PAGES FREE(KB) base64 34924 0.23 151 86260 base64 34962 0.26 149 86260 head 34931 0.24 150 86260 base64 34902 0.19 148 86260 head 34963 0.19 151 86228 base64 34959 0.17 151 86228 head 34965 0.29 190 86228 base64 34957 0.24 152 86228 summond 34870 0.15 151 86080 summond 34870 0.12 115 86184 USAGE message: # ./drsnoop -h usage: drsnoop.py [-h] [-T] [-U] [-p PID] [-t TID] [-u UID] [-d DURATION] [-n NAME] Trace direct reclaim optional arguments: -h, --help show this help message and exit -T, --timestamp include timestamp on output -U, --print-uid print UID column -p PID, --pid PID trace this PID only -t TID, --tid TID trace this TID only -u UID, --uid UID trace this UID only -d DURATION, --duration DURATION total duration of trace in seconds -n NAME, --name NAME only print process names containing this name examples: ./drsnoop # trace all direct reclaim ./drsnoop -T # include timestamps ./drsnoop -U # include UID ./drsnoop -p 181 # only trace PID 181 ./drsnoop -t 123 # only trace TID 123 ./drsnoop -u 1000 # only trace UID 1000 ./drsnoop -d 10 # trace for 10 seconds only ./drsnoop -n main # only print process names containing "main" bpfcc-0.12.0/tools/execsnoop.py000077500000000000000000000167501357404205000164000ustar00rootroot00000000000000#!/usr/bin/python # @lint-avoid-python-3-compatibility-imports # # execsnoop Trace new processes via exec() syscalls. # For Linux, uses BCC, eBPF. Embedded C. # # USAGE: execsnoop [-h] [-T] [-t] [-x] [-q] [-n NAME] [-l LINE] # [--max-args MAX_ARGS] # # This currently will print up to a maximum of 19 arguments, plus the process # name, so 20 fields in total (MAXARG). # # This won't catch all new processes: an application may fork() but not exec(). # # Copyright 2016 Netflix, Inc. # Licensed under the Apache License, Version 2.0 (the "License") # # 07-Feb-2016 Brendan Gregg Created this. from __future__ import print_function from bcc import BPF from bcc.utils import ArgString, printb import bcc.utils as utils import argparse import re import time from collections import defaultdict from time import strftime # arguments examples = """examples: ./execsnoop # trace all exec() syscalls ./execsnoop -x # include failed exec()s ./execsnoop -T # include time (HH:MM:SS) ./execsnoop -t # include timestamps ./execsnoop -q # add "quotemarks" around arguments ./execsnoop -n main # only print command lines containing "main" ./execsnoop -l tpkg # only print command where arguments contains "tpkg" """ parser = argparse.ArgumentParser( description="Trace exec() syscalls", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("-T", "--time", action="store_true", help="include time column on output (HH:MM:SS)") parser.add_argument("-t", "--timestamp", action="store_true", help="include timestamp on output") parser.add_argument("-x", "--fails", action="store_true", help="include failed exec()s") parser.add_argument("-q", "--quote", action="store_true", help="Add quotemarks (\") around arguments." ) parser.add_argument("-n", "--name", type=ArgString, help="only print commands matching this name (regex), any arg") parser.add_argument("-l", "--line", type=ArgString, help="only print commands where arg contains this line (regex)") parser.add_argument("--max-args", default="20", help="maximum number of arguments parsed and displayed, defaults to 20") parser.add_argument("--ebpf", action="store_true", help=argparse.SUPPRESS) args = parser.parse_args() # define BPF program bpf_text = """ #include #include #include #define ARGSIZE 128 enum event_type { EVENT_ARG, EVENT_RET, }; struct data_t { u32 pid; // PID as in the userspace term (i.e. task->tgid in kernel) u32 ppid; // Parent PID as in the userspace term (i.e task->real_parent->tgid in kernel) char comm[TASK_COMM_LEN]; enum event_type type; char argv[ARGSIZE]; int retval; }; BPF_PERF_OUTPUT(events); static int __submit_arg(struct pt_regs *ctx, void *ptr, struct data_t *data) { bpf_probe_read(data->argv, sizeof(data->argv), ptr); events.perf_submit(ctx, data, sizeof(struct data_t)); return 1; } static int submit_arg(struct pt_regs *ctx, void *ptr, struct data_t *data) { const char *argp = NULL; bpf_probe_read(&argp, sizeof(argp), ptr); if (argp) { return __submit_arg(ctx, (void *)(argp), data); } return 0; } int syscall__execve(struct pt_regs *ctx, const char __user *filename, const char __user *const __user *__argv, const char __user *const __user *__envp) { // create data here and pass to submit_arg to save stack space (#555) struct data_t data = {}; struct task_struct *task; data.pid = bpf_get_current_pid_tgid() >> 32; task = (struct task_struct *)bpf_get_current_task(); // Some kernels, like Ubuntu 4.13.0-generic, return 0 // as the real_parent->tgid. // We use the get_ppid function as a fallback in those cases. (#1883) data.ppid = task->real_parent->tgid; bpf_get_current_comm(&data.comm, sizeof(data.comm)); data.type = EVENT_ARG; __submit_arg(ctx, (void *)filename, &data); // skip first arg, as we submitted filename #pragma unroll for (int i = 1; i < MAXARG; i++) { if (submit_arg(ctx, (void *)&__argv[i], &data) == 0) goto out; } // handle truncated argument list char ellipsis[] = "..."; __submit_arg(ctx, (void *)ellipsis, &data); out: return 0; } int do_ret_sys_execve(struct pt_regs *ctx) { struct data_t data = {}; struct task_struct *task; data.pid = bpf_get_current_pid_tgid() >> 32; task = (struct task_struct *)bpf_get_current_task(); // Some kernels, like Ubuntu 4.13.0-generic, return 0 // as the real_parent->tgid. // We use the get_ppid function as a fallback in those cases. (#1883) data.ppid = task->real_parent->tgid; bpf_get_current_comm(&data.comm, sizeof(data.comm)); data.type = EVENT_RET; data.retval = PT_REGS_RC(ctx); events.perf_submit(ctx, &data, sizeof(data)); return 0; } """ bpf_text = bpf_text.replace("MAXARG", args.max_args) if args.ebpf: print(bpf_text) exit() # initialize BPF b = BPF(text=bpf_text) execve_fnname = b.get_syscall_fnname("execve") b.attach_kprobe(event=execve_fnname, fn_name="syscall__execve") b.attach_kretprobe(event=execve_fnname, fn_name="do_ret_sys_execve") # header if args.time: print("%-9s" % ("TIME"), end="") if args.timestamp: print("%-8s" % ("TIME(s)"), end="") print("%-16s %-6s %-6s %3s %s" % ("PCOMM", "PID", "PPID", "RET", "ARGS")) class EventType(object): EVENT_ARG = 0 EVENT_RET = 1 start_ts = time.time() argv = defaultdict(list) # This is best-effort PPID matching. Short-lived processes may exit # before we get a chance to read the PPID. # This is a fallback for when fetching the PPID from task->real_parent->tgip # returns 0, which happens in some kernel versions. def get_ppid(pid): try: with open("/proc/%d/status" % pid) as status: for line in status: if line.startswith("PPid:"): return int(line.split()[1]) except IOError: pass return 0 # process event def print_event(cpu, data, size): event = b["events"].event(data) skip = False if event.type == EventType.EVENT_ARG: argv[event.pid].append(event.argv) elif event.type == EventType.EVENT_RET: if event.retval != 0 and not args.fails: skip = True if args.name and not re.search(bytes(args.name), event.comm): skip = True if args.line and not re.search(bytes(args.line), b' '.join(argv[event.pid])): skip = True if args.quote: argv[event.pid] = [ b"\"" + arg.replace(b"\"", b"\\\"") + b"\"" for arg in argv[event.pid] ] if not skip: if args.time: printb(b"%-9s" % strftime("%H:%M:%S").encode('ascii'), nl="") if args.timestamp: printb(b"%-8.3f" % (time.time() - start_ts), nl="") ppid = event.ppid if event.ppid > 0 else get_ppid(event.pid) ppid = b"%d" % ppid if ppid > 0 else b"?" argv_text = b' '.join(argv[event.pid]).replace(b'\n', b'\\n') printb(b"%-16s %-6d %-6s %3d %s" % (event.comm, event.pid, ppid, event.retval, argv_text)) try: del(argv[event.pid]) except Exception: pass # loop with callback to print_event b["events"].open_perf_buffer(print_event) while 1: try: b.perf_buffer_poll() except KeyboardInterrupt: exit() bpfcc-0.12.0/tools/execsnoop_example.txt000066400000000000000000000113401357404205000202650ustar00rootroot00000000000000Demonstrations of execsnoop, the Linux eBPF/bcc version. execsnoop traces new processes. For example, tracing the commands invoked when running "man ls": # ./execsnoop PCOMM PID RET ARGS bash 15887 0 /usr/bin/man ls preconv 15894 0 /usr/bin/preconv -e UTF-8 man 15896 0 /usr/bin/tbl man 15897 0 /usr/bin/nroff -mandoc -rLL=169n -rLT=169n -Tutf8 man 15898 0 /usr/bin/pager -s nroff 15900 0 /usr/bin/locale charmap nroff 15901 0 /usr/bin/groff -mtty-char -Tutf8 -mandoc -rLL=169n -rLT=169n groff 15902 0 /usr/bin/troff -mtty-char -mandoc -rLL=169n -rLT=169n -Tutf8 groff 15903 0 /usr/bin/grotty The output shows the parent process/command name (PCOMM), the PID, the return value of the exec() (RET), and the filename with arguments (ARGS). This works by traces the execve() system call (commonly used exec() variant), and shows details of the arguments and return value. This catches new processes that follow the fork->exec sequence, as well as processes that re-exec() themselves. Some applications fork() but do not exec(), eg, for worker processes, which won't be included in the execsnoop output. The -x option can be used to include failed exec()s. For example: # ./execsnoop -x PCOMM PID RET ARGS supervise 9660 0 ./run supervise 9661 0 ./run mkdir 9662 0 /bin/mkdir -p ./main run 9663 0 ./run chown 9664 0 /bin/chown nobody:nobody ./main run 9665 0 /bin/mkdir -p ./main supervise 9667 0 ./run run 9660 -2 /usr/local/bin/setuidgid nobody /command/multilog t ./main chown 9668 0 /bin/chown nobody:nobody ./main run 9666 0 /bin/chmod 0777 main run 9663 -2 /usr/local/bin/setuidgid nobody /command/multilog t ./main run 9669 0 /bin/mkdir -p ./main run 9661 -2 /usr/local/bin/setuidgid nobody /command/multilog t ./main supervise 9670 0 ./run [...] This example shows various regular system daemon activity, including some failures (trying to execute a /usr/local/bin/setuidgid, which I just noticed doesn't exist). A -T option can be used to include a time column, a -t option to include a timestamp column, and a -n option to match on a name. Regular expressions are allowed. For example, matching commands containing "mount": # ./execsnoop -Ttn mount TIME TIME(s) PCOMM PID PPID RET ARGS 14:08:23 2.849 mount 18049 1045 0 /bin/mount -p The -l option can be used to only show command where one of the arguments matches specified line. The limitation is that we are looking only into first 20 arguments of the command. For example, matching all command where one of the argument is "testpkg": # ./execsnoop.py -l testpkg PCOMM PID PPID RET ARGS service 3344535 4146419 0 /usr/sbin/service testpkg status systemctl 3344535 4146419 0 /bin/systemctl status testpkg.service yum 3344856 4146419 0 /usr/local/bin/yum remove testpkg python 3344856 4146419 0 /usr/local/bin/python /usr/local/bin/yum remove testpkg yum 3344856 4146419 0 /usr/bin/yum remove testpkg yum 3345086 4146419 0 /usr/local/bin/yum install testpkg python 3345086 4146419 0 /usr/local/bin/python /usr/local/bin/yum install testpkg yum 3345086 4146419 0 /usr/bin/yum install testpkg rpm 3345452 4146419 0 /bin/rpm -qa testpkg USAGE message: # ./execsnoop -h usage: execsnoop [-h] [-T] [-t] [-x] [-q] [-n NAME] [-l LINE] [--max-args MAX_ARGS] Trace exec() syscalls optional arguments: -h, --help show this help message and exit -T, --time include time column on output (HH:MM:SS) -t, --timestamp include timestamp on output -x, --fails include failed exec()s -q, --quote Add quotemarks (") around arguments -n NAME, --name NAME only print commands matching this name (regex), any arg -l LINE, --line LINE only print commands where arg contains this line (regex) --max-args MAX_ARGS maximum number of arguments parsed and displayed, defaults to 20 examples: ./execsnoop # trace all exec() syscalls ./execsnoop -x # include failed exec()s ./execsnoop -T # include time (HH:MM:SS) ./execsnoop -t # include timestamps ./execsnoop -q # add "quotemarks" around arguments ./execsnoop -n main # only print command lines containing "main" ./execsnoop -l tpkg # only print command where arguments contains "tpkg" bpfcc-0.12.0/tools/exitsnoop.py000077500000000000000000000241551357404205000164230ustar00rootroot00000000000000#!/usr/bin/python # @lint-avoid-python-3-compatibility-imports from __future__ import print_function import argparse import ctypes as ct import os import platform import re import signal import sys from bcc import BPF from datetime import datetime from time import strftime # # exitsnoop Trace all process termination (exit, fatal signal) # For Linux, uses BCC, eBPF. Embedded C. # # USAGE: exitsnoop [-h] [-x] [-t] [--utc] [--label[=LABEL]] [-p PID] # _examples = """examples: exitsnoop # trace all process termination exitsnoop -x # trace only fails, exclude exit(0) exitsnoop -t # include timestamps (local time) exitsnoop --utc # include timestamps (UTC) exitsnoop -p 181 # only trace PID 181 exitsnoop --label=exit # label each output line with 'exit' """ """ Exit status (from ): 0 EX_OK Success 2 argparse error 70 EX_SOFTWARE syntax error detected by compiler, or verifier error from kernel 77 EX_NOPERM Need sudo (CAP_SYS_ADMIN) for BPF() system call The template for this script was Brendan Gregg's execsnoop https://github.com/iovisor/bcc/blob/master/tools/execsnoop.py More information about this script is in bcc/tools/exitsnoop_example.txt Copyright 2016 Netflix, Inc. Copyright 2019 Instana, Inc. Licensed under the Apache License, Version 2.0 (the "License") 07-Feb-2016 Brendan Gregg (Netflix) Created execsnoop 04-May-2019 Arturo Martin-de-Nicolas (Instana) Created exitsnoop 13-May-2019 Jeroen Soeters (Instana) Refactor to import as module """ def _getParser(): parser = argparse.ArgumentParser( description="Trace all process termination (exit, fatal signal)", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=_examples) a=parser.add_argument a("-t", "--timestamp", action="store_true", help="include timestamp (local time default)") a("--utc", action="store_true", help="include timestamp in UTC (-t implied)") a("-p", "--pid", help="trace this PID only") a("--label", help="label each line") a("-x", "--failed", action="store_true", help="trace only fails, exclude exit(0)") # print the embedded C program and exit, for debugging a("--ebpf", action="store_true", help=argparse.SUPPRESS) # RHEL 7.6 keeps task->start_time as struct timespec, convert to u64 nanoseconds a("--timespec", action="store_true", help=argparse.SUPPRESS) return parser.parse_args class Global(): parse_args = _getParser() args = None argv = None SIGNUM_TO_SIGNAME = dict((v, re.sub("^SIG", "", k)) for k,v in signal.__dict__.items() if re.match("^SIG[A-Z]+$", k)) class Data(ct.Structure): """Event data matching struct data_t in _embedded_c().""" _TASK_COMM_LEN = 16 # linux/sched.h _pack_ = 1 _fields_ = [ ("start_time", ct.c_ulonglong), # task->start_time, see --timespec arg ("exit_time", ct.c_ulonglong), # bpf_ktime_get_ns() ("pid", ct.c_uint), # task->tgid, thread group id == sys_getpid() ("tid", ct.c_uint), # task->pid, thread id == sys_gettid() ("ppid", ct.c_uint),# task->parent->tgid, notified of exit ("exit_code", ct.c_int), ("sig_info", ct.c_uint), ("task", ct.c_char * _TASK_COMM_LEN) ] def _embedded_c(args): """Generate C program for sched_process_exit tracepoint in kernel/exit.c.""" c = """ EBPF_COMMENT #include BPF_STATIC_ASSERT_DEF struct data_t { u64 start_time; u64 exit_time; u32 pid; u32 tid; u32 ppid; int exit_code; u32 sig_info; char task[TASK_COMM_LEN]; } __attribute__((packed)); BPF_STATIC_ASSERT(sizeof(struct data_t) == CTYPES_SIZEOF_DATA); BPF_PERF_OUTPUT(events); TRACEPOINT_PROBE(sched, sched_process_exit) { struct task_struct *task = (typeof(task))bpf_get_current_task(); if (FILTER_PID || FILTER_EXIT_CODE) { return 0; } struct data_t data = { .start_time = PROCESS_START_TIME_NS, .exit_time = bpf_ktime_get_ns(), .pid = task->tgid, .tid = task->pid, .ppid = task->parent->tgid, .exit_code = task->exit_code >> 8, .sig_info = task->exit_code & 0xFF, }; bpf_get_current_comm(&data.task, sizeof(data.task)); events.perf_submit(args, &data, sizeof(data)); return 0; } """ # TODO: this macro belongs in bcc/src/cc/export/helpers.h bpf_static_assert_def = r""" #ifndef BPF_STATIC_ASSERT #define BPF_STATIC_ASSERT(condition) __attribute__((unused)) \ extern int bpf_static_assert[(condition) ? 1 : -1] #endif """ code_substitutions = [ ('EBPF_COMMENT', '' if not Global.args.ebpf else _ebpf_comment()), ("BPF_STATIC_ASSERT_DEF", bpf_static_assert_def), ("CTYPES_SIZEOF_DATA", str(ct.sizeof(Data))), ('FILTER_PID', '0' if not Global.args.pid else "task->tgid != %s" % Global.args.pid), ('FILTER_EXIT_CODE', '0' if not Global.args.failed else 'task->exit_code == 0'), ('PROCESS_START_TIME_NS', 'task->start_time' if not Global.args.timespec else '(task->start_time.tv_sec * 1000000000L) + task->start_time.tv_nsec'), ] for old,new in code_substitutions: c = c.replace(old, new) return c def _ebpf_comment(): """Return a C-style comment with information about the generated code.""" comment=('Created by %s at %s:\n\t%s' % (sys.argv[0], strftime("%Y-%m-%d %H:%M:%S %Z"), _embedded_c.__doc__)) args = str(vars(Global.args)).replace('{','{\n\t').replace(', ',',\n\t').replace('}',',\n }\n\n') return ("\n /*" + ("\n %s\n\n ARGV = %s\n\n ARGS = %s/" % (comment, ' '.join(Global.argv), args)) .replace('\n','\n\t*').replace('\t',' ')) def _print_header(): if Global.args.timestamp: title = 'TIME-' + ('UTC' if Global.args.utc else strftime("%Z")) print("%-13s" % title, end="") if Global.args.label is not None: print("%-6s" % "LABEL", end="") print("%-16s %-6s %-6s %-6s %-7s %-10s" % ("PCOMM", "PID", "PPID", "TID", "AGE(s)", "EXIT_CODE")) def _print_event(cpu, data, size): # callback """Print the exit event.""" e = ct.cast(data, ct.POINTER(Data)).contents if Global.args.timestamp: now = datetime.utcnow() if Global.args.utc else datetime.now() print("%-13s" % (now.strftime("%H:%M:%S.%f")[:-3]), end="") if Global.args.label is not None: label = Global.args.label if len(Global.args.label) else 'exit' print("%-6s" % label, end="") age = (e.exit_time - e.start_time) / 1e9 print("%-16s %-6d %-6d %-6d %-7.2f " % (e.task.decode(), e.pid, e.ppid, e.tid, age), end="") if e.sig_info == 0: print("0" if e.exit_code == 0 else "code %d" % e.exit_code) else: sig = e.sig_info & 0x7F if sig: print("signal %d (%s)" % (sig, signum_to_signame(sig)), end="") if e.sig_info & 0x80: print(", core dumped ", end="") print() # ============================= # Module: These functions are available for import # ============================= def initialize(arg_list = sys.argv[1:]): """Trace all process termination. arg_list - list of args, if omitted then uses command line args arg_list is passed to argparse.ArgumentParser.parse_args() For example, if arg_list = [ '-x', '-t' ] args.failed == True args.timestamp == True Returns a tuple (return_code, result) 0 = Ok, result is the return value from BPF() 1 = args.ebpf is requested, result is the generated C code os.EX_NOPERM: need CAP_SYS_ADMIN, result is error message os.EX_SOFTWARE: internal software error, result is error message """ Global.argv = arg_list Global.args = Global.parse_args(arg_list) if Global.args.utc and not Global.args.timestamp: Global.args.timestamp = True if not Global.args.ebpf and os.geteuid() != 0: return (os.EX_NOPERM, "Need sudo (CAP_SYS_ADMIN) for BPF() system call") if re.match('^3\.10\..*el7.*$', platform.release()): # Centos/Red Hat Global.args.timespec = True for _ in range(2): c = _embedded_c(Global.args) if Global.args.ebpf: return (1, c) try: return (os.EX_OK, BPF(text=c)) except Exception as e: error = format(e) if (not Global.args.timespec and error.find('struct timespec') and error.find('start_time')): print('This kernel keeps task->start_time in a struct timespec.\n' + 'Retrying with --timespec') Global.args.timespec = True continue return (os.EX_SOFTWARE, "BPF error: " + error) except: return (os.EX_SOFTWARE, "Unexpected error: {0}".format(sys.exc_info()[0])) def snoop(bpf, event_handler): """Call event_handler for process termination events. bpf - result returned by successful initialize() event_handler - callback function to handle termination event args.pid - Return after event_handler is called, only monitoring this pid """ bpf["events"].open_perf_buffer(event_handler) while True: bpf.perf_buffer_poll() if Global.args.pid: return def signum_to_signame(signum): """Return the name of the signal corresponding to signum.""" return Global.SIGNUM_TO_SIGNAME.get(signum, "unknown") # ============================= # Script: invoked as a script # ============================= def main(): try: rc, buffer = initialize() if rc: print(buffer) sys.exit(0 if Global.args.ebpf else rc) _print_header() snoop(buffer, _print_event) except KeyboardInterrupt: print() sys.exit() return 0 if __name__ == '__main__': main() bpfcc-0.12.0/tools/exitsnoop_example.txt000066400000000000000000000141501357404205000203140ustar00rootroot00000000000000Demonstrations of exitsnoop. This Linux tool traces all process terminations and reason, it - is implemented using BPF, which requires CAP_SYS_ADMIN and should therefore be invoked with sudo - traces sched_process_exit tracepoint in kernel/exit.c - includes processes by root and all users - includes processes in containers - includes processes that become zombie The following example shows the termination of the 'sleep' and 'bash' commands when run in a loop that is interrupted with Ctrl-C from the terminal: # ./exitsnoop.py > exitlog & [1] 18997 # for((i=65;i<100;i+=5)); do bash -c "sleep 1.$i;exit $i"; done ^C # fg ./exitsnoop.py > exitlog ^C # cat exitlog PCOMM PID PPID TID AGE(s) EXIT_CODE sleep 19004 19003 19004 1.65 0 bash 19003 17656 19003 1.65 code 65 sleep 19007 19006 19007 1.70 0 bash 19006 17656 19006 1.70 code 70 sleep 19010 19009 19010 1.75 0 bash 19009 17656 19009 1.75 code 75 sleep 19014 19013 19014 0.23 signal 2 (INT) bash 19013 17656 19013 0.23 signal 2 (INT) # The output shows the process/command name (PCOMM), the PID, the process that will be notified (PPID), the thread (TID), the AGE of the process with hundredth of a second resolution, and the reason for the process exit (EXIT_CODE). A -t option can be used to include a timestamp column, it shows local time by default. The --utc option shows the time in UTC. The --label option adds a column indicating the tool that generated the output, 'exit' by default. If other tools follow this format their outputs can be merged into a single trace with a simple lexical sort increasing in time order with each line labeled to indicate the event, e.g. 'exec', 'open', 'exit', etc. Time is displayed with millisecond resolution. The -x option will show only non-zero exits and fatal signals, which excludes processes that exit with 0 code: # ./exitsnoop.py -t --utc -x --label= > exitlog & [1] 18289 # for((i=65;i<100;i+=5)); do bash -c "sleep 1.$i;exit $i"; done ^C # fg ./exitsnoop.py -t --utc -x --label= > exitlog ^C # cat exitlog TIME-UTC LABEL PCOMM PID PPID TID AGE(s) EXIT_CODE 13:20:22.997 exit bash 18300 17656 18300 1.65 code 65 13:20:24.701 exit bash 18303 17656 18303 1.70 code 70 13:20:26.456 exit bash 18306 17656 18306 1.75 code 75 13:20:28.260 exit bash 18310 17656 18310 1.80 code 80 13:20:30.113 exit bash 18313 17656 18313 1.85 code 85 13:20:31.495 exit sleep 18318 18317 18318 1.38 signal 2 (INT) 13:20:31.495 exit bash 18317 17656 18317 1.38 signal 2 (INT) # USAGE message: # ./exitsnoop.py -h usage: exitsnoop.py [-h] [-t] [--utc] [-p PID] [--label LABEL] [-x] Trace all process termination (exit, fatal signal) optional arguments: -h, --help show this help message and exit -t, --timestamp include timestamp (local time default) --utc include timestamp in UTC (-t implied) -p PID, --pid PID trace this PID only --label LABEL label each line -x, --failed trace only fails, exclude exit(0) examples: exitsnoop # trace all process termination exitsnoop -x # trace only fails, exclude exit(0) exitsnoop -t # include timestamps (local time) exitsnoop --utc # include timestamps (UTC) exitsnoop -p 181 # only trace PID 181 exitsnoop --label=exit # label each output line with 'exit' Exit status: 0 EX_OK Success 2 argparse error 70 EX_SOFTWARE syntax error detected by compiler, or verifier error from kernel 77 EX_NOPERM Need sudo (CAP_SYS_ADMIN) for BPF() system call About process termination in Linux ---------------------------------- A program/process on Linux terminates normally - by explicitly invoking the exit( int ) system call - in C/C++ by returning an int from main(), ...which is then used as the value for exit() - by reaching the end of main() without a return ...which is equivalent to return 0 (C99 and C++) Notes: - Linux keeps only the least significant eight bits of the exit value - an exit value of 0 means success - an exit value of 1-255 means an error A process terminates abnormally if it - receives a signal which is not ignored or blocked and has no handler ... the default action is to terminate with optional core dump - is selected by the kernel's "Out of Memory Killer", equivalent to being sent SIGKILL (9), which cannot be ignored or blocked Notes: - any signal can be sent asynchronously via the kill() system call - synchronous signals are the result of the CPU detecting a fault or trap during execution of the program, a kernel handler is dispatched which determines the cause and the corresponding signal, examples are - attempting to fetch data or instructions at invalid or privileged addresses, - attempting to divide by zero, unmasked floating point exceptions - hitting a breakpoint Linux keeps process termination information in 'exit_code', an int within struct 'task_struct' defined in - if the process terminated normally: - the exit value is in bits 15:8 - the least significant 8 bits of exit_code are zero (bits 7:0) - if the process terminates abnormally: - the signal number (>= 1) is in bits 6:0 - bit 7 indicates a 'core dump' action, whether a core dump was actually done depends on ulimit. Success is indicated with an exit value of zero. The meaning of a non zero exit value depends on the program. Some programs document their exit values and their meaning. This script uses exit values as defined in References: https://github.com/torvalds/linux/blob/master/kernel/exit.c https://github.com/torvalds/linux/blob/master/arch/x86/include/uapi/asm/signal.h https://code.woboq.org/userspace/glibc/misc/sysexits.h.html bpfcc-0.12.0/tools/ext4dist.py000077500000000000000000000145201357404205000161360ustar00rootroot00000000000000#!/usr/bin/python # @lint-avoid-python-3-compatibility-imports # # ext4dist Summarize ext4 operation latency. # For Linux, uses BCC, eBPF. # # USAGE: ext4dist [-h] [-T] [-m] [-p PID] [interval] [count] # # Copyright 2016 Netflix, Inc. # Licensed under the Apache License, Version 2.0 (the "License") # # 12-Feb-2016 Brendan Gregg Created this. from __future__ import print_function from bcc import BPF from time import sleep, strftime import argparse # symbols kallsyms = "/proc/kallsyms" # arguments examples = """examples: ./ext4dist # show operation latency as a histogram ./ext4dist -p 181 # trace PID 181 only ./ext4dist 1 10 # print 1 second summaries, 10 times ./ext4dist -m 5 # 5s summaries, milliseconds """ parser = argparse.ArgumentParser( description="Summarize ext4 operation latency", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("-T", "--notimestamp", action="store_true", help="don't include timestamp on interval output") parser.add_argument("-m", "--milliseconds", action="store_true", help="output in milliseconds") parser.add_argument("-p", "--pid", help="trace this PID only") parser.add_argument("interval", nargs="?", help="output interval, in seconds") parser.add_argument("count", nargs="?", default=99999999, help="number of outputs") parser.add_argument("--ebpf", action="store_true", help=argparse.SUPPRESS) args = parser.parse_args() pid = args.pid countdown = int(args.count) if args.milliseconds: factor = 1000000 label = "msecs" else: factor = 1000 label = "usecs" if args.interval and int(args.interval) == 0: print("ERROR: interval 0. Exiting.") exit() debug = 0 # define BPF program bpf_text = """ #include #include #include #define OP_NAME_LEN 8 typedef struct dist_key { char op[OP_NAME_LEN]; u64 slot; } dist_key_t; BPF_HASH(start, u32); BPF_HISTOGRAM(dist, dist_key_t); // time operation int trace_entry(struct pt_regs *ctx) { u32 pid = bpf_get_current_pid_tgid(); if (FILTER_PID) return 0; u64 ts = bpf_ktime_get_ns(); start.update(&pid, &ts); return 0; } EXT4_TRACE_READ_CODE static int trace_return(struct pt_regs *ctx, const char *op) { u64 *tsp; u32 pid = bpf_get_current_pid_tgid(); // fetch timestamp and calculate delta tsp = start.lookup(&pid); if (tsp == 0) { return 0; // missed start or filtered } u64 delta = bpf_ktime_get_ns() - *tsp; start.delete(&pid); // Skip entries with backwards time: temp workaround for #728 if ((s64) delta < 0) return 0; delta /= FACTOR; // store as histogram dist_key_t key = {.slot = bpf_log2l(delta)}; __builtin_memcpy(&key.op, op, sizeof(key.op)); dist.increment(key); return 0; } int trace_read_return(struct pt_regs *ctx) { char *op = "read"; return trace_return(ctx, op); } int trace_write_return(struct pt_regs *ctx) { char *op = "write"; return trace_return(ctx, op); } int trace_open_return(struct pt_regs *ctx) { char *op = "open"; return trace_return(ctx, op); } int trace_fsync_return(struct pt_regs *ctx) { char *op = "fsync"; return trace_return(ctx, op); } """ # Starting from Linux 4.10 ext4_file_operations.read_iter has been changed from # using generic_file_read_iter() to its own ext4_file_read_iter(). # # To detect the proper function to trace check if ext4_file_read_iter() is # defined in /proc/kallsyms, if it's defined attach to that function, otherwise # use generic_file_read_iter() and inside the trace hook filter on ext4 read # events (checking if file->f_op == ext4_file_operations). if BPF.get_kprobe_functions(b'ext4_file_read_iter'): ext4_read_fn = 'ext4_file_read_iter' ext4_trace_read_fn = 'trace_entry' ext4_trace_read_code = '' else: ext4_read_fn = 'generic_file_read_iter' ext4_trace_read_fn = 'trace_read_entry' ext4_file_ops_addr = '' with open(kallsyms) as syms: for line in syms: (addr, size, name) = line.rstrip().split(" ", 2) name = name.split("\t")[0] if name == "ext4_file_operations": ext4_file_ops_addr = "0x" + addr break if ext4_file_ops_addr == '': print("ERROR: no ext4_file_operations in /proc/kallsyms. Exiting.") print("HINT: the kernel should be built with CONFIG_KALLSYMS_ALL.") exit() ext4_trace_read_code = """ int trace_read_entry(struct pt_regs *ctx, struct kiocb *iocb) { u32 pid = bpf_get_current_pid_tgid(); if (FILTER_PID) return 0; // ext4 filter on file->f_op == ext4_file_operations struct file *fp = iocb->ki_filp; if ((u64)fp->f_op != %s) return 0; u64 ts = bpf_ktime_get_ns(); start.update(&pid, &ts); return 0; }""" % ext4_file_ops_addr # code replacements bpf_text = bpf_text.replace('EXT4_TRACE_READ_CODE', ext4_trace_read_code) bpf_text = bpf_text.replace('FACTOR', str(factor)) if args.pid: bpf_text = bpf_text.replace('FILTER_PID', 'pid != %s' % pid) else: bpf_text = bpf_text.replace('FILTER_PID', '0') if debug or args.ebpf: print(bpf_text) if args.ebpf: exit() # load BPF program b = BPF(text=bpf_text) b.attach_kprobe(event=ext4_read_fn, fn_name=ext4_trace_read_fn) b.attach_kprobe(event="ext4_file_write_iter", fn_name="trace_entry") b.attach_kprobe(event="ext4_file_open", fn_name="trace_entry") b.attach_kprobe(event="ext4_sync_file", fn_name="trace_entry") b.attach_kretprobe(event=ext4_read_fn, fn_name='trace_read_return') b.attach_kretprobe(event="ext4_file_write_iter", fn_name="trace_write_return") b.attach_kretprobe(event="ext4_file_open", fn_name="trace_open_return") b.attach_kretprobe(event="ext4_sync_file", fn_name="trace_fsync_return") print("Tracing ext4 operation latency... Hit Ctrl-C to end.") # output exiting = 0 dist = b.get_table("dist") while (1): try: if args.interval: sleep(int(args.interval)) else: sleep(99999999) except KeyboardInterrupt: exiting = 1 print() if args.interval and (not args.notimestamp): print(strftime("%H:%M:%S:")) dist.print_log2_hist(label, "operation", section_print_fn=bytes.decode) dist.clear() countdown -= 1 if exiting or countdown == 0: exit() bpfcc-0.12.0/tools/ext4dist_example.txt000066400000000000000000000214371357404205000200420ustar00rootroot00000000000000Demonstrations of ext4dist, the Linux eBPF/bcc version. ext4dist traces ext4 reads, writes, opens, and fsyncs, and summarizes their latency as a power-of-2 histogram. For example: # ./ext4dist Tracing ext4 operation latency... Hit Ctrl-C to end. ^C operation = 'read' usecs : count distribution 0 -> 1 : 1210 |****************************************| 2 -> 3 : 126 |**** | 4 -> 7 : 376 |************ | 8 -> 15 : 86 |** | 16 -> 31 : 9 | | 32 -> 63 : 47 |* | 64 -> 127 : 6 | | 128 -> 255 : 24 | | 256 -> 511 : 137 |**** | 512 -> 1023 : 66 |** | 1024 -> 2047 : 13 | | 2048 -> 4095 : 7 | | 4096 -> 8191 : 13 | | 8192 -> 16383 : 3 | | operation = 'write' usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 75 |****************************************| 16 -> 31 : 5 |** | operation = 'open' usecs : count distribution 0 -> 1 : 1278 |****************************************| 2 -> 3 : 40 |* | 4 -> 7 : 4 | | 8 -> 15 : 1 | | 16 -> 31 : 1 | | This output shows a bi-modal distribution for read latency, with a faster mode of less than 7 microseconds, and a slower mode of between 256 and 1023 microseconds. The count column shows how many events fell into that latency range. 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. An optional interval and a count can be provided, as well as -m to show the distributions in milliseconds. For example: # ./ext4dist -m 1 5 Tracing ext4 operation latency... Hit Ctrl-C to end. 10:19:00: operation = 'read' msecs : count distribution 0 -> 1 : 576 |****************************************| 2 -> 3 : 5 | | 4 -> 7 : 6 | | 8 -> 15 : 13 | | 16 -> 31 : 17 |* | 32 -> 63 : 5 | | 64 -> 127 : 1 | | operation = 'write' msecs : count distribution 0 -> 1 : 20 |****************************************| operation = 'open' msecs : count distribution 0 -> 1 : 346 |****************************************| 10:19:01: operation = 'read' msecs : count distribution 0 -> 1 : 584 |****************************************| 2 -> 3 : 10 | | 4 -> 7 : 11 | | 8 -> 15 : 16 |* | 16 -> 31 : 6 | | 32 -> 63 : 4 | | 64 -> 127 : 2 | | 128 -> 255 : 1 | | operation = 'write' msecs : count distribution 0 -> 1 : 20 |****************************************| operation = 'open' msecs : count distribution 0 -> 1 : 336 |****************************************| 10:19:02: operation = 'read' msecs : count distribution 0 -> 1 : 678 |****************************************| 2 -> 3 : 7 | | 4 -> 7 : 9 | | 8 -> 15 : 25 |* | 16 -> 31 : 10 | | 32 -> 63 : 3 | | operation = 'write' msecs : count distribution 0 -> 1 : 19 |****************************************| 2 -> 3 : 1 |** | operation = 'open' msecs : count distribution 0 -> 1 : 390 |****************************************| 10:19:03: operation = 'read' msecs : count distribution 0 -> 1 : 567 |****************************************| 2 -> 3 : 7 | | 4 -> 7 : 9 | | 8 -> 15 : 20 |* | 16 -> 31 : 15 |* | 32 -> 63 : 5 | | 64 -> 127 : 2 | | operation = 'write' msecs : count distribution 0 -> 1 : 20 |****************************************| operation = 'open' msecs : count distribution 0 -> 1 : 417 |****************************************| 10:19:04: operation = 'read' msecs : count distribution 0 -> 1 : 762 |****************************************| 2 -> 3 : 9 | | 4 -> 7 : 9 | | 8 -> 15 : 11 | | 16 -> 31 : 20 |* | 32 -> 63 : 4 | | 64 -> 127 : 1 | | operation = 'write' msecs : count distribution 0 -> 1 : 20 |****************************************| operation = 'open' msecs : count distribution 0 -> 1 : 427 |****************************************| This shows a mixed read/write workload. USAGE message: # ./ext4dist -h usage: ext4dist [-h] [-T] [-m] [-p PID] [interval] [count] Summarize ext4 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: ./ext4dist # show operation latency as a histogram ./ext4dist -p 181 # trace PID 181 only ./ext4dist 1 10 # print 1 second summaries, 10 times ./ext4dist -m 5 # 5s summaries, milliseconds bpfcc-0.12.0/tools/ext4slower.py000077500000000000000000000233171357404205000165120ustar00rootroot00000000000000#!/usr/bin/python # @lint-avoid-python-3-compatibility-imports # # ext4slower Trace slow ext4 operations. # For Linux, uses BCC, eBPF. # # USAGE: ext4slower [-h] [-j] [-p PID] [min_ms] # # This script traces common ext4 file operations: reads, writes, opens, and # syncs. It measures the time spent in these operations, and prints details # for each that exceeded a threshold. # # WARNING: This adds low-overhead instrumentation to these ext4 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 (even if it prints no "slower" events) can # begin to become significant. # # By default, a minimum millisecond threshold of 10 is used. # # Copyright 2016 Netflix, Inc. # Licensed under the Apache License, Version 2.0 (the "License") # # 11-Feb-2016 Brendan Gregg Created this. # 15-Oct-2016 Dina Goldshtein -p to filter by process ID. # 13-Jun-2018 Joe Yin modify generic_file_read_iter to ext4_file_read_iter. from __future__ import print_function from bcc import BPF import argparse from time import strftime # symbols kallsyms = "/proc/kallsyms" # arguments examples = """examples: ./ext4slower # trace operations slower than 10 ms (default) ./ext4slower 1 # trace operations slower than 1 ms ./ext4slower -j 1 # ... 1 ms, parsable output (csv) ./ext4slower 0 # trace all operations (warning: verbose) ./ext4slower -p 185 # trace PID 185 only """ parser = argparse.ArgumentParser( description="Trace common ext4 file operations slower than a threshold", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("-j", "--csv", action="store_true", help="just print fields: comma-separated values") parser.add_argument("-p", "--pid", help="trace this PID only") parser.add_argument("min_ms", nargs="?", default='10', help="minimum I/O duration to trace, in ms (default 10)") parser.add_argument("--ebpf", action="store_true", help=argparse.SUPPRESS) args = parser.parse_args() min_ms = int(args.min_ms) pid = args.pid csv = args.csv debug = 0 # define BPF program bpf_text = """ #include #include #include #include // XXX: switch these to char's when supported #define TRACE_READ 0 #define TRACE_WRITE 1 #define TRACE_OPEN 2 #define TRACE_FSYNC 3 struct val_t { u64 ts; u64 offset; struct file *fp; }; struct data_t { // XXX: switch some to u32's when supported u64 ts_us; u64 type; u64 size; u64 offset; u64 delta_us; u64 pid; char task[TASK_COMM_LEN]; char file[DNAME_INLINE_LEN]; }; BPF_HASH(entryinfo, u64, struct val_t); BPF_PERF_OUTPUT(events); // // Store timestamp and size on entry // // The current ext4 (Linux 4.5) uses generic_file_read_iter(), instead of it's // own function, for reads. So we need to trace that and then filter on ext4, // which I do by checking file->f_op. // The new Linux version (since form 4.10) uses ext4_file_read_iter(), And if the 'CONFIG_FS_DAX' // is not set ,then ext4_file_read_iter() will call generic_file_read_iter(), else it will call // ext4_dax_read_iter(), and trace generic_file_read_iter() will fail. int trace_read_entry(struct pt_regs *ctx, struct kiocb *iocb) { u64 id = bpf_get_current_pid_tgid(); u32 pid = id >> 32; // PID is higher part if (FILTER_PID) return 0; // ext4 filter on file->f_op == ext4_file_operations struct file *fp = iocb->ki_filp; if ((u64)fp->f_op != EXT4_FILE_OPERATIONS) return 0; // store filep and timestamp by id struct val_t val = {}; val.ts = bpf_ktime_get_ns(); val.fp = fp; val.offset = iocb->ki_pos; if (val.fp) entryinfo.update(&id, &val); return 0; } // ext4_file_write_iter(): int trace_write_entry(struct pt_regs *ctx, struct kiocb *iocb) { u64 id = bpf_get_current_pid_tgid(); u32 pid = id >> 32; // PID is higher part if (FILTER_PID) return 0; // store filep and timestamp by id struct val_t val = {}; val.ts = bpf_ktime_get_ns(); val.fp = iocb->ki_filp; val.offset = iocb->ki_pos; if (val.fp) entryinfo.update(&id, &val); return 0; } // ext4_file_open(): int trace_open_entry(struct pt_regs *ctx, struct inode *inode, struct file *file) { u64 id = bpf_get_current_pid_tgid(); u32 pid = id >> 32; // PID is higher part if (FILTER_PID) return 0; // store filep and timestamp by id struct val_t val = {}; val.ts = bpf_ktime_get_ns(); val.fp = file; val.offset = 0; if (val.fp) entryinfo.update(&id, &val); return 0; } // ext4_sync_file(): int trace_fsync_entry(struct pt_regs *ctx, struct file *file) { u64 id = bpf_get_current_pid_tgid(); u32 pid = id >> 32; // PID is higher part if (FILTER_PID) return 0; // store filep and timestamp by id struct val_t val = {}; val.ts = bpf_ktime_get_ns(); val.fp = file; val.offset = 0; if (val.fp) entryinfo.update(&id, &val); return 0; } // // Output // static int trace_return(struct pt_regs *ctx, int type) { struct val_t *valp; u64 id = bpf_get_current_pid_tgid(); u32 pid = id >> 32; // PID is higher part valp = entryinfo.lookup(&id); if (valp == 0) { // missed tracing issue or filtered return 0; } // calculate delta u64 ts = bpf_ktime_get_ns(); u64 delta_us = (ts - valp->ts) / 1000; entryinfo.delete(&id); if (FILTER_US) return 0; // populate output struct u32 size = PT_REGS_RC(ctx); struct data_t data = {.type = type, .size = size, .delta_us = delta_us, .pid = pid}; data.ts_us = ts / 1000; data.offset = valp->offset; bpf_get_current_comm(&data.task, sizeof(data.task)); // workaround (rewriter should handle file to d_name in one step): struct dentry *de = NULL; struct qstr qs = {}; de = valp->fp->f_path.dentry; qs = de->d_name; if (qs.len == 0) return 0; bpf_probe_read(&data.file, sizeof(data.file), (void *)qs.name); // output events.perf_submit(ctx, &data, sizeof(data)); return 0; } int trace_read_return(struct pt_regs *ctx) { return trace_return(ctx, TRACE_READ); } int trace_write_return(struct pt_regs *ctx) { return trace_return(ctx, TRACE_WRITE); } int trace_open_return(struct pt_regs *ctx) { return trace_return(ctx, TRACE_OPEN); } int trace_fsync_return(struct pt_regs *ctx) { return trace_return(ctx, TRACE_FSYNC); } """ # code replacements with open(kallsyms) as syms: ops = '' for line in syms: (addr, size, name) = line.rstrip().split(" ", 2) name = name.split("\t")[0] if name == "ext4_file_operations": ops = "0x" + addr break if ops == '': print("ERROR: no ext4_file_operations in /proc/kallsyms. Exiting.") print("HINT: the kernel should be built with CONFIG_KALLSYMS_ALL.") exit() bpf_text = bpf_text.replace('EXT4_FILE_OPERATIONS', ops) if min_ms == 0: bpf_text = bpf_text.replace('FILTER_US', '0') else: bpf_text = bpf_text.replace('FILTER_US', 'delta_us <= %s' % str(min_ms * 1000)) if args.pid: bpf_text = bpf_text.replace('FILTER_PID', 'pid != %s' % pid) else: bpf_text = bpf_text.replace('FILTER_PID', '0') if debug or args.ebpf: print(bpf_text) if args.ebpf: exit() # process event def print_event(cpu, data, size): event = b["events"].event(data) type = 'R' if event.type == 1: type = 'W' elif event.type == 2: type = 'O' elif event.type == 3: type = 'S' if (csv): print("%d,%s,%d,%s,%d,%d,%d,%s" % ( event.ts_us, event.task.decode('utf-8', 'replace'), event.pid, type, event.size, event.offset, event.delta_us, event.file.decode('utf-8', 'replace'))) return print("%-8s %-14.14s %-6s %1s %-7s %-8d %7.2f %s" % (strftime("%H:%M:%S"), event.task.decode('utf-8', 'replace'), event.pid, type, event.size, event.offset / 1024, float(event.delta_us) / 1000, event.file.decode('utf-8', 'replace'))) # initialize BPF b = BPF(text=bpf_text) # Common file functions. See earlier comment about generic_file_read_iter(). if BPF.get_kprobe_functions(b'ext4_file_read_iter'): b.attach_kprobe(event="ext4_file_read_iter", fn_name="trace_read_entry") else: b.attach_kprobe(event="generic_file_read_iter", fn_name="trace_read_entry") b.attach_kprobe(event="ext4_file_write_iter", fn_name="trace_write_entry") b.attach_kprobe(event="ext4_file_open", fn_name="trace_open_entry") b.attach_kprobe(event="ext4_sync_file", fn_name="trace_fsync_entry") if BPF.get_kprobe_functions(b'ext4_file_read_iter'): b.attach_kretprobe(event="ext4_file_read_iter", fn_name="trace_read_return") else: b.attach_kretprobe(event="generic_file_read_iter", fn_name="trace_read_return") b.attach_kretprobe(event="ext4_file_write_iter", fn_name="trace_write_return") b.attach_kretprobe(event="ext4_file_open", fn_name="trace_open_return") b.attach_kretprobe(event="ext4_sync_file", fn_name="trace_fsync_return") # header if (csv): print("ENDTIME_us,TASK,PID,TYPE,BYTES,OFFSET_b,LATENCY_us,FILE") else: if min_ms == 0: print("Tracing ext4 operations") else: print("Tracing ext4 operations slower than %d ms" % min_ms) print("%-8s %-14s %-6s %1s %-7s %-8s %7s %s" % ("TIME", "COMM", "PID", "T", "BYTES", "OFF_KB", "LAT(ms)", "FILENAME")) # read events b["events"].open_perf_buffer(print_event, page_cnt=64) while 1: try: b.perf_buffer_poll() except KeyboardInterrupt: exit() bpfcc-0.12.0/tools/ext4slower_example.txt000066400000000000000000000261071357404205000204110ustar00rootroot00000000000000Demonstrations of ext4slower, the Linux eBPF/bcc version. ext4slower shows ext4 reads, writes, opens, and fsyncs, slower than a threshold. For example: # ./ext4slower Tracing ext4 operations slower than 10 ms TIME COMM PID T BYTES OFF_KB LAT(ms) FILENAME 06:35:01 cron 16464 R 1249 0 16.05 common-auth 06:35:01 cron 16463 R 1249 0 16.04 common-auth 06:35:01 cron 16465 R 1249 0 16.03 common-auth 06:35:01 cron 16465 R 4096 0 10.62 login.defs 06:35:01 cron 16464 R 4096 0 10.61 login.defs 06:35:01 cron 16463 R 4096 0 10.63 login.defs 06:35:01 cron 16465 R 2972 0 18.52 pam_env.conf 06:35:01 cron 16464 R 2972 0 18.51 pam_env.conf 06:35:01 cron 16463 R 2972 0 18.49 pam_env.conf 06:35:01 dumpsystemstat 16473 R 128 0 12.58 date 06:35:01 debian-sa1 16474 R 283 0 12.66 sysstat 06:35:01 debian-sa1 16474 R 128 0 10.39 sa1 06:35:01 dumpsystemstat 16491 R 128 0 13.22 ifconfig 06:35:01 DumpThreads 16534 R 128 0 12.78 cut 06:35:01 cron 16545 R 128 0 14.76 sendmail 06:35:01 sendmail 16545 R 274 0 10.88 dynamicmaps.cf 06:35:02 postdrop 16546 R 118 0 32.94 Universal 06:35:02 pickup 9574 R 118 0 21.02 localtime [...] This shows various system tasks reading from ext4. The high latency here is due to disk I/O, as I had just evicted the file system cache for this example. 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. The threshold can be provided as an argument. Eg, I/O slower than 1 ms: # ./ext4slower 1 Tracing ext4 operations slower than 1 ms TIME COMM PID T BYTES OFF_KB LAT(ms) FILENAME 06:49:17 bash 3616 R 128 0 7.75 cksum 06:49:17 cksum 3616 R 39552 0 1.34 [ 06:49:17 cksum 3616 R 96 0 5.36 2to3-2.7 06:49:17 cksum 3616 R 96 0 14.94 2to3-3.4 06:49:17 cksum 3616 R 10320 0 6.82 411toppm 06:49:17 cksum 3616 R 65536 0 4.01 a2p 06:49:17 cksum 3616 R 55400 0 8.77 ab 06:49:17 cksum 3616 R 36792 0 16.34 aclocal-1.14 06:49:17 cksum 3616 R 15008 0 19.31 acpi_listen 06:49:17 cksum 3616 R 6123 0 17.23 add-apt-repository 06:49:17 cksum 3616 R 6280 0 18.40 addpart 06:49:17 cksum 3616 R 27696 0 2.16 addr2line 06:49:17 cksum 3616 R 58080 0 10.11 ag 06:49:17 cksum 3616 R 906 0 6.30 ec2-meta-data 06:49:17 cksum 3616 R 6320 0 10.00 animate.im6 06:49:17 cksum 3616 R 5680 0 18.69 anytopnm 06:49:17 cksum 3616 R 2671 0 20.27 apport-bug 06:49:17 cksum 3616 R 12566 0 16.72 apport-cli 06:49:17 cksum 3616 R 1622 0 7.95 apport-unpack 06:49:17 cksum 3616 R 10440 0 2.37 appres 06:49:17 cksum 3616 R 48112 0 5.42 whatis 06:49:17 cksum 3616 R 14832 0 6.24 apt 06:49:17 cksum 3616 R 65536 0 24.74 apt-cache 06:49:17 cksum 3616 R 27264 0 1.68 apt-cdrom 06:49:17 cksum 3616 R 23224 0 5.31 apt-extracttemplates 06:49:17 cksum 3616 R 65536 0 8.08 apt-ftparchive 06:49:17 cksum 3616 R 65536 128 2.92 apt-ftparchive 06:49:17 cksum 3616 R 65536 0 9.58 aptitude-curses 06:49:17 cksum 3616 R 65536 128 44.25 aptitude-curses 06:49:17 cksum 3616 R 65536 384 1.69 aptitude-curses [...] This time a cksum(1) command can be seen reading various files (from /usr/bin). A threshold of 0 will trace all operations. Warning: the output will be verbose, as it will include all file system cache hits. # ./ext4slower 0 Tracing ext4 operations TIME COMM PID T BYTES OFF_KB LAT(ms) FILENAME 06:58:05 supervise 1884 O 0 0 0.00 status.new 06:58:05 supervise 1884 W 18 0 0.02 status.new 06:58:05 supervise 1884 O 0 0 0.00 status.new 06:58:05 supervise 1884 W 18 0 0.01 status.new 06:58:05 supervise 15817 O 0 0 0.00 run 06:58:05 supervise 15817 R 92 0 0.00 run 06:58:05 supervise 15817 O 0 0 0.00 bash 06:58:05 supervise 15817 R 128 0 0.00 bash 06:58:05 supervise 15817 R 504 0 0.00 bash 06:58:05 supervise 15817 R 28 0 0.00 bash 06:58:05 supervise 15817 O 0 0 0.00 ld-2.19.so 06:58:05 supervise 15817 R 64 0 0.00 ld-2.19.so 06:58:05 supervise 15817 R 392 0 0.00 ld-2.19.so 06:58:05 run 15817 O 0 0 0.00 ld.so.cache 06:58:05 run 15817 O 0 0 0.00 libtinfo.so.5.9 06:58:05 run 15817 R 832 0 0.00 libtinfo.so.5.9 06:58:05 run 15817 O 0 0 0.00 libdl-2.19.so 06:58:05 run 15817 R 832 0 0.00 libdl-2.19.so 06:58:05 run 15817 O 0 0 0.00 libc-2.19.so 06:58:05 run 15817 R 832 0 0.00 libc-2.19.so 06:58:05 supervise 1876 O 0 0 0.00 status.new 06:58:05 supervise 1876 W 18 0 0.01 status.new 06:58:05 supervise 1895 O 0 0 0.00 status.new 06:58:05 supervise 1895 W 18 0 0.02 status.new 06:58:05 supervise 1876 O 0 0 0.00 status.new 06:58:05 supervise 1876 W 18 0 0.01 status.new 06:58:05 supervise 1872 O 0 0 0.00 status.new 06:58:05 supervise 1872 W 18 0 0.02 status.new 06:58:05 supervise 1895 O 0 0 0.00 status.new 06:58:05 supervise 1895 W 18 0 0.01 status.new 06:58:05 supervise 15818 R 92 0 0.00 run 06:58:05 supervise 15818 O 0 0 0.00 bash 06:58:05 supervise 15818 R 128 0 0.00 bash 06:58:05 supervise 15818 R 504 0 0.00 bash 06:58:05 supervise 15818 R 28 0 0.00 bash 06:58:05 supervise 15818 O 0 0 0.00 ld-2.19.so 06:58:05 supervise 15818 R 64 0 0.00 ld-2.19.so 06:58:05 supervise 15818 R 392 0 0.00 ld-2.19.so 06:58:05 supervise 15818 O 0 0 0.00 run 06:58:05 supervise 1888 O 0 0 0.00 status.new 06:58:05 supervise 1888 W 18 0 0.01 status.new 06:58:05 supervise 1888 O 0 0 0.00 status.new 06:58:05 supervise 1888 W 18 0 0.02 status.new 06:58:05 supervise 15822 R 119 0 0.00 run 06:58:05 supervise 15822 O 0 0 0.00 bash 06:58:05 supervise 15822 R 128 0 0.00 bash 06:58:05 supervise 15822 R 504 0 0.00 bash 06:58:05 supervise 15822 R 28 0 0.00 bash 06:58:05 supervise 15822 O 0 0 0.00 ld-2.19.so 06:58:05 supervise 15822 R 64 0 0.00 ld-2.19.so 06:58:05 supervise 15822 R 392 0 0.00 ld-2.19.so 06:58:05 supervise 1892 O 0 0 0.00 status.new 06:58:05 supervise 1892 W 18 0 0.02 status.new 06:58:05 supervise 1892 O 0 0 0.00 status.new 06:58:05 supervise 1892 W 18 0 0.02 status.new 06:58:05 supervise 15820 O 0 0 0.00 run [...] The output now includes open operations ("O"), and writes ("W"). A -j option will print just the fields (parsable output, csv): # ./ext4slower -j 1 ENDTIME_us,TASK,PID,TYPE,BYTES,OFFSET_b,LATENCY_us,FILE 127200712278,bash,17225,R,128,0,14329,cksum 127200722986,cksum,17225,R,3274,0,8368,command-not-found 127200735581,cksum,17225,R,65536,0,10903,libbfd-2.24-system.so 127200738482,cksum,17225,R,65536,131072,2419,libbfd-2.24-system.so 127200749226,cksum,17225,R,65536,655360,8995,libbfd-2.24-system.so 127200776273,cksum,17225,R,55080,0,25297,libbind9.so.90.0.9 127200784688,cksum,17225,R,65536,0,7873,libblas.so.3.0 127200787551,cksum,17225,R,65536,131072,2386,libblas.so.3.0 127200795524,cksum,17225,R,18624,0,4947,libcpupower.so.3.13.0-49 127200802073,cksum,17225,R,65536,0,6410,libcwidget.so.3.0.0 127200808718,cksum,17225,R,65536,131072,6181,libcwidget.so.3.0.0 127200829518,cksum,17225,R,65536,0,14213,libdns.so.100.2.2 127200832916,cksum,17225,R,65536,131072,2911,libdns.so.100.2.2 127200841044,cksum,17225,R,65536,655360,6376,libdns.so.100.2.2 127200853646,cksum,17225,R,956,0,1022,libdumbnet.la 127200857814,cksum,17225,R,61096,0,4111,libdumbnet.so.1.0.1 127200869655,cksum,17225,R,65536,0,11252,libgettextlib-0.18.3.so 127200872985,cksum,17225,R,65536,131072,2882,libgettextlib-0.18.3.so 127200883063,cksum,17225,R,65536,0,9661,libgettextsrc-0.18.3.so 127200884767,cksum,17225,R,65536,131072,1251,libgettextsrc-0.18.3.so 127200904830,cksum,17225,R,65536,0,19571,libgirepository-1.0.so.1.0.0 127200906354,cksum,17225,R,65536,131072,1080,libgirepository-1.0.so.1.0.0 127200936047,cksum,17225,R,65536,0,28674,libGraphicsMagick.a 127200939091,cksum,17225,R,65536,131072,2576,libGraphicsMagick.a 127200947295,cksum,17225,R,65536,655360,6463,libGraphicsMagick.a 127200958793,cksum,17225,R,65536,1966080,7034,libGraphicsMagick.a [...] This may be useful for visualizing with another tool, for example, for producing a scatter plot of ENDTIME vs LATENCY, to look for time-based patterns. USAGE message: # ./ext4slower -h usage: ext4slower [-h] [-j] [-p PID] [min_ms] Trace common ext4 file operations slower than a threshold positional arguments: min_ms minimum I/O duration to trace, in ms (default 10) optional arguments: -h, --help show this help message and exit -j, --csv just print fields: comma-separated values -p PID, --pid PID trace this PID only examples: ./ext4slower # trace operations slower than 10 ms (default) ./ext4slower 1 # trace operations slower than 1 ms ./ext4slower -j 1 # ... 1 ms, parsable output (csv) ./ext4slower 0 # trace all operations (warning: verbose) ./ext4slower -p 185 # trace PID 185 only bpfcc-0.12.0/tools/filelife.py000077500000000000000000000070401357404205000161440ustar00rootroot00000000000000#!/usr/bin/python # @lint-avoid-python-3-compatibility-imports # # filelife Trace the lifespan of short-lived files. # For Linux, uses BCC, eBPF. Embedded C. # # This traces the creation and deletion of files, providing information # on who deleted the file, the file age, and the file name. The intent is to # provide information on short-lived files, for debugging or performance # analysis. # # USAGE: filelife [-h] [-p PID] # # Copyright 2016 Netflix, Inc. # Licensed under the Apache License, Version 2.0 (the "License") # # 08-Feb-2015 Brendan Gregg Created this. # 17-Feb-2016 Allan McAleavy updated for BPF_PERF_OUTPUT from __future__ import print_function from bcc import BPF import argparse from time import strftime # arguments examples = """examples: ./filelife # trace all stat() syscalls ./filelife -p 181 # only trace PID 181 """ parser = argparse.ArgumentParser( description="Trace stat() syscalls", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("-p", "--pid", help="trace this PID only") parser.add_argument("--ebpf", action="store_true", help=argparse.SUPPRESS) args = parser.parse_args() debug = 0 # define BPF program bpf_text = """ #include #include #include struct data_t { u32 pid; u64 delta; char comm[TASK_COMM_LEN]; char fname[DNAME_INLINE_LEN]; }; BPF_HASH(birth, struct dentry *); BPF_PERF_OUTPUT(events); // trace file creation time int trace_create(struct pt_regs *ctx, struct inode *dir, struct dentry *dentry) { u32 pid = bpf_get_current_pid_tgid(); FILTER u64 ts = bpf_ktime_get_ns(); birth.update(&dentry, &ts); return 0; }; // trace file deletion and output details int trace_unlink(struct pt_regs *ctx, struct inode *dir, struct dentry *dentry) { struct data_t data = {}; u32 pid = bpf_get_current_pid_tgid(); FILTER u64 *tsp, delta; tsp = birth.lookup(&dentry); if (tsp == 0) { return 0; // missed create } delta = (bpf_ktime_get_ns() - *tsp) / 1000000; birth.delete(&dentry); struct qstr d_name = dentry->d_name; if (d_name.len == 0) return 0; if (bpf_get_current_comm(&data.comm, sizeof(data.comm)) == 0) { data.pid = pid; data.delta = delta; bpf_probe_read(&data.fname, sizeof(data.fname), d_name.name); } events.perf_submit(ctx, &data, sizeof(data)); return 0; } """ if args.pid: bpf_text = bpf_text.replace('FILTER', 'if (pid != %s) { return 0; }' % args.pid) else: bpf_text = bpf_text.replace('FILTER', '') if debug or args.ebpf: print(bpf_text) if args.ebpf: exit() # initialize BPF b = BPF(text=bpf_text) b.attach_kprobe(event="vfs_create", fn_name="trace_create") # newer kernels (say, 4.8) may don't fire vfs_create, so record (or overwrite) # the timestamp in security_inode_create(): b.attach_kprobe(event="security_inode_create", fn_name="trace_create") b.attach_kprobe(event="vfs_unlink", fn_name="trace_unlink") # header print("%-8s %-6s %-16s %-7s %s" % ("TIME", "PID", "COMM", "AGE(s)", "FILE")) # process event def print_event(cpu, data, size): event = b["events"].event(data) print("%-8s %-6d %-16s %-7.2f %s" % (strftime("%H:%M:%S"), event.pid, event.comm.decode('utf-8', 'replace'), float(event.delta) / 1000, event.fname.decode('utf-8', 'replace'))) b["events"].open_perf_buffer(print_event) while 1: try: b.perf_buffer_poll() except KeyboardInterrupt: exit() bpfcc-0.12.0/tools/filelife_example.txt000066400000000000000000000040541357404205000200450ustar00rootroot00000000000000Demonstrations of filelife, the Linux eBPF/bcc version. filelife traces short-lived files: those that have been created and then deleted while tracing. For example: # ./filelife TIME PID COMM AGE(s) FILE 05:57:59 8556 gcc 0.04 ccCB5EDe.s 05:57:59 8560 rm 0.02 .entry_64.o.d 05:57:59 8563 gcc 0.02 cc5UFHXf.s 05:57:59 8567 rm 0.01 .thunk_64.o.d 05:57:59 8578 rm 0.02 .syscall_64.o.d 05:58:00 8589 rm 0.03 .common.o.d 05:58:00 8596 rm 0.01 .8592.tmp 05:58:00 8601 rm 0.01 .8597.tmp 05:58:00 8606 rm 0.01 .8602.tmp 05:58:00 8639 rm 0.02 .vma.o.d 05:58:00 8650 rm 0.02 .vdso32-setup.o.d 05:58:00 8656 rm 0.00 .vdso.lds.d 05:58:00 8659 gcc 0.01 ccveeJAz.s 05:58:00 8663 rm 0.01 .vdso-note.o.d 05:58:00 8674 rm 0.02 .vclock_gettime.o.d 05:58:01 8684 rm 0.01 .vgetcpu.o.d 05:58:01 8690 collect2 0.00 ccvKMxdm.ld This has caught short-lived files that were created during a Linux kernel build. The PID shows the process ID that finally deleted the file, and COMM is its process name. The AGE(s) column shows the age of the file, in seconds, when it was deleted. These are all short-lived, and existed for less than one tenth of a second. Creating, populating, and then deleting files as part of another process can be an inefficient method of inter-process communication. It can cause disk I/O as files are closed and their file descriptors flushed, only later to be deleted. As such, short-lived files can be a target of performance optimizations. USAGE message: # ./filelife -h usage: filelife [-h] [-p PID] Trace stat() syscalls optional arguments: -h, --help show this help message and exit -p PID, --pid PID trace this PID only examples: ./filelife # trace all stat() syscalls ./filelife -p 181 # only trace PID 181 bpfcc-0.12.0/tools/fileslower.py000077500000000000000000000156721357404205000165520ustar00rootroot00000000000000#!/usr/bin/python # @lint-avoid-python-3-compatibility-imports # # fileslower Trace slow synchronous file reads and writes. # For Linux, uses BCC, eBPF. # # USAGE: fileslower [-h] [-p PID] [-a] [min_ms] # # This script uses kernel dynamic tracing of synchronous reads and writes # at the VFS interface, to identify slow file reads and writes for any file # system. # # This works by tracing __vfs_read() and __vfs_write(), and filtering for # synchronous I/O (the path to new_sync_read() and new_sync_write()), and # for I/O with filenames. This approach provides a view of just two file # system request types. There are typically many others: asynchronous I/O, # directory operations, file handle operations, etc, that this tool does not # instrument. # # WARNING: This traces VFS reads and writes, which can be extremely frequent, # and so the overhead of this tool can become severe depending on the # workload. # # By default, a minimum millisecond threshold of 10 is used. # # Copyright 2016 Netflix, Inc. # Licensed under the Apache License, Version 2.0 (the "License") # # 06-Feb-2016 Brendan Gregg Created this. from __future__ import print_function from bcc import BPF import argparse import time # arguments examples = """examples: ./fileslower # trace sync file I/O slower than 10 ms (default) ./fileslower 1 # trace sync file I/O slower than 1 ms ./fileslower -p 185 # trace PID 185 only """ parser = argparse.ArgumentParser( description="Trace slow synchronous file reads and writes", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("-p", "--pid", type=int, metavar="PID", dest="tgid", help="trace this PID only") parser.add_argument("-a", "--all-files", action="store_true", help="include non-regular file types (sockets, FIFOs, etc)") parser.add_argument("min_ms", nargs="?", default='10', help="minimum I/O duration to trace, in ms (default 10)") parser.add_argument("--ebpf", action="store_true", help=argparse.SUPPRESS) args = parser.parse_args() min_ms = int(args.min_ms) tgid = args.tgid debug = 0 # define BPF program bpf_text = """ #include #include #include enum trace_mode { MODE_READ, MODE_WRITE }; struct val_t { u32 sz; u64 ts; u32 name_len; // de->d_name.name may point to de->d_iname so limit len accordingly char name[DNAME_INLINE_LEN]; char comm[TASK_COMM_LEN]; }; struct data_t { enum trace_mode mode; u32 pid; u32 sz; u64 delta_us; u32 name_len; char name[DNAME_INLINE_LEN]; char comm[TASK_COMM_LEN]; }; BPF_HASH(entryinfo, pid_t, struct val_t); BPF_PERF_OUTPUT(events); // store timestamp and size on entry static int trace_rw_entry(struct pt_regs *ctx, struct file *file, char __user *buf, size_t count) { u32 tgid = bpf_get_current_pid_tgid() >> 32; if (TGID_FILTER) return 0; u32 pid = bpf_get_current_pid_tgid(); // skip I/O lacking a filename struct dentry *de = file->f_path.dentry; int mode = file->f_inode->i_mode; if (de->d_name.len == 0 || TYPE_FILTER) return 0; // store size and timestamp by pid struct val_t val = {}; val.sz = count; val.ts = bpf_ktime_get_ns(); struct qstr d_name = de->d_name; val.name_len = d_name.len; bpf_probe_read(&val.name, sizeof(val.name), d_name.name); bpf_get_current_comm(&val.comm, sizeof(val.comm)); entryinfo.update(&pid, &val); return 0; } int trace_read_entry(struct pt_regs *ctx, struct file *file, char __user *buf, size_t count) { // skip non-sync I/O; see kernel code for __vfs_read() if (!(file->f_op->read_iter)) return 0; return trace_rw_entry(ctx, file, buf, count); } int trace_write_entry(struct pt_regs *ctx, struct file *file, char __user *buf, size_t count) { // skip non-sync I/O; see kernel code for __vfs_write() if (!(file->f_op->write_iter)) return 0; return trace_rw_entry(ctx, file, buf, count); } // output static int trace_rw_return(struct pt_regs *ctx, int type) { struct val_t *valp; u32 pid = bpf_get_current_pid_tgid(); valp = entryinfo.lookup(&pid); if (valp == 0) { // missed tracing issue or filtered return 0; } u64 delta_us = (bpf_ktime_get_ns() - valp->ts) / 1000; entryinfo.delete(&pid); if (delta_us < MIN_US) return 0; struct data_t data = {}; data.mode = type; data.pid = pid; data.sz = valp->sz; data.delta_us = delta_us; data.name_len = valp->name_len; bpf_probe_read(&data.name, sizeof(data.name), valp->name); bpf_probe_read(&data.comm, sizeof(data.comm), valp->comm); events.perf_submit(ctx, &data, sizeof(data)); return 0; } int trace_read_return(struct pt_regs *ctx) { return trace_rw_return(ctx, MODE_READ); } int trace_write_return(struct pt_regs *ctx) { return trace_rw_return(ctx, MODE_WRITE); } """ bpf_text = bpf_text.replace('MIN_US', str(min_ms * 1000)) if args.tgid: bpf_text = bpf_text.replace('TGID_FILTER', 'tgid != %d' % tgid) else: bpf_text = bpf_text.replace('TGID_FILTER', '0') if args.all_files: bpf_text = bpf_text.replace('TYPE_FILTER', '0') else: bpf_text = bpf_text.replace('TYPE_FILTER', '!S_ISREG(mode)') if debug or args.ebpf: print(bpf_text) if args.ebpf: exit() # initialize BPF b = BPF(text=bpf_text) # I'd rather trace these via new_sync_read/new_sync_write (which used to be # do_sync_read/do_sync_write), but those became static. So trace these from # the parent functions, at the cost of more overhead, instead. # Ultimately, we should be using [V]FS tracepoints. b.attach_kprobe(event="__vfs_read", fn_name="trace_read_entry") b.attach_kretprobe(event="__vfs_read", fn_name="trace_read_return") try: b.attach_kprobe(event="__vfs_write", fn_name="trace_write_entry") b.attach_kretprobe(event="__vfs_write", fn_name="trace_write_return") except Exception: # older kernels don't have __vfs_write so try vfs_write instead b.attach_kprobe(event="vfs_write", fn_name="trace_write_entry") b.attach_kretprobe(event="vfs_write", fn_name="trace_write_return") mode_s = { 0: 'R', 1: 'W', } # header print("Tracing sync read/writes slower than %d ms" % min_ms) print("%-8s %-14s %-6s %1s %-7s %7s %s" % ("TIME(s)", "COMM", "TID", "D", "BYTES", "LAT(ms)", "FILENAME")) start_ts = time.time() DNAME_INLINE_LEN = 32 def print_event(cpu, data, size): event = b["events"].event(data) ms = float(event.delta_us) / 1000 name = event.name.decode('utf-8', 'replace') if event.name_len > DNAME_INLINE_LEN: name = name[:-3] + "..." print("%-8.3f %-14.14s %-6s %1s %-7s %7.2f %s" % ( time.time() - start_ts, event.comm.decode('utf-8', 'replace'), event.pid, mode_s[event.mode], event.sz, ms, name)) b["events"].open_perf_buffer(print_event, page_cnt=64) while 1: try: b.perf_buffer_poll() except KeyboardInterrupt: exit() bpfcc-0.12.0/tools/fileslower_example.txt000066400000000000000000000131171357404205000204410ustar00rootroot00000000000000Demonstrations of fileslower, the Linux eBPF/bcc version. fileslower shows file-based synchronous reads and writes slower than a threshold. For example: # ./fileslower Tracing sync read/writes slower than 10 ms TIME(s) COMM PID D BYTES LAT(ms) FILENAME 0.000 randread.pl 4762 R 8192 12.70 data1 8.850 randread.pl 4762 R 8192 11.26 data1 12.852 randread.pl 4762 R 8192 10.43 data1 This showed a few reads from a "randread.pl" program, each 8 Kbytes in size, and from a "data1" file. These all had over 10 ms latency. This "latency" is measured from when the read or write was issued at the VFS interface, 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 file reads and writes: other file system operations (eg, directory operations, open(), fflush()) are not traced. The threshold can be provided as an argument. Eg, I/O slower than 1 ms: # ./fileslower 1 Tracing sync read/writes slower than 1 ms TIME(s) COMM PID D BYTES LAT(ms) FILENAME 0.000 randread.pl 6925 R 8192 1.06 data1 0.082 randread.pl 6925 R 8192 2.42 data1 0.116 randread.pl 6925 R 8192 1.78 data1 0.153 randread.pl 6925 R 8192 2.31 data1 0.330 randread.pl 6925 R 8192 1.14 data1 0.345 randread.pl 6925 R 8192 1.52 data1 0.359 randread.pl 6925 R 8192 1.04 data1 0.532 randread.pl 6925 R 8192 2.56 data1 0.609 supervise 1892 W 18 3.65 status.new 0.610 randread.pl 6925 R 8192 1.37 data1 0.614 randread.pl 6925 R 8192 3.04 data1 0.729 randread.pl 6925 R 8192 2.90 data1 0.755 randread.pl 6925 R 8192 1.12 data1 0.762 randread.pl 6925 R 8192 2.62 data1 0.771 randread.pl 6925 R 8192 1.07 data1 0.816 randread.pl 6925 R 8192 10.50 data1 0.983 randread.pl 6925 R 8192 1.73 data1 0.989 randread.pl 6925 R 8192 2.12 data1 0.992 randread.pl 6925 R 8192 2.17 data1 1.001 randread.pl 6925 R 8192 1.93 data1 1.007 randread.pl 6925 R 8192 2.03 data1 1.210 randread.pl 6925 R 8192 1.82 data1 1.213 randread.pl 6925 R 8192 2.58 data1 1.219 randread.pl 6925 R 8192 2.20 data1 1.430 randread.pl 6925 R 8192 1.01 data1 1.448 randread.pl 6925 R 8192 2.22 data1 [...] There's now much more output (this spans only 1.4 seconds, the previous output spanned 12 seconds), and the lower threshold is catching more I/O. In the following example, the file system caches were dropped before running fileslower, and then in another session a "man ls" was executed. The command and files read from disk can be seen: # echo 3 > /proc/sys/vm/drop_caches; ./fileslower 1 Tracing sync read/writes slower than 1 ms TIME(s) COMM PID D BYTES LAT(ms) FILENAME 0.000 bash 9647 R 128 5.83 man 0.050 man 9647 R 832 19.52 libmandb-2.6.7.1.so 0.066 man 9647 R 832 15.79 libman-2.6.7.1.so 0.123 man 9647 R 832 56.36 libpipeline.so.1.3.0 0.135 man 9647 R 832 9.79 libgdbm.so.3.0.0 0.323 man 9647 R 4096 59.52 locale.alias 0.540 man 9648 R 8192 11.11 ls.1.gz 0.558 man 9647 R 72 6.97 index.db 0.563 man 9647 R 4096 5.12 index.db 0.723 man 9658 R 128 12.06 less 0.725 man 9656 R 128 14.52 nroff 0.779 man 9655 R 128 68.86 tbl 0.814 nroff 9660 R 128 14.55 locale 0.830 pager 9658 R 4096 28.27 .lesshst 0.866 man 9654 R 128 163.12 preconv 0.980 nroff 9684 R 128 13.80 groff 0.999 groff 9684 R 4096 14.29 DESC 1.036 groff 9685 R 128 5.94 troff 1.038 groff 9686 R 128 7.76 grotty 1.065 troff 9685 R 4096 6.33 R 1.082 troff 9685 R 4096 10.52 BI 1.096 troff 9685 R 4096 8.70 troffrc 1.176 troff 9685 R 4096 80.12 composite.tmac 1.195 troff 9685 R 4096 19.20 fallbacks.tmac 1.202 troff 9685 R 4096 6.79 tty.tmac 1.221 troff 9685 R 4096 7.87 man.local 2.977 supervise 1876 W 18 4.23 status.new This caught an individual I/O reaching 163.12 ms, for the "preconv" file. While the file system cache was flushed, causing these to need to be read from disk, the duration here may not be entirely disk I/O: it can include file system locks, run queue latency, etc. These can be explored using other commands. USAGE message: # ./fileslower -h usage: fileslower.py [-h] [-p PID] [-a] [min_ms] Trace slow synchronous file reads and writes positional arguments: min_ms minimum I/O duration to trace, in ms (default 10) optional arguments: -h, --help show this help message and exit -p PID, --pid PID trace this PID only -a, --all-files include non-regular file types examples: ./fileslower # trace sync file I/O slower than 10 ms (default) ./fileslower 1 # trace sync file I/O slower than 1 ms ./fileslower -p 185 # trace PID 185 only bpfcc-0.12.0/tools/filetop.py000077500000000000000000000137031357404205000160320ustar00rootroot00000000000000#!/usr/bin/python # @lint-avoid-python-3-compatibility-imports # # filetop file reads and writes by process. # For Linux, uses BCC, eBPF. # # USAGE: filetop.py [-h] [-C] [-r MAXROWS] [interval] [count] # # This uses in-kernel eBPF maps to store per process summaries for efficiency. # # Copyright 2016 Netflix, Inc. # Licensed under the Apache License, Version 2.0 (the "License") # # 06-Feb-2016 Brendan Gregg Created this. from __future__ import print_function from bcc import BPF from time import sleep, strftime import argparse import signal from subprocess import call # arguments examples = """examples: ./filetop # file I/O top, 1 second refresh ./filetop -C # don't clear the screen ./filetop -p 181 # PID 181 only ./filetop 5 # 5 second summaries ./filetop 5 10 # 5 second summaries, 10 times only """ parser = argparse.ArgumentParser( description="File reads and writes by process", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("-a", "--all-files", action="store_true", help="include non-regular file types (sockets, FIFOs, etc)") parser.add_argument("-C", "--noclear", action="store_true", help="don't clear the screen") parser.add_argument("-r", "--maxrows", default=20, help="maximum rows to print, default 20") parser.add_argument("-s", "--sort", default="all", choices=["all", "reads", "writes", "rbytes", "wbytes"], help="sort column, default rbytes") parser.add_argument("-p", "--pid", type=int, metavar="PID", dest="tgid", help="trace this PID only") parser.add_argument("interval", nargs="?", default=1, help="output interval, in seconds") parser.add_argument("count", nargs="?", default=99999999, help="number of outputs") parser.add_argument("--ebpf", action="store_true", help=argparse.SUPPRESS) args = parser.parse_args() interval = int(args.interval) countdown = int(args.count) maxrows = int(args.maxrows) clear = not int(args.noclear) debug = 0 # linux stats loadavg = "/proc/loadavg" # signal handler def signal_ignore(signal_value, frame): print() # define BPF program bpf_text = """ #include #include // the key for the output summary struct info_t { u32 pid; u32 name_len; char comm[TASK_COMM_LEN]; // de->d_name.name may point to de->d_iname so limit len accordingly char name[DNAME_INLINE_LEN]; char type; }; // the value of the output summary struct val_t { u64 reads; u64 writes; u64 rbytes; u64 wbytes; }; BPF_HASH(counts, struct info_t, struct val_t); static int do_entry(struct pt_regs *ctx, struct file *file, char __user *buf, size_t count, int is_read) { u32 tgid = bpf_get_current_pid_tgid() >> 32; if (TGID_FILTER) return 0; u32 pid = bpf_get_current_pid_tgid(); // skip I/O lacking a filename struct dentry *de = file->f_path.dentry; int mode = file->f_inode->i_mode; struct qstr d_name = de->d_name; if (d_name.len == 0 || TYPE_FILTER) return 0; // store counts and sizes by pid & file struct info_t info = {.pid = pid}; bpf_get_current_comm(&info.comm, sizeof(info.comm)); info.name_len = d_name.len; bpf_probe_read(&info.name, sizeof(info.name), d_name.name); if (S_ISREG(mode)) { info.type = 'R'; } else if (S_ISSOCK(mode)) { info.type = 'S'; } else { info.type = 'O'; } struct val_t *valp, zero = {}; valp = counts.lookup_or_try_init(&info, &zero); if (valp) { if (is_read) { valp->reads++; valp->rbytes += count; } else { valp->writes++; valp->wbytes += count; } } return 0; } int trace_read_entry(struct pt_regs *ctx, struct file *file, char __user *buf, size_t count) { return do_entry(ctx, file, buf, count, 1); } int trace_write_entry(struct pt_regs *ctx, struct file *file, char __user *buf, size_t count) { return do_entry(ctx, file, buf, count, 0); } """ if args.tgid: bpf_text = bpf_text.replace('TGID_FILTER', 'tgid != %d' % args.tgid) else: bpf_text = bpf_text.replace('TGID_FILTER', '0') if args.all_files: bpf_text = bpf_text.replace('TYPE_FILTER', '0') else: bpf_text = bpf_text.replace('TYPE_FILTER', '!S_ISREG(mode)') if debug or args.ebpf: print(bpf_text) if args.ebpf: exit() # initialize BPF b = BPF(text=bpf_text) b.attach_kprobe(event="vfs_read", fn_name="trace_read_entry") b.attach_kprobe(event="vfs_write", fn_name="trace_write_entry") DNAME_INLINE_LEN = 32 # linux/dcache.h print('Tracing... Output every %d secs. Hit Ctrl-C to end' % interval) def sort_fn(counts): if args.sort == "all": return (counts[1].rbytes + counts[1].wbytes + counts[1].reads + counts[1].writes) else: return getattr(counts[1], args.sort) # output exiting = 0 while 1: try: sleep(interval) except KeyboardInterrupt: exiting = 1 # header if clear: call("clear") else: print() with open(loadavg) as stats: print("%-8s loadavg: %s" % (strftime("%H:%M:%S"), stats.read())) print("%-6s %-16s %-6s %-6s %-7s %-7s %1s %s" % ("TID", "COMM", "READS", "WRITES", "R_Kb", "W_Kb", "T", "FILE")) # by-TID output counts = b.get_table("counts") line = 0 for k, v in reversed(sorted(counts.items(), key=sort_fn)): name = k.name.decode('utf-8', 'replace') if k.name_len > DNAME_INLINE_LEN: name = name[:-3] + "..." # print line print("%-6d %-16s %-6d %-6d %-7d %-7d %1s %s" % (k.pid, k.comm.decode('utf-8', 'replace'), v.reads, v.writes, v.rbytes / 1024, v.wbytes / 1024, k.type.decode('utf-8', 'replace'), name)) line += 1 if line >= maxrows: break counts.clear() countdown -= 1 if exiting or countdown == 0: print("Detaching...") exit() bpfcc-0.12.0/tools/filetop_example.txt000066400000000000000000000154701357404205000177340ustar00rootroot00000000000000Demonstrations of filetop, the Linux eBPF/bcc version. filetop shows reads and writes by file, with process details. For example: # ./filetop -C Tracing... Output every 1 secs. Hit Ctrl-C to end 08:00:23 loadavg: 0.91 0.33 0.23 3/286 26635 PID COMM READS WRITES R_Kb W_Kb T FILE 26628 ld 161 186 643 152 R built-in.o 26634 cc1 1 0 200 0 R autoconf.h 26618 cc1 1 0 200 0 R autoconf.h 26634 cc1 12 0 192 0 R tracepoint.h 26584 cc1 2 0 143 0 R mm.h 26634 cc1 2 0 143 0 R mm.h 26631 make 34 0 136 0 R auto.conf 26634 cc1 1 0 98 0 R fs.h 26584 cc1 1 0 98 0 R fs.h 26634 cc1 1 0 91 0 R sched.h 26634 cc1 1 0 78 0 R printk.c 26634 cc1 3 0 73 0 R mmzone.h 26628 ld 18 0 72 0 R hibernate.o 26628 ld 16 0 64 0 R suspend.o 26628 ld 16 0 64 0 R snapshot.o 26628 ld 16 0 64 0 R qos.o 26628 ld 13 0 52 0 R main.o 26628 ld 12 0 52 0 R swap.o [...] This shows various files read and written during a Linux kernel build. By default the output is sorted by the total read size in Kbytes (R_Kb). Sorting order can be changed via -s option. This is instrumenting at the VFS interface, so this is reads and writes that may return entirely from the file system cache (page cache). While not printed, the average read and write size can be calculated by dividing R_Kb by READS, and the same for writes. The "T" column indicates the type of the file: "R" for regular files, "S" for sockets, and "O" for other (including pipes). By default only regular files are shown; use the -a option to show all file types. This script works by tracing the vfs_read() and vfs_write() functions using kernel dynamic tracing, which instruments explicit read and write calls. If files are read or written using another means (eg, via mmap()), then they will not be visible using this tool. This should be useful for file system workload characterization when analyzing the performance of applications. Note that tracing VFS level reads and writes can be a frequent activity, and this tool can begin to cost measurable overhead at high I/O rates. A -C option will stop clearing the screen, and -r with a number will restrict the output to that many rows (20 by default). For example, not clearing the screen and showing the top 5 only: # ./filetop -Cr 5 Tracing... Output every 1 secs. Hit Ctrl-C to end 08:05:11 loadavg: 0.75 0.35 0.25 3/285 822 PID COMM READS WRITES R_Kb W_Kb T FILE 32672 cksum 5006 0 320384 0 R data1 809 run 2 0 8 0 R nsswitch.conf 811 run 2 0 8 0 R nsswitch.conf 804 chown 2 0 8 0 R nsswitch.conf 08:05:12 loadavg: 0.75 0.35 0.25 3/285 845 PID COMM READS WRITES R_Kb W_Kb T FILE 32672 cksum 4986 0 319104 0 R data1 845 chown 2 0 8 0 R nsswitch.conf 828 run 2 0 8 0 R nsswitch.conf 835 run 2 0 8 0 R nsswitch.conf 830 run 2 0 8 0 R nsswitch.conf 08:05:13 loadavg: 0.75 0.35 0.25 3/285 868 PID COMM READS WRITES R_Kb W_Kb T FILE 32672 cksum 4985 0 319040 0 R data1 857 run 2 0 8 0 R nsswitch.conf 858 run 2 0 8 0 R nsswitch.conf 859 run 2 0 8 0 R nsswitch.conf 848 run 2 0 8 0 R nsswitch.conf [...] This output shows a cksum command reading data1. An optional interval and optional count can also be added to the end of the command line. For example, for 1 second interval, and 3 summaries in total: # ./filetop -Cr 5 -a 1 3 Tracing... Output every 1 secs. Hit Ctrl-C to end 08:08:20 loadavg: 0.30 0.42 0.31 3/282 5187 PID COMM READS WRITES R_Kb W_Kb T FILE 12421 sshd 14101 0 225616 0 O ptmx 12296 sshd 4 0 64 0 O ptmx 12421 sshd 3 14104 48 778 S TCP 5178 run 2 0 8 0 R nsswitch.conf 5165 run 2 0 8 0 R nsswitch.conf 08:08:21 loadavg: 0.30 0.42 0.31 5/282 5210 PID COMM READS WRITES R_Kb W_Kb T FILE 12421 sshd 9159 0 146544 0 O ptmx 12421 sshd 3 9161 48 534 S TCP 12296 sshd 1 0 16 0 S TCP 5188 run 2 0 8 0 R nsswitch.conf 5203 run 2 0 8 0 R nsswitch.conf 08:08:22 loadavg: 0.30 0.42 0.31 2/282 5233 PID COMM READS WRITES R_Kb W_Kb T FILE 12421 sshd 26166 0 418656 0 O ptmx 12421 sshd 4 26171 64 1385 S TCP 12296 sshd 1 0 16 0 O ptmx 5214 run 2 0 8 0 R nsswitch.conf 5227 run 2 0 8 0 R nsswitch.conf Detaching... This example shows the -a option to include all file types. It caught heavy socket I/O from an sshd process, showing up as non-regular file types (the "O" for other, and "S" for socket, in the type column: "T"). USAGE message: # ./filetop -h usage: filetop.py [-h] [-a] [-C] [-r MAXROWS] [-p PID] [interval] [count] File reads and writes by process positional arguments: interval output interval, in seconds count number of outputs optional arguments: -h, --help show this help message and exit -a, --all-files include non-regular file types (sockets, FIFOs, etc) -C, --noclear don't clear the screen -r MAXROWS, --maxrows MAXROWS maximum rows to print, default 20 -s {reads,writes,rbytes,wbytes}, --sort {reads,writes,rbytes,wbytes} sort column, default rbytes -p PID, --pid PID trace this PID only examples: ./filetop # file I/O top, 1 second refresh ./filetop -C # don't clear the screen ./filetop -p 181 # PID 181 only ./filetop 5 # 5 second summaries ./filetop 5 10 # 5 second summaries, 10 times only bpfcc-0.12.0/tools/funccount.py000077500000000000000000000302511357404205000163710ustar00rootroot00000000000000#!/usr/bin/python # @lint-avoid-python-3-compatibility-imports # # funccount Count functions, tracepoints, and USDT probes. # For Linux, uses BCC, eBPF. # # USAGE: funccount [-h] [-p PID] [-i INTERVAL] [-d DURATION] [-T] [-r] pattern # # The pattern is a string with optional '*' wildcards, similar to file # globbing. If you'd prefer to use regular expressions, use the -r option. # # Copyright (c) 2015 Brendan Gregg. # Licensed under the Apache License, Version 2.0 (the "License") # # 09-Sep-2015 Brendan Gregg Created this. # 18-Oct-2016 Sasha Goldshtein Generalized for uprobes, tracepoints, USDT. from __future__ import print_function from bcc import ArgString, BPF, USDT from time import sleep, strftime import argparse import os import re import signal import sys import traceback debug = False def verify_limit(num): probe_limit = 1000 if num > probe_limit: raise Exception("maximum of %d probes allowed, attempted %d" % (probe_limit, num)) class Probe(object): def __init__(self, pattern, use_regex=False, pid=None): """Init a new probe. Init the probe from the pattern provided by the user. The supported patterns mimic the 'trace' and 'argdist' tools, but are simpler because we don't have to distinguish between probes and retprobes. func -- probe a kernel function lib:func -- probe a user-space function in the library 'lib' /path:func -- probe a user-space function in binary '/path' p::func -- same thing as 'func' p:lib:func -- same thing as 'lib:func' t:cat:event -- probe a kernel tracepoint u:lib:probe -- probe a USDT tracepoint """ parts = bytes(pattern).split(b':') if len(parts) == 1: parts = [b"p", b"", parts[0]] elif len(parts) == 2: parts = [b"p", parts[0], parts[1]] elif len(parts) == 3: if parts[0] == b"t": parts = [b"t", b"", b"%s:%s" % tuple(parts[1:])] if parts[0] not in [b"p", b"t", b"u"]: raise Exception("Type must be 'p', 't', or 'u', but got %s" % parts[0]) else: raise Exception("Too many ':'-separated components in pattern %s" % pattern) (self.type, self.library, self.pattern) = parts if not use_regex: self.pattern = self.pattern.replace(b'*', b'.*') self.pattern = b'^' + self.pattern + b'$' if (self.type == b"p" and self.library) or self.type == b"u": libpath = BPF.find_library(self.library) if libpath is None: # This might be an executable (e.g. 'bash') libpath = BPF.find_exe(self.library) if libpath is None or len(libpath) == 0: raise Exception("unable to find library %s" % self.library) self.library = libpath self.pid = pid self.matched = 0 self.trace_functions = {} # map location number to function name def is_kernel_probe(self): return self.type == b"t" or (self.type == b"p" and self.library == b"") def attach(self): if self.type == b"p" and not self.library: for index, function in self.trace_functions.items(): self.bpf.attach_kprobe( event=function, fn_name="trace_count_%d" % index) elif self.type == b"p" and self.library: for index, function in self.trace_functions.items(): self.bpf.attach_uprobe( name=self.library, sym=function, fn_name="trace_count_%d" % index, pid=self.pid or -1) elif self.type == b"t": for index, function in self.trace_functions.items(): self.bpf.attach_tracepoint( tp=function, fn_name="trace_count_%d" % index) elif self.type == b"u": pass # Nothing to do -- attach already happened in `load` def _add_function(self, template, probe_name): new_func = b"trace_count_%d" % self.matched text = template.replace(b"PROBE_FUNCTION", new_func) text = text.replace(b"LOCATION", b"%d" % self.matched) self.trace_functions[self.matched] = probe_name self.matched += 1 return text def _generate_functions(self, template): self.usdt = None text = b"" if self.type == b"p" and not self.library: functions = BPF.get_kprobe_functions(self.pattern) verify_limit(len(functions)) for function in functions: text += self._add_function(template, function) elif self.type == b"p" and self.library: # uprobes are tricky because the same function may have multiple # addresses, and the same address may be mapped to multiple # functions. We aren't allowed to create more than one uprobe # per address, so track unique addresses and ignore functions that # map to an address that we've already seen. Also ignore functions # that may repeat multiple times with different addresses. addresses, functions = (set(), set()) functions_and_addresses = BPF.get_user_functions_and_addresses( self.library, self.pattern) verify_limit(len(functions_and_addresses)) for function, address in functions_and_addresses: if address in addresses or function in functions: continue addresses.add(address) functions.add(function) text += self._add_function(template, function) elif self.type == b"t": tracepoints = BPF.get_tracepoints(self.pattern) verify_limit(len(tracepoints)) for tracepoint in tracepoints: text += self._add_function(template, tracepoint) elif self.type == b"u": self.usdt = USDT(path=self.library, pid=self.pid) matches = [] for probe in self.usdt.enumerate_probes(): if not self.pid and (probe.bin_path != self.library): continue if re.match(self.pattern, probe.name): matches.append(probe.name) verify_limit(len(matches)) for match in matches: new_func = b"trace_count_%d" % self.matched text += self._add_function(template, match) self.usdt.enable_probe(match, new_func) if debug: print(self.usdt.get_text()) return text def load(self): trace_count_text = b""" int PROBE_FUNCTION(void *ctx) { FILTER int loc = LOCATION; u64 *val = counts.lookup(&loc); if (!val) { return 0; // Should never happen, # of locations is known } (*val)++; return 0; } """ bpf_text = b"""#include BPF_ARRAY(counts, u64, NUMLOCATIONS); """ # We really mean the tgid from the kernel's perspective, which is in # the top 32 bits of bpf_get_current_pid_tgid(). if self.pid: trace_count_text = trace_count_text.replace(b'FILTER', b"""u32 pid = bpf_get_current_pid_tgid() >> 32; if (pid != %d) { return 0; }""" % self.pid) else: trace_count_text = trace_count_text.replace(b'FILTER', b'') bpf_text += self._generate_functions(trace_count_text) bpf_text = bpf_text.replace(b"NUMLOCATIONS", b"%d" % len(self.trace_functions)) if debug: print(bpf_text) if self.matched == 0: raise Exception("No functions matched by pattern %s" % self.pattern) self.bpf = BPF(text=bpf_text, usdt_contexts=[self.usdt] if self.usdt else []) self.clear() # Initialize all array items to zero def counts(self): return self.bpf["counts"] def clear(self): counts = self.bpf["counts"] for location, _ in list(self.trace_functions.items()): counts[counts.Key(location)] = counts.Leaf() class Tool(object): def __init__(self): examples = """examples: ./funccount 'vfs_*' # count kernel fns starting with "vfs" ./funccount -r '^vfs.*' # same as above, using regular expressions ./funccount -Ti 5 'vfs_*' # output every 5 seconds, with timestamps ./funccount -d 10 'vfs_*' # trace for 10 seconds only ./funccount -p 185 'vfs_*' # count vfs calls for PID 181 only ./funccount t:sched:sched_fork # count calls to the sched_fork tracepoint ./funccount -p 185 u:node:gc* # count all GC USDT probes in node, PID 185 ./funccount c:malloc # count all malloc() calls in libc ./funccount go:os.* # count all "os.*" calls in libgo ./funccount -p 185 go:os.* # count all "os.*" calls in libgo, PID 185 ./funccount ./test:read* # count "read*" calls in the ./test binary """ parser = argparse.ArgumentParser( description="Count functions, tracepoints, and USDT probes", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("-p", "--pid", type=int, help="trace this PID only") parser.add_argument("-i", "--interval", help="summary interval, seconds") parser.add_argument("-d", "--duration", help="total duration of trace, seconds") parser.add_argument("-T", "--timestamp", action="store_true", help="include timestamp on output") parser.add_argument("-r", "--regexp", action="store_true", help="use regular expressions. Default is \"*\" wildcards only.") parser.add_argument("-D", "--debug", action="store_true", help="print BPF program before starting (for debugging purposes)") parser.add_argument("pattern", type=ArgString, help="search expression for events") self.args = parser.parse_args() global debug debug = self.args.debug self.probe = Probe(self.args.pattern, self.args.regexp, self.args.pid) if self.args.duration and not self.args.interval: self.args.interval = self.args.duration if not self.args.interval: self.args.interval = 99999999 @staticmethod def _signal_ignore(signal, frame): print() def run(self): self.probe.load() self.probe.attach() print("Tracing %d functions for \"%s\"... Hit Ctrl-C to end." % (self.probe.matched, bytes(self.args.pattern))) exiting = 0 if self.args.interval else 1 seconds = 0 while True: try: sleep(int(self.args.interval)) seconds += int(self.args.interval) except KeyboardInterrupt: exiting = 1 # as cleanup can take many seconds, trap Ctrl-C: signal.signal(signal.SIGINT, Tool._signal_ignore) if self.args.duration and seconds >= int(self.args.duration): exiting = 1 print() if self.args.timestamp: print("%-8s\n" % strftime("%H:%M:%S"), end="") print("%-36s %8s" % ("FUNC", "COUNT")) counts = self.probe.counts() for k, v in sorted(counts.items(), key=lambda counts: counts[1].value): if v.value == 0: continue print("%-36s %8d" % (self.probe.trace_functions[k.value], v.value)) if exiting: print("Detaching...") exit() else: self.probe.clear() if __name__ == "__main__": try: Tool().run() except Exception: if debug: traceback.print_exc() elif sys.exc_info()[0] is not SystemExit: print(sys.exc_info()[1]) bpfcc-0.12.0/tools/funccount_example.txt000066400000000000000000000300711357404205000202700ustar00rootroot00000000000000Demonstrations of funccount, the Linux eBPF/bcc version. This program traces functions, tracepoints, or USDT probes that match a specified pattern, and when Ctrl-C is hit prints a summary of their count while tracing. Eg, tracing all kernel functions that begin with "vfs_": # ./funccount 'vfs_*' Tracing... Ctrl-C to end. ^C FUNC COUNT vfs_create 1 vfs_rename 1 vfs_fsync_range 2 vfs_lock_file 30 vfs_fstatat 152 vfs_fstat 154 vfs_write 166 vfs_getattr_nosec 262 vfs_getattr 262 vfs_open 264 vfs_read 470 Detaching... The above output shows that while tracing the vfs_read() function was called 470 times, and vfs_open() 264 times, etc. This is useful for exploring kernel code, to figure out which functions are in use and which are not. This can narrow down an investigation to just a few functions, whose counts are similar to the workload investigated. Tracing all tcp functions: # ./funccount 'tcp_*' Tracing... Ctrl-C to end. ^C FUNC COUNT tcp_try_undo_recovery 1 tcp_twsk_destructor 1 tcp_enter_recovery 1 tcp_xmit_retransmit_queue 1 tcp_update_scoreboard 1 tcp_verify_retransmit_hint 1 tcp_tsq_handler.part.31 1 tcp_sacktag_write_queue 1 tcp_match_skb_to_sack 1 tcp_time_wait 1 tcp_mark_head_lost 1 tcp_init_cwnd_reduction 1 tcp_sacktag_one 1 tcp_sacktag_walk 1 tcp_retransmit_skb 1 tcp_tasklet_func 1 tcp_resume_early_retransmit 1 tcp_dsack_set 1 tcp_v4_syn_recv_sock 2 tcp_ca_openreq_child 2 tcp_try_fastopen 2 tcp_openreq_init_rwin 2 tcp_v4_init_req 2 tcp_create_openreq_child 2 tcp_v4_send_synack 2 tcp_v4_init_sequence 2 tcp_fragment 2 tcp_v4_conn_request 2 tcp_conn_request 2 tcp_v4_route_req 2 tcp_fragment_tstamp 2 tcp_try_keep_open 2 tcp_v4_reqsk_destructor 2 tcp_may_send_now 2 tcp_make_synack 2 tcp_child_process 2 tcp_check_req 2 tcp_fastretrans_alert 2 tcp_set_keepalive 2 tcp_finish_connect 3 tcp_connect_queue_skb 3 tcp_v4_connect 3 tcp_init_sock 3 tcp_v4_init_sock 3 tcp_connect 3 tcp_any_retrans_done.part.35 3 tcp_clear_retrans 3 tcp_setsockopt 4 tcp_update_metrics 5 tcp_done 5 tcp_initialize_rcv_mss 5 tcp_sndbuf_expand 5 tcp_fin 5 tcp_init_xmit_timers 5 tcp_close 5 tcp_init_congestion_control 5 tcp_init_metrics 5 tcp_gro_complete 5 tcp_free_fastopen_req 5 tcp_v4_destroy_sock 5 tcp_cleanup_congestion_control 5 tcp_send_fin 5 tcp_init_buffer_space 5 tcp_init_cwnd 5 tcp_select_initial_window 5 tcp_check_oom 5 tcp_default_init_rwnd 5 tcp_assign_congestion_control 5 tcp_getsockopt 6 tcp_ioctl 6 tcp_mtup_init 8 tcp_parse_options 8 tcp_mss_to_mtu 8 tcp_try_rmem_schedule 8 tcp_get_metrics 10 tcp_try_coalesce 10 tcp_rcv_state_process 14 tcp_sync_mss 14 tcp_write_timer_handler 15 tcp_write_timer 16 tcp_grow_window.isra.27 22 tcp_set_state 23 tcp_send_ack 37 tcp_delack_timer 42 tcp_delack_timer_handler 42 tcp_validate_incoming 91 tcp_prequeue_process 112 tcp_v4_early_demux 117 tcp_gro_receive 146 tcp_queue_rcv 167 tcp_data_queue 215 tcp_urg 219 tcp_send_delayed_ack 257 tcp_send_mss 275 tcp_push 275 tcp_sendmsg 275 tcp_event_data_recv 275 tcp_nagle_check 279 tcp_write_xmit 282 tcp_event_new_data_sent 282 tcp_current_mss 284 tcp_init_tso_segs 284 tcp_wfree 286 tcp_schedule_loss_probe 305 tcp_v4_send_check 323 tcp_transmit_skb 323 tcp_recvmsg 323 tcp_options_write 325 tcp_rcv_space_adjust 328 tcp_check_space 332 tcp_rcv_established 337 tcp_ack 337 tcp_parse_aligned_timestamp.part.43 345 tcp_prequeue 346 tcp_v4_do_rcv 351 tcp_v4_rcv 351 tcp_parse_md5sig_option 351 tcp_cleanup_rbuf 436 tcp_poll 468 tcp_established_options 604 tcp_v4_md5_lookup 615 tcp_release_cb 736 tcp_rearm_rto 843 tcp_md5_do_lookup 968 Detaching... The current implementation can take many seconds to detach from tracing, after Ctrl-C has been hit. User functions can be traced in executables or libraries, and per-process filtering is allowed: # ./funccount -p 1442 /home/ubuntu/contentions:* Tracing 15 functions for "/home/ubuntu/contentions:*"... Hit Ctrl-C to end. ^C FUNC COUNT main 1 _start 1 primes_thread 2 insert_result 87186 is_prime 1252772 Detaching... If /home/ubuntu is in the $PATH, then the following command will also work: # ./funccount -p 1442 contentions:* Counting libc write and read calls using regular expression syntax (-r): # ./funccount -r 'c:(write|read)$' Tracing 2 functions for "c:(write|read)$"... Hit Ctrl-C to end. ^C FUNC COUNT read 2 write 4 Detaching... Kernel tracepoints are also available as targets. For example, trace common block I/O tracepoints and see how often they are invoked: # ./funccount t:block:* Tracing 19 functions for "t:block:*"... Hit Ctrl-C to end. ^C FUNC COUNT block:block_rq_complete 7 block:block_rq_issue 7 block:block_getrq 7 block:block_rq_insert 7 Detaching... Likewise, user-mode statically defined traces (USDT) can also be probed. For example, count mutex-related events in pthreads: # ./funccount u:pthread:*mutex* -p 1442 Tracing 7 functions for "u:pthread:*mutex*"... Hit Ctrl-C to end. ^C FUNC COUNT mutex_init 1 mutex_entry 547122 mutex_acquired 547175 mutex_release 547185 Detaching... An interval can be provided. Eg, printing output every 1 second for vfs calls: # ./funccount -i 1 'vfs_*' Tracing... Ctrl-C to end. FUNC COUNT vfs_fstatat 1 vfs_fstat 16 vfs_getattr_nosec 17 vfs_getattr 17 vfs_write 52 vfs_read 79 vfs_open 98 FUNC COUNT vfs_fstatat 10 vfs_fstat 10 vfs_open 13 vfs_getattr_nosec 20 vfs_getattr 20 vfs_write 28 vfs_read 39 FUNC COUNT vfs_fsync_range 2 vfs_lock_file 30 vfs_write 107 vfs_fstatat 129 vfs_fstat 130 vfs_open 154 vfs_getattr_nosec 222 vfs_getattr 222 vfs_read 384 ^C Detaching... This can be useful for making some ad hoc tools, exposing new counts of kernel activity that aren't visible in other metrics. Include -T to print timestamps on output. A maximum duration can be set. For example, to print 5 x 1 second summaries of vfs_read() calls: # ./funccount -i 1 -d 5 vfs_read Tracing 1 functions for "vfs_read"... Hit Ctrl-C to end. FUNC COUNT vfs_read 30 FUNC COUNT vfs_read 26 FUNC COUNT vfs_read 54 FUNC COUNT vfs_read 25 FUNC COUNT vfs_read 31 Detaching... By leaving off the "-i 1", this will print a single 5 second summary: # funccount.py -d 5 vfs_read Tracing 1 functions for "vfs_read"... Hit Ctrl-C to end. FUNC COUNT vfs_read 167 Detaching... This can be useful for finding out rates: trace all functions for ten seconds and then divide by ten for the per-second rate. The "*" wildcard can be used multiple times. Eg, matching functions that contain the word "readdir": # ./funccount '*readdir*' Tracing... Ctrl-C to end. ^C FUNC COUNT ext4_readdir 4 Detaching... Matching "tcp" then "send": # ./funccount '*tcp*send*' Tracing... Ctrl-C to end. ^C FUNC COUNT tcp_send_ack 4 tcp_send_delayed_ack 19 tcp_send_mss 26 tcp_sendmsg 26 tcp_v4_send_check 30 __tcp_v4_send_check 30 Detaching... Full USAGE: # ./funccount -h usage: funccount [-h] [-p PID] [-i INTERVAL] [-d DURATION] [-T] [-r] [-D] pattern Count functions, tracepoints, and USDT probes positional arguments: pattern search expression for events optional arguments: -h, --help show this help message and exit -p PID, --pid PID trace this PID only -i INTERVAL, --interval INTERVAL summary interval, seconds -d DURATION, --duration DURATION total duration of trace, seconds -T, --timestamp include timestamp on output -r, --regexp use regular expressions. Default is "*" wildcards only. -D, --debug print BPF program before starting (for debugging purposes) examples: ./funccount 'vfs_*' # count kernel fns starting with "vfs" ./funccount -r '^vfs.*' # same as above, using regular expressions ./funccount -Ti 5 'vfs_*' # output every 5 seconds, with timestamps ./funccount -d 10 'vfs_*' # trace for 10 seconds only ./funccount -p 185 'vfs_*' # count vfs calls for PID 181 only ./funccount t:sched:sched_fork # count calls to the sched_fork tracepoint ./funccount -p 185 u:node:gc* # count all GC USDT probes in node, PID 185 ./funccount c:malloc # count all malloc() calls in libc ./funccount go:os.* # count all "os.*" calls in libgo ./funccount -p 185 go:os.* # count all "os.*" calls in libgo, PID 185 ./funccount ./test:read* # count "read*" calls in the ./test binary bpfcc-0.12.0/tools/funclatency.py000077500000000000000000000174451357404205000167120ustar00rootroot00000000000000#!/usr/bin/python # @lint-avoid-python-3-compatibility-imports # # funclatency Time functions and print latency as a histogram. # For Linux, uses BCC, eBPF. # # USAGE: funclatency [-h] [-p PID] [-i INTERVAL] [-T] [-u] [-m] [-F] [-r] [-v] # pattern # # Run "funclatency -h" for full usage. # # The pattern is a string with optional '*' wildcards, similar to file # globbing. If you'd prefer to use regular expressions, use the -r option. # # Currently nested or recursive functions are not supported properly, and # timestamps will be overwritten, creating dubious output. Try to match single # functions, or groups of functions that run at the same stack layer, and # don't ultimately call each other. # # Copyright (c) 2015 Brendan Gregg. # Licensed under the Apache License, Version 2.0 (the "License") # # 20-Sep-2015 Brendan Gregg Created this. # 06-Oct-2016 Sasha Goldshtein Added user function support. from __future__ import print_function from bcc import BPF from time import sleep, strftime import argparse import signal # arguments examples = """examples: ./funclatency do_sys_open # time the do_sys_open() kernel function ./funclatency c:read # time the read() C library function ./funclatency -u vfs_read # time vfs_read(), in microseconds ./funclatency -m do_nanosleep # time do_nanosleep(), in milliseconds ./funclatency -i 2 -d 10 c:open # output every 2 seconds, for duration 10s ./funclatency -mTi 5 vfs_read # output every 5 seconds, with timestamps ./funclatency -p 181 vfs_read # time process 181 only ./funclatency 'vfs_fstat*' # time both vfs_fstat() and vfs_fstatat() ./funclatency 'c:*printf' # time the *printf family of functions ./funclatency -F 'vfs_r*' # show one histogram per matched function """ parser = argparse.ArgumentParser( description="Time functions and print latency as a histogram", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("-p", "--pid", type=int, help="trace this PID only") parser.add_argument("-i", "--interval", type=int, help="summary interval, in seconds") parser.add_argument("-d", "--duration", type=int, help="total duration of trace, in seconds") parser.add_argument("-T", "--timestamp", action="store_true", help="include timestamp on output") parser.add_argument("-u", "--microseconds", action="store_true", help="microsecond histogram") parser.add_argument("-m", "--milliseconds", action="store_true", help="millisecond histogram") parser.add_argument("-F", "--function", action="store_true", help="show a separate histogram per function") parser.add_argument("-r", "--regexp", action="store_true", help="use regular expressions. Default is \"*\" wildcards only.") parser.add_argument("-v", "--verbose", action="store_true", help="print the BPF program (for debugging purposes)") parser.add_argument("pattern", help="search expression for functions") parser.add_argument("--ebpf", action="store_true", help=argparse.SUPPRESS) args = parser.parse_args() if args.duration and not args.interval: args.interval = args.duration if not args.interval: args.interval = 99999999 def bail(error): print("Error: " + error) exit(1) parts = args.pattern.split(':') if len(parts) == 1: library = None pattern = args.pattern elif len(parts) == 2: library = parts[0] libpath = BPF.find_library(library) or BPF.find_exe(library) if not libpath: bail("can't resolve library %s" % library) library = libpath pattern = parts[1] else: bail("unrecognized pattern format '%s'" % pattern) if not args.regexp: pattern = pattern.replace('*', '.*') pattern = '^' + pattern + '$' # define BPF program bpf_text = """ #include typedef struct ip_pid { u64 ip; u64 pid; } ip_pid_t; typedef struct hist_key { ip_pid_t key; u64 slot; } hist_key_t; BPF_HASH(start, u32); STORAGE int trace_func_entry(struct pt_regs *ctx) { u64 pid_tgid = bpf_get_current_pid_tgid(); u32 pid = pid_tgid; u32 tgid = pid_tgid >> 32; u64 ts = bpf_ktime_get_ns(); FILTER ENTRYSTORE start.update(&pid, &ts); return 0; } int trace_func_return(struct pt_regs *ctx) { u64 *tsp, delta; u64 pid_tgid = bpf_get_current_pid_tgid(); u32 pid = pid_tgid; u32 tgid = pid_tgid >> 32; // calculate delta time tsp = start.lookup(&pid); if (tsp == 0) { return 0; // missed start } delta = bpf_ktime_get_ns() - *tsp; start.delete(&pid); FACTOR // store as histogram STORE return 0; } """ # do we need to store the IP and pid for each invocation? need_key = args.function or (library and not args.pid) # code substitutions if args.pid: bpf_text = bpf_text.replace('FILTER', 'if (tgid != %d) { return 0; }' % args.pid) else: bpf_text = bpf_text.replace('FILTER', '') if args.milliseconds: bpf_text = bpf_text.replace('FACTOR', 'delta /= 1000000;') label = "msecs" elif args.microseconds: bpf_text = bpf_text.replace('FACTOR', 'delta /= 1000;') label = "usecs" else: bpf_text = bpf_text.replace('FACTOR', '') label = "nsecs" if need_key: bpf_text = bpf_text.replace('STORAGE', 'BPF_HASH(ipaddr, u32);\n' + 'BPF_HISTOGRAM(dist, hist_key_t);') # stash the IP on entry, as on return it's kretprobe_trampoline: bpf_text = bpf_text.replace('ENTRYSTORE', 'u64 ip = PT_REGS_IP(ctx); ipaddr.update(&pid, &ip);') pid = '-1' if not library else 'tgid' bpf_text = bpf_text.replace('STORE', """ u64 ip, *ipp = ipaddr.lookup(&pid); if (ipp) { ip = *ipp; hist_key_t key; key.key.ip = ip; key.key.pid = %s; key.slot = bpf_log2l(delta); dist.increment(key); ipaddr.delete(&pid); } """ % pid) else: bpf_text = bpf_text.replace('STORAGE', 'BPF_HISTOGRAM(dist);') bpf_text = bpf_text.replace('ENTRYSTORE', '') bpf_text = bpf_text.replace('STORE', 'dist.increment(bpf_log2l(delta));') if args.verbose or args.ebpf: print(bpf_text) if args.ebpf: exit() # signal handler def signal_ignore(signal, frame): print() # load BPF program b = BPF(text=bpf_text) # attach probes if not library: b.attach_kprobe(event_re=pattern, fn_name="trace_func_entry") b.attach_kretprobe(event_re=pattern, fn_name="trace_func_return") matched = b.num_open_kprobes() else: b.attach_uprobe(name=library, sym_re=pattern, fn_name="trace_func_entry", pid=args.pid or -1) b.attach_uretprobe(name=library, sym_re=pattern, fn_name="trace_func_return", pid=args.pid or -1) matched = b.num_open_uprobes() if matched == 0: print("0 functions matched by \"%s\". Exiting." % args.pattern) exit() # header print("Tracing %d functions for \"%s\"... Hit Ctrl-C to end." % (matched / 2, args.pattern)) # output def print_section(key): if not library: return BPF.sym(key[0], -1) else: return "%s [%d]" % (BPF.sym(key[0], key[1]), key[1]) exiting = 0 if args.interval else 1 seconds = 0 dist = b.get_table("dist") while (1): try: sleep(args.interval) seconds += args.interval except KeyboardInterrupt: exiting = 1 # as cleanup can take many seconds, trap Ctrl-C: signal.signal(signal.SIGINT, signal_ignore) if args.duration and seconds >= args.duration: exiting = 1 print() if args.timestamp: print("%-8s\n" % strftime("%H:%M:%S"), end="") if need_key: dist.print_log2_hist(label, "Function", section_print_fn=print_section, bucket_fn=lambda k: (k.ip, k.pid)) else: dist.print_log2_hist(label) dist.clear() if exiting: print("Detaching...") exit() bpfcc-0.12.0/tools/funclatency_example.txt000066400000000000000000000505201357404205000206000ustar00rootroot00000000000000Demonstrations of funclatency, the Linux eBPF/bcc version. Timing the do_sys_open() kernel function until Ctrl-C: # ./funclatency do_sys_open Tracing do_sys_open... Hit Ctrl-C to end. ^C nsecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 0 | | 128 -> 255 : 0 | | 256 -> 511 : 0 | | 512 -> 1023 : 0 | | 1024 -> 2047 : 0 | | 2048 -> 4095 : 124 |**************** | 4096 -> 8191 : 291 |**************************************| 8192 -> 16383 : 36 |**** | 16384 -> 32767 : 16 |** | 32768 -> 65535 : 8 |* | 65536 -> 131071 : 0 | | 131072 -> 262143 : 0 | | 262144 -> 524287 : 0 | | 524288 -> 1048575 : 0 | | 1048576 -> 2097151 : 0 | | 2097152 -> 4194303 : 1 | | Detaching... The output shows a histogram of function latency (call time), measured from when the function began executing (was called) to when it finished (returned). This example output shows that most of the time, do_sys_open() took between 2048 and 65536 nanoseconds (2 to 65 microseconds). The peak of this distribution shows 291 calls of between 4096 and 8191 nanoseconds. There was also one occurrence, an outlier, in the 2 to 4 millisecond range. How this works: the function entry and return are traced using the kernel kprobe and kretprobe tracer. Timestamps are collected, the delta time calculated, which is the bucketized and stored as an in-kernel histogram for efficiency. The histogram is visible in the output: it's the "count" column; everything else is decoration. Only the count column is copied to user-level on output. This is an efficient way to time kernel functions and examine their latency distribution. Now trace a user function, pthread_mutex_lock in libpthread, to determine if there is considerable lock contention: # ./funclatency pthread:pthread_mutex_lock -p $(pidof contentions) Tracing 1 function for "pthread:pthread_mutex_lock"... Hit Ctrl-C to end. nsecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 0 | | 128 -> 255 : 0 | | 256 -> 511 : 0 | | 512 -> 1023 : 0 | | 1024 -> 2047 : 0 | | 2048 -> 4095 : 508967 |****************************************| 4096 -> 8191 : 70072 |***** | 8192 -> 16383 : 27686 |** | 16384 -> 32767 : 5075 | | 32768 -> 65535 : 2318 | | 65536 -> 131071 : 581 | | 131072 -> 262143 : 38 | | 262144 -> 524287 : 5 | | 524288 -> 1048575 : 1 | | 1048576 -> 2097151 : 9 | | Detaching... It seems that most calls to pthread_mutex_lock completed rather quickly (in under 4us), but there were some cases of considerable contention, sometimes over a full millisecond. Run a quick-and-dirty profiler over all the functions in an executable: # ./funclatency /home/user/primes:* -p $(pidof primes) -F Tracing 15 functions for "/home/user/primes:*"... Hit Ctrl-C to end. ^C Function = is_prime [6556] nsecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 0 | | 128 -> 255 : 0 | | 256 -> 511 : 0 | | 512 -> 1023 : 0 | | 1024 -> 2047 : 0 | | 2048 -> 4095 : 1495322 |****************************************| 4096 -> 8191 : 95744 |** | 8192 -> 16383 : 9926 | | 16384 -> 32767 : 3070 | | 32768 -> 65535 : 1415 | | 65536 -> 131071 : 112 | | 131072 -> 262143 : 9 | | 262144 -> 524287 : 3 | | 524288 -> 1048575 : 0 | | 1048576 -> 2097151 : 8 | | Function = insert_result [6556] nsecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 0 | | 128 -> 255 : 0 | | 256 -> 511 : 0 | | 512 -> 1023 : 0 | | 1024 -> 2047 : 0 | | 2048 -> 4095 : 111047 |****************************************| 4096 -> 8191 : 3998 |* | 8192 -> 16383 : 720 | | 16384 -> 32767 : 238 | | 32768 -> 65535 : 106 | | 65536 -> 131071 : 5 | | 131072 -> 262143 : 4 | | Detaching... From the results, we can see that the is_prime function has something resembling an exponential distribution -- very few primes take a very long time to test, while most numbers are verified as prime or composite in less than 4us. The insert_result function exhibits a similar phenomenon, likely due to contention over a shared results container. Now vfs_read() is traced, and a microseconds histogram printed: # ./funclatency -u vfs_read Tracing vfs_read... Hit Ctrl-C to end. ^C usecs : count distribution 0 -> 1 : 1143 |**************************************| 2 -> 3 : 420 |************* | 4 -> 7 : 159 |***** | 8 -> 15 : 295 |********* | 16 -> 31 : 25 | | 32 -> 63 : 5 | | 64 -> 127 : 1 | | 128 -> 255 : 0 | | 256 -> 511 : 0 | | 512 -> 1023 : 0 | | 1024 -> 2047 : 1 | | 2048 -> 4095 : 0 | | 4096 -> 8191 : 5 | | 8192 -> 16383 : 0 | | 16384 -> 32767 : 0 | | 32768 -> 65535 : 0 | | 65536 -> 131071 : 7 | | 131072 -> 262143 : 7 | | 262144 -> 524287 : 3 | | 524288 -> 1048575 : 7 | | Detaching... This shows a bimodal distribution. Many vfs_read() calls were faster than 15 microseconds, however, there was also a small handful between 65 milliseconds and 1 second, seen at the bottom of the table. These are likely network reads from SSH, waiting on interactive keystrokes. Tracing do_nanosleep() in milliseconds: # ./funclatency -m do_nanosleep Tracing do_nanosleep... Hit Ctrl-C to end. ^C msecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 0 | | 128 -> 255 : 0 | | 256 -> 511 : 0 | | 512 -> 1023 : 328 |**************************************| 1024 -> 2047 : 0 | | 2048 -> 4095 : 0 | | 4096 -> 8191 : 32 |*** | 8192 -> 16383 : 0 | | 16384 -> 32767 : 0 | | 32768 -> 65535 : 2 | | Detaching... This looks like it has found threads that are sleeping every 1, 5, and 60 seconds. An interval can be provided using -i, and timestamps added using -T. For example, tracing vfs_read() latency in milliseconds and printing output every 5 seconds: # ./funclatency -mTi 5 vfs_read Tracing vfs_read... Hit Ctrl-C to end. 20:10:08 msecs : count distribution 0 -> 1 : 1500 |*************************************+| 2 -> 3 : 3 | | 4 -> 7 : 1 | | 8 -> 15 : 2 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 4 | | 128 -> 255 : 3 | | 256 -> 511 : 1 | | 512 -> 1023 : 7 | | 20:10:13 msecs : count distribution 0 -> 1 : 1251 |*************************************+| 2 -> 3 : 3 | | 4 -> 7 : 2 | | 8 -> 15 : 0 | | 16 -> 31 : 2 | | 32 -> 63 : 3 | | 64 -> 127 : 5 | | 128 -> 255 : 5 | | 256 -> 511 : 3 | | 512 -> 1023 : 6 | | 1024 -> 2047 : 2 | | 20:10:18 msecs : count distribution 0 -> 1 : 1265 |*************************************+| 2 -> 3 : 0 | | 4 -> 7 : 5 | | 8 -> 15 : 9 | | 16 -> 31 : 7 | | 32 -> 63 : 1 | | 64 -> 127 : 2 | | 128 -> 255 : 3 | | 256 -> 511 : 5 | | 512 -> 1023 : 5 | | 1024 -> 2047 : 0 | | 2048 -> 4095 : 1 | | ^C 20:10:20 msecs : count distribution 0 -> 1 : 249 |*************************************+| 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 1 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 0 | | 128 -> 255 : 0 | | 256 -> 511 : 0 | | 512 -> 1023 : 1 | | Detaching... A single process can be traced, which filters in-kernel for efficiency. Here, the vfs_read() function is timed as milliseconds for PID 17064, which is a bash shell: # ./funclatency -mp 17064 vfs_read Tracing vfs_read... Hit Ctrl-C to end. ^C msecs : count distribution 0 -> 1 : 1 |** | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 1 |** | 16 -> 31 : 2 |***** | 32 -> 63 : 0 | | 64 -> 127 : 13 |**************************************| 128 -> 255 : 10 |***************************** | 256 -> 511 : 4 |*********** | Detaching... The distribution between 64 and 511 milliseconds shows keystroke latency. The -F option can be used to print a histogram per function. Eg: # ./funclatency -uF 'vfs_r*' Tracing 5 functions for "vfs_r*"... Hit Ctrl-C to end. ^C Function = vfs_read usecs : count distribution 0 -> 1 : 1044 |****************************************| 2 -> 3 : 383 |************** | 4 -> 7 : 76 |** | 8 -> 15 : 41 |* | 16 -> 31 : 26 | | 32 -> 63 : 0 | | 64 -> 127 : 1 | | 128 -> 255 : 0 | | 256 -> 511 : 0 | | 512 -> 1023 : 0 | | 1024 -> 2047 : 0 | | 2048 -> 4095 : 4 | | 4096 -> 8191 : 2 | | 8192 -> 16383 : 0 | | 16384 -> 32767 : 0 | | 32768 -> 65535 : 2 | | 65536 -> 131071 : 5 | | 131072 -> 262143 : 5 | | 262144 -> 524287 : 3 | | 524288 -> 1048575 : 7 | | Function = vfs_rename usecs : count distribution 0 -> 1 : 2 |**** | 2 -> 3 : 2 |**** | 4 -> 7 : 2 |**** | 8 -> 15 : 0 | | 16 -> 31 : 6 |************* | 32 -> 63 : 18 |****************************************| Detaching... USAGE message: # ./funclatency -h usage: funclatency [-h] [-p PID] [-i INTERVAL] [-T] [-u] [-m] [-F] [-r] [-v] pattern Time functions and print latency as a histogram positional arguments: pattern search expression for functions optional arguments: -h, --help show this help message and exit -p PID, --pid PID trace this PID only -i INTERVAL, --interval INTERVAL summary interval, in seconds -d DURATION, --duration DURATION total duration of trace, in seconds -T, --timestamp include timestamp on output -u, --microseconds microsecond histogram -m, --milliseconds millisecond histogram -F, --function show a separate histogram per function -r, --regexp use regular expressions. Default is "*" wildcards only. -v, --verbose print the BPF program (for debugging purposes) examples: ./funclatency do_sys_open # time the do_sys_open() kernel function ./funclatency c:read # time the read() C library function ./funclatency -u vfs_read # time vfs_read(), in microseconds ./funclatency -m do_nanosleep # time do_nanosleep(), in milliseconds ./funclatency -i 2 -d 10 c:open # output every 2 seconds, for duration 10s ./funclatency -mTi 5 vfs_read # output every 5 seconds, with timestamps ./funclatency -p 181 vfs_read # time process 181 only ./funclatency 'vfs_fstat*' # time both vfs_fstat() and vfs_fstatat() ./funclatency 'c:*printf' # time the *printf family of functions ./funclatency -F 'vfs_r*' # show one histogram per matched function bpfcc-0.12.0/tools/funcslower.py000077500000000000000000000236141357404205000165610ustar00rootroot00000000000000#!/usr/bin/python # @lint-avoid-python-3-compatibility-imports # # funcslower Trace slow kernel or user function calls. # For Linux, uses BCC, eBPF. # # USAGE: funcslower [-h] [-p PID] [-m MIN_MS] [-u MIN_US] [-a ARGUMENTS] # [-T] [-t] [-v] function [function ...] # # WARNING: This tool traces function calls by instrumenting the entry and # return from each function. For commonly-invoked functions like memory allocs # or file writes, this can be extremely expensive. Mind the overhead. # # NOTE: This tool cannot trace nested functions in the same invocation # due to instrumentation specifics, only innermost calls will be visible. # # By default, a minimum millisecond threshold of 1 is used. # # Copyright 2017, Sasha Goldshtein # Licensed under the Apache License, Version 2.0 (the "License") # # 30-Mar-2017 Sasha Goldshtein Created this. from __future__ import print_function from bcc import BPF import argparse import time examples = """examples: ./funcslower vfs_write # trace vfs_write calls slower than 1ms ./funcslower -m 10 vfs_write # same, but slower than 10ms ./funcslower -u 10 c:open # trace open calls slower than 10us ./funcslower -p 135 c:open # trace pid 135 only ./funcslower c:malloc c:free # trace both malloc and free slower than 1ms ./funcslower -a 2 c:open # show first two arguments to open ./funcslower -UK -m 10 c:open # Show user and kernel stack frame of open calls slower than 10ms ./funcslower -f -UK c:open # Output in folded format for flame graphs """ parser = argparse.ArgumentParser( description="Trace slow kernel or user function calls.", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("-p", "--pid", type=int, metavar="PID", dest="tgid", help="trace this PID only") parser.add_argument("-m", "--min-ms", type=float, dest="min_ms", help="minimum duration to trace (ms)") parser.add_argument("-u", "--min-us", type=float, dest="min_us", help="minimum duration to trace (us)") parser.add_argument("-a", "--arguments", type=int, help="print this many entry arguments, as hex") parser.add_argument("-T", "--time", action="store_true", help="show HH:MM:SS timestamp") parser.add_argument("-t", "--timestamp", action="store_true", help="show timestamp in seconds at us resolution") parser.add_argument("-v", "--verbose", action="store_true", help="print the BPF program for debugging purposes") parser.add_argument(metavar="function", nargs="+", dest="functions", help="function(s) to trace") parser.add_argument("--ebpf", action="store_true", help=argparse.SUPPRESS) parser.add_argument("-f", "--folded", action="store_true", help="output folded format, one line per stack (for flame graphs)") parser.add_argument("-U", "--user-stack", action="store_true", help="output user stack trace") parser.add_argument("-K", "--kernel-stack", action="store_true", help="output kernel stack trace") args = parser.parse_args() # fractions are allowed, but rounded to an integer nanosecond if args.min_ms: duration_ns = int(args.min_ms * 1000000) elif args.min_us: duration_ns = int(args.min_us * 1000) else: duration_ns = 1000000 # default to 1ms bpf_text = """ #include #include // for TASK_COMM_LEN struct entry_t { u64 id; u64 start_ns; #ifdef GRAB_ARGS u64 args[6]; #endif }; struct data_t { u64 id; u64 tgid_pid; u64 start_ns; u64 duration_ns; u64 retval; char comm[TASK_COMM_LEN]; #ifdef GRAB_ARGS u64 args[6]; #endif #ifdef USER_STACKS int user_stack_id; #endif #ifdef KERNEL_STACKS int kernel_stack_id; u64 kernel_ip; #endif }; BPF_HASH(entryinfo, u64, struct entry_t); BPF_PERF_OUTPUT(events); #if defined(USER_STACKS) || defined(KERNEL_STACKS) BPF_STACK_TRACE(stacks, 2048); #endif static int trace_entry(struct pt_regs *ctx, int id) { u64 tgid_pid = bpf_get_current_pid_tgid(); u32 tgid = tgid_pid >> 32; if (TGID_FILTER) return 0; u32 pid = tgid_pid; struct entry_t entry = {}; entry.start_ns = bpf_ktime_get_ns(); entry.id = id; #ifdef GRAB_ARGS entry.args[0] = PT_REGS_PARM1(ctx); entry.args[1] = PT_REGS_PARM2(ctx); entry.args[2] = PT_REGS_PARM3(ctx); entry.args[3] = PT_REGS_PARM4(ctx); entry.args[4] = PT_REGS_PARM5(ctx); entry.args[5] = PT_REGS_PARM6(ctx); #endif entryinfo.update(&tgid_pid, &entry); return 0; } int trace_return(struct pt_regs *ctx) { struct entry_t *entryp; u64 tgid_pid = bpf_get_current_pid_tgid(); entryp = entryinfo.lookup(&tgid_pid); if (entryp == 0) { return 0; } u64 delta_ns = bpf_ktime_get_ns() - entryp->start_ns; entryinfo.delete(&tgid_pid); if (delta_ns < DURATION_NS) return 0; struct data_t data = {}; data.id = entryp->id; data.tgid_pid = tgid_pid; data.start_ns = entryp->start_ns; data.duration_ns = delta_ns; data.retval = PT_REGS_RC(ctx); #ifdef USER_STACKS data.user_stack_id = stacks.get_stackid(ctx, BPF_F_USER_STACK); #endif #ifdef KERNEL_STACKS data.kernel_stack_id = stacks.get_stackid(ctx, 0); if (data.kernel_stack_id >= 0) { u64 ip = PT_REGS_IP(ctx); u64 page_offset; // if ip isn't sane, leave key ips as zero for later checking #if defined(CONFIG_X86_64) && defined(__PAGE_OFFSET_BASE) // x64, 4.16, ..., 4.11, etc., but some earlier kernel didn't have it page_offset = __PAGE_OFFSET_BASE; #elif defined(CONFIG_X86_64) && defined(__PAGE_OFFSET_BASE_L4) // x64, 4.17, and later #if defined(CONFIG_DYNAMIC_MEMORY_LAYOUT) && defined(CONFIG_X86_5LEVEL) page_offset = __PAGE_OFFSET_BASE_L5; #else page_offset = __PAGE_OFFSET_BASE_L4; #endif #else // earlier x86_64 kernels, e.g., 4.6, comes here // arm64, s390, powerpc, x86_32 page_offset = PAGE_OFFSET; #endif if (ip > page_offset) { data.kernel_ip = ip; } } #endif #ifdef GRAB_ARGS bpf_probe_read(&data.args[0], sizeof(data.args), entryp->args); #endif bpf_get_current_comm(&data.comm, sizeof(data.comm)); events.perf_submit(ctx, &data, sizeof(data)); return 0; } """ bpf_text = bpf_text.replace('DURATION_NS', str(duration_ns)) if args.arguments: bpf_text = "#define GRAB_ARGS\n" + bpf_text if args.user_stack: bpf_text = "#define USER_STACKS\n" + bpf_text if args.kernel_stack: bpf_text = "#define KERNEL_STACKS\n" + bpf_text if args.tgid: bpf_text = bpf_text.replace('TGID_FILTER', 'tgid != %d' % args.tgid) else: bpf_text = bpf_text.replace('TGID_FILTER', '0') for i in range(len(args.functions)): bpf_text += """ int trace_%d(struct pt_regs *ctx) { return trace_entry(ctx, %d); } """ % (i, i) if args.verbose or args.ebpf: print(bpf_text) if args.ebpf: exit() b = BPF(text=bpf_text) for i, function in enumerate(args.functions): if ":" in function: library, func = function.split(":") b.attach_uprobe(name=library, sym=func, fn_name="trace_%d" % i) b.attach_uretprobe(name=library, sym=func, fn_name="trace_return") else: b.attach_kprobe(event=function, fn_name="trace_%d" % i) b.attach_kretprobe(event=function, fn_name="trace_return") time_designator = "us" if args.min_us else "ms" time_value = args.min_us or args.min_ms or 1 time_multiplier = 1000 if args.min_us else 1000000 time_col = args.time or args.timestamp # Do not print header when folded if not args.folded: print("Tracing function calls slower than %g %s... Ctrl+C to quit." % (time_value, time_designator)) print((("%-10s " % "TIME" if time_col else "") + "%-14s %-6s %7s %16s %s") % ("COMM", "PID", "LAT(%s)" % time_designator, "RVAL", "FUNC" + (" ARGS" if args.arguments else ""))) earliest_ts = 0 def time_str(event): if args.time: return "%-10s " % time.strftime("%H:%M:%S") if args.timestamp: global earliest_ts if earliest_ts == 0: earliest_ts = event.start_ns return "%-10.6f " % ((event.start_ns - earliest_ts) / 1000000000.0) return "" def args_str(event): if not args.arguments: return "" return str.join(" ", ["0x%x" % arg for arg in event.args[:args.arguments]]) def print_stack(event): user_stack = [] stack_traces = b.get_table("stacks") if args.user_stack and event.user_stack_id > 0: user_stack = stack_traces.walk(event.user_stack_id) kernel_stack = [] if args.kernel_stack and event.kernel_stack_id > 0: kernel_tmp = stack_traces.walk(event.kernel_stack_id) # fix kernel stack for addr in kernel_tmp: kernel_stack.append(addr) do_delimiter = user_stack and kernel_stack if args.folded: # print folded stack output user_stack = list(user_stack) kernel_stack = list(kernel_stack) line = [event.comm.decode('utf-8', 'replace')] + \ [b.sym(addr, event.tgid_pid) for addr in reversed(user_stack)] + \ (do_delimiter and ["-"] or []) + \ [b.ksym(addr) for addr in reversed(kernel_stack)] print("%s %d" % (";".join(line), 1)) else: # print default multi-line stack output. for addr in kernel_stack: print(" %s" % b.ksym(addr)) for addr in user_stack: print(" %s" % b.sym(addr, event.tgid_pid)) def print_event(cpu, data, size): event = b["events"].event(data) ts = float(event.duration_ns) / time_multiplier if not args.folded: print((time_str(event) + "%-14.14s %-6s %7.2f %16x %s %s") % (event.comm.decode('utf-8', 'replace'), event.tgid_pid >> 32, ts, event.retval, args.functions[event.id], args_str(event))) if args.user_stack or args.kernel_stack: print_stack(event) b["events"].open_perf_buffer(print_event, page_cnt=64) while True: try: b.perf_buffer_poll() except KeyboardInterrupt: exit() bpfcc-0.12.0/tools/funcslower_example.txt000066400000000000000000000152021357404205000204520ustar00rootroot00000000000000Demonstrations of funcslower, the Linux eBPF/bcc version. funcslower shows kernel or user function invocations slower than a threshold. This can be used for last-resort diagnostics when aggregation-based tools have failed. For example, trace the open() function in libc when it is slower than 1 microsecond (us): # ./funcslower c:open -u 1 Tracing function calls slower than 1 us... Ctrl+C to quit. COMM PID LAT(us) RVAL FUNC less 27074 33.77 3 c:open less 27074 9.96 ffffffffffffffff c:open less 27074 5.92 ffffffffffffffff c:open less 27074 15.88 ffffffffffffffff c:open less 27074 8.89 3 c:open less 27074 15.89 3 c:open sh 27075 20.97 4 c:open bash 27075 20.14 4 c:open lesspipe.sh 27075 18.77 4 c:open lesspipe.sh 27075 11.21 4 c:open lesspipe.sh 27075 13.68 4 c:open file 27076 14.83 ffffffffffffffff c:open file 27076 8.02 4 c:open file 27076 10.26 4 c:open file 27076 6.55 4 c:open less 27074 11.67 4 c:open ^C This shows several open operations performed by less and some helpers it invoked in the process. The latency (in microseconds) is shown, as well as the return value from the open() function, which helps indicate if there is a correlation between failures and slow invocations. Most open() calls seemed to have completed successfully (returning a valid file descriptor), but some have failed and returned -1. You can also trace kernel functions: # ./funcslower -m 10 vfs_read Tracing function calls slower than 10 ms... Ctrl+C to quit. COMM PID LAT(ms) RVAL FUNC bash 11527 78.97 1 vfs_read bash 11527 101.26 1 vfs_read bash 11527 1053.60 1 vfs_read bash 11527 44.21 1 vfs_read bash 11527 79.50 1 vfs_read bash 11527 33.37 1 vfs_read bash 11527 112.17 1 vfs_read bash 11527 101.49 1 vfs_read ^C Occasionally, it is also useful to see the arguments passed to the functions. The raw hex values of the arguments are available when using the -a switch: # ./funcslower __kmalloc -a 2 -u 1 Tracing function calls slower than 1 us... Ctrl+C to quit. COMM PID LAT(us) RVAL FUNC ARGS kworker/0:2 27077 7.46 ffff90054f9f8e40 __kmalloc 0x98 0x1400000 kworker/0:2 27077 6.84 ffff90054f9f8e40 __kmalloc 0x98 0x1400000 bash 11527 6.87 ffff90054f9f8e40 __kmalloc 0x90 0x1408240 bash 11527 1.15 ffff90054f9f8e40 __kmalloc 0x90 0x1408240 bash 11527 1.15 ffff90055a1b8c00 __kmalloc 0x2c 0x1400240 bash 11527 1.18 ffff90054b87d240 __kmalloc 0x1c 0x1400040 bash 11527 10.59 ffff900546d60000 __kmalloc 0x10000 0x14082c0 bash 11527 1.49 ffff90054fbd4c00 __kmalloc 0x280 0x15080c0 bash 11527 1.00 ffff90054789b000 __kmalloc 0x800 0x15012c0 bash 27128 3.47 ffff90057ca1a200 __kmalloc 0x150 0x1400240 bash 27128 1.82 ffff90054fbd4c00 __kmalloc 0x230 0x14000c0 bash 27128 1.17 ffff90054b87d5a0 __kmalloc 0x1c 0x14000c0 perf 27128 4.81 ffff90054f9f8e40 __kmalloc 0x90 0x1408240 perf 27128 24.71 ffff900566990000 __kmalloc 0x10000 0x14082c0 ^C This shows the first two arguments to __kmalloc -- the first one is the size of the requested allocation. The return value is also shown (null return values would indicate a failure). # ./funcslower -U -m 30 '/usr/sbin/nginx:database_write' Tracing function calls slower than 30 ms... Ctrl+C to quit. COMM PID LAT(ms) RVAL FUNC nginx 1617 30.15 9 /usr/sbin/nginx:database_write DataBaseProvider::setData(std::string const&, record_s&) UserDataProvider::saveRecordData(RecordData const&) RequestProcessor::writeResponse(int) RequestProcessor::processRequest() RequestRouter::processRequest(RequestWrapper*, ResponseWrapper*) ngx_http_core_content_phase ngx_http_core_run_phases ngx_http_process_request ngx_process_events_and_timers ngx_spawn_process ngx_master_process_cycle main __libc_start_main [unknown] nginx 1629 30.14 9 /usr/sbin/nginx:database_write DataBaseProvider::setData(std::string const&, record_s&) UserDataProvider::saveRecordData(RecordData const&) RequestProcessor::writeResponse(int) RequestProcessor::processRequest() RequestRouter::processRequest(RequestWrapper*, ResponseWrapper*) ngx_http_core_content_phase ngx_http_core_run_phases ngx_http_process_request ngx_process_events_and_timers ngx_spawn_process ngx_master_process_cycle main __libc_start_main [unknown] ^C Shows the user space stack trace of calls to the user space function call open taking longer than 30 ms. USAGE message: usage: funcslower.py [-hf] [-p PID] [-U | -K] [-m MIN_MS] [-u MIN_US] [-a ARGUMENTS] [-T] [-t] [-v] function [function ...] Trace slow kernel or user function calls. positional arguments: function function(s) to trace optional arguments: -h, --help show this help message and exit -p PID, --pid PID trace this PID only -m MIN_MS, --min-ms MIN_MS minimum duration to trace (ms) -u MIN_US, --min-us MIN_US minimum duration to trace (us) -U, --user-stack show stacks from user space -K, --kernel-stack show stacks from kernel space -f print output in folded stack format. -a ARGUMENTS, --arguments ARGUMENTS print this many entry arguments, as hex -T, --time show HH:MM:SS timestamp -t, --timestamp show timestamp in seconds at us resolution -v, --verbose print the BPF program for debugging purposes examples: ./funcslower vfs_write # trace vfs_write calls slower than 1ms ./funcslower -m 10 vfs_write # same, but slower than 10ms ./funcslower -u 10 c:open # trace open calls slower than 10us ./funcslower -p 135 c:open # trace pid 135 only ./funcslower c:malloc c:free # trace both malloc and free slower than 1ms ./funcslower -a 2 c:open # show first two arguments to open bpfcc-0.12.0/tools/gethostlatency.py000077500000000000000000000073521357404205000174300ustar00rootroot00000000000000#!/usr/bin/python # # gethostlatency Show latency for getaddrinfo/gethostbyname[2] calls. # For Linux, uses BCC, eBPF. Embedded C. # # 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. # # Copyright 2016 Netflix, Inc. # Licensed under the Apache License, Version 2.0 (the "License") # # 28-Jan-2016 Brendan Gregg Created this. # 30-Mar-2016 Allan McAleavy updated for BPF_PERF_OUTPUT from __future__ import print_function from bcc import BPF from time import strftime import argparse examples = """examples: ./gethostlatency # time getaddrinfo/gethostbyname[2] calls ./gethostlatency -p 181 # only trace PID 181 """ parser = argparse.ArgumentParser( description="Show latency for getaddrinfo/gethostbyname[2] calls", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("-p", "--pid", help="trace this PID only", type=int, default=-1) parser.add_argument("--ebpf", action="store_true", help=argparse.SUPPRESS) args = parser.parse_args() # load BPF program bpf_text = """ #include #include struct val_t { u32 pid; char comm[TASK_COMM_LEN]; char host[80]; u64 ts; }; struct data_t { u32 pid; u64 delta; char comm[TASK_COMM_LEN]; char host[80]; }; BPF_HASH(start, u32, struct val_t); BPF_PERF_OUTPUT(events); int do_entry(struct pt_regs *ctx) { if (!PT_REGS_PARM1(ctx)) return 0; struct val_t val = {}; u32 pid = bpf_get_current_pid_tgid(); if (bpf_get_current_comm(&val.comm, sizeof(val.comm)) == 0) { bpf_probe_read(&val.host, sizeof(val.host), (void *)PT_REGS_PARM1(ctx)); val.pid = bpf_get_current_pid_tgid(); val.ts = bpf_ktime_get_ns(); start.update(&pid, &val); } return 0; } int do_return(struct pt_regs *ctx) { struct val_t *valp; struct data_t data = {}; u64 delta; u32 pid = bpf_get_current_pid_tgid(); u64 tsp = bpf_ktime_get_ns(); valp = start.lookup(&pid); if (valp == 0) return 0; // missed start bpf_probe_read(&data.comm, sizeof(data.comm), valp->comm); bpf_probe_read(&data.host, sizeof(data.host), (void *)valp->host); data.pid = valp->pid; data.delta = tsp - valp->ts; events.perf_submit(ctx, &data, sizeof(data)); start.delete(&pid); return 0; } """ if args.ebpf: print(bpf_text) exit() b = BPF(text=bpf_text) b.attach_uprobe(name="c", sym="getaddrinfo", fn_name="do_entry", pid=args.pid) b.attach_uprobe(name="c", sym="gethostbyname", fn_name="do_entry", pid=args.pid) b.attach_uprobe(name="c", sym="gethostbyname2", fn_name="do_entry", pid=args.pid) b.attach_uretprobe(name="c", sym="getaddrinfo", fn_name="do_return", pid=args.pid) b.attach_uretprobe(name="c", sym="gethostbyname", fn_name="do_return", pid=args.pid) b.attach_uretprobe(name="c", sym="gethostbyname2", fn_name="do_return", pid=args.pid) # header print("%-9s %-6s %-16s %10s %s" % ("TIME", "PID", "COMM", "LATms", "HOST")) def print_event(cpu, data, size): event = b["events"].event(data) print("%-9s %-6d %-16s %10.2f %s" % (strftime("%H:%M:%S"), event.pid, event.comm.decode('utf-8', 'replace'), (float(event.delta) / 1000000), event.host.decode('utf-8', 'replace'))) # loop with callback to print_event b["events"].open_perf_buffer(print_event) while 1: try: b.perf_buffer_poll() except KeyboardInterrupt: exit() bpfcc-0.12.0/tools/gethostlatency_example.txt000066400000000000000000000024451357404205000213250ustar00rootroot00000000000000Demonstrations of gethostlatency, the Linux eBPF/bcc 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 TIME PID COMM LATms HOST 06:10:24 28011 wget 90.00 www.iovisor.org 06:10:28 28127 wget 0.00 www.iovisor.org 06:10:41 28404 wget 9.00 www.netflix.com 06:10:48 28544 curl 35.00 www.netflix.com.au 06:11:10 29054 curl 31.00 www.plumgrid.com 06:11:16 29195 curl 3.00 www.facebook.com 06:11:25 29404 curl 72.00 foo 06:11:28 29475 curl 1.00 foo In this example, the first call to lookup "www.iovisor.org" took 90 ms, and the second took 0 ms (cached). The slowest call in this example was to "foo", which was an unsuccessful lookup. USAGE message: # ./gethostlatency -h usage: gethostlatency [-h] [-p PID] Show latency for getaddrinfo/gethostbyname[2] calls optional arguments: -h, --help show this help message and exit -p PID, --pid PID trace this PID only examples: ./gethostlatency # time getaddrinfo/gethostbyname[2] calls ./gethostlatency -p 181 # only trace PID 181 bpfcc-0.12.0/tools/hardirqs.py000077500000000000000000000121131357404205000161770ustar00rootroot00000000000000#!/usr/bin/python # @lint-avoid-python-3-compatibility-imports # # hardirqs Summarize hard IRQ (interrupt) event time. # For Linux, uses BCC, eBPF. # # USAGE: hardirqs [-h] [-T] [-N] [-C] [-d] [interval] [outputs] # # Thanks Amer Ather for help understanding irq behavior. # # Copyright (c) 2015 Brendan Gregg. # Licensed under the Apache License, Version 2.0 (the "License") # # 19-Oct-2015 Brendan Gregg Created this. from __future__ import print_function from bcc import BPF from time import sleep, strftime import argparse # arguments examples = """examples: ./hardirqs # sum hard irq event time ./hardirqs -d # show hard irq event time as histograms ./hardirqs 1 10 # print 1 second summaries, 10 times ./hardirqs -NT 1 # 1s summaries, nanoseconds, and timestamps """ parser = argparse.ArgumentParser( description="Summarize hard irq event time as histograms", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("-T", "--timestamp", action="store_true", help="include timestamp on output") parser.add_argument("-N", "--nanoseconds", action="store_true", help="output in nanoseconds") parser.add_argument("-C", "--count", action="store_true", help="show event counts instead of timing") parser.add_argument("-d", "--dist", action="store_true", help="show distributions as histograms") parser.add_argument("interval", nargs="?", default=99999999, help="output interval, in seconds") parser.add_argument("outputs", nargs="?", default=99999999, help="number of outputs") parser.add_argument("--ebpf", action="store_true", help=argparse.SUPPRESS) args = parser.parse_args() countdown = int(args.outputs) if args.count and (args.dist or args.nanoseconds): print("The --count option can't be used with time-based options") exit() if args.count: factor = 1 label = "count" elif args.nanoseconds: factor = 1 label = "nsecs" else: factor = 1000 label = "usecs" debug = 0 # define BPF program bpf_text = """ #include #include #include #include typedef struct irq_key { char name[32]; u64 slot; } irq_key_t; BPF_HASH(start, u32); BPF_HASH(irqdesc, u32, struct irq_desc *); BPF_HISTOGRAM(dist, irq_key_t); // count IRQ int count_only(struct pt_regs *ctx, struct irq_desc *desc) { u32 pid = bpf_get_current_pid_tgid(); struct irqaction *action = desc->action; char *name = (char *)action->name; irq_key_t key = {.slot = 0 /* ignore */}; bpf_probe_read(&key.name, sizeof(key.name), name); dist.increment(key); return 0; } // time IRQ int trace_start(struct pt_regs *ctx, struct irq_desc *desc) { u32 pid = bpf_get_current_pid_tgid(); u64 ts = bpf_ktime_get_ns(); start.update(&pid, &ts); irqdesc.update(&pid, &desc); return 0; } int trace_completion(struct pt_regs *ctx) { u64 *tsp, delta; struct irq_desc **descp; u32 pid = bpf_get_current_pid_tgid(); // fetch timestamp and calculate delta tsp = start.lookup(&pid); descp = irqdesc.lookup(&pid); if (tsp == 0 || descp == 0) { return 0; // missed start } struct irq_desc *desc = *descp; struct irqaction *action = desc->action; char *name = (char *)action->name; delta = bpf_ktime_get_ns() - *tsp; // store as sum or histogram STORE start.delete(&pid); irqdesc.delete(&pid); return 0; } """ # code substitutions if args.dist: bpf_text = bpf_text.replace('STORE', 'irq_key_t key = {.slot = bpf_log2l(delta / %d)};' % factor + 'bpf_probe_read(&key.name, sizeof(key.name), name);' + 'dist.increment(key);') else: bpf_text = bpf_text.replace('STORE', 'irq_key_t key = {.slot = 0 /* ignore */};' + 'bpf_probe_read(&key.name, sizeof(key.name), name);' + 'dist.increment(key, delta);') if debug or args.ebpf: print(bpf_text) if args.ebpf: exit() # load BPF program b = BPF(text=bpf_text) # these should really use irq:irq_handler_entry/exit tracepoints: if args.count: b.attach_kprobe(event="handle_irq_event_percpu", fn_name="count_only") print("Tracing hard irq events... Hit Ctrl-C to end.") else: b.attach_kprobe(event="handle_irq_event_percpu", fn_name="trace_start") b.attach_kretprobe(event="handle_irq_event_percpu", fn_name="trace_completion") print("Tracing hard irq event time... Hit Ctrl-C to end.") # output exiting = 0 if args.interval else 1 dist = b.get_table("dist") while (1): try: sleep(int(args.interval)) except KeyboardInterrupt: exiting = 1 print() if args.timestamp: print("%-8s\n" % strftime("%H:%M:%S"), end="") if args.dist: dist.print_log2_hist(label, "hardirq") else: print("%-26s %11s" % ("HARDIRQ", "TOTAL_" + label)) for k, v in sorted(dist.items(), key=lambda dist: dist[1].value): print("%-26s %11d" % (k.name.decode('utf-8', 'replace'), v.value / factor)) dist.clear() countdown -= 1 if exiting or countdown == 0: exit() bpfcc-0.12.0/tools/hardirqs_example.txt000066400000000000000000001120601357404205000201000ustar00rootroot00000000000000Demonstrations of hardirqs, the Linux eBPF/bcc version. This program traces hard interrupts (irqs), and stores timing statistics in-kernel for efficiency. For example: # ./hardirqs Tracing hard irq event time... Hit Ctrl-C to end. ^C HARDIRQ TOTAL_usecs callfuncsingle0 2 callfuncsingle5 5 callfuncsingle6 5 callfuncsingle7 21 blkif 66 timer7 84 resched5 94 resched0 97 resched3 102 resched7 111 resched6 255 timer3 362 resched4 367 timer5 474 timer1 529 timer6 679 timer2 746 timer4 943 resched1 1048 timer0 1558 resched2 1750 eth0 11441 The HARDIRQ column prints the interrupt action name. While tracing, the eth0 hard irq action ran for 11441 microseconds (11 milliseconds) in total. Many other interrupts are visible in the output: this is an 8 CPU system, and some of these interrupts have a separate action per-CPU (eg, "timer", "resched"). An interval can be provided, and also optionally a count. Eg, printing output every 1 second, and including timestamps (-T): # ./hardirqs -T 1 3 Tracing hard irq event time... Hit Ctrl-C to end. 22:16:14 HARDIRQ TOTAL_usecs callfuncsingle0 2 callfuncsingle7 5 callfuncsingle3 5 callfuncsingle2 5 callfuncsingle6 6 callfuncsingle1 11 resched0 32 blkif 51 resched5 71 resched7 71 resched4 72 resched6 82 timer7 172 resched1 187 resched2 236 timer3 252 resched3 282 timer1 320 timer2 374 timer6 396 timer5 427 timer4 470 timer0 1430 eth0 7498 22:16:15 HARDIRQ TOTAL_usecs callfuncsingle7 6 callfuncsingle5 11 callfuncsingle4 13 timer2 17 callfuncsingle6 18 resched0 21 blkif 33 resched3 40 resched5 60 resched4 69 resched6 70 resched7 74 timer7 86 resched2 91 timer3 134 resched1 293 timer5 354 timer1 433 timer6 497 timer4 1112 timer0 1768 eth0 6972 22:16:16 HARDIRQ TOTAL_usecs callfuncsingle7 5 callfuncsingle3 5 callfuncsingle2 6 timer3 10 resched0 18 callfuncsingle4 22 resched5 27 resched6 44 blkif 45 resched7 65 resched4 69 timer4 77 resched2 97 timer7 98 resched3 103 timer2 169 resched1 226 timer5 525 timer1 691 timer6 697 timer0 1415 eth0 7152 This can be useful for quantifying where CPU cycles are spent among the hard interrupts (summarized as the %irq column from mpstat(1)). The output above shows that most time was spent processing for eth0 (network interface), which was around 7 milliseconds per second (total across all CPUs). Note that the time spent among the "timer" interrupts was low, and usually less than one microsecond per second. Here's the hardirq per-second output when the perf tool is performing a 999 Hertz CPU profile ("perf record -F 999 -a ..."): 22:13:59 HARDIRQ TOTAL_usecs callfuncsingle7 5 callfuncsingle5 5 callfuncsingle3 6 callfuncsingle4 7 callfuncsingle6 19 blkif 66 resched0 66 resched2 82 resched7 87 resched3 96 resched4 118 resched5 120 resched6 130 resched1 230 timer3 946 timer1 1981 timer7 2618 timer5 3063 timer6 3141 timer4 3511 timer2 3554 timer0 5044 eth0 16015 This sheds some light into the CPU overhead of the perf profiler, which cost around 3 milliseconds per second. Note that I'm usually profiling at a much lower rate, 99 Hertz, which looks like this: 22:22:12 HARDIRQ TOTAL_usecs callfuncsingle3 5 callfuncsingle6 5 callfuncsingle5 22 blkif 46 resched6 47 resched5 57 resched4 66 resched7 78 resched2 97 resched0 214 timer2 326 timer0 498 timer5 536 timer6 576 timer1 600 timer4 982 resched1 1315 timer7 1364 timer3 1825 resched3 5708 eth0 9743 Much lower (and remember to compare this to the baseline). Note that perf has other overheads (non-irq CPU cycles, file system storage). The distribution of interrupt run time can be printed as a histogram with the -d option. Eg: # ./hardirqs -d Tracing hard irq event time... Hit Ctrl-C to end. ^C hardirq = 'callfuncsingle1' usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 0 | | 128 -> 255 : 0 | | 256 -> 511 : 0 | | 512 -> 1023 : 0 | | 1024 -> 2047 : 0 | | 2048 -> 4095 : 0 | | 4096 -> 8191 : 0 | | 8192 -> 16383 : 1 |****************************************| hardirq = 'callfuncsingle0' usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 0 | | 128 -> 255 : 0 | | 256 -> 511 : 0 | | 512 -> 1023 : 0 | | 1024 -> 2047 : 0 | | 2048 -> 4095 : 1 |****************************************| hardirq = 'callfuncsingle3' usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 0 | | 128 -> 255 : 0 | | 256 -> 511 : 0 | | 512 -> 1023 : 0 | | 1024 -> 2047 : 0 | | 2048 -> 4095 : 0 | | 4096 -> 8191 : 3 |****************************************| hardirq = 'callfuncsingle2' usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 0 | | 128 -> 255 : 0 | | 256 -> 511 : 0 | | 512 -> 1023 : 0 | | 1024 -> 2047 : 0 | | 2048 -> 4095 : 0 | | 4096 -> 8191 : 2 |****************************************| hardirq = 'callfuncsingle5' usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 0 | | 128 -> 255 : 0 | | 256 -> 511 : 0 | | 512 -> 1023 : 0 | | 1024 -> 2047 : 0 | | 2048 -> 4095 : 0 | | 4096 -> 8191 : 5 |****************************************| hardirq = 'callfuncsingle4' usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 0 | | 128 -> 255 : 0 | | 256 -> 511 : 0 | | 512 -> 1023 : 0 | | 1024 -> 2047 : 0 | | 2048 -> 4095 : 0 | | 4096 -> 8191 : 6 |****************************************| hardirq = 'callfuncsingle7' usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 0 | | 128 -> 255 : 0 | | 256 -> 511 : 0 | | 512 -> 1023 : 0 | | 1024 -> 2047 : 0 | | 2048 -> 4095 : 0 | | 4096 -> 8191 : 4 |****************************************| hardirq = 'callfuncsingle6' usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 0 | | 128 -> 255 : 0 | | 256 -> 511 : 0 | | 512 -> 1023 : 0 | | 1024 -> 2047 : 0 | | 2048 -> 4095 : 0 | | 4096 -> 8191 : 4 |****************************************| hardirq = 'eth0' usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 0 | | 128 -> 255 : 0 | | 256 -> 511 : 0 | | 512 -> 1023 : 5102 |********* | 1024 -> 2047 : 20617 |****************************************| 2048 -> 4095 : 4832 |********* | 4096 -> 8191 : 12 | | hardirq = 'timer7' usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 0 | | 128 -> 255 : 0 | | 256 -> 511 : 0 | | 512 -> 1023 : 0 | | 1024 -> 2047 : 9 |*** | 2048 -> 4095 : 70 |***************************** | 4096 -> 8191 : 94 |****************************************| hardirq = 'timer6' usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 0 | | 128 -> 255 : 0 | | 256 -> 511 : 0 | | 512 -> 1023 : 0 | | 1024 -> 2047 : 1 | | 2048 -> 4095 : 86 |*********** | 4096 -> 8191 : 295 |****************************************| 8192 -> 16383 : 28 |*** | hardirq = 'timer5' usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 0 | | 128 -> 255 : 0 | | 256 -> 511 : 0 | | 512 -> 1023 : 1 | | 1024 -> 2047 : 0 | | 2048 -> 4095 : 137 |****************************************| 4096 -> 8191 : 123 |*********************************** | 8192 -> 16383 : 8 |** | hardirq = 'timer4' usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 0 | | 128 -> 255 : 0 | | 256 -> 511 : 0 | | 512 -> 1023 : 0 | | 1024 -> 2047 : 0 | | 2048 -> 4095 : 46 |********* | 4096 -> 8191 : 198 |****************************************| 8192 -> 16383 : 49 |********* | hardirq = 'timer3' usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 0 | | 128 -> 255 : 0 | | 256 -> 511 : 0 | | 512 -> 1023 : 0 | | 1024 -> 2047 : 4 | | 2048 -> 4095 : 210 |****************************************| 4096 -> 8191 : 186 |*********************************** | hardirq = 'timer2' usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 0 | | 128 -> 255 : 0 | | 256 -> 511 : 0 | | 512 -> 1023 : 0 | | 1024 -> 2047 : 0 | | 2048 -> 4095 : 245 |****************************************| 4096 -> 8191 : 227 |************************************* | 8192 -> 16383 : 6 | | hardirq = 'timer1' usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 0 | | 128 -> 255 : 0 | | 256 -> 511 : 0 | | 512 -> 1023 : 0 | | 1024 -> 2047 : 6 |* | 2048 -> 4095 : 112 |************************ | 4096 -> 8191 : 181 |****************************************| 8192 -> 16383 : 7 |* | hardirq = 'timer0' usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 0 | | 128 -> 255 : 0 | | 256 -> 511 : 0 | | 512 -> 1023 : 0 | | 1024 -> 2047 : 0 | | 2048 -> 4095 : 0 | | 4096 -> 8191 : 887 |****************************************| 8192 -> 16383 : 92 |**** | hardirq = 'blkif' usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 0 | | 128 -> 255 : 0 | | 256 -> 511 : 0 | | 512 -> 1023 : 0 | | 1024 -> 2047 : 0 | | 2048 -> 4095 : 0 | | 4096 -> 8191 : 9 |****************************************| 8192 -> 16383 : 7 |******************************* | 16384 -> 32767 : 2 |******** | hardirq = 'resched4' usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 0 | | 128 -> 255 : 0 | | 256 -> 511 : 0 | | 512 -> 1023 : 0 | | 1024 -> 2047 : 104 |****************************************| 2048 -> 4095 : 80 |****************************** | hardirq = 'resched5' usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 0 | | 128 -> 255 : 0 | | 256 -> 511 : 0 | | 512 -> 1023 : 27 |***** | 1024 -> 2047 : 216 |****************************************| 2048 -> 4095 : 27 |***** | 4096 -> 8191 : 1 | | hardirq = 'resched6' usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 0 | | 128 -> 255 : 0 | | 256 -> 511 : 0 | | 512 -> 1023 : 480 |******************* | 1024 -> 2047 : 1003 |****************************************| 2048 -> 4095 : 64 |** | hardirq = 'resched7' usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 0 | | 128 -> 255 : 0 | | 256 -> 511 : 0 | | 512 -> 1023 : 46 |********* | 1024 -> 2047 : 190 |****************************************| 2048 -> 4095 : 42 |******** | hardirq = 'resched0' usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 0 | | 128 -> 255 : 0 | | 256 -> 511 : 0 | | 512 -> 1023 : 11 |**** | 1024 -> 2047 : 100 |****************************************| 2048 -> 4095 : 23 |********* | hardirq = 'resched1' usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 0 | | 128 -> 255 : 0 | | 256 -> 511 : 0 | | 512 -> 1023 : 96 |******** | 1024 -> 2047 : 462 |****************************************| 2048 -> 4095 : 36 |*** | hardirq = 'resched2' usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 0 | | 128 -> 255 : 0 | | 256 -> 511 : 0 | | 512 -> 1023 : 120 |************************** | 1024 -> 2047 : 183 |****************************************| 2048 -> 4095 : 41 |******** | hardirq = 'resched3' usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 0 | | 128 -> 255 : 0 | | 256 -> 511 : 0 | | 512 -> 1023 : 0 | | 1024 -> 2047 : 789 |****************************************| 2048 -> 4095 : 39 |* | Sometimes you just want counts of events, and don't need the distribution of times. You can use the -C or --count option: # ./hardirqs.py -C Tracing hard irq events... Hit Ctrl-C to end. ^C HARDIRQ TOTAL_count blkif 2 callfuncsingle3 8 callfuncsingle2 10 callfuncsingle1 18 resched7 25 callfuncsingle6 25 callfuncsingle5 27 callfuncsingle0 27 eth0 34 resched2 40 resched1 66 timer7 70 resched6 71 resched0 73 resched5 79 resched4 90 timer6 95 timer4 100 timer1 109 timer2 115 timer0 117 timer3 123 resched3 140 timer5 288 USAGE message: # ./hardirqs -h usage: hardirqs [-h] [-T] [-N] [-C] [-d] [interval] [outputs] Summarize hard irq event time as histograms positional arguments: interval output interval, in seconds outputs number of outputs optional arguments: -h, --help show this help message and exit -T, --timestamp include timestamp on output -N, --nanoseconds output in nanoseconds -C, --count show event counts instead of timing -d, --dist show distributions as histograms examples: ./hardirqs # sum hard irq event time ./hardirqs -d # show hard irq event time as histograms ./hardirqs 1 10 # print 1 second summaries, 10 times ./hardirqs -NT 1 # 1s summaries, nanoseconds, and timestamps bpfcc-0.12.0/tools/inject.py000077500000000000000000000400471357404205000156450ustar00rootroot00000000000000#!/usr/bin/python # # This script generates a BPF program with structure inspired by trace.py. The # generated program operates on PID-indexed stacks. Generally speaking, # bookkeeping is done at every intermediate function kprobe/kretprobe to enforce # the goal of "fail iff this call chain and these predicates". # # Top level functions(the ones at the end of the call chain) are responsible for # creating the pid_struct and deleting it from the map in kprobe and kretprobe # respectively. # # Intermediate functions(between should_fail_whatever and the top level # functions) are responsible for updating the stack to indicate "I have been # called and one of my predicate(s) passed" in their entry probes. In their exit # probes, they do the opposite, popping their stack to maintain correctness. # This implementation aims to ensure correctness in edge cases like recursive # calls, so there's some additional information stored in pid_struct for that. # # At the bottom level function(should_fail_whatever), we do a simple check to # ensure all necessary calls/predicates have passed before error injection. # # Note: presently there are a few hacks to get around various rewriter/verifier # issues. # # Note: this tool requires: # - CONFIG_BPF_KPROBE_OVERRIDE # # USAGE: inject [-h] [-I header] [-P probability] [-v] mode spec # # Copyright (c) 2018 Facebook, Inc. # Licensed under the Apache License, Version 2.0 (the "License") # # 16-Mar-2018 Howard McLauchlan Created this. import argparse import re from bcc import BPF class Probe: errno_mapping = { "kmalloc": "-ENOMEM", "bio": "-EIO", "alloc_page" : "true", } @classmethod def configure(cls, mode, probability, count): cls.mode = mode cls.probability = probability cls.count = count def __init__(self, func, preds, length, entry): # length of call chain self.length = length self.func = func self.preds = preds self.is_entry = entry def _bail(self, err): raise ValueError("error in probe '%s': %s" % (self.spec, err)) def _get_err(self): return Probe.errno_mapping[Probe.mode] def _get_if_top(self): # ordering guarantees that if this function is top, the last tup is top chk = self.preds[0][1] == 0 if not chk: return "" if Probe.probability == 1: early_pred = "false" else: early_pred = "bpf_get_prandom_u32() > %s" % str(int((1<<32)*Probe.probability)) # init the map # dont do an early exit here so the singular case works automatically # have an early exit for probability option enter = """ /* * Early exit for probability case */ if (%s) return 0; /* * Top level function init map */ struct pid_struct p_struct = {0, 0}; m.insert(&pid, &p_struct); """ % early_pred # kill the entry exit = """ /* * Top level function clean up map */ m.delete(&pid); """ return enter if self.is_entry else exit def _get_heading(self): # we need to insert identifier and ctx into self.func # gonna make a lot of formatting assumptions to make this work left = self.func.find("(") right = self.func.rfind(")") # self.event and self.func_name need to be accessible self.event = self.func[0:left] self.func_name = self.event + ("_entry" if self.is_entry else "_exit") func_sig = "struct pt_regs *ctx" # assume theres something in there, no guarantee its well formed if right > left + 1 and self.is_entry: func_sig += ", " + self.func[left + 1:right] return "int %s(%s)" % (self.func_name, func_sig) def _get_entry_logic(self): # there is at least one tup(pred, place) for this function text = """ if (p->conds_met >= %s) return 0; if (p->conds_met == %s && %s) { p->stack[%s] = p->curr_call; p->conds_met++; }""" text = text % (self.length, self.preds[0][1], self.preds[0][0], self.preds[0][1]) # for each additional pred for tup in self.preds[1:]: text += """ else if (p->conds_met == %s && %s) { p->stack[%s] = p->curr_call; p->conds_met++; } """ % (tup[1], tup[0], tup[1]) return text def _generate_entry(self): prog = self._get_heading() + """ { u32 pid = bpf_get_current_pid_tgid(); %s struct pid_struct *p = m.lookup(&pid); if (!p) return 0; /* * preparation for predicate, if necessary */ %s /* * Generate entry logic */ %s p->curr_call++; return 0; }""" prog = prog % (self._get_if_top(), self.prep, self._get_entry_logic()) return prog # only need to check top of stack def _get_exit_logic(self): text = """ if (p->conds_met < 1 || p->conds_met >= %s) return 0; if (p->stack[p->conds_met - 1] == p->curr_call) p->conds_met--; """ return text % str(self.length + 1) def _generate_exit(self): prog = self._get_heading() + """ { u32 pid = bpf_get_current_pid_tgid(); struct pid_struct *p = m.lookup(&pid); if (!p) return 0; p->curr_call--; /* * Generate exit logic */ %s %s return 0; }""" prog = prog % (self._get_exit_logic(), self._get_if_top()) return prog # Special case for should_fail_whatever def _generate_bottom(self): pred = self.preds[0][0] text = self._get_heading() + """ { u32 overriden = 0; int zero = 0; u32* val; val = count.lookup(&zero); if (val) overriden = *val; /* * preparation for predicate, if necessary */ %s /* * If this is the only call in the chain and predicate passes */ if (%s == 1 && %s && overriden < %s) { count.increment(zero); bpf_override_return(ctx, %s); return 0; } u32 pid = bpf_get_current_pid_tgid(); struct pid_struct *p = m.lookup(&pid); if (!p) return 0; /* * If all conds have been met and predicate passes */ if (p->conds_met == %s && %s && overriden < %s) { count.increment(zero); bpf_override_return(ctx, %s); } return 0; }""" return text % (self.prep, self.length, pred, Probe.count, self._get_err(), self.length - 1, pred, Probe.count, self._get_err()) # presently parses and replaces STRCMP # STRCMP exists because string comparison is inconvenient and somewhat buggy # https://github.com/iovisor/bcc/issues/1617 def _prepare_pred(self): self.prep = "" for i in range(len(self.preds)): new_pred = "" pred = self.preds[i][0] place = self.preds[i][1] start, ind = 0, 0 while start < len(pred): ind = pred.find("STRCMP(", start) if ind == -1: break new_pred += pred[start:ind] # 7 is len("STRCMP(") start = pred.find(")", start + 7) + 1 # then ind ... start is STRCMP(...) ptr, literal = pred[ind + 7:start - 1].split(",") literal = literal.strip() # x->y->z, some string literal # we make unique id with place_ind uuid = "%s_%s" % (place, ind) unique_bool = "is_true_%s" % uuid self.prep += """ char *str_%s = %s; bool %s = true;\n""" % (uuid, ptr.strip(), unique_bool) check = "\t%s &= *(str_%s++) == '%%s';\n" % (unique_bool, uuid) for ch in literal: self.prep += check % ch self.prep += check % r'\0' new_pred += unique_bool new_pred += pred[start:] self.preds[i] = (new_pred, place) def generate_program(self): # generate code to work around various rewriter issues self._prepare_pred() # special case for bottom if self.preds[-1][1] == self.length - 1: return self._generate_bottom() return self._generate_entry() if self.is_entry else self._generate_exit() def attach(self, bpf): if self.is_entry: bpf.attach_kprobe(event=self.event, fn_name=self.func_name) else: bpf.attach_kretprobe(event=self.event, fn_name=self.func_name) class Tool: examples =""" EXAMPLES: # ./inject.py kmalloc -v 'SyS_mount()' Fails all calls to syscall mount # ./inject.py kmalloc -v '(true) => SyS_mount()(true)' Explicit rewriting of above # ./inject.py kmalloc -v 'mount_subtree() => btrfs_mount()' Fails btrfs mounts only # ./inject.py kmalloc -v 'd_alloc_parallel(struct dentry *parent, const struct \\ qstr *name)(STRCMP(name->name, 'bananas'))' Fails dentry allocations of files named 'bananas' # ./inject.py kmalloc -v -P 0.01 'SyS_mount()' Fails calls to syscall mount with 1% probability """ # add cases as necessary error_injection_mapping = { "kmalloc": "should_failslab(struct kmem_cache *s, gfp_t gfpflags)", "bio": "should_fail_bio(struct bio *bio)", "alloc_page": "should_fail_alloc_page(gfp_t gfp_mask, unsigned int order)", } def __init__(self): parser = argparse.ArgumentParser(description="Fail specified kernel" + " functionality when call chain and predicates are met", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=Tool.examples) parser.add_argument(dest="mode", choices=["kmalloc", "bio", "alloc_page"], help="indicate which base kernel function to fail") parser.add_argument(metavar="spec", dest="spec", help="specify call chain") parser.add_argument("-I", "--include", action="append", metavar="header", help="additional header files to include in the BPF program") parser.add_argument("-P", "--probability", default=1, metavar="probability", type=float, help="probability that this call chain will fail") parser.add_argument("-v", "--verbose", action="store_true", help="print BPF program") parser.add_argument("-c", "--count", action="store", default=-1, help="Number of fails before bypassing the override") self.args = parser.parse_args() self.program = "" self.spec = self.args.spec self.map = {} self.probes = [] self.key = Tool.error_injection_mapping[self.args.mode] # create_probes and associated stuff def _create_probes(self): self._parse_spec() Probe.configure(self.args.mode, self.args.probability, self.args.count) # self, func, preds, total, entry # create all the pair probes for fx, preds in self.map.items(): # do the enter self.probes.append(Probe(fx, preds, self.length, True)) if self.key == fx: continue # do the exit self.probes.append(Probe(fx, preds, self.length, False)) def _parse_frames(self): # sentinel data = self.spec + '\0' start, count = 0, 0 frames = [] cur_frame = [] i = 0 last_frame_added = 0 while i < len(data): # improper input if count < 0: raise Exception("Check your parentheses") c = data[i] count += c == '(' count -= c == ')' if not count: if c == '\0' or (c == '=' and data[i + 1] == '>'): # This block is closing a chunk. This means cur_frame must # have something in it. if not cur_frame: raise Exception("Cannot parse spec, missing parens") if len(cur_frame) == 2: frame = tuple(cur_frame) elif cur_frame[0][0] == '(': frame = self.key, cur_frame[0] else: frame = cur_frame[0], '(true)' frames.append(frame) del cur_frame[:] i += 1 start = i + 1 elif c == ')': cur_frame.append(data[start:i + 1].strip()) start = i + 1 last_frame_added = start i += 1 # We only permit spaces after the last frame if self.spec[last_frame_added:].strip(): raise Exception("Invalid characters found after last frame"); # improper input if count: raise Exception("Check your parentheses") return frames def _parse_spec(self): frames = self._parse_frames() frames.reverse() absolute_order = 0 for f in frames: # default case func, pred = f[0], f[1] if not self._validate_predicate(pred): raise Exception("Invalid predicate") if not self._validate_identifier(func): raise Exception("Invalid function identifier") tup = (pred, absolute_order) if func not in self.map: self.map[func] = [tup] else: self.map[func].append(tup) absolute_order += 1 if self.key not in self.map: self.map[self.key] = [('(true)', absolute_order)] absolute_order += 1 self.length = absolute_order def _validate_identifier(self, func): # We've already established paren balancing. We will only look for # identifier validity here. paren_index = func.find("(") potential_id = func[:paren_index] pattern = '[_a-zA-z][_a-zA-Z0-9]*$' if re.match(pattern, potential_id): return True return False def _validate_predicate(self, pred): if len(pred) > 0 and pred[0] == "(": open = 1 for i in range(1, len(pred)): if pred[i] == "(": open += 1 elif pred[i] == ")": open -= 1 if open != 0: # not well formed, break return False return True def _def_pid_struct(self): text = """ struct pid_struct { u64 curr_call; /* book keeping to handle recursion */ u64 conds_met; /* stack pointer */ u64 stack[%s]; }; """ % self.length return text def _attach_probes(self): self.bpf = BPF(text=self.program) for p in self.probes: p.attach(self.bpf) def _generate_program(self): # leave out auto includes for now self.program += '#include \n' for include in (self.args.include or []): self.program += "#include <%s>\n" % include self.program += self._def_pid_struct() self.program += "BPF_HASH(m, u32, struct pid_struct);\n" self.program += "BPF_ARRAY(count, u32, 1);\n" for p in self.probes: self.program += p.generate_program() + "\n" if self.args.verbose: print(self.program) def _main_loop(self): while True: try: self.bpf.perf_buffer_poll() except KeyboardInterrupt: exit() def run(self): self._create_probes() self._generate_program() self._attach_probes() self._main_loop() if __name__ == "__main__": Tool().run() bpfcc-0.12.0/tools/inject_example.txt000066400000000000000000000152601357404205000175430ustar00rootroot00000000000000Some examples for inject inject guarantees the appropriate erroneous return of the specified injection mode (kmalloc,bio,etc) given a call chain and an optional set of predicates. You can also optionally print out the generated BPF program for modification/debugging purposes. As a simple example, let's say you wanted to fail all mounts. As of 4.17 we can fail syscalls directly, so let's do that: # ./inject.py kmalloc -v 'SyS_mount()' The first argument indicates the mode (or what to fail). Appropriate headers are specified, if necessary. The verbosity flag prints the generated program. Note that some syscalls will be available as 'SyS_xyz' and some will be available as 'sys_xyz'. This is largely dependent on the number of arguments each syscall takes. Trying to mount various filesystems will fail and report an inability to allocate memory, as expected. Whenever a predicate is missing, an implicit "(true)" is inserted. The example above can be explicitly written as: # ./inject.py kmalloc -v '(true) => SyS_mount()(true)' The "(true)" without an associated function is a predicate for the error injection mechanism of the current mode. In the case of kmalloc, the predicate would have access to the arguments of: should_failslab(struct kmem_cache *s, gfp_t gfpflags) Other modes work similarly. "bio" has access to the arguments of: should_fail_bio(struct bio *bio) "alloc_page" has access to the arguments of: should_fail_alloc_page(gfp_t gfp_mask, unsigned int order) We also note that it's unnecessary to state the arguments of the function if you have no intention to reference them in the associated predicate. Now let's say we want to be a bit more specific; suppose you want to fail kmalloc() from mount_subtree() when called from btrfs_mount(). This will fail only btrfs mounts: # ./inject.py kmalloc -v 'mount_subtree() => btrfs_mount()' Attempting to mount btrfs filesystem during the execution of this command will yield an error, but other filesystems will be fine. Next, lets say we want to hit one of the BUG_ONs in fs/btrfs. As of 4.16-rc3, there is a BUG_ON in btrfs_prepare_close_one_device() at fs/btrfs/volumes.c:1002 To hit this, we can use the following: # ./inject.py kmalloc -v 'btrfs_alloc_device() => btrfs_close_devices()' While the script was executing, I mounted and unmounted btrfs, causing a segfault on umount(since that satisfied the call path indicated). A look at dmesg will confirm that the erroneous return value injected by the script tripped the BUG_ON, causing a segfault down the line. In general, it's worth noting that the required specificity of the call chain is dependent on how much granularity you need. The example above might have performed as expected without the intermediate btrfs_alloc_device, but might have also done something unexpected(an earlier kmalloc could have failed before the one we were targeting). For hot paths, the approach outlined above isn't enough. If a path is traversed very often, we can distinguish distinct calls with function arguments. Let's say we want to fail the dentry allocation of a file creatively named 'bananas'. We can do the following: # ./inject.py kmalloc -v 'd_alloc_parallel(struct dentry *parent, const struct qstr *name)(STRCMP(name->name, 'bananas'))' While this script is executing, any operation that would cause a dentry allocation where the name is 'bananas' fails, as expected. Here, since we're referencing a function argument in our predicate, we need to provide the function signature up to the argument we're using. To note, STRCMP is a workaround for some rewriter issues. It will take input of the form (x->...->z, 'literal'), and generate some equivalent code that the verifier is more friendly about. It's not horribly robust, but works for the purposes of making string comparisons a bit easier. Finally, we briefly demonstrate how to inject bio failures. The mechanism is identical, so any information from above will apply. Let's say we want to fail bio requests when the request is to some specific sector. An example use case would be to fail superblock writes in btrfs. For btrfs, we know that there must be a superblock at 65536 bytes, or sector 128. This allows us to run the following: # ./inject.py bio -v -I 'linux/blkdev.h' '(({struct gendisk *d = bio->bi_disk; struct disk_part_tbl *tbl = d->part_tbl; struct hd_struct **parts = (void *)tbl + sizeof(struct disk_part_tbl); struct hd_struct **partp = parts + bio->bi_partno; struct hd_struct *p = *partp; dev_t disk = p->__dev.devt; disk == MKDEV(254,16);}) && bio->bi_iter.bi_sector == 128)' The predicate in the command above has two parts. The first is a compound statement which shortens to "only if the system is btrfs", but is long due to rewriter/verifier shenanigans. The major/minor information can be found however; I used Python. The second part simply checks the starting address of bi_iter. While executing, this script effectively fails superblock writes to the superblock at sector 128 without affecting other filesystems. As an extension to the above, one could easily fail all btrfs superblock writes (we only fail the primary) by calculating the sector number of the mirrors and amending the predicate accordingly. Inject also provides a probability option; this allows you to fail the path+predicates some percentage of the time. For example, let's say we want to fail our mounts half the time: # ./inject.py kmalloc -v -P 0.01 'SyS_mount()' USAGE message: usage: inject.py [-h] [-I header] [-P probability] [-v] [-c COUNT] {kmalloc,bio,alloc_page} spec Fail specified kernel functionality when call chain and predicates are met positional arguments: {kmalloc,bio,alloc_page} indicate which base kernel function to fail spec specify call chain optional arguments: -h, --help show this help message and exit -I header, --include header additional header files to include in the BPF program -P probability, --probability probability probability that this call chain will fail -v, --verbose print BPF program -c COUNT, --count COUNT Number of fails before bypassing the override EXAMPLES: # ./inject.py kmalloc -v 'SyS_mount()' Fails all calls to syscall mount # ./inject.py kmalloc -v '(true) => SyS_mount()(true)' Explicit rewriting of above # ./inject.py kmalloc -v 'mount_subtree() => btrfs_mount()' Fails btrfs mounts only # ./inject.py kmalloc -v 'd_alloc_parallel(struct dentry *parent, const struct \ qstr *name)(STRCMP(name->name, 'bananas'))' Fails dentry allocations of files named 'bananas' # ./inject.py kmalloc -v -P 0.01 'SyS_mount()' Fails calls to syscall mount with 1% probability bpfcc-0.12.0/tools/javacalls.sh000077500000000000000000000000761357404205000163110ustar00rootroot00000000000000#!/bin/bash lib=$(dirname $0)/lib $lib/ucalls.py -l java "$@" bpfcc-0.12.0/tools/javacalls_example.txt000077700000000000000000000000001357404205000245232lib/ucalls_example.txtustar00rootroot00000000000000bpfcc-0.12.0/tools/javaflow.sh000077500000000000000000000000751357404205000161610ustar00rootroot00000000000000#!/bin/bash lib=$(dirname $0)/lib $lib/uflow.py -l java "$@" bpfcc-0.12.0/tools/javaflow_example.txt000077700000000000000000000000001357404205000242452lib/uflow_example.txtustar00rootroot00000000000000bpfcc-0.12.0/tools/javagc.sh000077500000000000000000000000731357404205000156010ustar00rootroot00000000000000#!/bin/bash lib=$(dirname $0)/lib $lib/ugc.py -l java "$@" bpfcc-0.12.0/tools/javagc_example.txt000077700000000000000000000000001357404205000233112lib/ugc_example.txtustar00rootroot00000000000000bpfcc-0.12.0/tools/javaobjnew.sh000077500000000000000000000000771357404205000165000ustar00rootroot00000000000000#!/bin/bash lib=$(dirname $0)/lib $lib/uobjnew.py -l java "$@" bpfcc-0.12.0/tools/javaobjnew_example.txt000077700000000000000000000000001357404205000250772lib/uobjnew_example.txtustar00rootroot00000000000000bpfcc-0.12.0/tools/javastat.sh000077500000000000000000000000751357404205000161650ustar00rootroot00000000000000#!/bin/bash lib=$(dirname $0)/lib $lib/ustat.py -l java "$@" bpfcc-0.12.0/tools/javastat_example.txt000077700000000000000000000000001357404205000242552lib/ustat_example.txtustar00rootroot00000000000000bpfcc-0.12.0/tools/javathreads.sh000077500000000000000000000001001357404205000166310ustar00rootroot00000000000000#!/bin/bash lib=$(dirname $0)/lib $lib/uthreads.py -l java "$@" bpfcc-0.12.0/tools/javathreads_example.txt000077700000000000000000000000001357404205000254132lib/uthreads_example.txtustar00rootroot00000000000000bpfcc-0.12.0/tools/killsnoop.py000077500000000000000000000065161357404205000164060ustar00rootroot00000000000000#!/usr/bin/python # @lint-avoid-python-3-compatibility-imports # # killsnoop Trace signals issued by the kill() syscall. # For Linux, uses BCC, eBPF. Embedded C. # # USAGE: killsnoop [-h] [-x] [-p PID] # # Copyright (c) 2015 Brendan Gregg. # Licensed under the Apache License, Version 2.0 (the "License") # # 20-Sep-2015 Brendan Gregg Created this. # 19-Feb-2016 Allan McAleavy migrated to BPF_PERF_OUTPUT from __future__ import print_function from bcc import BPF from bcc.utils import ArgString, printb import argparse from time import strftime # arguments examples = """examples: ./killsnoop # trace all kill() signals ./killsnoop -x # only show failed kills ./killsnoop -p 181 # only trace PID 181 """ parser = argparse.ArgumentParser( description="Trace signals issued by the kill() syscall", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("-x", "--failed", action="store_true", help="only show failed kill syscalls") parser.add_argument("-p", "--pid", help="trace this PID only") parser.add_argument("--ebpf", action="store_true", help=argparse.SUPPRESS) args = parser.parse_args() debug = 0 # define BPF program bpf_text = """ #include #include struct val_t { u64 pid; int sig; int tpid; char comm[TASK_COMM_LEN]; }; struct data_t { u64 pid; int tpid; int sig; int ret; char comm[TASK_COMM_LEN]; }; BPF_HASH(infotmp, u32, struct val_t); BPF_PERF_OUTPUT(events); int syscall__kill(struct pt_regs *ctx, int tpid, int sig) { u32 pid = bpf_get_current_pid_tgid(); FILTER struct val_t val = {.pid = pid}; if (bpf_get_current_comm(&val.comm, sizeof(val.comm)) == 0) { val.tpid = tpid; val.sig = sig; infotmp.update(&pid, &val); } return 0; }; int do_ret_sys_kill(struct pt_regs *ctx) { struct data_t data = {}; struct val_t *valp; u32 pid = bpf_get_current_pid_tgid(); valp = infotmp.lookup(&pid); if (valp == 0) { // missed entry return 0; } bpf_probe_read(&data.comm, sizeof(data.comm), valp->comm); data.pid = pid; data.tpid = valp->tpid; data.ret = PT_REGS_RC(ctx); data.sig = valp->sig; events.perf_submit(ctx, &data, sizeof(data)); infotmp.delete(&pid); return 0; } """ if args.pid: bpf_text = bpf_text.replace('FILTER', 'if (pid != %s) { return 0; }' % args.pid) else: bpf_text = bpf_text.replace('FILTER', '') if debug or args.ebpf: print(bpf_text) if args.ebpf: exit() # initialize BPF b = BPF(text=bpf_text) kill_fnname = b.get_syscall_fnname("kill") b.attach_kprobe(event=kill_fnname, fn_name="syscall__kill") b.attach_kretprobe(event=kill_fnname, fn_name="do_ret_sys_kill") # header print("%-9s %-6s %-16s %-4s %-6s %s" % ( "TIME", "PID", "COMM", "SIG", "TPID", "RESULT")) # process event def print_event(cpu, data, size): event = b["events"].event(data) if (args.failed and (event.ret >= 0)): return printb(b"%-9s %-6d %-16s %-4d %-6d %d" % (strftime("%H:%M:%S").encode('ascii'), event.pid, event.comm, event.sig, event.tpid, event.ret)) # loop with callback to print_event b["events"].open_perf_buffer(print_event) while 1: try: b.perf_buffer_poll() except KeyboardInterrupt: exit() bpfcc-0.12.0/tools/killsnoop_example.txt000066400000000000000000000017421357404205000203010ustar00rootroot00000000000000Demonstrations of killsnoop, the Linux eBPF/bcc version. This traces signals sent via the kill() syscall. For example: # ./killsnoop TIME PID COMM SIG TPID RESULT 12:10:51 13967 bash 9 13885 0 12:11:34 13967 bash 9 1024 -3 12:11:41 815 systemd-udevd 15 14076 0 The first line showed a SIGKILL (9) sent from PID 13967 (a bash shell) to PID 13885. The result, 0, means success. The second line showed the same signal sent, this time resulting in a -3 (ESRCH: no such process). USAGE message: # ./killsnoop -h usage: killsnoop [-h] [-x] [-p PID] Trace signals issued by the kill() syscall optional arguments: -h, --help show this help message and exit -x, --failed only show failed kill syscalls -p PID, --pid PID trace this PID only examples: ./killsnoop # trace all kill() signals ./killsnoop -x # only show failed kills ./killsnoop -p 181 # only trace PID 181 bpfcc-0.12.0/tools/klockstat.py000077500000000000000000000260651357404205000163740ustar00rootroot00000000000000#!/usr/bin/python # # klockstat traces lock events and display locks statistics. # # USAGE: klockstat # from __future__ import print_function from bcc import BPF, USDT import argparse import subprocess import ctypes as ct from time import sleep, strftime from datetime import datetime, timedelta import errno from sys import stderr examples = """ klockstat # trace system wide klockstat -d 5 # trace for 5 seconds only klockstat -i 5 # display stats every 5 seconds klockstat -p 123 # trace locks for PID 123 klockstat -t 321 # trace locks for PID 321 klockstat -c pipe_ # display stats only for lock callers with 'pipe_' substring klockstat -S acq_count # sort lock acquired results on acquired count klockstat -S hld_total # sort lock held results on total held time klockstat -S acq_count,hld_total # combination of above klockstat -n 3 # display 3 locks klockstat -s 3 # display 3 levels of stack """ # arg validation def positive_int(val): try: ival = int(val) except ValueError: raise argparse.ArgumentTypeError("must be an integer") if ival < 0: raise argparse.ArgumentTypeError("must be positive") return ival def positive_nonzero_int(val): ival = positive_int(val) if ival == 0: raise argparse.ArgumentTypeError("must be nonzero") return ival def stack_id_err(stack_id): # -EFAULT in get_stackid normally means the stack-trace is not availible, # Such as getting kernel stack trace in userspace code return (stack_id < 0) and (stack_id != -errno.EFAULT) parser = argparse.ArgumentParser( description="", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) time_group = parser.add_mutually_exclusive_group() time_group.add_argument("-d", "--duration", type=int, help="total duration of trace in seconds") time_group.add_argument("-i", "--interval", type=int, help="print summary at this interval (seconds)") parser.add_argument("-n", "--locks", type=int, default=999999999, help="print given number of locks") parser.add_argument("-s", "--stacks", type=int, default=1, help="print given number of stack entries") parser.add_argument("-c", "--caller", help="print locks taken by given caller") parser.add_argument("-S", "--sort", help="sort data on , fields: acq_[max|total|count] hld_[max|total|count]") parser.add_argument("-p", "--pid", help="trace this PID only") parser.add_argument("-t", "--tid", help="trace this TID only") parser.add_argument("--stack-storage-size", default=16384, type=positive_nonzero_int, help="the number of unique stack traces that can be stored and " "displayed (default 16384)") args = parser.parse_args() program = """ #include struct depth_id { u64 id; u64 depth; }; BPF_ARRAY(enabled, u64, 1); BPF_HASH(track, u64, u64); BPF_HASH(time_aq, u64, u64); BPF_HASH(lock_depth, u64, u64); BPF_HASH(time_held, struct depth_id, u64); BPF_HASH(stack, struct depth_id, int); BPF_HASH(aq_report_count, int, u64); BPF_HASH(aq_report_max, int, u64); BPF_HASH(aq_report_total, int, u64); BPF_HASH(hl_report_count, int, u64); BPF_HASH(hl_report_max, int, u64); BPF_HASH(hl_report_total, int, u64); BPF_STACK_TRACE(stack_traces, STACK_STORAGE_SIZE); static bool is_enabled(void) { int key = 0; u64 *ret; ret = enabled.lookup(&key); return ret && *ret == 1; } static bool allow_pid(u64 id) { u32 pid = id >> 32; // PID is higher part u32 tid = id; // Cast and get the lower part FILTER return 1; } int mutex_lock_enter(struct pt_regs *ctx) { if (!is_enabled()) return 0; u64 id = bpf_get_current_pid_tgid(); if (!allow_pid(id)) return 0; u64 one = 1, zero = 0; track.update(&id, &one); u64 *depth = lock_depth.lookup(&id); if (!depth) { lock_depth.update(&id, &zero); depth = lock_depth.lookup(&id); /* something is wrong.. */ if (!depth) return 0; } int stackid = stack_traces.get_stackid(ctx, 0); struct depth_id did = { .id = id, .depth = *depth, }; stack.update(&did, &stackid); u64 ts = bpf_ktime_get_ns(); time_aq.update(&id, &ts); *depth += 1; return 0; } static void update_aq_report_count(int *stackid) { u64 *count, one = 1; count = aq_report_count.lookup(stackid); if (!count) { aq_report_count.update(stackid, &one); } else { *count += 1; } } static void update_hl_report_count(int *stackid) { u64 *count, one = 1; count = hl_report_count.lookup(stackid); if (!count) { hl_report_count.update(stackid, &one); } else { *count += 1; } } static void update_aq_report_max(int *stackid, u64 time) { u64 *max; max = aq_report_max.lookup(stackid); if (!max || *max < time) aq_report_max.update(stackid, &time); } static void update_hl_report_max(int *stackid, u64 time) { u64 *max; max = hl_report_max.lookup(stackid); if (!max || *max < time) hl_report_max.update(stackid, &time); } static void update_aq_report_total(int *stackid, u64 delta) { u64 *count, *time; count = aq_report_count.lookup(stackid); if (!count) return; time = aq_report_total.lookup(stackid); if (!time) { aq_report_total.update(stackid, &delta); } else { *time = *time + delta; } } static void update_hl_report_total(int *stackid, u64 delta) { u64 *count, *time; count = hl_report_count.lookup(stackid); if (!count) return; time = hl_report_total.lookup(stackid); if (!time) { hl_report_total.update(stackid, &delta); } else { *time = *time + delta; } } int mutex_lock_return(struct pt_regs *ctx) { if (!is_enabled()) return 0; u64 id = bpf_get_current_pid_tgid(); if (!allow_pid(id)) return 0; u64 *one = track.lookup(&id); if (!one) return 0; track.delete(&id); u64 *depth = lock_depth.lookup(&id); if (!depth) return 0; struct depth_id did = { .id = id, .depth = *depth - 1, }; u64 *aq = time_aq.lookup(&id); if (!aq) return 0; int *stackid = stack.lookup(&did); if (!stackid) return 0; int stackid_ = *stackid; u64 cur = bpf_ktime_get_ns(); if (cur > *aq) { int val = cur - *aq; update_aq_report_count(&stackid_); update_aq_report_max(&stackid_, val); update_aq_report_total(&stackid_, val); } time_held.update(&did, &cur); return 0; } int mutex_unlock_enter(struct pt_regs *ctx) { if (!is_enabled()) return 0; u64 id = bpf_get_current_pid_tgid(); if (!allow_pid(id)) return 0; u64 *depth = lock_depth.lookup(&id); if (!depth || *depth == 0) return 0; *depth -= 1; struct depth_id did = { .id = id, .depth = *depth, }; u64 *held = time_held.lookup(&did); if (!held) return 0; int *stackid = stack.lookup(&did); if (!stackid) return 0; int stackid_ = *stackid; u64 cur = bpf_ktime_get_ns(); if (cur > *held) { u64 val = cur - *held; update_hl_report_count(&stackid_); update_hl_report_max(&stackid_, val); update_hl_report_total(&stackid_, val); } stack.delete(&did); time_held.delete(&did); return 0; } """ def sort_list(maxs, totals, counts): if (not args.sort): return maxs; for field in args.sort.split(','): if (field == "acq_max" or field == "hld_max"): return maxs if (field == "acq_total" or field == "hld_total"): return totals if (field == "acq_count" or field == "hld_count"): return counts print("Wrong sort argument: %s", args.sort) exit(-1) def display(sort, maxs, totals, counts): global missing_stacks global has_enomem for k, v in sorted(sort.items(), key=lambda sort: sort[1].value, reverse=True)[:args.locks]: missing_stacks += int(stack_id_err(k.value)) has_enomem = has_enomem or (k.value == -errno.ENOMEM) caller = "[Missed Kernel Stack]" stack = [] if (k.value >= 0): stack = list(stack_traces.walk(k.value)) caller = b.ksym(stack[1], show_offset=True) if (args.caller and caller.find(args.caller)): continue avg = totals[k].value / counts[k].value print("%40s %10lu %6lu %10lu %10lu" % (caller, avg, counts[k].value, maxs[k].value, totals[k].value)) for addr in stack[1:args.stacks]: print("%40s" % b.ksym(addr, show_offset=True)) if args.tid: # TID trumps PID program = program.replace('FILTER', 'if (tid != %s) { return 0; }' % args.tid) elif args.pid: program = program.replace('FILTER', 'if (pid != %s) { return 0; }' % args.pid) else: program = program.replace('FILTER', '') program = program.replace('STACK_STORAGE_SIZE', str(args.stack_storage_size)) b = BPF(text=program) b.attach_kprobe(event="mutex_unlock", fn_name="mutex_unlock_enter") b.attach_kretprobe(event="mutex_lock", fn_name="mutex_lock_return") b.attach_kprobe(event="mutex_lock", fn_name="mutex_lock_enter") enabled = b.get_table("enabled"); stack_traces = b.get_table("stack_traces") aq_counts = b.get_table("aq_report_count") aq_maxs = b.get_table("aq_report_max") aq_totals = b.get_table("aq_report_total") hl_counts = b.get_table("hl_report_count") hl_maxs = b.get_table("hl_report_max") hl_totals = b.get_table("hl_report_total") aq_sort = sort_list(aq_maxs, aq_totals, aq_counts) hl_sort = sort_list(hl_maxs, hl_totals, hl_counts) print("Tracing lock events... Hit Ctrl-C to end.") # duration and interval are mutualy exclusive exiting = 0 if args.interval else 1 exiting = 1 if args.duration else 0 seconds = 999999999 if args.interval: seconds = args.interval if args.duration: seconds = args.duration missing_stacks = 0 has_enomem = False while (1): enabled[ct.c_int(0)] = ct.c_int(1) try: sleep(seconds) except KeyboardInterrupt: exiting = 1 enabled[ct.c_int(0)] = ct.c_int(0) print("\n%40s %10s %6s %10s %10s" % ("Caller", "Avg Spin", "Count", "Max spin", "Total spin")) display(aq_sort, aq_maxs, aq_totals, aq_counts) print("\n%40s %10s %6s %10s %10s" % ("Caller", "Avg Hold", "Count", "Max hold", "Total hold")) display(hl_sort, hl_maxs, hl_totals, hl_counts) if exiting: break; stack_traces.clear() aq_counts.clear() aq_maxs.clear() aq_totals.clear() hl_counts.clear() hl_maxs.clear() hl_totals.clear() if missing_stacks > 0: enomem_str = " Consider increasing --stack-storage-size." print("WARNING: %d stack traces lost and could not be displayed.%s" % (missing_stacks, (enomem_str if has_enomem else "")), file=stderr) bpfcc-0.12.0/tools/klockstat_example.txt000066400000000000000000000205301357404205000202620ustar00rootroot00000000000000Demonstrations of klockstat, the Linux eBPF/bcc version. klockstat traces kernel mutex lock events and display locks statistics # klockstat.py Tracing lock events... Hit Ctrl-C to end. ^C Caller Avg Spin Count Max spin Total spin psi_avgs_work+0x2e 3675 5 5468 18379 flush_to_ldisc+0x22 2833 2 4210 5667 n_tty_write+0x30c 3914 1 3914 3914 isig+0x5d 2390 1 2390 2390 tty_buffer_flush+0x2a 1604 1 1604 1604 commit_echoes+0x22 1400 1 1400 1400 n_tty_receive_buf_common+0x3b9 1399 1 1399 1399 Caller Avg Hold Count Max hold Total hold flush_to_ldisc+0x22 42558 2 76135 85116 psi_avgs_work+0x2e 14821 5 20446 74106 n_tty_receive_buf_common+0x3b9 12300 1 12300 12300 n_tty_write+0x30c 10712 1 10712 10712 isig+0x5d 3362 1 3362 3362 tty_buffer_flush+0x2a 3078 1 3078 3078 commit_echoes+0x22 3017 1 3017 3017 Every caller to using kernel's mutex is displayed on every line. First portion of lines show the lock acquiring data, showing the amount of time it took to acquired given lock. 'Caller' - symbol acquiring the mutex 'Average Spin' - average time to acquire the mutex 'Count' - number of times mutex was acquired 'Max spin' - maximum time to acquire the mutex 'Total spin' - total time spent in acquiring the mutex Second portion of lines show the lock holding data, showing the amount of time it took to hold given lock. 'Caller' - symbol holding the mutex 'Average Hold' - average time mutex was held 'Count' - number of times mutex was held 'Max hold' - maximum time mutex was held 'Total hold' - total time spent in holding the mutex This works by tracing mutex_lock/unlock kprobes, updating the lock stats in maps and processing them in the python part. An -i option can be used to display stats in interval (5 seconds in example below): # klockstat.py -i 5 Tracing lock events... Hit Ctrl-C to end. Caller Avg Spin Count Max spin Total spin psi_avgs_work+0x2e 3822 15 5650 57338 flush_to_ldisc+0x22 4630 1 4630 4630 work_fn+0x4f 4185 1 4185 4185 Caller Avg Hold Count Max hold Total hold psi_avgs_work+0x2e 12155 15 15739 182329 flush_to_ldisc+0x22 13809 1 13809 13809 work_fn+0x4f 5274 1 5274 5274 Caller Avg Spin Count Max spin Total spin psi_avgs_work+0x2e 3715 17 4374 63163 Caller Avg Hold Count Max hold Total hold psi_avgs_work+0x2e 13141 17 19510 223399 ^C A -p option can be used to trace only selected process: # klockstat.py -p 883 Tracing lock events... Hit Ctrl-C to end. ^C Caller Avg Spin Count Max spin Total spin pipe_wait+0xa9 625 412686 16930 258277958 pipe_read+0x3f 420 413425 16872 174017649 pipe_write+0x35 471 413425 16765 194792253 Caller Avg Hold Count Max hold Total hold pipe_read+0x3f 473 413425 20063 195773647 pipe_wait+0xa9 604 412686 16972 249598153 pipe_write+0x35 481 413425 16944 199008064 A -c option can be used to display only callers with specific substring: # klockstat.py -c pipe_ Tracing lock events... Hit Ctrl-C to end. ^C Caller Avg Spin Count Max spin Total spin pipe_read+0x3f 422 469554 18665 198354705 pipe_wait+0xa9 679 469536 17196 319017069 pipe_write+0x35 469 469554 17057 220338525 Caller Avg Hold Count Max hold Total hold pipe_write+0x35 638 469554 17330 299857180 pipe_wait+0xa9 779 469535 16912 366047392 pipe_read+0x3f 575 469554 13251 270005394 An -n option can be used to display only specific number of callers: # klockstat.py -n 3 Tracing lock events... Hit Ctrl-C to end. ^C Caller Avg Spin Count Max spin Total spin pipe_read+0x3f 420 334120 16964 140632284 pipe_wait+0xa9 688 334116 16876 229957062 pipe_write+0x35 463 334120 16791 154981747 Caller Avg Hold Count Max hold Total hold flush_to_ldisc+0x22 27754 3 63270 83264 pipe_read+0x3f 571 334120 17123 190976463 pipe_wait+0xa9 759 334115 17068 253747213 An -s option can be used to display number of callers backtrace entries: # klockstat.py -n 1 -s 3 Tracing lock events... Hit Ctrl-C to end. ^C Caller Avg Spin Count Max spin Total spin pipe_wait+0xa9 685 811947 17376 556542328 pipe_wait+0xa9 pipe_read+0x206 Caller Avg Hold Count Max hold Total hold flush_to_ldisc+0x22 28145 3 63872 84437 flush_to_ldisc+0x22 process_one_work+0x1b0 Output can be sorted by using -S option on various fields, the acq_total will force the acquired table to be sorted on 'Total spin' column: # klockstat.py -S acq_total Tracing lock events... Hit Ctrl-C to end. ^C Caller Avg Spin Count Max spin Total spin pipe_wait+0xa9 691 269343 17190 186263983 pipe_write+0x35 464 269351 11730 125205417 pipe_read+0x3f 422 269351 17107 113724697 psi_avgs_work+0x2e 2499 11 4454 27494 flush_to_ldisc+0x22 3111 3 5096 9334 n_tty_write+0x30c 2764 1 2764 2764 isig+0x5d 1287 1 1287 1287 tty_buffer_flush+0x2a 961 1 961 961 commit_echoes+0x22 892 1 892 892 n_tty_receive_buf_common+0x3b9 868 1 868 868 Caller Avg Hold Count Max hold Total hold pipe_wait+0xa9 788 269343 17128 212496240 pipe_write+0x35 637 269351 17209 171596811 pipe_read+0x3f 585 269351 11834 157606323 psi_avgs_work+0x2e 8726 11 19177 95996 flush_to_ldisc+0x22 22158 3 43731 66474 n_tty_write+0x30c 9770 1 9770 9770 n_tty_receive_buf_common+0x3b9 6830 1 6830 6830 isig+0x5d 3114 1 3114 3114 tty_buffer_flush+0x2a 2032 1 2032 2032 commit_echoes+0x22 1616 1 1616 1616 bpfcc-0.12.0/tools/lib/000077500000000000000000000000001357404205000145555ustar00rootroot00000000000000bpfcc-0.12.0/tools/lib/CMakeLists.txt000066400000000000000000000005351357404205000173200ustar00rootroot00000000000000file(GLOB PY_FILES *.py) file(GLOB TXT_FILES *.txt) list(REMOVE_ITEM TXT_FILES ${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt) foreach(FIL ${PY_FILES}) get_filename_component(FIL_WE ${FIL} NAME_WE) install(PROGRAMS ${FIL} DESTINATION share/bcc/tools/lib RENAME ${FIL_WE}) endforeach() install(FILES ${TXT_FILES} DESTINATION share/bcc/tools/doc/lib) bpfcc-0.12.0/tools/lib/ucalls.py000077500000000000000000000272541357404205000164270ustar00rootroot00000000000000#!/usr/bin/python # @lint-avoid-python-3-compatibility-imports # # ucalls Summarize method calls in high-level languages and/or system calls. # For Linux, uses BCC, eBPF. # # USAGE: ucalls [-l {java,perl,php,python,ruby,tcl}] [-h] [-T TOP] [-L] [-S] [-v] [-m] # pid [interval] # # Copyright 2016 Sasha Goldshtein # Licensed under the Apache License, Version 2.0 (the "License") # # 19-Oct-2016 Sasha Goldshtein Created this. from __future__ import print_function import argparse from time import sleep from bcc import BPF, USDT, utils from bcc.syscall import syscall_name languages = ["java", "perl", "php", "python", "ruby", "tcl"] examples = """examples: ./ucalls -l java 185 # trace Java calls and print statistics on ^C ./ucalls -l python 2020 1 # trace Python calls and print every second ./ucalls -l java 185 -S # trace Java calls and syscalls ./ucalls 6712 -S # trace only syscall counts ./ucalls -l ruby 1344 -T 10 # trace top 10 Ruby method calls ./ucalls -l ruby 1344 -L # trace Ruby calls including latency ./ucalls -l php 443 -LS # trace PHP calls and syscalls with latency ./ucalls -l python 2020 -mL # trace Python calls including latency in ms """ parser = argparse.ArgumentParser( description="Summarize method calls in high-level languages.", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("pid", type=int, help="process id to attach to") parser.add_argument("interval", type=int, nargs='?', help="print every specified number of seconds") parser.add_argument("-l", "--language", choices=languages + ["none"], help="language to trace (if none, trace syscalls only)") parser.add_argument("-T", "--top", type=int, help="number of most frequent/slow calls to print") parser.add_argument("-L", "--latency", action="store_true", help="record method latency from enter to exit (except recursive calls)") parser.add_argument("-S", "--syscalls", action="store_true", help="record syscall latency (adds overhead)") parser.add_argument("-v", "--verbose", action="store_true", help="verbose mode: print the BPF program (for debugging purposes)") parser.add_argument("-m", "--milliseconds", action="store_true", help="report times in milliseconds (default is microseconds)") parser.add_argument("--ebpf", action="store_true", help=argparse.SUPPRESS) args = parser.parse_args() language = args.language if not language: language = utils.detect_language(languages, args.pid) # We assume that the entry and return probes have the same arguments. This is # the case for Java, Python, Ruby, and PHP. If there's a language where it's # not the case, we will need to build a custom correlator from entry to exit. extra_message = "" if language == "java": # TODO for JVM entries, we actually have the real length of the class # and method strings in arg3 and arg5 respectively, so we can insert # the null terminator in its proper position. entry_probe = "method__entry" return_probe = "method__return" read_class = "bpf_usdt_readarg(2, ctx, &clazz);" read_method = "bpf_usdt_readarg(4, ctx, &method);" extra_message = ("If you do not see any results, make sure you ran java" " with option -XX:+ExtendedDTraceProbes") elif language == "perl": entry_probe = "sub__entry" return_probe = "sub__return" read_class = "bpf_usdt_readarg(2, ctx, &clazz);" # filename really read_method = "bpf_usdt_readarg(1, ctx, &method);" elif language == "php": entry_probe = "function__entry" return_probe = "function__return" read_class = "bpf_usdt_readarg(4, ctx, &clazz);" read_method = "bpf_usdt_readarg(1, ctx, &method);" extra_message = ("If you do not see any results, make sure the environment" " variable USE_ZEND_DTRACE is set to 1") elif language == "python": entry_probe = "function__entry" return_probe = "function__return" read_class = "bpf_usdt_readarg(1, ctx, &clazz);" # filename really read_method = "bpf_usdt_readarg(2, ctx, &method);" elif language == "ruby": # TODO Also probe cmethod__entry and cmethod__return with same arguments entry_probe = "method__entry" return_probe = "method__return" read_class = "bpf_usdt_readarg(1, ctx, &clazz);" read_method = "bpf_usdt_readarg(2, ctx, &method);" elif language == "tcl": # TODO Also consider probe cmd__entry and cmd__return with same arguments entry_probe = "proc__entry" return_probe = "proc__return" read_class = "" # no class/file info available read_method = "bpf_usdt_readarg(1, ctx, &method);" elif not language or language == "none": if not args.syscalls: print("Nothing to do; use -S to trace syscalls.") exit(1) entry_probe, return_probe, read_class, read_method = ("", "", "", "") if language: language = None program = """ #include #define MAX_STRING_LENGTH 80 DEFINE_NOLANG DEFINE_LATENCY DEFINE_SYSCALLS struct method_t { char clazz[MAX_STRING_LENGTH]; char method[MAX_STRING_LENGTH]; }; struct entry_t { u64 pid; struct method_t method; }; struct info_t { u64 num_calls; u64 total_ns; }; struct syscall_entry_t { u64 timestamp; u64 id; }; #ifndef LATENCY BPF_HASH(counts, struct method_t, u64); // number of calls #ifdef SYSCALLS BPF_HASH(syscounts, u64, u64); // number of calls per IP #endif // SYSCALLS #else BPF_HASH(times, struct method_t, struct info_t); BPF_HASH(entry, struct entry_t, u64); // timestamp at entry #ifdef SYSCALLS BPF_HASH(systimes, u64, struct info_t); // latency per IP BPF_HASH(sysentry, u64, struct syscall_entry_t); // ts + IP at entry #endif // SYSCALLS #endif #ifndef NOLANG int trace_entry(struct pt_regs *ctx) { u64 clazz = 0, method = 0, val = 0; u64 *valp; struct entry_t data = {0}; #ifdef LATENCY u64 timestamp = bpf_ktime_get_ns(); data.pid = bpf_get_current_pid_tgid(); #endif READ_CLASS READ_METHOD bpf_probe_read(&data.method.clazz, sizeof(data.method.clazz), (void *)clazz); bpf_probe_read(&data.method.method, sizeof(data.method.method), (void *)method); #ifndef LATENCY valp = counts.lookup_or_try_init(&data.method, &val); if (valp) { ++(*valp); } #endif #ifdef LATENCY entry.update(&data, ×tamp); #endif return 0; } #ifdef LATENCY int trace_return(struct pt_regs *ctx) { u64 *entry_timestamp, clazz = 0, method = 0; struct info_t *info, zero = {}; struct entry_t data = {}; data.pid = bpf_get_current_pid_tgid(); READ_CLASS READ_METHOD bpf_probe_read(&data.method.clazz, sizeof(data.method.clazz), (void *)clazz); bpf_probe_read(&data.method.method, sizeof(data.method.method), (void *)method); entry_timestamp = entry.lookup(&data); if (!entry_timestamp) { return 0; // missed the entry event } info = times.lookup_or_try_init(&data.method, &zero); if (info) { info->num_calls += 1; info->total_ns += bpf_ktime_get_ns() - *entry_timestamp; } entry.delete(&data); return 0; } #endif // LATENCY #endif // NOLANG #ifdef SYSCALLS TRACEPOINT_PROBE(raw_syscalls, sys_enter) { u64 pid = bpf_get_current_pid_tgid(); u64 *valp, id = args->id, val = 0; PID_FILTER #ifdef LATENCY struct syscall_entry_t data = {}; data.timestamp = bpf_ktime_get_ns(); data.id = id; sysentry.update(&pid, &data); #endif #ifndef LATENCY valp = syscounts.lookup_or_try_init(&id, &val); if (valp) { ++(*valp); } #endif return 0; } #ifdef LATENCY TRACEPOINT_PROBE(raw_syscalls, sys_exit) { struct syscall_entry_t *e; struct info_t *info, zero = {}; u64 pid = bpf_get_current_pid_tgid(), id; PID_FILTER e = sysentry.lookup(&pid); if (!e) { return 0; // missed the entry event } id = e->id; info = systimes.lookup_or_try_init(&id, &zero); if (info) { info->num_calls += 1; info->total_ns += bpf_ktime_get_ns() - e->timestamp; } sysentry.delete(&pid); return 0; } #endif // LATENCY #endif // SYSCALLS """.replace("READ_CLASS", read_class) \ .replace("READ_METHOD", read_method) \ .replace("PID_FILTER", "if ((pid >> 32) != %d) { return 0; }" % args.pid) \ .replace("DEFINE_NOLANG", "#define NOLANG" if not language else "") \ .replace("DEFINE_LATENCY", "#define LATENCY" if args.latency else "") \ .replace("DEFINE_SYSCALLS", "#define SYSCALLS" if args.syscalls else "") if language: usdt = USDT(pid=args.pid) usdt.enable_probe_or_bail(entry_probe, "trace_entry") if args.latency: usdt.enable_probe_or_bail(return_probe, "trace_return") else: usdt = None if args.ebpf or args.verbose: if args.verbose and usdt: print(usdt.get_text()) print(program) if args.ebpf: exit() bpf = BPF(text=program, usdt_contexts=[usdt] if usdt else []) if args.syscalls: print("Attached kernel tracepoints for syscall tracing.") def get_data(): # Will be empty when no language was specified for tracing if args.latency: data = list(map(lambda kv: (kv[0].clazz.decode('utf-8', 'replace') \ + "." + \ kv[0].method.decode('utf-8', 'replace'), (kv[1].num_calls, kv[1].total_ns)), bpf["times"].items())) else: data = list(map(lambda kv: (kv[0].clazz.decode('utf-8', 'replace') \ + "." + \ kv[0].method.decode('utf-8', 'replace'), (kv[1].value, 0)), bpf["counts"].items())) if args.syscalls: if args.latency: syscalls = map(lambda kv: (syscall_name(kv[0].value).decode('utf-8', 'replace'), (kv[1].num_calls, kv[1].total_ns)), bpf["systimes"].items()) data.extend(syscalls) else: syscalls = map(lambda kv: (syscall_name(kv[0].value).decode('utf-8', 'replace'), (kv[1].value, 0)), bpf["syscounts"].items()) data.extend(syscalls) return sorted(data, key=lambda kv: kv[1][1 if args.latency else 0]) def clear_data(): if args.latency: bpf["times"].clear() else: bpf["counts"].clear() if args.syscalls: if args.latency: bpf["systimes"].clear() else: bpf["syscounts"].clear() exit_signaled = False print("Tracing calls in process %d (language: %s)... Ctrl-C to quit." % (args.pid, language or "none")) if extra_message: print(extra_message) while True: try: sleep(args.interval or 99999999) except KeyboardInterrupt: exit_signaled = True print() data = get_data() # [(function, (num calls, latency in ns))] if args.latency: time_col = "TIME (ms)" if args.milliseconds else "TIME (us)" print("%-50s %8s %8s" % ("METHOD", "# CALLS", time_col)) else: print("%-50s %8s" % ("METHOD", "# CALLS")) if args.top: data = data[-args.top:] for key, value in data: if args.latency: time = value[1] / 1000000.0 if args.milliseconds else \ value[1] / 1000.0 print("%-50s %8d %6.2f" % (key, value[0], time)) else: print("%-50s %8d" % (key, value[0])) if args.interval and not exit_signaled: clear_data() else: if args.syscalls: print("Detaching kernel probes, please wait...") exit() bpfcc-0.12.0/tools/lib/ucalls_example.txt000066400000000000000000000076401357404205000203230ustar00rootroot00000000000000Demonstrations of ucalls. ucalls summarizes method calls in various high-level languages, including Java, Perl, PHP, Python, Ruby, Tcl, and Linux system calls. It displays statistics on the most frequently called methods, as well as the latency (duration) of these methods. Through the syscalls support, ucalls can provide basic information on a process' interaction with the system including syscall counts and latencies. This can then be used for further exploration with other BCC tools like trace, argdist, biotop, fileslower, and others. For example, to trace method call latency in a Java application: # ucalls -L $(pidof java) Tracing calls in process 26877 (language: java)... Ctrl-C to quit. METHOD # CALLS TIME (us) java/io/BufferedInputStream.getBufIfOpen 1 7.00 slowy/App.isSimplePrime 8970 8858.35 slowy/App.isDivisible 3228196 3076985.12 slowy/App.isPrime 8969 4841017.64 ^C To trace only syscalls in a particular process and print the top 10 most frequently-invoked ones: # ucalls -l none -ST 10 7913 Attached kernel tracepoints for syscall tracing. Tracing calls in process 7913 (language: none)... Ctrl-C to quit. ^C METHOD # CALLS timerfd_settime 9 tgkill 10 getpid 10 stat 80 writev 158 pselect6 204 rt_sigreturn 301 rt_sigprocmask 872 poll 1387 recvmsg 1417 Detaching kernel probes, please wait... To print only the top 5 methods and report times in milliseconds (the default is microseconds): # ucalls -mT 5 $(pidof python) Tracing calls in process 26914 (language: python)... Ctrl-C to quit. METHOD # CALLS . 1 .fibo 14190928 ^C USAGE message: # ./ucalls.py -h usage: ucalls.py [-h] [-l {java,perl,php,python,ruby,tcl,none}] [-T TOP] [-L] [-S] [-v] [-m] pid [interval] Summarize method calls in high-level languages. positional arguments: pid process id to attach to interval print every specified number of seconds optional arguments: -h, --help show this help message and exit -l {java,perl,php,python,ruby,tcl,none}, --language {java,perl,php,python,ruby,tcl,none} language to trace (if none, trace syscalls only) -T TOP, --top TOP number of most frequent/slow calls to print -L, --latency record method latency from enter to exit (except recursive calls) -S, --syscalls record syscall latency (adds overhead) -v, --verbose verbose mode: print the BPF program (for debugging purposes) -m, --milliseconds report times in milliseconds (default is microseconds) examples: ./ucalls -l java 185 # trace Java calls and print statistics on ^C ./ucalls -l python 2020 1 # trace Python calls and print every second ./ucalls -l java 185 -S # trace Java calls and syscalls ./ucalls 6712 -S # trace only syscall counts ./ucalls -l ruby 1344 -T 10 # trace top 10 Ruby method calls ./ucalls -l ruby 1344 -L # trace Ruby calls including latency ./ucalls -l php 443 -LS # trace PHP calls and syscalls with latency ./ucalls -l python 2020 -mL # trace Python calls including latency in ms bpfcc-0.12.0/tools/lib/uflow.py000077500000000000000000000176471357404205000163050ustar00rootroot00000000000000#!/usr/bin/python # @lint-avoid-python-3-compatibility-imports # # uflow Trace method execution flow in high-level languages. # For Linux, uses BCC, eBPF. # # USAGE: uflow [-C CLASS] [-M METHOD] [-v] {java,perl,php,python,ruby,tcl} pid # # Copyright 2016 Sasha Goldshtein # Licensed under the Apache License, Version 2.0 (the "License") # # 27-Oct-2016 Sasha Goldshtein Created this. from __future__ import print_function import argparse from bcc import BPF, USDT, utils import ctypes as ct import time import os languages = ["java", "perl", "php", "python", "ruby", "tcl"] examples = """examples: ./uflow -l java 185 # trace Java method calls in process 185 ./uflow -l ruby 134 # trace Ruby method calls in process 134 ./uflow -M indexOf -l java 185 # trace only 'indexOf'-prefixed methods ./uflow -C '' -l python 180 # trace only REPL-defined methods """ parser = argparse.ArgumentParser( description="Trace method execution flow in high-level languages.", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("-l", "--language", choices=languages, help="language to trace") parser.add_argument("pid", type=int, help="process id to attach to") parser.add_argument("-M", "--method", help="trace only calls to methods starting with this prefix") parser.add_argument("-C", "--class", dest="clazz", help="trace only calls to classes starting with this prefix") parser.add_argument("-v", "--verbose", action="store_true", help="verbose mode: print the BPF program (for debugging purposes)") parser.add_argument("--ebpf", action="store_true", help=argparse.SUPPRESS) args = parser.parse_args() usdt = USDT(pid=args.pid) program = """ struct call_t { u64 depth; // first bit is direction (0 entry, 1 return) u64 pid; // (tgid << 32) + pid from bpf_get_current... char clazz[80]; char method[80]; }; BPF_PERF_OUTPUT(calls); BPF_HASH(entry, u64, u64); """ prefix_template = """ static inline bool prefix_%s(char *actual) { char expected[] = "%s"; for (int i = 0; i < sizeof(expected) - 1; ++i) { if (expected[i] != actual[i]) { return false; } } return true; } """ if args.clazz: program += prefix_template % ("class", args.clazz) if args.method: program += prefix_template % ("method", args.method) trace_template = """ int NAME(struct pt_regs *ctx) { u64 *depth, zero = 0, clazz = 0, method = 0 ; struct call_t data = {}; READ_CLASS READ_METHOD bpf_probe_read(&data.clazz, sizeof(data.clazz), (void *)clazz); bpf_probe_read(&data.method, sizeof(data.method), (void *)method); FILTER_CLASS FILTER_METHOD data.pid = bpf_get_current_pid_tgid(); depth = entry.lookup_or_try_init(&data.pid, &zero); if (!depth) { depth = &zero; } data.depth = DEPTH; UPDATE calls.perf_submit(ctx, &data, sizeof(data)); return 0; } """ def enable_probe(probe_name, func_name, read_class, read_method, is_return): global program, trace_template, usdt depth = "*depth + 1" if not is_return else "*depth | (1ULL << 63)" update = "++(*depth);" if not is_return else "if (*depth) --(*depth);" filter_class = "if (!prefix_class(data.clazz)) { return 0; }" \ if args.clazz else "" filter_method = "if (!prefix_method(data.method)) { return 0; }" \ if args.method else "" program += trace_template.replace("NAME", func_name) \ .replace("READ_CLASS", read_class) \ .replace("READ_METHOD", read_method) \ .replace("FILTER_CLASS", filter_class) \ .replace("FILTER_METHOD", filter_method) \ .replace("DEPTH", depth) \ .replace("UPDATE", update) usdt.enable_probe_or_bail(probe_name, func_name) usdt = USDT(pid=args.pid) language = args.language if not language: language = utils.detect_language(languages, args.pid) if language == "java": enable_probe("method__entry", "java_entry", "bpf_usdt_readarg(2, ctx, &clazz);", "bpf_usdt_readarg(4, ctx, &method);", is_return=False) enable_probe("method__return", "java_return", "bpf_usdt_readarg(2, ctx, &clazz);", "bpf_usdt_readarg(4, ctx, &method);", is_return=True) elif language == "perl": enable_probe("sub__entry", "perl_entry", "bpf_usdt_readarg(2, ctx, &clazz);", "bpf_usdt_readarg(1, ctx, &method);", is_return=False) enable_probe("sub__return", "perl_return", "bpf_usdt_readarg(2, ctx, &clazz);", "bpf_usdt_readarg(1, ctx, &method);", is_return=True) elif language == "php": enable_probe("function__entry", "php_entry", "bpf_usdt_readarg(4, ctx, &clazz);", "bpf_usdt_readarg(1, ctx, &method);", is_return=False) enable_probe("function__return", "php_return", "bpf_usdt_readarg(4, ctx, &clazz);", "bpf_usdt_readarg(1, ctx, &method);", is_return=True) elif language == "python": enable_probe("function__entry", "python_entry", "bpf_usdt_readarg(1, ctx, &clazz);", # filename really "bpf_usdt_readarg(2, ctx, &method);", is_return=False) enable_probe("function__return", "python_return", "bpf_usdt_readarg(1, ctx, &clazz);", # filename really "bpf_usdt_readarg(2, ctx, &method);", is_return=True) elif language == "ruby": enable_probe("method__entry", "ruby_entry", "bpf_usdt_readarg(1, ctx, &clazz);", "bpf_usdt_readarg(2, ctx, &method);", is_return=False) enable_probe("method__return", "ruby_return", "bpf_usdt_readarg(1, ctx, &clazz);", "bpf_usdt_readarg(2, ctx, &method);", is_return=True) enable_probe("cmethod__entry", "ruby_centry", "bpf_usdt_readarg(1, ctx, &clazz);", "bpf_usdt_readarg(2, ctx, &method);", is_return=False) enable_probe("cmethod__return", "ruby_creturn", "bpf_usdt_readarg(1, ctx, &clazz);", "bpf_usdt_readarg(2, ctx, &method);", is_return=True) elif language == "tcl": enable_probe("proc__args", "tcl_entry", "", # no class/file info available "bpf_usdt_readarg(1, ctx, &method);", is_return=False) enable_probe("proc__return", "tcl_return", "", # no class/file info available "bpf_usdt_readarg(1, ctx, &method);", is_return=True) else: print("No language detected; use -l to trace a language.") exit(1) if args.ebpf or args.verbose: if args.verbose: print(usdt.get_text()) print(program) if args.ebpf: exit() bpf = BPF(text=program, usdt_contexts=[usdt]) print("Tracing method calls in %s process %d... Ctrl-C to quit." % (language, args.pid)) print("%-3s %-6s %-6s %-8s %s" % ("CPU", "PID", "TID", "TIME(us)", "METHOD")) class CallEvent(ct.Structure): _fields_ = [ ("depth", ct.c_ulonglong), ("pid", ct.c_ulonglong), ("clazz", ct.c_char * 80), ("method", ct.c_char * 80) ] start_ts = time.time() def print_event(cpu, data, size): event = ct.cast(data, ct.POINTER(CallEvent)).contents depth = event.depth & (~(1 << 63)) direction = "<- " if event.depth & (1 << 63) else "-> " print("%-3d %-6d %-6d %-8.3f %-40s" % (cpu, event.pid >> 32, event.pid & 0xFFFFFFFF, time.time() - start_ts, (" " * (depth - 1)) + direction + \ event.clazz.decode('utf-8', 'replace') + "." + \ event.method.decode('utf-8', 'replace'))) bpf["calls"].open_perf_buffer(print_event) while 1: try: bpf.perf_buffer_poll() except KeyboardInterrupt: exit() bpfcc-0.12.0/tools/lib/uflow_example.txt000066400000000000000000000136011357404205000201660ustar00rootroot00000000000000Demonstrations of uflow. uflow traces method entry and exit events and prints a visual flow graph that shows how methods are entered and exited, similar to a tracing debugger with breakpoints. This can be useful for understanding program flow in high-level languages such as Java, Perl, PHP, Python, Ruby, and Tcl which provide USDT probes for method invocations. For example, trace all Ruby method calls in a specific process: # ./uflow -l ruby 27245 Tracing method calls in ruby process 27245... Ctrl-C to quit. CPU PID TID TIME(us) METHOD 3 27245 27245 4.536 <- IO.gets 3 27245 27245 4.536 <- IRB::StdioInputMethod.gets 3 27245 27245 4.536 -> IRB::Context.verbose? 3 27245 27245 4.536 -> NilClass.nil? 3 27245 27245 4.536 <- NilClass.nil? 3 27245 27245 4.536 -> IO.tty? 3 27245 27245 4.536 <- IO.tty? 3 27245 27245 4.536 -> Kernel.kind_of? 3 27245 27245 4.536 <- Kernel.kind_of? 3 27245 27245 4.536 <- IRB::Context.verbose? 3 27245 27245 4.536 <- IRB::Irb.signal_status 3 27245 27245 4.536 -> String.chars 3 27245 27245 4.536 <- String.chars ^C In the preceding output, indentation indicates the depth of the flow graph, and the <- and -> arrows indicate the direction of the event (exit or entry). Often, the amount of output can be overwhelming. You can filter specific classes or methods. For example, trace only methods from the Thread class: # ./uflow -C java/lang/Thread $(pidof java) Tracing method calls in java process 27722... Ctrl-C to quit. CPU PID TID TIME(us) METHOD 3 27722 27731 3.144 -> java/lang/Thread. 3 27722 27731 3.144 -> java/lang/Thread.init 3 27722 27731 3.144 -> java/lang/Thread.init 3 27722 27731 3.144 -> java/lang/Thread.currentThread 3 27722 27731 3.144 <- java/lang/Thread.currentThread 3 27722 27731 3.144 -> java/lang/Thread.getThreadGroup 3 27722 27731 3.144 <- java/lang/Thread.getThreadGroup 3 27722 27731 3.144 -> java/lang/ThreadGroup.checkAccess 3 27722 27731 3.144 <- java/lang/ThreadGroup.checkAccess 3 27722 27731 3.144 -> java/lang/ThreadGroup.addUnstarted 3 27722 27731 3.144 <- java/lang/ThreadGroup.addUnstarted 3 27722 27731 3.145 -> java/lang/Thread.isDaemon 3 27722 27731 3.145 <- java/lang/Thread.isDaemon 3 27722 27731 3.145 -> java/lang/Thread.getPriority 3 27722 27731 3.145 <- java/lang/Thread.getPriority 3 27722 27731 3.145 -> java/lang/Thread.getContextClassLoader 3 27722 27731 3.145 <- java/lang/Thread.getContextClassLoader 3 27722 27731 3.145 -> java/lang/Thread.setPriority 3 27722 27731 3.145 -> java/lang/Thread.checkAccess 3 27722 27731 3.145 <- java/lang/Thread.checkAccess 3 27722 27731 3.145 -> java/lang/Thread.getThreadGroup 3 27722 27731 3.145 <- java/lang/Thread.getThreadGroup 3 27722 27731 3.145 -> java/lang/ThreadGroup.getMaxPriority 3 27722 27731 3.145 <- java/lang/ThreadGroup.getMaxPriority 3 27722 27731 3.145 -> java/lang/Thread.setPriority0 3 27722 27731 3.145 <- java/lang/Thread.setPriority0 3 27722 27731 3.145 <- java/lang/Thread.setPriority 3 27722 27731 3.145 -> java/lang/Thread.nextThreadID 3 27722 27731 3.145 <- java/lang/Thread.nextThreadID 3 27722 27731 3.145 <- java/lang/Thread.init 3 27722 27731 3.145 <- java/lang/Thread.init 3 27722 27731 3.145 <- java/lang/Thread. 3 27722 27731 3.145 -> java/lang/Thread.start 3 27722 27731 3.145 -> java/lang/ThreadGroup.add 3 27722 27731 3.145 <- java/lang/ThreadGroup.add 3 27722 27731 3.145 -> java/lang/Thread.start0 3 27722 27731 3.145 <- java/lang/Thread.start0 3 27722 27731 3.146 <- java/lang/Thread.start 2 27722 27742 3.146 -> java/lang/Thread.run ^C The reason that the CPU number is printed in the first column is that events from different threads can be reordered when running on different CPUs, and produce non-sensible output. By looking for changes in the CPU column, you can easily see if the events you're following make sense and belong to the same thread running on the same CPU. USAGE message: # ./uflow -h usage: uflow.py [-h] [-l {java,perl,php,python,ruby,tcl}] [-M METHOD] [-C CLAZZ] [-v] pid Trace method execution flow in high-level languages. positional arguments: pid process id to attach to optional arguments: -h, --help show this help message and exit -l {java,perl,php,python,ruby,tcl}, --language {java,perl,php,python,ruby,tcl} language to trace -M METHOD, --method METHOD trace only calls to methods starting with this prefix -C CLAZZ, --class CLAZZ trace only calls to classes starting with this prefix -v, --verbose verbose mode: print the BPF program (for debugging purposes) examples: ./uflow -l java 185 # trace Java method calls in process 185 ./uflow -l ruby 134 # trace Ruby method calls in process 134 ./uflow -M indexOf -l java 185 # trace only 'indexOf'-prefixed methods ./uflow -C '' -l python 180 # trace only REPL-defined methods bpfcc-0.12.0/tools/lib/ugc.py000077500000000000000000000172031357404205000157130ustar00rootroot00000000000000#!/usr/bin/python # @lint-avoid-python-3-compatibility-imports # # ugc Summarize garbage collection events in high-level languages. # For Linux, uses BCC, eBPF. # # USAGE: ugc [-v] [-m] [-M MSEC] [-F FILTER] {java,node,python,ruby} pid # # Copyright 2016 Sasha Goldshtein # Licensed under the Apache License, Version 2.0 (the "License") # # 19-Oct-2016 Sasha Goldshtein Created this. from __future__ import print_function import argparse from bcc import BPF, USDT, utils import ctypes as ct import time import os languages = ["java", "node", "python", "ruby"] examples = """examples: ./ugc -l java 185 # trace Java GCs in process 185 ./ugc -l ruby 1344 -m # trace Ruby GCs reporting in ms ./ugc -M 10 -l java 185 # trace only Java GCs longer than 10ms """ parser = argparse.ArgumentParser( description="Summarize garbage collection events in high-level languages.", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("-l", "--language", choices=languages, help="language to trace") parser.add_argument("pid", type=int, help="process id to attach to") parser.add_argument("-v", "--verbose", action="store_true", help="verbose mode: print the BPF program (for debugging purposes)") parser.add_argument("-m", "--milliseconds", action="store_true", help="report times in milliseconds (default is microseconds)") parser.add_argument("-M", "--minimum", type=int, default=0, help="display only GCs longer than this many milliseconds") parser.add_argument("-F", "--filter", type=str, help="display only GCs whose description contains this text") parser.add_argument("--ebpf", action="store_true", help=argparse.SUPPRESS) args = parser.parse_args() usdt = USDT(pid=args.pid) program = """ struct gc_event_t { u64 probe_index; u64 elapsed_ns; u64 field1; u64 field2; u64 field3; u64 field4; char string1[32]; char string2[32]; }; struct entry_t { u64 start_ns; u64 field1; u64 field2; }; BPF_PERF_OUTPUT(gcs); BPF_HASH(entry, u64, struct entry_t); """ class Probe(object): def __init__(self, begin, end, begin_save, end_save, formatter): self.begin = begin self.end = end self.begin_save = begin_save self.end_save = end_save self.formatter = formatter def generate(self): text = """ int trace_%s(struct pt_regs *ctx) { u64 pid = bpf_get_current_pid_tgid(); struct entry_t e = {}; e.start_ns = bpf_ktime_get_ns(); %s entry.update(&pid, &e); return 0; } int trace_%s(struct pt_regs *ctx) { u64 elapsed; struct entry_t *e; struct gc_event_t event = {}; u64 pid = bpf_get_current_pid_tgid(); e = entry.lookup(&pid); if (!e) { return 0; // missed the entry event on this thread } elapsed = bpf_ktime_get_ns() - e->start_ns; if (elapsed < %d) { return 0; } event.elapsed_ns = elapsed; %s gcs.perf_submit(ctx, &event, sizeof(event)); return 0; } """ % (self.begin, self.begin_save, self.end, args.minimum * 1000000, self.end_save) return text def attach(self): usdt.enable_probe_or_bail(self.begin, "trace_%s" % self.begin) usdt.enable_probe_or_bail(self.end, "trace_%s" % self.end) def format(self, data): return self.formatter(data) probes = [] language = args.language if not language: language = utils.detect_language(languages, args.pid) # # Java # if language == "java": # Oddly, the gc__begin/gc__end probes don't really have any useful # information, while the mem__pool* ones do. There's also a bunch of # probes described in the hotspot_gc*.stp file which aren't there # when looking at a live Java process. begin_save = """ bpf_usdt_readarg(6, ctx, &e.field1); // used bytes bpf_usdt_readarg(8, ctx, &e.field2); // max bytes """ end_save = """ event.field1 = e->field1; // used bytes at start event.field2 = e->field2; // max bytes at start bpf_usdt_readarg(6, ctx, &event.field3); // used bytes at end bpf_usdt_readarg(8, ctx, &event.field4); // max bytes at end u64 manager = 0, pool = 0; bpf_usdt_readarg(1, ctx, &manager); // ptr to manager name bpf_usdt_readarg(3, ctx, &pool); // ptr to pool name bpf_probe_read(&event.string1, sizeof(event.string1), (void *)manager); bpf_probe_read(&event.string2, sizeof(event.string2), (void *)pool); """ def formatter(e): "%s %s used=%d->%d max=%d->%d" % \ (e.string1, e.string2, e.field1, e.field3, e.field2, e.field4) probes.append(Probe("mem__pool__gc__begin", "mem__pool__gc__end", begin_save, end_save, formatter)) probes.append(Probe("gc__begin", "gc__end", "", "", lambda _: "no additional info available")) # # Node # elif language == "node": end_save = """ u32 gc_type = 0; bpf_usdt_readarg(1, ctx, &gc_type); event.field1 = gc_type; """ descs = {"GC scavenge": 1, "GC mark-sweep-compact": 2, "GC incremental mark": 4, "GC weak callbacks": 8} probes.append(Probe("gc__start", "gc__done", "", end_save, lambda e: str.join(", ", [desc for desc, val in descs.items() if e.field1 & val != 0]))) # # Python # elif language == "python": begin_save = """ int gen = 0; bpf_usdt_readarg(1, ctx, &gen); e.field1 = gen; """ end_save = """ long objs = 0; bpf_usdt_readarg(1, ctx, &objs); event.field1 = e->field1; event.field2 = objs; """ def formatter(event): "gen %d GC collected %d objects" % \ (event.field1, event.field2) probes.append(Probe("gc__start", "gc__done", begin_save, end_save, formatter)) # # Ruby # elif language == "ruby": # Ruby GC probes do not have any additional information available. probes.append(Probe("gc__mark__begin", "gc__mark__end", "", "", lambda _: "GC mark stage")) probes.append(Probe("gc__sweep__begin", "gc__sweep__end", "", "", lambda _: "GC sweep stage")) else: print("No language detected; use -l to trace a language.") exit(1) for probe in probes: program += probe.generate() probe.attach() if args.ebpf or args.verbose: if args.verbose: print(usdt.get_text()) print(program) if args.ebpf: exit() bpf = BPF(text=program, usdt_contexts=[usdt]) print("Tracing garbage collections in %s process %d... Ctrl-C to quit." % (language, args.pid)) time_col = "TIME (ms)" if args.milliseconds else "TIME (us)" print("%-8s %-8s %-40s" % ("START", time_col, "DESCRIPTION")) class GCEvent(ct.Structure): _fields_ = [ ("probe_index", ct.c_ulonglong), ("elapsed_ns", ct.c_ulonglong), ("field1", ct.c_ulonglong), ("field2", ct.c_ulonglong), ("field3", ct.c_ulonglong), ("field4", ct.c_ulonglong), ("string1", ct.c_char * 32), ("string2", ct.c_char * 32) ] start_ts = time.time() def print_event(cpu, data, size): event = ct.cast(data, ct.POINTER(GCEvent)).contents elapsed = event.elapsed_ns / 1000000 if args.milliseconds else \ event.elapsed_ns / 1000 description = probes[event.probe_index].format(event) if args.filter and args.filter not in description: return print("%-8.3f %-8.2f %s" % (time.time() - start_ts, elapsed, description)) bpf["gcs"].open_perf_buffer(print_event) while 1: try: bpf.perf_buffer_poll() except KeyboardInterrupt: exit() bpfcc-0.12.0/tools/lib/ugc_example.txt000066400000000000000000000074331357404205000176160ustar00rootroot00000000000000Demonstrations of ugc. ugc traces garbage collection events in high-level languages, including Java, Python, Ruby, and Node. Each GC event is printed with some additional information provided by that language's runtime, if available. The duration of the GC event is also provided. For example, to trace all garbage collection events in a specific Node process: # ugc $(pidof node) Tracing garbage collections in node process 30012... Ctrl-C to quit. START TIME (us) DESCRIPTION 1.500 1181.00 GC scavenge 1.505 1704.00 GC scavenge 1.509 1534.00 GC scavenge 1.515 1953.00 GC scavenge 1.519 2155.00 GC scavenge 1.525 2055.00 GC scavenge 1.530 2164.00 GC scavenge 1.536 2170.00 GC scavenge 1.541 2237.00 GC scavenge 1.547 1982.00 GC scavenge 1.551 2333.00 GC scavenge 1.557 2043.00 GC scavenge 1.561 2028.00 GC scavenge 1.573 3650.00 GC scavenge 1.580 4443.00 GC scavenge 1.604 6236.00 GC scavenge 1.615 8324.00 GC scavenge 1.659 11249.00 GC scavenge 1.678 16084.00 GC scavenge 1.747 15250.00 GC scavenge 1.937 191.00 GC incremental mark 2.001 63120.00 GC mark-sweep-compact 3.185 153.00 GC incremental mark 3.207 20847.00 GC mark-sweep-compact ^C The above output shows some fairly long GCs, notably around 2 seconds in there is a collection that takes over 60ms (mark-sweep-compact). Occasionally, it might be useful to filter out collections that are very short, or display only collections that have a specific description. The -M and -F switches can be useful for this: # ugc -F Tenured $(pidof java) Tracing garbage collections in java process 29907... Ctrl-C to quit. START TIME (us) DESCRIPTION 0.360 4309.00 MarkSweepCompact Tenured Gen used=287528->287528 max=173408256->173408256 2.459 4232.00 MarkSweepCompact Tenured Gen used=287528->287528 max=173408256->173408256 4.648 4139.00 MarkSweepCompact Tenured Gen used=287528->287528 max=173408256->173408256 ^C # ugc -M 1 $(pidof java) Tracing garbage collections in java process 29907... Ctrl-C to quit. START TIME (us) DESCRIPTION 0.160 3715.00 MarkSweepCompact Code Cache used=287528->3209472 max=173408256->251658240 0.160 3975.00 MarkSweepCompact Metaspace used=287528->3092104 max=173408256->18446744073709551615 0.160 4058.00 MarkSweepCompact Compressed Class Space used=287528->266840 max=173408256->1073741824 0.160 4110.00 MarkSweepCompact Eden Space used=287528->0 max=173408256->69337088 0.160 4159.00 MarkSweepCompact Survivor Space used=287528->0 max=173408256->8650752 0.160 4207.00 MarkSweepCompact Tenured Gen used=287528->287528 max=173408256->173408256 0.160 4289.00 used=0->0 max=0->0 ^C USAGE message: # ugc -h usage: ugc.py [-h] [-l {java,python,ruby,node}] [-v] [-m] [-M MINIMUM] [-F FILTER] pid Summarize garbage collection events in high-level languages. positional arguments: pid process id to attach to optional arguments: -h, --help show this help message and exit -l {java,python,ruby,node}, --language {java,python,ruby,node} language to trace -v, --verbose verbose mode: print the BPF program (for debugging purposes) -m, --milliseconds report times in milliseconds (default is microseconds) -M MINIMUM, --minimum MINIMUM display only GCs longer than this many milliseconds -F FILTER, --filter FILTER display only GCs whose description contains this text examples: ./ugc -l java 185 # trace Java GCs in process 185 ./ugc -l ruby 1344 -m # trace Ruby GCs reporting in ms ./ugc -M 10 -l java 185 # trace only Java GCs longer than 10ms bpfcc-0.12.0/tools/lib/uobjnew.py000077500000000000000000000137111357404205000166060ustar00rootroot00000000000000#!/usr/bin/python # @lint-avoid-python-3-compatibility-imports # # uobjnew Summarize object allocations in high-level languages. # For Linux, uses BCC, eBPF. # # USAGE: uobjnew [-h] [-T TOP] [-v] {c,java,ruby,tcl} pid [interval] # # Copyright 2016 Sasha Goldshtein # Licensed under the Apache License, Version 2.0 (the "License") # # 25-Oct-2016 Sasha Goldshtein Created this. from __future__ import print_function import argparse from bcc import BPF, USDT, utils from time import sleep import os # C needs to be the last language. languages = ["c", "java", "ruby", "tcl"] examples = """examples: ./uobjnew -l java 145 # summarize Java allocations in process 145 ./uobjnew -l c 2020 1 # grab malloc() sizes and print every second ./uobjnew -l ruby 6712 -C 10 # top 10 Ruby types by number of allocations ./uobjnew -l ruby 6712 -S 10 # top 10 Ruby types by total size """ parser = argparse.ArgumentParser( description="Summarize object allocations in high-level languages.", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("-l", "--language", choices=languages, help="language to trace") parser.add_argument("pid", type=int, help="process id to attach to") parser.add_argument("interval", type=int, nargs='?', help="print every specified number of seconds") parser.add_argument("-C", "--top-count", type=int, help="number of most frequently allocated types to print") parser.add_argument("-S", "--top-size", type=int, help="number of largest types by allocated bytes to print") parser.add_argument("-v", "--verbose", action="store_true", help="verbose mode: print the BPF program (for debugging purposes)") parser.add_argument("--ebpf", action="store_true", help=argparse.SUPPRESS) args = parser.parse_args() language = args.language if not language: language = utils.detect_language(languages, args.pid) program = """ #include struct key_t { #if MALLOC_TRACING u64 size; #else char name[50]; #endif }; struct val_t { u64 total_size; u64 num_allocs; }; BPF_HASH(allocs, struct key_t, struct val_t); """.replace("MALLOC_TRACING", "1" if language == "c" else "0") usdt = USDT(pid=args.pid) # # C # if language == "c": program += """ int alloc_entry(struct pt_regs *ctx, size_t size) { struct key_t key = {}; struct val_t *valp, zero = {}; key.size = size; valp = allocs.lookup_or_try_init(&key, &zero); if (valp) { valp->total_size += size; valp->num_allocs += 1; } return 0; } """ # # Java # elif language == "java": program += """ int alloc_entry(struct pt_regs *ctx) { struct key_t key = {}; struct val_t *valp, zero = {}; u64 classptr = 0, size = 0; bpf_usdt_readarg(2, ctx, &classptr); bpf_usdt_readarg(4, ctx, &size); bpf_probe_read(&key.name, sizeof(key.name), (void *)classptr); valp = allocs.lookup_or_try_init(&key, &zero); if (valp) { valp->total_size += size; valp->num_allocs += 1; } return 0; } """ usdt.enable_probe_or_bail("object__alloc", "alloc_entry") # # Ruby # elif language == "ruby": create_template = """ int THETHING_alloc_entry(struct pt_regs *ctx) { struct key_t key = { .name = "THETHING" }; struct val_t *valp, zero = {}; u64 size = 0; bpf_usdt_readarg(1, ctx, &size); valp = allocs.lookup_or_try_init(&key, &zero); if (valp) { valp->total_size += size; valp->num_allocs += 1; } return 0; } """ program += """ int object_alloc_entry(struct pt_regs *ctx) { struct key_t key = {}; struct val_t *valp, zero = {}; u64 classptr = 0; bpf_usdt_readarg(1, ctx, &classptr); bpf_probe_read(&key.name, sizeof(key.name), (void *)classptr); valp = allocs.lookup_or_try_init(&key, &zero); if (valp) { valp->num_allocs += 1; // We don't know the size, unfortunately } return 0; } """ usdt.enable_probe_or_bail("object__create", "object_alloc_entry") for thing in ["string", "hash", "array"]: program += create_template.replace("THETHING", thing) usdt.enable_probe_or_bail("%s__create" % thing, "%s_alloc_entry" % thing) # # Tcl # elif language == "tcl": program += """ int alloc_entry(struct pt_regs *ctx) { struct key_t key = { .name = "" }; struct val_t *valp, zero = {}; valp = allocs.lookup_or_try_init(&key, &zero); if (valp) { valp->num_allocs += 1; } return 0; } """ usdt.enable_probe_or_bail("obj__create", "alloc_entry") else: print("No language detected; use -l to trace a language.") exit(1) if args.ebpf or args.verbose: if args.verbose: print(usdt.get_text()) print(program) if args.ebpf: exit() bpf = BPF(text=program, usdt_contexts=[usdt]) if language == "c": bpf.attach_uprobe(name="c", sym="malloc", fn_name="alloc_entry", pid=args.pid) exit_signaled = False print("Tracing allocations in process %d (language: %s)... Ctrl-C to quit." % (args.pid, language or "none")) while True: try: sleep(args.interval or 99999999) except KeyboardInterrupt: exit_signaled = True print() data = bpf["allocs"] if args.top_count: data = sorted(data.items(), key=lambda kv: kv[1].num_allocs) data = data[-args.top_count:] elif args.top_size: data = sorted(data.items(), key=lambda kv: kv[1].total_size) data = data[-args.top_size:] else: data = sorted(data.items(), key=lambda kv: kv[1].total_size) print("%-30s %8s %12s" % ("NAME/TYPE", "# ALLOCS", "# BYTES")) for key, value in data: if language == "c": obj_type = "block size %d" % key.size else: obj_type = key.name print("%-30s %8d %12d" % (obj_type, value.num_allocs, value.total_size)) if args.interval and not exit_signaled: bpf["allocs"].clear() else: exit() bpfcc-0.12.0/tools/lib/uobjnew_example.txt000066400000000000000000000057441357404205000205140ustar00rootroot00000000000000Demonstrations of uobjnew. uobjnew summarizes new object allocation events and prints out statistics on which object type has been allocated frequently, and how many bytes of that type have been allocated. This helps diagnose common allocation paths, which can in turn cause heavy garbage collection. For example, trace Ruby object allocations when running some simple commands in irb (the Ruby REPL): # ./uobjnew -l ruby 27245 Tracing allocations in process 27245 (language: ruby)... Ctrl-C to quit. TYPE # ALLOCS # BYTES NameError 1 0 RubyToken::TkSPACE 1 0 RubyToken::TkSTRING 1 0 String 7 0 RubyToken::TkNL 2 0 RubyToken::TkIDENTIFIER 2 0 array 55 129 string 344 1348 ^C Plain C/C++ allocations (through "malloc") are also supported. We can't report the type being allocated, but we can report the object sizes at least. Also, print only the top 10 rows by number of bytes allocated: # ./uobjnew -S 10 -l c 27245 Tracing allocations in process 27245 (language: c)... Ctrl-C to quit. TYPE # ALLOCS # BYTES block size 64 22 1408 block size 992 2 1984 block size 32 68 2176 block size 48 48 2304 block size 944 4 3776 block size 1104 4 4416 block size 160 32 5120 block size 535 15 8025 block size 128 112 14336 block size 80 569 45520 ^C USAGE message: # ./uobjnew -h usage: uobjnew.py [-h] [-l {c,java,ruby,tcl}] [-C TOP_COUNT] [-S TOP_SIZE] [-v] pid [interval] Summarize object allocations in high-level languages. positional arguments: pid process id to attach to interval print every specified number of seconds optional arguments: -h, --help show this help message and exit -l {c,java,ruby,tcl}, --language {c,java,ruby,tcl} language to trace -C TOP_COUNT, --top-count TOP_COUNT number of most frequently allocated types to print -S TOP_SIZE, --top-size TOP_SIZE number of largest types by allocated bytes to print -v, --verbose verbose mode: print the BPF program (for debugging purposes) examples: ./uobjnew -l java 145 # summarize Java allocations in process 145 ./uobjnew -l c 2020 1 # grab malloc() sizes and print every second ./uobjnew -l ruby 6712 -C 10 # top 10 Ruby types by number of allocations ./uobjnew -l ruby 6712 -S 10 # top 10 Ruby types by total size bpfcc-0.12.0/tools/lib/ustat.py000077500000000000000000000301721357404205000162750ustar00rootroot00000000000000#!/usr/bin/python # @lint-avoid-python-3-compatibility-imports # # ustat Activity stats from high-level languages, including exceptions, # method calls, class loads, garbage collections, and more. # For Linux, uses BCC, eBPF. # # USAGE: ustat [-l {java,node,perl,php,python,ruby,tcl}] [-C] # [-S {cload,excp,gc,method,objnew,thread}] [-r MAXROWS] [-d] # [interval [count]] # # This uses in-kernel eBPF maps to store per process summaries for efficiency. # Newly-created processes might only be traced at the next interval, if the # relevant USDT probe requires enabling through a semaphore. # # Copyright 2016 Sasha Goldshtein # Licensed under the Apache License, Version 2.0 (the "License") # # 26-Oct-2016 Sasha Goldshtein Created this. from __future__ import print_function import argparse from bcc import BPF, USDT, USDTException import os import sys from subprocess import call from time import sleep, strftime class Category(object): THREAD = "THREAD" METHOD = "METHOD" OBJNEW = "OBJNEW" CLOAD = "CLOAD" EXCP = "EXCP" GC = "GC" class Probe(object): def __init__(self, language, procnames, events): """ Initialize a new probe object with a specific language, set of process names to monitor for that language, and a dictionary of events and categories. The dictionary is a mapping of USDT probe names (such as 'gc__start') to event categories supported by this tool -- from the Category class. """ self.language = language self.procnames = procnames self.events = events def _find_targets(self): """Find pids where the comm is one of the specified list""" self.targets = {} all_pids = [int(pid) for pid in os.listdir('/proc') if pid.isdigit()] for pid in all_pids: try: comm = open('/proc/%d/comm' % pid).read().strip() if comm in self.procnames: cmdline = open('/proc/%d/cmdline' % pid).read() self.targets[pid] = cmdline.replace('\0', ' ') except IOError: continue # process may already have terminated def _enable_probes(self): self.usdts = [] for pid in self.targets: try: usdt = USDT(pid=pid) except USDTException: # avoid race condition on pid going away. print("failed to instrument %d" % pid, file=sys.stderr) continue for event in self.events: try: usdt.enable_probe(event, "%s_%s" % (self.language, event)) except Exception: # This process might not have a recent version of the USDT # probes enabled, or might have been compiled without USDT # probes at all. The process could even have been shut down # and the pid been recycled. We have to gracefully handle # the possibility that we can't attach probes to it at all. pass self.usdts.append(usdt) def _generate_tables(self): text = """ BPF_HASH(%s_%s_counts, u32, u64); // pid to event count """ return str.join('', [text % (self.language, event) for event in self.events]) def _generate_functions(self): text = """ int %s_%s(void *ctx) { u64 *valp, zero = 0; u32 tgid = bpf_get_current_pid_tgid() >> 32; valp = %s_%s_counts.lookup_or_try_init(&tgid, &zero); if (valp) { ++(*valp); } return 0; } """ lang = self.language return str.join('', [text % (lang, event, lang, event) for event in self.events]) def get_program(self): self._find_targets() self._enable_probes() return self._generate_tables() + self._generate_functions() def get_usdts(self): return self.usdts def get_counts(self, bpf): """Return a map of event counts per process""" event_dict = dict([(category, 0) for category in self.events.values()]) result = dict([(pid, event_dict.copy()) for pid in self.targets]) for event, category in self.events.items(): counts = bpf["%s_%s_counts" % (self.language, event)] for pid, count in counts.items(): if pid.value not in result: print("result was not found for %d" % pid.value, file=sys.stderr) continue result[pid.value][category] = count.value counts.clear() return result def cleanup(self): self.usdts = None class Tool(object): def _parse_args(self): examples = """examples: ./ustat # stats for all languages, 1 second refresh ./ustat -C # don't clear the screen ./ustat -l java # Java processes only ./ustat 5 # 5 second summaries ./ustat 5 10 # 5 second summaries, 10 times only """ parser = argparse.ArgumentParser( description="Activity stats from high-level languages.", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("-l", "--language", choices=["java", "node", "perl", "php", "python", "ruby", "tcl"], help="language to trace (default: all languages)") parser.add_argument("-C", "--noclear", action="store_true", help="don't clear the screen") parser.add_argument("-S", "--sort", choices=[cat.lower() for cat in dir(Category) if cat.isupper()], help="sort by this field (descending order)") parser.add_argument("-r", "--maxrows", default=20, type=int, help="maximum rows to print, default 20") parser.add_argument("-d", "--debug", action="store_true", help="Print the resulting BPF program (for debugging purposes)") parser.add_argument("interval", nargs="?", default=1, type=int, help="output interval, in seconds") parser.add_argument("count", nargs="?", default=99999999, type=int, help="number of outputs") parser.add_argument("--ebpf", action="store_true", help=argparse.SUPPRESS) self.args = parser.parse_args() def _create_probes(self): probes_by_lang = { "java": Probe("java", ["java"], { "gc__begin": Category.GC, "mem__pool__gc__begin": Category.GC, "thread__start": Category.THREAD, "class__loaded": Category.CLOAD, "object__alloc": Category.OBJNEW, "method__entry": Category.METHOD, "ExceptionOccurred__entry": Category.EXCP }), "node": Probe("node", ["node"], { "gc__start": Category.GC }), "perl": Probe("perl", ["perl"], { "sub__entry": Category.METHOD }), "php": Probe("php", ["php"], { "function__entry": Category.METHOD, "compile__file__entry": Category.CLOAD, "exception__thrown": Category.EXCP }), "python": Probe("python", ["python"], { "function__entry": Category.METHOD, "gc__start": Category.GC }), "ruby": Probe("ruby", ["ruby", "irb"], { "method__entry": Category.METHOD, "cmethod__entry": Category.METHOD, "gc__mark__begin": Category.GC, "gc__sweep__begin": Category.GC, "object__create": Category.OBJNEW, "hash__create": Category.OBJNEW, "string__create": Category.OBJNEW, "array__create": Category.OBJNEW, "require__entry": Category.CLOAD, "load__entry": Category.CLOAD, "raise": Category.EXCP }), "tcl": Probe("tcl", ["tclsh", "wish"], { "proc__entry": Category.METHOD, "obj__create": Category.OBJNEW }), } if self.args.language: self.probes = [probes_by_lang[self.args.language]] else: self.probes = probes_by_lang.values() def _attach_probes(self): program = str.join('\n', [p.get_program() for p in self.probes]) if self.args.debug or self.args.ebpf: print(program) if self.args.ebpf: exit() for probe in self.probes: print("Attached to %s processes:" % probe.language, str.join(', ', map(str, probe.targets))) self.bpf = BPF(text=program) usdts = [usdt for probe in self.probes for usdt in probe.get_usdts()] # Filter out duplicates when we have multiple processes with the same # uprobe. We are attaching to these probes manually instead of using # the USDT support from the bcc module, because the USDT class attaches # to each uprobe with a specific pid. When there is more than one # process from some language, we end up attaching more than once to the # same uprobe (albeit with different pids), which is not allowed. # Instead, we use a global attach (with pid=-1). uprobes = set([(path, func, addr) for usdt in usdts for (path, func, addr, _) in usdt.enumerate_active_probes()]) for (path, func, addr) in uprobes: self.bpf.attach_uprobe(name=path, fn_name=func, addr=addr, pid=-1) def _detach_probes(self): for probe in self.probes: probe.cleanup() # Cleans up USDT contexts self.bpf.cleanup() # Cleans up all attached probes self.bpf = None def _loop_iter(self): self._attach_probes() try: sleep(self.args.interval) except KeyboardInterrupt: self.exiting = True if not self.args.noclear: call("clear") else: print() with open("/proc/loadavg") as stats: print("%-8s loadavg: %s" % (strftime("%H:%M:%S"), stats.read())) print("%-6s %-20s %-10s %-6s %-10s %-8s %-6s %-6s" % ( "PID", "CMDLINE", "METHOD/s", "GC/s", "OBJNEW/s", "CLOAD/s", "EXC/s", "THR/s")) line = 0 counts = {} targets = {} for probe in self.probes: counts.update(probe.get_counts(self.bpf)) targets.update(probe.targets) if self.args.sort: sort_field = self.args.sort.upper() counts = sorted(counts.items(), key=lambda kv: -kv[1].get(sort_field, 0)) else: counts = sorted(counts.items(), key=lambda kv: kv[0]) for pid, stats in counts: print("%-6d %-20s %-10d %-6d %-10d %-8d %-6d %-6d" % ( pid, targets[pid][:20], stats.get(Category.METHOD, 0) / self.args.interval, stats.get(Category.GC, 0) / self.args.interval, stats.get(Category.OBJNEW, 0) / self.args.interval, stats.get(Category.CLOAD, 0) / self.args.interval, stats.get(Category.EXCP, 0) / self.args.interval, stats.get(Category.THREAD, 0) / self.args.interval )) line += 1 if line >= self.args.maxrows: break self._detach_probes() def run(self): self._parse_args() self._create_probes() print('Tracing... Output every %d secs. Hit Ctrl-C to end' % self.args.interval) countdown = self.args.count self.exiting = False while True: self._loop_iter() countdown -= 1 if self.exiting or countdown == 0: print("Detaching...") exit() if __name__ == "__main__": try: Tool().run() except KeyboardInterrupt: pass bpfcc-0.12.0/tools/lib/ustat_example.txt000066400000000000000000000057541357404205000202040ustar00rootroot00000000000000Demonstrations of ustat. ustat is a "top"-like tool for monitoring events in high-level languages. It prints statistics about garbage collections, method calls, object allocations, and various other events for every process that it recognizes with a Java, Node, Perl, PHP, Python, Ruby, and Tcl runtime. For example: # ./ustat.py Tracing... Output every 10 secs. Hit Ctrl-C to end 12:17:17 loadavg: 0.33 0.08 0.02 5/211 26284 PID CMDLINE METHOD/s GC/s OBJNEW/s CLOAD/s EXC/s THR/s 3018 node/node 0 3 0 0 0 0 ^C Detaching... If desired, you can instruct ustat to print a certain number of entries and exit, which can be useful to get a quick picture on what's happening on the system over a short time interval. Here, we ask ustat to print 5-second summaries 12 times (for a total time of 1 minute): # ./ustat.py -C 5 12 Tracing... Output every 5 secs. Hit Ctrl-C to end 12:18:26 loadavg: 0.27 0.11 0.04 2/336 26455 PID CMDLINE METHOD/s GC/s OBJNEW/s CLOAD/s EXC/s THR/s 3018 node/node 0 1 0 0 0 0 12:18:31 loadavg: 0.33 0.12 0.04 2/336 26456 PID CMDLINE METHOD/s GC/s OBJNEW/s CLOAD/s EXC/s THR/s 3018 node/node 0 0 0 0 0 0 26439 java -XX:+ExtendedDT 2776045 0 0 0 0 0 12:18:37 loadavg: 0.38 0.14 0.05 2/336 26457 PID CMDLINE METHOD/s GC/s OBJNEW/s CLOAD/s EXC/s THR/s 3018 node/node 0 0 0 0 0 0 26439 java -XX:+ExtendedDT 2804378 0 0 0 0 0 (...more output omitted for brevity) USAGE message: # ./ustat.py -h usage: ustat.py [-h] [-l {java,node,perl,php,python,ruby,tcl}] [-C] [-S {cload,excp,gc,method,objnew,thread}] [-r MAXROWS] [-d] [interval] [count] Activity stats from high-level languages. positional arguments: interval output interval, in seconds count number of outputs optional arguments: -h, --help show this help message and exit -l {java,node,perl,php,python,ruby,tcl}, --language {java,node,perl,php,python,ruby,tcl} language to trace (default: all languages) -C, --noclear don't clear the screen -S {cload,excp,gc,method,objnew,thread}, --sort {cload,excp,gc,method,objnew,thread} sort by this field (descending order) -r MAXROWS, --maxrows MAXROWS maximum rows to print, default 20 -d, --debug Print the resulting BPF program (for debugging purposes) examples: ./ustat # stats for all languages, 1 second refresh ./ustat -C # don't clear the screen ./ustat -l java # Java processes only ./ustat 5 # 5 second summaries ./ustat 5 10 # 5 second summaries, 10 times only bpfcc-0.12.0/tools/lib/uthreads.py000077500000000000000000000077751357404205000167710ustar00rootroot00000000000000#!/usr/bin/python # @lint-avoid-python-3-compatibility-imports # # uthreads Trace thread creation/destruction events in high-level languages. # For Linux, uses BCC, eBPF. # # USAGE: uthreads [-l {c,java,none}] [-v] pid # # Copyright 2016 Sasha Goldshtein # Licensed under the Apache License, Version 2.0 (the "License") # # 25-Oct-2016 Sasha Goldshtein Created this. from __future__ import print_function import argparse from bcc import BPF, USDT, utils import ctypes as ct import time import os languages = ["c", "java"] examples = """examples: ./uthreads -l java 185 # trace Java threads in process 185 ./uthreads -l none 12245 # trace only pthreads in process 12245 """ parser = argparse.ArgumentParser( description="Trace thread creation/destruction events in " + "high-level languages.", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("-l", "--language", choices=languages + ["none"], help="language to trace (none for pthreads only)") parser.add_argument("pid", type=int, help="process id to attach to") parser.add_argument("-v", "--verbose", action="store_true", help="verbose mode: print the BPF program (for debugging purposes)") parser.add_argument("--ebpf", action="store_true", help=argparse.SUPPRESS) args = parser.parse_args() usdt = USDT(pid=args.pid) program = """ struct thread_event_t { u64 runtime_id; u64 native_id; char type[8]; char name[80]; }; BPF_PERF_OUTPUT(threads); int trace_pthread(struct pt_regs *ctx) { struct thread_event_t te = {}; u64 start_routine = 0; char type[] = "pthread"; te.native_id = bpf_get_current_pid_tgid() & 0xFFFFFFFF; bpf_usdt_readarg(2, ctx, &start_routine); te.runtime_id = start_routine; // This is really a function pointer __builtin_memcpy(&te.type, type, sizeof(te.type)); threads.perf_submit(ctx, &te, sizeof(te)); return 0; } """ usdt.enable_probe_or_bail("pthread_start", "trace_pthread") language = args.language if not language: language = utils.detect_language(languages, args.pid) if language == "c": # Nothing to add pass elif language == "java": template = """ int %s(struct pt_regs *ctx) { char type[] = "%s"; struct thread_event_t te = {}; u64 nameptr = 0, id = 0, native_id = 0; bpf_usdt_readarg(1, ctx, &nameptr); bpf_usdt_readarg(3, ctx, &id); bpf_usdt_readarg(4, ctx, &native_id); bpf_probe_read(&te.name, sizeof(te.name), (void *)nameptr); te.runtime_id = id; te.native_id = native_id; __builtin_memcpy(&te.type, type, sizeof(te.type)); threads.perf_submit(ctx, &te, sizeof(te)); return 0; } """ program += template % ("trace_start", "start") program += template % ("trace_stop", "stop") usdt.enable_probe_or_bail("thread__start", "trace_start") usdt.enable_probe_or_bail("thread__stop", "trace_stop") if args.ebpf or args.verbose: if args.verbose: print(usdt.get_text()) print(program) if args.ebpf: exit() bpf = BPF(text=program, usdt_contexts=[usdt]) print("Tracing thread events in process %d (language: %s)... Ctrl-C to quit." % (args.pid, language or "none")) print("%-8s %-16s %-8s %-30s" % ("TIME", "ID", "TYPE", "DESCRIPTION")) class ThreadEvent(ct.Structure): _fields_ = [ ("runtime_id", ct.c_ulonglong), ("native_id", ct.c_ulonglong), ("type", ct.c_char * 8), ("name", ct.c_char * 80), ] start_ts = time.time() def print_event(cpu, data, size): event = ct.cast(data, ct.POINTER(ThreadEvent)).contents name = event.name if event.type == "pthread": name = bpf.sym(event.runtime_id, args.pid, show_module=True) tid = event.native_id else: tid = "R=%s/N=%s" % (event.runtime_id, event.native_id) print("%-8.3f %-16s %-8s %-30s" % ( time.time() - start_ts, tid, event.type, name)) bpf["threads"].open_perf_buffer(print_event) while 1: try: bpf.perf_buffer_poll() except KeyboardInterrupt: exit() bpfcc-0.12.0/tools/lib/uthreads_example.txt000066400000000000000000000041241357404205000206510ustar00rootroot00000000000000Demonstrations of uthreads. uthreads traces thread creation events in Java or raw (C) pthreads, and prints details about the newly created thread. For Java threads, the thread name is printed; for pthreads, the thread's start function is printed, if there is symbol information to resolve it. For example, trace all Java thread creation events: # ./uthreads -l java 27420 Tracing thread events in process 27420 (language: java)... Ctrl-C to quit. TIME ID TYPE DESCRIPTION 18.596 R=9/N=0 start SIGINT handler 18.596 R=4/N=0 stop Signal Dispatcher ^C The ID column in the preceding output shows the thread's runtime ID and native ID, when available. The accuracy of this information depends on the Java runtime. Next, trace only pthread creation events in some native application: # ./uthreads 27450 Tracing thread events in process 27450 (language: c)... Ctrl-C to quit. TIME ID TYPE DESCRIPTION 0.924 27462 pthread primes_thread [primes] 0.927 27463 pthread primes_thread [primes] 0.928 27464 pthread primes_thread [primes] 0.928 27465 pthread primes_thread [primes] ^C The thread name ("primes_thread" in this example) is resolved from debuginfo. If symbol information is not present, the thread's start address is printed instead. USAGE message: # ./uthreads -h usage: uthreads.py [-h] [-l {c,java,none}] [-v] pid Trace thread creation/destruction events in high-level languages. positional arguments: pid process id to attach to optional arguments: -h, --help show this help message and exit -l {c,java,none}, --language {c,java,none} language to trace (none for pthreads only) -v, --verbose verbose mode: print the BPF program (for debugging purposes) examples: ./uthreads -l java 185 # trace Java threads in process 185 ./uthreads -l none 12245 # trace only pthreads in process 12245 bpfcc-0.12.0/tools/llcstat.py000077500000000000000000000071511357404205000160360ustar00rootroot00000000000000#!/usr/bin/python # # llcstat.py Summarize cache references and cache misses by PID. # Cache reference and cache miss are corresponding events defined in # uapi/linux/perf_event.h, it varies to different architecture. # On x86-64, they mean LLC references and LLC misses. # # For Linux, uses BCC, eBPF. Embedded C. # # SEE ALSO: perf top -e cache-misses -e cache-references -a -ns pid,cpu,comm # # REQUIRES: Linux 4.9+ (BPF_PROG_TYPE_PERF_EVENT support). # # Copyright (c) 2016 Facebook, Inc. # Licensed under the Apache License, Version 2.0 (the "License") # # 19-Oct-2016 Teng Qin Created this. from __future__ import print_function import argparse from bcc import BPF, PerfType, PerfHWConfig import signal from time import sleep parser = argparse.ArgumentParser( description="Summarize cache references and misses by PID", formatter_class=argparse.RawDescriptionHelpFormatter) parser.add_argument( "-c", "--sample_period", type=int, default=100, help="Sample one in this many number of cache reference / miss events") parser.add_argument( "duration", nargs="?", default=10, help="Duration, in seconds, to run") parser.add_argument("--ebpf", action="store_true", help=argparse.SUPPRESS) args = parser.parse_args() # load BPF program bpf_text=""" #include #include struct key_t { int cpu; int pid; char name[TASK_COMM_LEN]; }; BPF_HASH(ref_count, struct key_t); BPF_HASH(miss_count, struct key_t); static inline __attribute__((always_inline)) void get_key(struct key_t* key) { key->cpu = bpf_get_smp_processor_id(); key->pid = bpf_get_current_pid_tgid(); bpf_get_current_comm(&(key->name), sizeof(key->name)); } int on_cache_miss(struct bpf_perf_event_data *ctx) { struct key_t key = {}; get_key(&key); miss_count.increment(key, ctx->sample_period); return 0; } int on_cache_ref(struct bpf_perf_event_data *ctx) { struct key_t key = {}; get_key(&key); ref_count.increment(key, ctx->sample_period); return 0; } """ if args.ebpf: print(bpf_text) exit() b = BPF(text=bpf_text) try: b.attach_perf_event( ev_type=PerfType.HARDWARE, ev_config=PerfHWConfig.CACHE_MISSES, fn_name="on_cache_miss", sample_period=args.sample_period) b.attach_perf_event( ev_type=PerfType.HARDWARE, ev_config=PerfHWConfig.CACHE_REFERENCES, fn_name="on_cache_ref", sample_period=args.sample_period) except Exception: print("Failed to attach to a hardware event. Is this a virtual machine?") exit() print("Running for {} seconds or hit Ctrl-C to end.".format(args.duration)) try: sleep(float(args.duration)) except KeyboardInterrupt: signal.signal(signal.SIGINT, lambda signal, frame: print()) miss_count = {} for (k, v) in b.get_table('miss_count').items(): miss_count[(k.pid, k.cpu, k.name)] = v.value print('PID NAME CPU REFERENCE MISS HIT%') tot_ref = 0 tot_miss = 0 for (k, v) in b.get_table('ref_count').items(): try: miss = miss_count[(k.pid, k.cpu, k.name)] except KeyError: miss = 0 tot_ref += v.value tot_miss += miss # This happens on some PIDs due to missed counts caused by sampling hit = (v.value - miss) if (v.value >= miss) else 0 print('{:<8d} {:<16s} {:<4d} {:>12d} {:>12d} {:>6.2f}%'.format( k.pid, k.name.decode('utf-8', 'replace'), k.cpu, v.value, miss, (float(hit) / float(v.value)) * 100.0)) print('Total References: {} Total Misses: {} Hit Rate: {:.2f}%'.format( tot_ref, tot_miss, (float(tot_ref - tot_miss) / float(tot_ref)) * 100.0)) bpfcc-0.12.0/tools/llcstat_example.txt000066400000000000000000000044751357404205000177430ustar00rootroot00000000000000Demonstrations of llcstat. llcstat traces cache reference and cache miss events system-wide, and summarizes them by PID and CPU. These events, defined in uapi/linux/perf_event.h, have different meanings on different architecture. For x86-64, they mean misses and references to LLC. Example output: # ./llcstat.py 20 -c 5000 Running for 20 seconds or hit Ctrl-C to end. PID NAME CPU REFERENCE MISS HIT% 0 swapper/15 15 3515000 640000 81.79% 238 migration/38 38 5000 0 100.00% 4512 ntpd 11 5000 0 100.00% 150867 ipmitool 3 25000 5000 80.00% 150895 lscpu 17 280000 25000 91.07% 151807 ipmitool 15 15000 5000 66.67% 150757 awk 2 15000 5000 66.67% 151213 chef-client 5 1770000 240000 86.44% 151822 scribe-dispatch 12 15000 0 100.00% 123386 mysqld 5 5000 0 100.00% [...] Total References: 518920000 Total Misses: 90265000 Hit Rate: 82.61% This shows each PID's cache hit rate during the 20 seconds run period. A count of 5000 was used in this example, which means that one in every 5,000 events will trigger an in-kernel counter to be incremented. This is refactored on the output, which is why it is always in multiples of 5,000. We don't instrument every single event since the overhead would be prohibitive, nor do we need to: this is a type of sampling profiler. Because of this, the processes that trigger the 5,000'th cache reference or misses can happen to some degree by chance. Overall it should make sense. But for low counts, you might find a case where -- by chance -- a process has been tallied with more misses than references, which would seem impossible. USAGE message: # ./llcstat.py --help usage: llcstat.py [-h] [-c SAMPLE_PERIOD] [duration] Summarize cache references and misses by PID positional arguments: duration Duration, in seconds, to run optional arguments: -h, --help show this help message and exit -c SAMPLE_PERIOD, --sample_period SAMPLE_PERIOD Sample one in this many number of cache reference and miss events bpfcc-0.12.0/tools/mdflush.py000077500000000000000000000040151357404205000160260ustar00rootroot00000000000000#!/usr/bin/python # @lint-avoid-python-3-compatibility-imports # # mdflush Trace md flush events. # For Linux, uses BCC, eBPF. # # Todo: add more details of the flush (latency, I/O count). # # Copyright 2016 Netflix, Inc. # Licensed under the Apache License, Version 2.0 (the "License") # # 13-Feb-2015 Brendan Gregg Created this. from __future__ import print_function from bcc import BPF from time import strftime # load BPF program b = BPF(text=""" #include #include #include #include struct data_t { u64 pid; char comm[TASK_COMM_LEN]; char disk[DISK_NAME_LEN]; }; BPF_PERF_OUTPUT(events); int kprobe__md_flush_request(struct pt_regs *ctx, void *mddev, struct bio *bio) { struct data_t data = {}; u32 pid = bpf_get_current_pid_tgid(); data.pid = pid; bpf_get_current_comm(&data.comm, sizeof(data.comm)); /* * The following deals with a kernel version change (in mainline 4.14, although * it may be backported to earlier kernels) with how the disk name is accessed. * We handle both pre- and post-change versions here. Please avoid kernel * version tests like this as much as possible: they inflate the code, test, * and maintenance burden. */ #ifdef bio_dev struct gendisk *bi_disk = bio->bi_disk; #else struct gendisk *bi_disk = bio->bi_bdev->bd_disk; #endif bpf_probe_read(&data.disk, sizeof(data.disk), bi_disk->disk_name); events.perf_submit(ctx, &data, sizeof(data)); return 0; } """) # header print("Tracing md flush requests... Hit Ctrl-C to end.") print("%-8s %-6s %-16s %s" % ("TIME", "PID", "COMM", "DEVICE")) # process event def print_event(cpu, data, size): event = b["events"].event(data) print("%-8s %-6d %-16s %s" % (strftime("%H:%M:%S"), event.pid, event.comm.decode('utf-8', 'replace'), event.disk.decode('utf-8', 'replace'))) # read events b["events"].open_perf_buffer(print_event) while 1: try: b.perf_buffer_poll() except KeyboardInterrupt: exit() bpfcc-0.12.0/tools/mdflush_example.txt000066400000000000000000000033641357404205000177330ustar00rootroot00000000000000Demonstrations of mdflush, the Linux eBPF/bcc version. The mdflush tool traces flushes at the md driver level, and prints details including the time of the flush: # ./mdflush 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. bpfcc-0.12.0/tools/memleak.py000077500000000000000000000443761357404205000160150ustar00rootroot00000000000000#!/usr/bin/python # # memleak Trace and display outstanding allocations to detect # memory leaks in user-mode processes and the kernel. # # USAGE: memleak [-h] [-p PID] [-t] [-a] [-o OLDER] [-c COMMAND] # [--combined-only] [-s SAMPLE_RATE] [-T TOP] [-z MIN_SIZE] # [-Z MAX_SIZE] [-O OBJ] # [interval] [count] # # Licensed under the Apache License, Version 2.0 (the "License") # Copyright (C) 2016 Sasha Goldshtein. from bcc import BPF from time import sleep from datetime import datetime import resource import argparse import subprocess import os import sys class Allocation(object): def __init__(self, stack, size): self.stack = stack self.count = 1 self.size = size def update(self, size): self.count += 1 self.size += size def run_command_get_output(command): p = subprocess.Popen(command.split(), stdout=subprocess.PIPE, stderr=subprocess.STDOUT) return iter(p.stdout.readline, b'') def run_command_get_pid(command): p = subprocess.Popen(command.split()) return p.pid examples = """ EXAMPLES: ./memleak -p $(pidof allocs) Trace allocations and display a summary of "leaked" (outstanding) allocations every 5 seconds ./memleak -p $(pidof allocs) -t Trace allocations and display each individual allocator function call ./memleak -ap $(pidof allocs) 10 Trace allocations and display allocated addresses, sizes, and stacks every 10 seconds for outstanding allocations ./memleak -c "./allocs" Run the specified command and trace its allocations ./memleak Trace allocations in kernel mode and display a summary of outstanding allocations every 5 seconds ./memleak -o 60000 Trace allocations in kernel mode and display a summary of outstanding allocations that are at least one minute (60 seconds) old ./memleak -s 5 Trace roughly every 5th allocation, to reduce overhead """ description = """ Trace outstanding memory allocations that weren't freed. Supports both user-mode allocations made with libc functions and kernel-mode allocations made with kmalloc/kmem_cache_alloc/get_free_pages and corresponding memory release functions. """ parser = argparse.ArgumentParser(description=description, formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("-p", "--pid", type=int, default=-1, help="the PID to trace; if not specified, trace kernel allocs") parser.add_argument("-t", "--trace", action="store_true", help="print trace messages for each alloc/free call") parser.add_argument("interval", nargs="?", default=5, type=int, help="interval in seconds to print outstanding allocations") parser.add_argument("count", nargs="?", type=int, help="number of times to print the report before exiting") parser.add_argument("-a", "--show-allocs", default=False, action="store_true", help="show allocation addresses and sizes as well as call stacks") parser.add_argument("-o", "--older", default=500, type=int, help="prune allocations younger than this age in milliseconds") parser.add_argument("-c", "--command", help="execute and trace the specified command") parser.add_argument("--combined-only", default=False, action="store_true", help="show combined allocation statistics only") parser.add_argument("-s", "--sample-rate", default=1, type=int, help="sample every N-th allocation to decrease the overhead") parser.add_argument("-T", "--top", type=int, default=10, help="display only this many top allocating stacks (by size)") parser.add_argument("-z", "--min-size", type=int, help="capture only allocations larger than this size") parser.add_argument("-Z", "--max-size", type=int, help="capture only allocations smaller than this size") parser.add_argument("-O", "--obj", type=str, default="c", help="attach to allocator functions in the specified object") parser.add_argument("--ebpf", action="store_true", help=argparse.SUPPRESS) parser.add_argument("--percpu", default=False, action="store_true", help="trace percpu allocations") args = parser.parse_args() pid = args.pid command = args.command kernel_trace = (pid == -1 and command is None) trace_all = args.trace interval = args.interval min_age_ns = 1e6 * args.older sample_every_n = args.sample_rate num_prints = args.count top_stacks = args.top min_size = args.min_size max_size = args.max_size obj = args.obj if min_size is not None and max_size is not None and min_size > max_size: print("min_size (-z) can't be greater than max_size (-Z)") exit(1) if command is not None: print("Executing '%s' and tracing the resulting process." % command) pid = run_command_get_pid(command) bpf_source = """ #include struct alloc_info_t { u64 size; u64 timestamp_ns; int stack_id; }; struct combined_alloc_info_t { u64 total_size; u64 number_of_allocs; }; BPF_HASH(sizes, u64); BPF_HASH(allocs, u64, struct alloc_info_t, 1000000); BPF_HASH(memptrs, u64, u64); BPF_STACK_TRACE(stack_traces, 10240); BPF_HASH(combined_allocs, u64, struct combined_alloc_info_t, 10240); static inline void update_statistics_add(u64 stack_id, u64 sz) { struct combined_alloc_info_t *existing_cinfo; struct combined_alloc_info_t cinfo = {0}; existing_cinfo = combined_allocs.lookup(&stack_id); if (existing_cinfo != 0) cinfo = *existing_cinfo; cinfo.total_size += sz; cinfo.number_of_allocs += 1; combined_allocs.update(&stack_id, &cinfo); } static inline void update_statistics_del(u64 stack_id, u64 sz) { struct combined_alloc_info_t *existing_cinfo; struct combined_alloc_info_t cinfo = {0}; existing_cinfo = combined_allocs.lookup(&stack_id); if (existing_cinfo != 0) cinfo = *existing_cinfo; if (sz >= cinfo.total_size) cinfo.total_size = 0; else cinfo.total_size -= sz; if (cinfo.number_of_allocs > 0) cinfo.number_of_allocs -= 1; combined_allocs.update(&stack_id, &cinfo); } static inline int gen_alloc_enter(struct pt_regs *ctx, size_t size) { SIZE_FILTER if (SAMPLE_EVERY_N > 1) { u64 ts = bpf_ktime_get_ns(); if (ts % SAMPLE_EVERY_N != 0) return 0; } u64 pid = bpf_get_current_pid_tgid(); u64 size64 = size; sizes.update(&pid, &size64); if (SHOULD_PRINT) bpf_trace_printk("alloc entered, size = %u\\n", size); return 0; } static inline int gen_alloc_exit2(struct pt_regs *ctx, u64 address) { u64 pid = bpf_get_current_pid_tgid(); u64* size64 = sizes.lookup(&pid); struct alloc_info_t info = {0}; if (size64 == 0) return 0; // missed alloc entry info.size = *size64; sizes.delete(&pid); info.timestamp_ns = bpf_ktime_get_ns(); info.stack_id = stack_traces.get_stackid(ctx, STACK_FLAGS); allocs.update(&address, &info); update_statistics_add(info.stack_id, info.size); if (SHOULD_PRINT) { bpf_trace_printk("alloc exited, size = %lu, result = %lx\\n", info.size, address); } return 0; } static inline int gen_alloc_exit(struct pt_regs *ctx) { return gen_alloc_exit2(ctx, PT_REGS_RC(ctx)); } static inline int gen_free_enter(struct pt_regs *ctx, void *address) { u64 addr = (u64)address; struct alloc_info_t *info = allocs.lookup(&addr); if (info == 0) return 0; allocs.delete(&addr); update_statistics_del(info->stack_id, info->size); if (SHOULD_PRINT) { bpf_trace_printk("free entered, address = %lx, size = %lu\\n", address, info->size); } return 0; } int malloc_enter(struct pt_regs *ctx, size_t size) { return gen_alloc_enter(ctx, size); } int malloc_exit(struct pt_regs *ctx) { return gen_alloc_exit(ctx); } int free_enter(struct pt_regs *ctx, void *address) { return gen_free_enter(ctx, address); } int calloc_enter(struct pt_regs *ctx, size_t nmemb, size_t size) { return gen_alloc_enter(ctx, nmemb * size); } int calloc_exit(struct pt_regs *ctx) { return gen_alloc_exit(ctx); } int realloc_enter(struct pt_regs *ctx, void *ptr, size_t size) { gen_free_enter(ctx, ptr); return gen_alloc_enter(ctx, size); } int realloc_exit(struct pt_regs *ctx) { return gen_alloc_exit(ctx); } int posix_memalign_enter(struct pt_regs *ctx, void **memptr, size_t alignment, size_t size) { u64 memptr64 = (u64)(size_t)memptr; u64 pid = bpf_get_current_pid_tgid(); memptrs.update(&pid, &memptr64); return gen_alloc_enter(ctx, size); } int posix_memalign_exit(struct pt_regs *ctx) { u64 pid = bpf_get_current_pid_tgid(); u64 *memptr64 = memptrs.lookup(&pid); void *addr; if (memptr64 == 0) return 0; memptrs.delete(&pid); if (bpf_probe_read(&addr, sizeof(void*), (void*)(size_t)*memptr64)) return 0; u64 addr64 = (u64)(size_t)addr; return gen_alloc_exit2(ctx, addr64); } int aligned_alloc_enter(struct pt_regs *ctx, size_t alignment, size_t size) { return gen_alloc_enter(ctx, size); } int aligned_alloc_exit(struct pt_regs *ctx) { return gen_alloc_exit(ctx); } int valloc_enter(struct pt_regs *ctx, size_t size) { return gen_alloc_enter(ctx, size); } int valloc_exit(struct pt_regs *ctx) { return gen_alloc_exit(ctx); } int memalign_enter(struct pt_regs *ctx, size_t alignment, size_t size) { return gen_alloc_enter(ctx, size); } int memalign_exit(struct pt_regs *ctx) { return gen_alloc_exit(ctx); } int pvalloc_enter(struct pt_regs *ctx, size_t size) { return gen_alloc_enter(ctx, size); } int pvalloc_exit(struct pt_regs *ctx) { return gen_alloc_exit(ctx); } """ bpf_source_kernel = """ TRACEPOINT_PROBE(kmem, kmalloc) { gen_alloc_enter((struct pt_regs *)args, args->bytes_alloc); return gen_alloc_exit2((struct pt_regs *)args, (size_t)args->ptr); } TRACEPOINT_PROBE(kmem, kmalloc_node) { gen_alloc_enter((struct pt_regs *)args, args->bytes_alloc); return gen_alloc_exit2((struct pt_regs *)args, (size_t)args->ptr); } TRACEPOINT_PROBE(kmem, kfree) { return gen_free_enter((struct pt_regs *)args, (void *)args->ptr); } TRACEPOINT_PROBE(kmem, kmem_cache_alloc) { gen_alloc_enter((struct pt_regs *)args, args->bytes_alloc); return gen_alloc_exit2((struct pt_regs *)args, (size_t)args->ptr); } TRACEPOINT_PROBE(kmem, kmem_cache_alloc_node) { gen_alloc_enter((struct pt_regs *)args, args->bytes_alloc); return gen_alloc_exit2((struct pt_regs *)args, (size_t)args->ptr); } TRACEPOINT_PROBE(kmem, kmem_cache_free) { return gen_free_enter((struct pt_regs *)args, (void *)args->ptr); } TRACEPOINT_PROBE(kmem, mm_page_alloc) { gen_alloc_enter((struct pt_regs *)args, PAGE_SIZE << args->order); return gen_alloc_exit2((struct pt_regs *)args, args->pfn); } TRACEPOINT_PROBE(kmem, mm_page_free) { return gen_free_enter((struct pt_regs *)args, (void *)args->pfn); } """ bpf_source_percpu = """ TRACEPOINT_PROBE(percpu, percpu_alloc_percpu) { gen_alloc_enter((struct pt_regs *)args, args->size); return gen_alloc_exit2((struct pt_regs *)args, (size_t)args->ptr); } TRACEPOINT_PROBE(percpu, percpu_free_percpu) { return gen_free_enter((struct pt_regs *)args, (void *)args->ptr); } """ if kernel_trace: if args.percpu: bpf_source += bpf_source_percpu else: bpf_source += bpf_source_kernel bpf_source = bpf_source.replace("SHOULD_PRINT", "1" if trace_all else "0") bpf_source = bpf_source.replace("SAMPLE_EVERY_N", str(sample_every_n)) bpf_source = bpf_source.replace("PAGE_SIZE", str(resource.getpagesize())) size_filter = "" if min_size is not None and max_size is not None: size_filter = "if (size < %d || size > %d) return 0;" % \ (min_size, max_size) elif min_size is not None: size_filter = "if (size < %d) return 0;" % min_size elif max_size is not None: size_filter = "if (size > %d) return 0;" % max_size bpf_source = bpf_source.replace("SIZE_FILTER", size_filter) stack_flags = "0" if not kernel_trace: stack_flags += "|BPF_F_USER_STACK" bpf_source = bpf_source.replace("STACK_FLAGS", stack_flags) if args.ebpf: print(bpf_source) exit() bpf = BPF(text=bpf_source) if not kernel_trace: print("Attaching to pid %d, Ctrl+C to quit." % pid) def attach_probes(sym, fn_prefix=None, can_fail=False): if fn_prefix is None: fn_prefix = sym try: bpf.attach_uprobe(name=obj, sym=sym, fn_name=fn_prefix + "_enter", pid=pid) bpf.attach_uretprobe(name=obj, sym=sym, fn_name=fn_prefix + "_exit", pid=pid) except Exception: if can_fail: return else: raise attach_probes("malloc") attach_probes("calloc") attach_probes("realloc") attach_probes("posix_memalign") attach_probes("valloc") attach_probes("memalign") attach_probes("pvalloc") attach_probes("aligned_alloc", can_fail=True) # added in C11 bpf.attach_uprobe(name=obj, sym="free", fn_name="free_enter", pid=pid) else: print("Attaching to kernel allocators, Ctrl+C to quit.") # No probe attaching here. Allocations are counted by attaching to # tracepoints. # # Memory allocations in Linux kernel are not limited to malloc/free # equivalents. It's also common to allocate a memory page or multiple # pages. Page allocator have two interfaces, one working with page # frame numbers (PFN), while other working with page addresses. It's # possible to allocate pages with one kind of functions, and free them # with another. Code in kernel can easy convert PFNs to addresses and # back, but it's hard to do the same in eBPF kprobe without fragile # hacks. # # Fortunately, Linux exposes tracepoints for memory allocations, which # can be instrumented by eBPF programs. Tracepoint for page allocations # gives access to PFNs for both allocator interfaces. So there is no # need to guess which allocation corresponds to which free. def print_outstanding(): print("[%s] Top %d stacks with outstanding allocations:" % (datetime.now().strftime("%H:%M:%S"), top_stacks)) alloc_info = {} allocs = bpf["allocs"] stack_traces = bpf["stack_traces"] for address, info in sorted(allocs.items(), key=lambda a: a[1].size): if BPF.monotonic_time() - min_age_ns < info.timestamp_ns: continue if info.stack_id < 0: continue if info.stack_id in alloc_info: alloc_info[info.stack_id].update(info.size) else: stack = list(stack_traces.walk(info.stack_id)) combined = [] for addr in stack: combined.append(bpf.sym(addr, pid, show_module=True, show_offset=True)) alloc_info[info.stack_id] = Allocation(combined, info.size) if args.show_allocs: print("\taddr = %x size = %s" % (address.value, info.size)) to_show = sorted(alloc_info.values(), key=lambda a: a.size)[-top_stacks:] for alloc in to_show: print("\t%d bytes in %d allocations from stack\n\t\t%s" % (alloc.size, alloc.count, b"\n\t\t".join(alloc.stack).decode("ascii"))) def print_outstanding_combined(): stack_traces = bpf["stack_traces"] stacks = sorted(bpf["combined_allocs"].items(), key=lambda a: -a[1].total_size) cnt = 1 entries = [] for stack_id, info in stacks: try: trace = [] for addr in stack_traces.walk(stack_id.value): sym = bpf.sym(addr, pid, show_module=True, show_offset=True) trace.append(sym) trace = "\n\t\t".join(trace) except KeyError: trace = "stack information lost" entry = ("\t%d bytes in %d allocations from stack\n\t\t%s" % (info.total_size, info.number_of_allocs, trace)) entries.append(entry) cnt += 1 if cnt > top_stacks: break print("[%s] Top %d stacks with outstanding allocations:" % (datetime.now().strftime("%H:%M:%S"), top_stacks)) print('\n'.join(reversed(entries))) count_so_far = 0 while True: if trace_all: print(bpf.trace_fields()) else: try: sleep(interval) except KeyboardInterrupt: exit() if args.combined_only: print_outstanding_combined() else: print_outstanding() sys.stdout.flush() count_so_far += 1 if num_prints is not None and count_so_far >= num_prints: exit() bpfcc-0.12.0/tools/memleak_example.txt000066400000000000000000000203321357404205000176760ustar00rootroot00000000000000Demonstrations of memleak. memleak traces and matches memory allocation and deallocation requests, and collects call stacks for each allocation. memleak can then print a summary of which call stacks performed allocations that weren't subsequently freed. For example: # ./memleak -p $(pidof allocs) Attaching to pid 5193, Ctrl+C to quit. [11:16:33] Top 2 stacks with outstanding allocations: 80 bytes in 5 allocations from stack main+0x6d [allocs] __libc_start_main+0xf0 [libc-2.21.so] [11:16:34] Top 2 stacks with outstanding allocations: 160 bytes in 10 allocations from stack main+0x6d [allocs] __libc_start_main+0xf0 [libc-2.21.so] Each entry printed is a set of allocations that originate from the same call stack, and that weren't freed yet. The number of bytes and number of allocs are followed by the call stack, top to bottom, of the allocation site. As time goes on, it becomes apparent that the main function in the allocs process is leaking memory, 16 bytes at a time. Fortunately, you don't have to inspect each allocation individually -- you get a nice summary of which stack is responsible for a large leak. Occasionally, you do want the individual allocation details. Perhaps the same stack is allocating various sizes and you want to confirm which sizes are prevalent. Use the -a switch: # ./memleak -p $(pidof allocs) -a Attaching to pid 5193, Ctrl+C to quit. [11:16:33] Top 2 stacks with outstanding allocations: addr = 948cd0 size = 16 addr = 948d10 size = 16 addr = 948d30 size = 16 addr = 948cf0 size = 16 64 bytes in 4 allocations from stack main+0x6d [allocs] __libc_start_main+0xf0 [libc-2.21.so] [11:16:34] Top 2 stacks with outstanding allocations: addr = 948d50 size = 16 addr = 948cd0 size = 16 addr = 948d10 size = 16 addr = 948d30 size = 16 addr = 948cf0 size = 16 addr = 948dd0 size = 16 addr = 948d90 size = 16 addr = 948db0 size = 16 addr = 948d70 size = 16 addr = 948df0 size = 16 160 bytes in 10 allocations from stack main+0x6d [allocs] __libc_start_main+0xf0 [libc-2.21.so] When using the -p switch, memleak traces the libc allocations of a particular process. Without this switch, kernel allocations are traced instead. For example: # ./memleak Attaching to kernel allocators, Ctrl+C to quit. ... 248 bytes in 4 allocations from stack bpf_prog_load [kernel] sys_bpf [kernel] 328 bytes in 1 allocations from stack perf_mmap [kernel] mmap_region [kernel] do_mmap [kernel] vm_mmap_pgoff [kernel] sys_mmap_pgoff [kernel] sys_mmap [kernel] 464 bytes in 1 allocations from stack traceprobe_command [kernel] traceprobe_probes_write [kernel] probes_write [kernel] __vfs_write [kernel] vfs_write [kernel] sys_write [kernel] entry_SYSCALL_64_fastpath [kernel] 8192 bytes in 1 allocations from stack alloc_and_copy_ftrace_hash.constprop.59 [kernel] ftrace_set_hash [kernel] ftrace_set_filter_ip [kernel] arm_kprobe [kernel] enable_kprobe [kernel] kprobe_register [kernel] perf_trace_init [kernel] perf_tp_event_init [kernel] Here you can see that arming the kprobe to which our eBPF program is attached consumed 8KB of memory. Loading the BPF program also consumed a couple hundred bytes (in bpf_prog_load). memleak stores each allocated block along with its size, timestamp, and the stack that allocated it. When the block is deleted, this information is freed to reduce the memory overhead. To avoid false positives, allocations younger than a certain age (500ms by default) are not printed. To change this threshold, use the -o switch. By default, memleak prints its output every 5 seconds. To change this interval, pass the interval as a positional parameter to memleak. You can also control the number of times the output will be printed before exiting. For example: # ./memleak 1 10 ... will print the outstanding allocation statistics every second, for ten times, and then exit. memleak may introduce considerable overhead if your application or kernel is allocating and freeing memory at a very high rate. In that case, you can control the overhead by sampling every N-th allocation. For example, to sample roughly 10% of the allocations and print the outstanding allocations every 5 seconds, 3 times before quitting: # ./memleak -p $(pidof allocs) -s 10 5 3 Attaching to pid 2614, Ctrl+C to quit. [11:16:33] Top 2 stacks with outstanding allocations: 16 bytes in 1 allocations from stack main+0x6d [allocs] __libc_start_main+0xf0 [libc-2.21.so] [11:16:38] Top 2 stacks with outstanding allocations: 16 bytes in 1 allocations from stack main+0x6d [allocs] __libc_start_main+0xf0 [libc-2.21.so] [11:16:43] Top 2 stacks with outstanding allocations: 32 bytes in 2 allocations from stack main+0x6d [allocs] __libc_start_main+0xf0 [libc-2.21.so] Note that even though the application leaks 16 bytes of memory every second, the report (printed every 5 seconds) doesn't "see" all the allocations because of the sampling rate applied. USAGE message: # ./memleak -h usage: memleak.py [-h] [-p PID] [-t] [-a] [-o OLDER] [-c COMMAND] [--combined-only] [-s SAMPLE_RATE] [-T TOP] [-z MIN_SIZE] [-Z MAX_SIZE] [-O OBJ] [interval] [count] Trace outstanding memory allocations that weren't freed. Supports both user-mode allocations made with libc functions and kernel-mode allocations made with kmalloc/kmem_cache_alloc/get_free_pages and corresponding memory release functions. positional arguments: interval interval in seconds to print outstanding allocations count number of times to print the report before exiting optional arguments: -h, --help show this help message and exit -p PID, --pid PID the PID to trace; if not specified, trace kernel allocs -t, --trace print trace messages for each alloc/free call -a, --show-allocs show allocation addresses and sizes as well as call stacks -o OLDER, --older OLDER prune allocations younger than this age in milliseconds -c COMMAND, --command COMMAND execute and trace the specified command --combined-only show combined allocation statistics only -s SAMPLE_RATE, --sample-rate SAMPLE_RATE sample every N-th allocation to decrease the overhead -T TOP, --top TOP display only this many top allocating stacks (by size) -z MIN_SIZE, --min-size MIN_SIZE capture only allocations larger than this size -Z MAX_SIZE, --max-size MAX_SIZE capture only allocations smaller than this size -O OBJ, --obj OBJ attach to allocator functions in the specified object EXAMPLES: ./memleak -p $(pidof allocs) Trace allocations and display a summary of "leaked" (outstanding) allocations every 5 seconds ./memleak -p $(pidof allocs) -t Trace allocations and display each individual allocator function call ./memleak -ap $(pidof allocs) 10 Trace allocations and display allocated addresses, sizes, and stacks every 10 seconds for outstanding allocations ./memleak -c "./allocs" Run the specified command and trace its allocations ./memleak Trace allocations in kernel mode and display a summary of outstanding allocations every 5 seconds ./memleak -o 60000 Trace allocations in kernel mode and display a summary of outstanding allocations that are at least one minute (60 seconds) old ./memleak -s 5 Trace roughly every 5th allocation, to reduce overhead bpfcc-0.12.0/tools/mountsnoop.py000077500000000000000000000304541357404205000166130ustar00rootroot00000000000000#!/usr/bin/python # # mountsnoop Trace mount() and umount syscalls. # For Linux, uses BCC, eBPF. Embedded C. # # USAGE: mountsnoop [-h] # # Copyright (c) 2016 Facebook, Inc. # Licensed under the Apache License, Version 2.0 (the "License") # # 14-Oct-2016 Omar Sandoval Created this. from __future__ import print_function import argparse import bcc import ctypes import errno import functools import sys bpf_text = r""" #include #include #include #include /* * XXX: struct mnt_namespace is defined in fs/mount.h, which is private to the * VFS and not installed in any kernel-devel packages. So, let's duplicate the * important part of the definition. There are actually more members in the * real struct, but we don't need them, and they're more likely to change. */ struct mnt_namespace { atomic_t count; struct ns_common ns; }; /* * XXX: this could really use first-class string support in BPF. target is a * NUL-terminated path up to PATH_MAX in length. source and type are * NUL-terminated strings up to PAGE_SIZE in length. data is a weird case: it's * almost always a NUL-terminated string, but for some filesystems (e.g., older * NFS variants), it's a binary structure with plenty of NUL bytes, so the * kernel always copies up to PAGE_SIZE bytes, stopping when it hits a fault. * * The best we can do with the existing BPF helpers is to copy as much of each * argument as we can. Our stack space is limited, and we need to leave some * headroom for the rest of the function, so this should be a decent value. */ #define MAX_STR_LEN 412 enum event_type { EVENT_MOUNT, EVENT_MOUNT_SOURCE, EVENT_MOUNT_TARGET, EVENT_MOUNT_TYPE, EVENT_MOUNT_DATA, EVENT_MOUNT_RET, EVENT_UMOUNT, EVENT_UMOUNT_TARGET, EVENT_UMOUNT_RET, }; struct data_t { enum event_type type; pid_t pid, tgid; union { /* EVENT_MOUNT, EVENT_UMOUNT */ struct { /* current->nsproxy->mnt_ns->ns.inum */ unsigned int mnt_ns; char comm[TASK_COMM_LEN]; unsigned long flags; } enter; /* * EVENT_MOUNT_SOURCE, EVENT_MOUNT_TARGET, EVENT_MOUNT_TYPE, * EVENT_MOUNT_DATA, EVENT_UMOUNT_TARGET */ char str[MAX_STR_LEN]; /* EVENT_MOUNT_RET, EVENT_UMOUNT_RET */ int retval; }; }; BPF_PERF_OUTPUT(events); int syscall__mount(struct pt_regs *ctx, char __user *source, char __user *target, char __user *type, unsigned long flags, char __user *data) { struct data_t event = {}; struct task_struct *task; struct nsproxy *nsproxy; struct mnt_namespace *mnt_ns; event.pid = bpf_get_current_pid_tgid() & 0xffffffff; event.tgid = bpf_get_current_pid_tgid() >> 32; event.type = EVENT_MOUNT; bpf_get_current_comm(event.enter.comm, sizeof(event.enter.comm)); event.enter.flags = flags; task = (struct task_struct *)bpf_get_current_task(); nsproxy = task->nsproxy; mnt_ns = nsproxy->mnt_ns; event.enter.mnt_ns = mnt_ns->ns.inum; events.perf_submit(ctx, &event, sizeof(event)); event.type = EVENT_MOUNT_SOURCE; __builtin_memset(event.str, 0, sizeof(event.str)); bpf_probe_read(event.str, sizeof(event.str), source); events.perf_submit(ctx, &event, sizeof(event)); event.type = EVENT_MOUNT_TARGET; __builtin_memset(event.str, 0, sizeof(event.str)); bpf_probe_read(event.str, sizeof(event.str), target); events.perf_submit(ctx, &event, sizeof(event)); event.type = EVENT_MOUNT_TYPE; __builtin_memset(event.str, 0, sizeof(event.str)); bpf_probe_read(event.str, sizeof(event.str), type); events.perf_submit(ctx, &event, sizeof(event)); event.type = EVENT_MOUNT_DATA; __builtin_memset(event.str, 0, sizeof(event.str)); bpf_probe_read(event.str, sizeof(event.str), data); events.perf_submit(ctx, &event, sizeof(event)); return 0; } int do_ret_sys_mount(struct pt_regs *ctx) { struct data_t event = {}; event.type = EVENT_MOUNT_RET; event.pid = bpf_get_current_pid_tgid() & 0xffffffff; event.tgid = bpf_get_current_pid_tgid() >> 32; event.retval = PT_REGS_RC(ctx); events.perf_submit(ctx, &event, sizeof(event)); return 0; } int syscall__umount(struct pt_regs *ctx, char __user *target, int flags) { struct data_t event = {}; struct task_struct *task; struct nsproxy *nsproxy; struct mnt_namespace *mnt_ns; event.pid = bpf_get_current_pid_tgid() & 0xffffffff; event.tgid = bpf_get_current_pid_tgid() >> 32; event.type = EVENT_UMOUNT; bpf_get_current_comm(event.enter.comm, sizeof(event.enter.comm)); event.enter.flags = flags; task = (struct task_struct *)bpf_get_current_task(); nsproxy = task->nsproxy; mnt_ns = nsproxy->mnt_ns; event.enter.mnt_ns = mnt_ns->ns.inum; events.perf_submit(ctx, &event, sizeof(event)); event.type = EVENT_UMOUNT_TARGET; __builtin_memset(event.str, 0, sizeof(event.str)); bpf_probe_read(event.str, sizeof(event.str), target); events.perf_submit(ctx, &event, sizeof(event)); return 0; } int do_ret_sys_umount(struct pt_regs *ctx) { struct data_t event = {}; event.type = EVENT_UMOUNT_RET; event.pid = bpf_get_current_pid_tgid() & 0xffffffff; event.tgid = bpf_get_current_pid_tgid() >> 32; event.retval = PT_REGS_RC(ctx); events.perf_submit(ctx, &event, sizeof(event)); return 0; } """ # sys/mount.h MS_MGC_VAL = 0xc0ed0000 MS_MGC_MSK = 0xffff0000 MOUNT_FLAGS = [ ('MS_RDONLY', 1), ('MS_NOSUID', 2), ('MS_NODEV', 4), ('MS_NOEXEC', 8), ('MS_SYNCHRONOUS', 16), ('MS_REMOUNT', 32), ('MS_MANDLOCK', 64), ('MS_DIRSYNC', 128), ('MS_NOATIME', 1024), ('MS_NODIRATIME', 2048), ('MS_BIND', 4096), ('MS_MOVE', 8192), ('MS_REC', 16384), ('MS_SILENT', 32768), ('MS_POSIXACL', 1 << 16), ('MS_UNBINDABLE', 1 << 17), ('MS_PRIVATE', 1 << 18), ('MS_SLAVE', 1 << 19), ('MS_SHARED', 1 << 20), ('MS_RELATIME', 1 << 21), ('MS_KERNMOUNT', 1 << 22), ('MS_I_VERSION', 1 << 23), ('MS_STRICTATIME', 1 << 24), ('MS_LAZYTIME', 1 << 25), ('MS_ACTIVE', 1 << 30), ('MS_NOUSER', 1 << 31), ] UMOUNT_FLAGS = [ ('MNT_FORCE', 1), ('MNT_DETACH', 2), ('MNT_EXPIRE', 4), ('UMOUNT_NOFOLLOW', 8), ] TASK_COMM_LEN = 16 # linux/sched.h MAX_STR_LEN = 412 class EventType(object): EVENT_MOUNT = 0 EVENT_MOUNT_SOURCE = 1 EVENT_MOUNT_TARGET = 2 EVENT_MOUNT_TYPE = 3 EVENT_MOUNT_DATA = 4 EVENT_MOUNT_RET = 5 EVENT_UMOUNT = 6 EVENT_UMOUNT_TARGET = 7 EVENT_UMOUNT_RET = 8 class EnterData(ctypes.Structure): _fields_ = [ ('mnt_ns', ctypes.c_uint), ('comm', ctypes.c_char * TASK_COMM_LEN), ('flags', ctypes.c_ulong), ] class DataUnion(ctypes.Union): _fields_ = [ ('enter', EnterData), ('str', ctypes.c_char * MAX_STR_LEN), ('retval', ctypes.c_int), ] class Event(ctypes.Structure): _fields_ = [ ('type', ctypes.c_uint), ('pid', ctypes.c_uint), ('tgid', ctypes.c_uint), ('union', DataUnion), ] def _decode_flags(flags, flag_list): str_flags = [] for flag, bit in flag_list: if flags & bit: str_flags.append(flag) flags &= ~bit if flags or not str_flags: str_flags.append('0x{:x}'.format(flags)) return str_flags def decode_flags(flags, flag_list): return '|'.join(_decode_flags(flags, flag_list)) def decode_mount_flags(flags): str_flags = [] if flags & MS_MGC_MSK == MS_MGC_VAL: flags &= ~MS_MGC_MSK str_flags.append('MS_MGC_VAL') str_flags.extend(_decode_flags(flags, MOUNT_FLAGS)) return '|'.join(str_flags) def decode_umount_flags(flags): return decode_flags(flags, UMOUNT_FLAGS) def decode_errno(retval): try: return '-' + errno.errorcode[-retval] except KeyError: return str(retval) _escape_chars = { ord('\a'): '\\a', ord('\b'): '\\b', ord('\t'): '\\t', ord('\n'): '\\n', ord('\v'): '\\v', ord('\f'): '\\f', ord('\r'): '\\r', ord('"'): '\\"', ord('\\'): '\\\\', } def escape_character(c): try: return _escape_chars[c] except KeyError: if 0x20 <= c <= 0x7e: return chr(c) else: return '\\x{:02x}'.format(c) if sys.version_info.major < 3: def decode_mount_string(s): return '"{}"'.format(''.join(escape_character(ord(c)) for c in s)) else: def decode_mount_string(s): return '"{}"'.format(''.join(escape_character(c) for c in s)) def print_event(mounts, umounts, cpu, data, size): event = ctypes.cast(data, ctypes.POINTER(Event)).contents try: if event.type == EventType.EVENT_MOUNT: mounts[event.pid] = { 'pid': event.pid, 'tgid': event.tgid, 'mnt_ns': event.union.enter.mnt_ns, 'comm': event.union.enter.comm, 'flags': event.union.enter.flags, } elif event.type == EventType.EVENT_MOUNT_SOURCE: mounts[event.pid]['source'] = event.union.str elif event.type == EventType.EVENT_MOUNT_TARGET: mounts[event.pid]['target'] = event.union.str elif event.type == EventType.EVENT_MOUNT_TYPE: mounts[event.pid]['type'] = event.union.str elif event.type == EventType.EVENT_MOUNT_DATA: # XXX: data is not always a NUL-terminated string mounts[event.pid]['data'] = event.union.str elif event.type == EventType.EVENT_UMOUNT: umounts[event.pid] = { 'pid': event.pid, 'tgid': event.tgid, 'mnt_ns': event.union.enter.mnt_ns, 'comm': event.union.enter.comm, 'flags': event.union.enter.flags, } elif event.type == EventType.EVENT_UMOUNT_TARGET: umounts[event.pid]['target'] = event.union.str elif (event.type == EventType.EVENT_MOUNT_RET or event.type == EventType.EVENT_UMOUNT_RET): if event.type == EventType.EVENT_MOUNT_RET: syscall = mounts.pop(event.pid) call = ('mount({source}, {target}, {type}, {flags}, {data}) ' + '= {retval}').format( source=decode_mount_string(syscall['source']), target=decode_mount_string(syscall['target']), type=decode_mount_string(syscall['type']), flags=decode_mount_flags(syscall['flags']), data=decode_mount_string(syscall['data']), retval=decode_errno(event.union.retval)) else: syscall = umounts.pop(event.pid) call = 'umount({target}, {flags}) = {retval}'.format( target=decode_mount_string(syscall['target']), flags=decode_umount_flags(syscall['flags']), retval=decode_errno(event.union.retval)) print('{:16} {:<7} {:<7} {:<11} {}'.format( syscall['comm'].decode('utf-8', 'replace'), syscall['tgid'], syscall['pid'], syscall['mnt_ns'], call)) except KeyError: # This might happen if we lost an event. pass def main(): parser = argparse.ArgumentParser( description='trace mount() and umount() syscalls' ) parser.add_argument("--ebpf", action="store_true", help=argparse.SUPPRESS) args = parser.parse_args() mounts = {} umounts = {} if args.ebpf: print(bpf_text) exit() b = bcc.BPF(text=bpf_text) mount_fnname = b.get_syscall_fnname("mount") b.attach_kprobe(event=mount_fnname, fn_name="syscall__mount") b.attach_kretprobe(event=mount_fnname, fn_name="do_ret_sys_mount") umount_fnname = b.get_syscall_fnname("umount") b.attach_kprobe(event=umount_fnname, fn_name="syscall__umount") b.attach_kretprobe(event=umount_fnname, fn_name="do_ret_sys_umount") b['events'].open_perf_buffer( functools.partial(print_event, mounts, umounts)) print('{:16} {:<7} {:<7} {:<11} {}'.format( 'COMM', 'PID', 'TID', 'MNT_NS', 'CALL')) while True: try: b.perf_buffer_poll() except KeyboardInterrupt: exit() if __name__ == '__main__': main() bpfcc-0.12.0/tools/mountsnoop_example.txt000066400000000000000000000022121357404205000205010ustar00rootroot00000000000000Demonstrations of mountsnoop. mountsnoop traces the mount() and umount syscalls system-wide. For example, running the following series of commands produces this output: # mount --bind /mnt /mnt # umount /mnt # unshare -m # mount --bind /mnt /mnt # umount /mnt # ./mountsnoop.py COMM PID TID MNT_NS CALL mount 710 710 4026531840 mount("/mnt", "/mnt", "", MS_MGC_VAL|MS_BIND, "") = 0 umount 714 714 4026531840 umount("/mnt", 0x0) = 0 unshare 717 717 4026532160 mount("none", "/", "", MS_REC|MS_PRIVATE, "") = 0 mount 725 725 4026532160 mount("/mnt", "/mnt", "", MS_MGC_VAL|MS_BIND, "") = 0 umount 728 728 4026532160 umount("/mnt", 0x0) = 0 The output shows the calling command, its process ID and thread ID, the mount namespace the call was made in, and the call itself. The mount namespace number is an inode number that uniquely identifies the namespace in the running system. This can also be obtained from readlink /proc/$PID/ns/mnt. Note that because of restrictions in BPF, the string arguments to either syscall may be truncated. bpfcc-0.12.0/tools/mysqld_qslower.py000077500000000000000000000057561357404205000174660ustar00rootroot00000000000000#!/usr/bin/python # # mysqld_qslower MySQL server queries slower than a threshold. # For Linux, uses BCC, BPF. Embedded C. # # USAGE: mysqld_qslower PID [min_ms] # # By default, a threshold of 1.0 ms is used. Set this to 0 ms to trace all # queries (verbose). # # This uses USDT probes, and needs a MySQL server with -DENABLE_DTRACE=1. # # Copyright 2016 Netflix, Inc. # Licensed under the Apache License, Version 2.0 (the "License") # # 30-Jul-2016 Brendan Gregg Created this. from __future__ import print_function from bcc import BPF, USDT import sys # arguments def usage(): print("USAGE: mysqld_qslower PID [min_ms]") exit() if len(sys.argv) < 2: usage() if sys.argv[1][0:1] == "-": usage() pid = int(sys.argv[1]) min_ns = 1 * 1000000 min_ms_text = 1 if len(sys.argv) == 3: min_ns = float(sys.argv[2]) * 1000000 min_ms_text = sys.argv[2] debug = 0 QUERY_MAX = 128 # load BPF program bpf_text = """ #include #define QUERY_MAX """ + str(QUERY_MAX) + """ struct start_t { u64 ts; char *query; }; struct data_t { u64 pid; u64 ts; u64 delta; char query[QUERY_MAX]; }; BPF_HASH(start_tmp, u32, struct start_t); BPF_PERF_OUTPUT(events); int do_start(struct pt_regs *ctx) { u32 pid = bpf_get_current_pid_tgid(); struct start_t start = {}; start.ts = bpf_ktime_get_ns(); bpf_usdt_readarg(1, ctx, &start.query); start_tmp.update(&pid, &start); return 0; }; int do_done(struct pt_regs *ctx) { u32 pid = bpf_get_current_pid_tgid(); struct start_t *sp; sp = start_tmp.lookup(&pid); if (sp == 0) { // missed tracing start return 0; } // check if query exceeded our threshold u64 delta = bpf_ktime_get_ns() - sp->ts; if (delta >= """ + str(min_ns) + """) { // populate and emit data struct struct data_t data = {.pid = pid, .ts = sp->ts, .delta = delta}; bpf_probe_read(&data.query, sizeof(data.query), (void *)sp->query); events.perf_submit(ctx, &data, sizeof(data)); } start_tmp.delete(&pid); return 0; }; """ # enable USDT probe from given PID u = USDT(pid=pid) u.enable_probe(probe="query__start", fn_name="do_start") u.enable_probe(probe="query__done", fn_name="do_done") if debug: print(u.get_text()) print(bpf_text) # initialize BPF b = BPF(text=bpf_text, usdt_contexts=[u]) # header print("Tracing MySQL server queries for PID %d slower than %s ms..." % (pid, min_ms_text)) print("%-14s %-6s %8s %s" % ("TIME(s)", "PID", "MS", "QUERY")) # process event start = 0 def print_event(cpu, data, size): global start event = b["events"].event(data) if start == 0: start = event.ts print("%-14.6f %-6d %8.3f %s" % (float(event.ts - start) / 1000000000, event.pid, float(event.delta) / 1000000, event.query)) # loop with callback to print_event b["events"].open_perf_buffer(print_event, page_cnt=64) while 1: try: b.perf_buffer_poll() except KeyboardInterrupt: exit() bpfcc-0.12.0/tools/mysqld_qslower_example.txt000066400000000000000000000044571357404205000213620ustar00rootroot00000000000000Demonstrations of mysqld_qslower, the Linux eBPF/bcc version. mysqld_qslower traces queries served by a MySQL server, and prints those that exceed a latency (query time) threshold. By default a threshold of 1 ms is used. For example: # ./mysqld_qslower.py `pgrep -n mysqld` Tracing MySQL server queries for PID 14371 slower than 1 ms... TIME(s) PID MS QUERY 0.000000 18608 130.751 SELECT * FROM words WHERE word REGEXP '^bre.*n$' 2.921535 18608 130.590 SELECT * FROM words WHERE word REGEXP '^alex.*$' 4.603549 18608 24.164 SELECT COUNT(*) FROM words 9.733847 18608 130.936 SELECT count(*) AS count FROM words WHERE word REGEXP '^bre.*n$' 17.864776 18608 130.298 SELECT * FROM words WHERE word REGEXP '^bre.*n$' ORDER BY word This traced 5 queries, 4 of which took about 130 milliseconds. A pgrep command was used to specify the PID of mysqld. In this example, a lower threshold is used of 0.1 ms: # ./mysqld_qslower.py `pgrep -n mysqld` 0.1 Tracing MySQL server queries for PID 14371 slower than 0.1 ms... TIME(s) PID MS QUERY 0.000000 18608 24.201 SELECT COUNT(*) FROM words 13.242390 18608 130.378 SELECT * FROM words WHERE word REGEXP '^bre.*n$' 23.601751 18608 119.198 SELECT * FROM words WHERE word REGEXP '^zzzzzzzz$' It worked, but I'm not catching any faster queries in this example. Notice I added a query that searched for "zzzzzzzz": it returned an empty set, and ran 11 ms faster. A 0 ms threshold can be specified to trace all queries: # ./mysqld_qslower.py `pgrep -n mysqld` 0 Tracing MySQL server queries for PID 14371 slower than 0 ms... TIME(s) PID MS QUERY 0.000000 18608 0.105 select @@version_comment limit 1 2.049312 18608 0.099 SELECT DATABASE() 2.050666 18608 0.274 show databases 2.051040 18608 0.176 show tables 5.730044 18608 130.365 SELECT count(*) AS count FROM words WHERE word REGEXP '^bre.*n$' 9.273837 18608 0.096 select 1 9.553742 18608 0.059 select 1 9.986087 18608 0.080 select 1 This includes an initialization of a mysql client command, and selecting the database. I also added some "select 1;" queries, which do no work and return quickly. USAGE: # ./mysqld_qslower.py -h USAGE: mysqld_latency PID [min_ms] bpfcc-0.12.0/tools/nfsdist.py000077500000000000000000000111661357404205000160430ustar00rootroot00000000000000#!/usr/bin/python # @lint-avoid-python-3-compatibility-imports # # nfsdist Summarize NFS operation latency # for Linux, uses BCC and eBPF # # USAGE: nfsdist [-h] [-T] [-m] [-p PID] [interval] [count] # # 4-Sep-2017 Samuel Nair created this from __future__ import print_function from bcc import BPF from time import sleep, strftime import argparse # arguments examples = """examples: ./nfsdist # show operation latency as a histogram ./nfsdist -p 181 # trace PID 181 only ./nfsdist 1 10 # print 1 second summaries, 10 times ./nfsdist -m 5 # 5s summaries, milliseconds """ parser = argparse.ArgumentParser( description="Summarize NFS operation latency", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("-T", "--notimestamp", action="store_true", help="don't include timestamp on interval output") parser.add_argument("-m", "--milliseconds", action="store_true", help="output in milliseconds") parser.add_argument("-p", "--pid", help="trace this PID only") parser.add_argument("interval", nargs="?", help="output interval, in seconds") parser.add_argument("count", nargs="?", default=99999999, help="number of outputs") parser.add_argument("--ebpf", action="store_true", help=argparse.SUPPRESS) args = parser.parse_args() pid = args.pid countdown = int(args.count) if args.milliseconds: factor = 1000000 label = "msecs" else: factor = 1000 label = "usecs" if args.interval and int(args.interval) == 0: print("ERROR: interval 0. Exiting.") exit() debug = 0 # define BPF program bpf_text = """ #include #include #include #define OP_NAME_LEN 8 typedef struct dist_key { char op[OP_NAME_LEN]; u64 slot; } dist_key_t; BPF_HASH(start, u32); BPF_HISTOGRAM(dist, dist_key_t); // time operation int trace_entry(struct pt_regs *ctx) { u32 pid = bpf_get_current_pid_tgid(); if (FILTER_PID) return 0; u64 ts = bpf_ktime_get_ns(); start.update(&pid, &ts); return 0; } static int trace_return(struct pt_regs *ctx, const char *op) { u64 *tsp; u32 pid = bpf_get_current_pid_tgid(); // fetch timestamp and calculate delta tsp = start.lookup(&pid); if (tsp == 0) { return 0; // missed start or filtered } u64 delta = (bpf_ktime_get_ns() - *tsp) / FACTOR; // store as histogram dist_key_t key = {.slot = bpf_log2l(delta)}; __builtin_memcpy(&key.op, op, sizeof(key.op)); dist.increment(key); start.delete(&pid); return 0; } int trace_read_return(struct pt_regs *ctx) { char *op = "read"; return trace_return(ctx, op); } int trace_write_return(struct pt_regs *ctx) { char *op = "write"; return trace_return(ctx, op); } int trace_open_return(struct pt_regs *ctx) { char *op = "open"; return trace_return(ctx, op); } int trace_getattr_return(struct pt_regs *ctx) { char *op = "getattr"; return trace_return(ctx, op); } """ bpf_text = bpf_text.replace('FACTOR', str(factor)) if args.pid: bpf_text = bpf_text.replace('FILTER_PID', 'pid != %s' % pid) else: bpf_text = bpf_text.replace('FILTER_PID', '0') if debug or args.ebpf: print(bpf_text) if args.ebpf: exit() # load BPF program b = BPF(text=bpf_text) # common file functions b.attach_kprobe(event="nfs_file_read", fn_name="trace_entry") b.attach_kprobe(event="nfs_file_write", fn_name="trace_entry") b.attach_kprobe(event="nfs4_file_open", fn_name="trace_entry") b.attach_kprobe(event="nfs_file_open", fn_name="trace_entry") b.attach_kprobe(event="nfs_getattr", fn_name="trace_entry") b.attach_kretprobe(event="nfs_file_read", fn_name="trace_read_return") b.attach_kretprobe(event="nfs_file_write", fn_name="trace_write_return") b.attach_kretprobe(event="nfs4_file_open", fn_name="trace_open_return") b.attach_kretprobe(event="nfs_file_open", fn_name="trace_open_return") b.attach_kretprobe(event="nfs_getattr", fn_name="trace_getattr_return") print("Tracing NFS operation latency... Hit Ctrl-C to end.") # output exiting = 0 dist = b.get_table("dist") while (1): try: if args.interval: sleep(int(args.interval)) else: sleep(99999999) except KeyboardInterrupt: exiting = 1 print() if args.interval and (not args.notimestamp): print(strftime("%H:%M:%S:")) dist.print_log2_hist(label, "operation", section_print_fn=bytes.decode) dist.clear() countdown -= 1 if exiting or countdown == 0: exit() bpfcc-0.12.0/tools/nfsdist_example.txt000066400000000000000000000204741357404205000177440ustar00rootroot00000000000000Demonstrations of nfsdist, the Linux eBPF/bcc version. nfsdist traces NFS reads, writes, opens, and getattr, and summarizes their latency as a power-of-2 histogram. For example: ./nfsdist.py Tracing NFS operation latency... Hit Ctrl-C to end. operation = read usecs : count distribution 0 -> 1 : 4 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 7107 |************** | 16 -> 31 : 19864 |****************************************| 32 -> 63 : 1494 |*** | 64 -> 127 : 491 | | 128 -> 255 : 1810 |*** | 256 -> 511 : 6356 |************ | 512 -> 1023 : 4860 |********* | 1024 -> 2047 : 3070 |****** | 2048 -> 4095 : 1853 |*** | 4096 -> 8191 : 921 |* | 8192 -> 16383 : 122 | | 16384 -> 32767 : 15 | | 32768 -> 65535 : 5 | | 65536 -> 131071 : 2 | | 131072 -> 262143 : 1 | | operation = write usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 1 | | 16 -> 31 : 0 | | 32 -> 63 : 9 | | 64 -> 127 : 19491 |****************************************| 128 -> 255 : 3064 |****** | 256 -> 511 : 940 |* | 512 -> 1023 : 365 | | 1024 -> 2047 : 312 | | 2048 -> 4095 : 119 | | 4096 -> 8191 : 31 | | 8192 -> 16383 : 84 | | 16384 -> 32767 : 31 | | 32768 -> 65535 : 5 | | 65536 -> 131071 : 3 | | 131072 -> 262143 : 0 | | 262144 -> 524287 : 1 | | operation = getattr usecs : count distribution 0 -> 1 : 27 |****************************************| 2 -> 3 : 2 |** | 4 -> 7 : 3 |**** | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 0 | | 128 -> 255 : 0 | | 256 -> 511 : 2 |** | 512 -> 1023 : 2 |** | operation = open usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 0 | | 128 -> 255 : 0 | | 256 -> 511 : 2 |****************************************| In this example you can see that the read traffic is rather bi-modal, with about 26K reads falling within 8 - 30 usecs and about 18K reads spread between 128 - 8191 usecs. Write traffic is largely clustered in the 64 - 127 usecs bracket. The faster read traffic is probably coming from a filesystem cache and the slower traffic from disk. The reason why the writes are so consistently fast is because this example test was run on a couple of VM's and I believe the hypervisor was caching all the write traffic to memory. 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: RPC latency, network latency, file system CPU cycles, file system locks, run queue latency, etc. This is a better measure of the latency suffered by applications reading from a NFS share and can better expose problems experienced by NFS clients. Note that this only traces the common NFS operations (read, write, open and getattr). I chose to include getattr as a significant percentage of NFS traffic end up being getattr calls and are a good indicator of problems with an NFS server. An optional interval and a count can be provided, as well as -m to show the distributions in milliseconds. For example: ./nfsdist -m 1 5 Tracing NFS operation latency... Hit Ctrl-C to end. 11:02:39: operation = write msecs : count distribution 0 -> 1 : 1 | | 2 -> 3 : 24 |******** | 4 -> 7 : 114 |****************************************| 8 -> 15 : 9 |*** | 16 -> 31 : 1 | | 32 -> 63 : 1 | | 11:02:40: operation = write msecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 11 |*** | 4 -> 7 : 111 |****************************************| 8 -> 15 : 13 |**** | 16 -> 31 : 1 | | 11:02:41: operation = write msecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 21 |****** | 4 -> 7 : 137 |****************************************| 8 -> 15 : 3 | | This shows a write workload, with writes hovering primarily in the 4-7ms range. USAGE message: ./nfsdist -h usage: nfsdist.py [-h] [-T] [-m] [-p PID] [interval] [count] Summarize NFS 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: ./nfsdist # show operation latency as a histogram ./nfsdist -p 181 # trace PID 181 only ./nfsdist 1 10 # print 1 second summaries, 10 times ./nfsdist -m 5 # 5s summaries, milliseconds bpfcc-0.12.0/tools/nfsslower.py000077500000000000000000000214611357404205000164120ustar00rootroot00000000000000#!/usr/bin/python # @lint-avoid-python-3-compatibility-imports # # nfsslower Trace slow NFS operations # for Linux using BCC & eBPF # # Usage: nfsslower [-h] [-p PID] [min_ms] # # This script traces some common NFS operations: read, write, opens and # getattr. It measures the time spent in these operations, and prints details # for each that exceeded a threshold. # # WARNING: This adds low-overhead instrumentation to these NFS 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 (even if it prints no "slower" events) can # begin to become significant. # # Most of this code is copied from similar tools (ext4slower, zfsslower etc) # # By default, a minimum millisecond threshold of 10 is used. # # This tool uses kprobes to instrument the kernel for entry and exit # information, in the future a preferred way would be to use tracepoints. # Currently there are'nt any tracepoints available for nfs_read_file, # nfs_write_file and nfs_open_file, nfs_getattr does have entry and exit # tracepoints but we chose to use kprobes for consistency # # 31-Aug-2017 Samuel Nair created this. Should work with NFSv{3,4} from __future__ import print_function from bcc import BPF import argparse from time import strftime examples = """ ./nfsslower # trace operations slower than 10ms ./nfsslower 1 # trace operations slower than 1ms ./nfsslower -j 1 # ... 1 ms, parsable output (csv) ./nfsslower 0 # trace all nfs operations ./nfsslower -p 121 # trace pid 121 only """ parser = argparse.ArgumentParser( description="""Trace READ, WRITE, OPEN \ and GETATTR NFS calls slower than a threshold,\ supports NFSv{3,4}""", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("-j", "--csv", action="store_true", help="just print fields: comma-separated values") parser.add_argument("-p", "--pid", help="Trace this pid only") parser.add_argument("min_ms", nargs="?", default='10', help="Minimum IO duration to trace in ms (default=10ms)") parser.add_argument("--ebpf", action="store_true", help=argparse.SUPPRESS) args = parser.parse_args() min_ms = int(args.min_ms) pid = args.pid csv = args.csv debug = 0 bpf_text = """ #include #include #include #include #define TRACE_READ 0 #define TRACE_WRITE 1 #define TRACE_OPEN 2 #define TRACE_GETATTR 3 struct val_t { u64 ts; u64 offset; struct file *fp; struct dentry *d; }; struct data_t { // XXX: switch some to u32's when supported u64 ts_us; u64 type; u64 size; u64 offset; u64 delta_us; u64 pid; char task[TASK_COMM_LEN]; char file[DNAME_INLINE_LEN]; }; BPF_HASH(entryinfo, u64, struct val_t); BPF_PERF_OUTPUT(events); int trace_rw_entry(struct pt_regs *ctx, struct kiocb *iocb, struct iov_iter *data) { u64 id = bpf_get_current_pid_tgid(); u32 pid = id >> 32; // PID is higher part if(FILTER_PID) return 0; // store filep and timestamp by id struct val_t val = {}; val.ts = bpf_ktime_get_ns(); val.fp = iocb->ki_filp; val.d = NULL; val.offset = iocb->ki_pos; if (val.fp) entryinfo.update(&id, &val); return 0; } int trace_file_open_entry (struct pt_regs *ctx, struct inode *inode, struct file *filp) { u64 id = bpf_get_current_pid_tgid(); u32 pid = id >> 32; // PID is higher part if(FILTER_PID) return 0; // store filep and timestamp by id struct val_t val = {}; val.ts = bpf_ktime_get_ns(); val.fp = filp; val.d = NULL; val.offset = 0; if (val.fp) entryinfo.update(&id, &val); return 0; } int trace_getattr_entry(struct pt_regs *ctx, struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) { u64 id = bpf_get_current_pid_tgid(); u32 pid = id >> 32; // PID is higher part if(FILTER_PID) return 0; struct val_t val = {}; val.ts = bpf_ktime_get_ns(); val.fp = NULL; val.d = dentry; val.offset = 0; if (val.d) entryinfo.update(&id, &val); return 0; } static int trace_exit(struct pt_regs *ctx, int type) { struct val_t *valp; u64 id = bpf_get_current_pid_tgid(); u32 pid = id >> 32; // PID is higher part valp = entryinfo.lookup(&id); if (valp == 0) { // missed tracing issue or filtered return 0; } // calculate delta u64 ts = bpf_ktime_get_ns(); u64 delta_us = (ts - valp->ts) / 1000; entryinfo.delete(&id); if (FILTER_US) return 0; // populate output struct u32 size = PT_REGS_RC(ctx); struct data_t data = {.type = type, .size = size, .delta_us = delta_us, .pid = pid}; data.ts_us = ts / 1000; data.offset = valp->offset; bpf_get_current_comm(&data.task, sizeof(data.task)); // workaround (rewriter should handle file to d_name in one step): struct dentry *de = NULL; struct qstr qs = {}; if(type == TRACE_GETATTR) { bpf_probe_read(&de,sizeof(de), &valp->d); } else { bpf_probe_read(&de, sizeof(de), &valp->fp->f_path.dentry); } bpf_probe_read(&qs, sizeof(qs), (void *)&de->d_name); if (qs.len == 0) return 0; bpf_probe_read(&data.file, sizeof(data.file), (void *)qs.name); // output events.perf_submit(ctx, &data, sizeof(data)); return 0; } int trace_file_open_return(struct pt_regs *ctx) { return trace_exit(ctx, TRACE_OPEN); } int trace_read_return(struct pt_regs *ctx) { return trace_exit(ctx, TRACE_READ); } int trace_write_return(struct pt_regs *ctx) { return trace_exit(ctx, TRACE_WRITE); } int trace_getattr_return(struct pt_regs *ctx) { return trace_exit(ctx, TRACE_GETATTR); } """ if min_ms == 0: bpf_text = bpf_text.replace('FILTER_US', '0') else: bpf_text = bpf_text.replace('FILTER_US', 'delta_us <= %s' % str(min_ms * 1000)) if args.pid: bpf_text = bpf_text.replace('FILTER_PID', 'pid != %s' % pid) else: bpf_text = bpf_text.replace('FILTER_PID', '0') if debug or args.ebpf: print(bpf_text) if args.ebpf: exit() # process event def print_event(cpu, data, size): event = b["events"].event(data) type = 'R' if event.type == 1: type = 'W' elif event.type == 2: type = 'O' elif event.type == 3: type = 'G' if(csv): print("%d,%s,%d,%s,%d,%d,%d,%s" % ( event.ts_us, event.task, event.pid, type, event.size, event.offset, event.delta_us, event.file)) return print("%-8s %-14.14s %-6s %1s %-7s %-8d %7.2f %s" % (strftime("%H:%M:%S"), event.task.decode('utf-8', 'replace'), event.pid, type, event.size, event.offset / 1024, float(event.delta_us) / 1000, event.file.decode('utf-8', 'replace'))) # Currently specifically works for NFSv4, the other kprobes are generic # so it should work with earlier NFS versions b = BPF(text=bpf_text) b.attach_kprobe(event="nfs_file_read", fn_name="trace_rw_entry") b.attach_kprobe(event="nfs_file_write", fn_name="trace_rw_entry") b.attach_kprobe(event="nfs4_file_open", fn_name="trace_file_open_entry") b.attach_kprobe(event="nfs_file_open", fn_name="trace_file_open_entry") b.attach_kprobe(event="nfs_getattr", fn_name="trace_getattr_entry") b.attach_kretprobe(event="nfs_file_read", fn_name="trace_read_return") b.attach_kretprobe(event="nfs_file_write", fn_name="trace_write_return") b.attach_kretprobe(event="nfs4_file_open", fn_name="trace_file_open_return") b.attach_kretprobe(event="nfs_file_open", fn_name="trace_file_open_return") b.attach_kretprobe(event="nfs_getattr", fn_name="trace_getattr_return") if(csv): print("ENDTIME_us,TASK,PID,TYPE,BYTES,OFFSET_b,LATENCY_us,FILE") else: if min_ms == 0: print("Tracing NFS operations... Ctrl-C to quit") else: print("""Tracing NFS operations that are slower than \ %d ms... Ctrl-C to quit""" % min_ms) print("%-8s %-14s %-6s %1s %-7s %-8s %7s %s" % ("TIME", "COMM", "PID", "T", "BYTES", "OFF_KB", "LAT(ms)", "FILENAME")) b["events"].open_perf_buffer(print_event, page_cnt=64) while 1: try: b.perf_buffer_poll() except KeyboardInterrupt: exit() bpfcc-0.12.0/tools/nfsslower_example.txt000066400000000000000000000172731357404205000203170ustar00rootroot00000000000000Demonstrations of nfsslower, the Linux eBPF/bcc version. nfsslower show NFS reads, writes, opens and getattrs, slower than a threshold. For example: ./nfsslower.py Tracing NFS operations that are slower than 10 ms TIME COMM PID T BYTES OFF_KB LAT(ms) FILENAME 11:25:16 dd 21295 W 1048576 15360 14.84 1.test 11:25:16 dd 21295 W 1048576 16384 12.73 1.test 11:25:16 dd 21295 W 1048576 17408 24.27 1.test 11:25:16 dd 21295 W 1048576 18432 22.93 1.test 11:25:16 dd 21295 W 1048576 19456 14.65 1.test 11:25:16 dd 21295 W 1048576 20480 12.58 1.test 11:25:16 dd 21297 W 1048576 6144 10.50 1.test.w 11:25:16 dd 21297 W 1048576 7168 16.65 1.test.w 11:25:16 dd 21297 W 1048576 8192 13.01 1.test.w 11:25:16 dd 21297 W 1048576 9216 14.06 1.test.w This shows NFS writes from dd each 1MB in size to 2 different files. The writes all had latency higher than 10ms. 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: RPC latency, network latency, file system CPU cycles, file system locks, run queue latency, etc. This is a better measure of the latency suffered by applications reading from a NFS share and can better expose problems experienced by NFS clients. Note that this only traces the common NFS operations (read,write,open and getattr). I chose to include getattr as a significant percentage of NFS traffic end up being getattr calls and are a good indicator of problems with an NFS server. The threshold can be provided as an argument. E.g. I/O slower than 1 ms: ./nfsslower.py 1 Tracing NFS operations that are slower than 1 ms TIME COMM PID T BYTES OFF_KB LAT(ms) FILENAME 11:40:16 cp 21583 R 131072 0 4.35 1.test 11:40:16 cp 21583 R 131072 256 1.87 1.test 11:40:16 cp 21583 R 131072 384 2.99 1.test 11:40:16 cp 21583 R 131072 512 4.19 1.test 11:40:16 cp 21583 R 131072 640 4.25 1.test 11:40:16 cp 21583 R 131072 768 4.65 1.test 11:40:16 cp 21583 R 131072 1280 1.08 1.test 11:40:16 cp 21583 R 131072 1408 3.29 1.test 11:40:16 cp 21583 R 131072 1792 3.12 1.test 11:40:16 cp 21583 R 131072 3712 3.55 1.test 11:40:16 cp 21583 R 131072 3840 1.12 1.test 11:40:16 cp 21583 R 131072 4096 3.23 1.test 11:40:16 cp 21583 R 131072 4224 2.73 1.test 11:40:16 cp 21583 R 131072 4352 2.73 1.test 11:40:16 cp 21583 R 131072 4480 6.09 1.test 11:40:16 cp 21583 R 131072 5120 4.40 1.test [...] This shows all NFS_READS that were more than 1ms. Depending on your latency to your fileserver, you might need to tweak this value to remove A threshold of 0 will trace all operations. Warning: the output will be verbose, as it will include all file system cache hits. ./nfsslower.py 0 Tracing NFS operations 11:56:50 dd 21852 W 1048576 0 0.42 1.test 11:56:50 dd 21852 W 1048576 1024 0.46 1.test 11:56:50 dd 21852 W 1048576 2048 0.36 1.test 11:56:50 cp 21854 G 0 0 0.35 1.test 11:56:50 cp 21854 O 0 0 0.33 1.test 11:56:50 cp 21854 G 0 0 0.00 1.test 11:56:50 cp 21854 R 131072 0 0.07 1.test 11:56:50 cp 21854 R 131072 128 0.02 1.test 11:56:50 cp 21854 R 131072 256 0.02 1.test 11:56:50 cp 21854 R 131072 384 0.02 1.test 11:56:50 cp 21854 R 131072 512 0.02 1.test 11:56:50 cp 21854 R 131072 640 0.02 1.test 11:56:50 cp 21854 R 131072 768 0.02 1.test 11:56:50 cp 21854 R 131072 896 0.02 1.test 11:56:50 cp 21854 R 131072 1024 0.02 1.test 11:56:50 cp 21854 R 131072 1152 0.02 1.test 11:56:50 cp 21854 R 131072 1280 0.02 1.test 11:56:50 cp 21854 R 131072 1408 0.02 1.test 11:56:50 cp 21854 R 131072 1536 0.02 1.test 11:56:50 cp 21854 R 131072 1664 0.02 1.test 11:56:50 cp 21854 R 131072 1792 0.02 1.test 11:56:50 cp 21854 R 131072 1920 0.02 1.test 11:56:50 cp 21854 R 131072 2048 0.02 1.test 11:56:50 cp 21854 R 131072 2176 0.04 1.test 11:56:50 cp 21854 R 131072 2304 0.02 1.test 11:56:50 cp 21854 R 131072 2432 0.03 1.test 11:56:50 cp 21854 R 131072 2560 0.03 1.test 11:56:50 cp 21854 R 131072 2688 0.02 1.test 11:56:50 cp 21854 R 131072 2816 0.03 1.test 11:56:50 cp 21854 R 131072 2944 0.02 1.test 11:56:50 cp 21854 R 0 3072 0.00 1.test 11:56:50 ls 21855 G 0 0 0.00 1.test 11:56:50 ls 21856 G 0 0 0.36 music 11:56:50 ls 21856 G 0 0 0.00 music 11:56:50 ls 21856 G 0 0 0.00 test 11:56:50 ls 21856 G 0 0 0.00 ff 11:56:50 ls 21856 G 0 0 0.00 34.log 11:56:50 ls 21856 G 0 0 0.00 vmlinuz-linux 11:56:50 ls 21856 G 0 0 0.00 2.test 11:56:50 ls 21856 G 0 0 0.00 rt.log 11:56:50 ls 21856 G 0 0 0.00 1.lod 11:56:50 ls 21856 G 0 0 0.00 COPYRIGHT.txt 11:56:50 ls 21856 G 0 0 0.00 gg 11:56:50 ls 21856 G 0 0 0.00 qw.log 11:56:50 ls 21856 G 0 0 0.00 README.md 11:56:50 ls 21856 G 0 0 0.00 1.log The output now includes open operations ("O"), and reads ("R") wand getattrs ("G"). A cp operation A -j option will print just the fields (parsable output, csv): ./nfsslower.py -j 0 ENDTIME_us,TASK,PID,TYPE,BYTES,OFFSET_b,LATENCY_us,FILE 87054476520,dd,22754,W,1048576,0,425,1.test 87054482916,dd,22754,W,1048576,1048576,320,1.test 87054488179,dd,22754,W,1048576,2097152,389,1.test 87054511340,cp,22756,G,0,0,371,1.test 87054511685,cp,22756,O,0,0,306,1.test 87054511700,cp,22756,G,0,0,2,1.test 87054512325,cp,22756,R,131072,0,56,1.test 87054512432,cp,22756,R,131072,131072,22,1.test 87054512520,cp,22756,R,131072,262144,32,1.test 87054512600,cp,22756,R,131072,393216,21,1.test 87054512678,cp,22756,R,131072,524288,21,1.test 87054512803,cp,22756,R,131072,655360,56,1.test This may be useful for visualizing with another tool, for example, for producing a scatter plot of ENDTIME vs LATENCY, to look for time-based patterns. USAGE message: usage: nfsslower.py [-h] [-j] [-p PID] [min_ms] Trace READ, WRITE, OPEN and GETATTR NFS calls slower than a threshold,supports NFSv{3,4} positional arguments: min_ms Minimum IO duration to trace in ms (default=10ms) optional arguments: -h, --help show this help message and exit -j, --csv just print fields: comma-separated values -p PID, --pid PID Trace this pid only ./nfsslower # trace operations slower than 10ms ./nfsslower 1 # trace operations slower than 1ms ./nfsslower -j 1 # ... 1 ms, parsable output (csv) ./nfsslower 0 # trace all nfs operations ./nfsslower -p 121 # trace pid 121 only bpfcc-0.12.0/tools/nodegc.sh000077500000000000000000000000731357404205000156050ustar00rootroot00000000000000#!/bin/bash lib=$(dirname $0)/lib $lib/ugc.py -l node "$@" bpfcc-0.12.0/tools/nodegc_example.txt000077700000000000000000000000001357404205000233152lib/ugc_example.txtustar00rootroot00000000000000bpfcc-0.12.0/tools/nodestat.sh000077500000000000000000000000751357404205000161710ustar00rootroot00000000000000#!/bin/bash lib=$(dirname $0)/lib $lib/ustat.py -l node "$@" bpfcc-0.12.0/tools/nodestat_example.txt000077700000000000000000000000001357404205000242612lib/ustat_example.txtustar00rootroot00000000000000bpfcc-0.12.0/tools/offcputime.py000077500000000000000000000267441357404205000165420ustar00rootroot00000000000000#!/usr/bin/python # # offcputime Summarize off-CPU time by stack trace # For Linux, uses BCC, eBPF. # # USAGE: offcputime [-h] [-p PID | -u | -k] [-U | -K] [-f] [duration] # # Copyright 2016 Netflix, Inc. # Licensed under the Apache License, Version 2.0 (the "License") # # 13-Jan-2016 Brendan Gregg Created this. from __future__ import print_function from bcc import BPF from sys import stderr from time import sleep, strftime import argparse import errno import signal # arg validation def positive_int(val): try: ival = int(val) except ValueError: raise argparse.ArgumentTypeError("must be an integer") if ival < 0: raise argparse.ArgumentTypeError("must be positive") return ival def positive_nonzero_int(val): ival = positive_int(val) if ival == 0: raise argparse.ArgumentTypeError("must be nonzero") return ival def stack_id_err(stack_id): # -EFAULT in get_stackid normally means the stack-trace is not availible, # Such as getting kernel stack trace in userspace code return (stack_id < 0) and (stack_id != -errno.EFAULT) # arguments examples = """examples: ./offcputime # trace off-CPU stack time until Ctrl-C ./offcputime 5 # trace for 5 seconds only ./offcputime -f 5 # 5 seconds, and output in folded format ./offcputime -m 1000 # trace only events that last more than 1000 usec ./offcputime -M 10000 # trace only events that last less than 10000 usec ./offcputime -p 185 # only trace threads for PID 185 ./offcputime -t 188 # only trace thread 188 ./offcputime -u # only trace user threads (no kernel) ./offcputime -k # only trace kernel threads (no user) ./offcputime -U # only show user space stacks (no kernel) ./offcputime -K # only show kernel space stacks (no user) """ parser = argparse.ArgumentParser( description="Summarize off-CPU time by stack trace", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) thread_group = parser.add_mutually_exclusive_group() # Note: this script provides --pid and --tid flags but their arguments are # referred to internally using kernel nomenclature: TGID and PID. thread_group.add_argument("-p", "--pid", metavar="PID", dest="tgid", help="trace this PID only", type=positive_int) thread_group.add_argument("-t", "--tid", metavar="TID", dest="pid", help="trace this TID only", type=positive_int) thread_group.add_argument("-u", "--user-threads-only", action="store_true", help="user threads only (no kernel threads)") thread_group.add_argument("-k", "--kernel-threads-only", action="store_true", help="kernel threads only (no user threads)") stack_group = parser.add_mutually_exclusive_group() stack_group.add_argument("-U", "--user-stacks-only", action="store_true", help="show stacks from user space only (no kernel space stacks)") stack_group.add_argument("-K", "--kernel-stacks-only", action="store_true", help="show stacks from kernel space only (no user space stacks)") parser.add_argument("-d", "--delimited", action="store_true", help="insert delimiter between kernel/user stacks") parser.add_argument("-f", "--folded", action="store_true", help="output folded format") parser.add_argument("--stack-storage-size", default=1024, type=positive_nonzero_int, help="the number of unique stack traces that can be stored and " "displayed (default 1024)") parser.add_argument("duration", nargs="?", default=99999999, type=positive_nonzero_int, help="duration of trace, in seconds") parser.add_argument("-m", "--min-block-time", default=1, type=positive_nonzero_int, help="the amount of time in microseconds over which we " + "store traces (default 1)") parser.add_argument("-M", "--max-block-time", default=(1 << 64) - 1, type=positive_nonzero_int, help="the amount of time in microseconds under which we " + "store traces (default U64_MAX)") parser.add_argument("--state", type=positive_int, help="filter on this thread state bitmask (eg, 2 == TASK_UNINTERRUPTIBLE" + ") see include/linux/sched.h") parser.add_argument("--ebpf", action="store_true", help=argparse.SUPPRESS) args = parser.parse_args() if args.pid and args.tgid: parser.error("specify only one of -p and -t") folded = args.folded duration = int(args.duration) debug = 0 # signal handler def signal_ignore(signal, frame): print() # define BPF program bpf_text = """ #include #include #define MINBLOCK_US MINBLOCK_US_VALUEULL #define MAXBLOCK_US MAXBLOCK_US_VALUEULL struct key_t { u32 pid; u32 tgid; int user_stack_id; int kernel_stack_id; char name[TASK_COMM_LEN]; }; BPF_HASH(counts, struct key_t); BPF_HASH(start, u32); BPF_STACK_TRACE(stack_traces, STACK_STORAGE_SIZE); int oncpu(struct pt_regs *ctx, struct task_struct *prev) { u32 pid = prev->pid; u32 tgid = prev->tgid; u64 ts, *tsp; // record previous thread sleep time if ((THREAD_FILTER) && (STATE_FILTER)) { ts = bpf_ktime_get_ns(); start.update(&pid, &ts); } // get the current thread's start time pid = bpf_get_current_pid_tgid(); tgid = bpf_get_current_pid_tgid() >> 32; tsp = start.lookup(&pid); if (tsp == 0) { return 0; // missed start or filtered } // calculate current thread's delta time u64 delta = bpf_ktime_get_ns() - *tsp; start.delete(&pid); delta = delta / 1000; if ((delta < MINBLOCK_US) || (delta > MAXBLOCK_US)) { return 0; } // create map key struct key_t key = {}; key.pid = pid; key.tgid = tgid; key.user_stack_id = USER_STACK_GET; key.kernel_stack_id = KERNEL_STACK_GET; bpf_get_current_comm(&key.name, sizeof(key.name)); counts.increment(key, delta); return 0; } """ # set thread filter thread_context = "" if args.tgid is not None: thread_context = "PID %d" % args.tgid thread_filter = 'tgid == %d' % args.tgid elif args.pid is not None: thread_context = "TID %d" % args.pid thread_filter = 'pid == %d' % args.pid elif args.user_threads_only: thread_context = "user threads" thread_filter = '!(prev->flags & PF_KTHREAD)' elif args.kernel_threads_only: thread_context = "kernel threads" thread_filter = 'prev->flags & PF_KTHREAD' else: thread_context = "all threads" thread_filter = '1' if args.state == 0: state_filter = 'prev->state == 0' elif args.state: # these states are sometimes bitmask checked state_filter = 'prev->state & %d' % args.state else: state_filter = '1' bpf_text = bpf_text.replace('THREAD_FILTER', thread_filter) bpf_text = bpf_text.replace('STATE_FILTER', state_filter) # set stack storage size bpf_text = bpf_text.replace('STACK_STORAGE_SIZE', str(args.stack_storage_size)) bpf_text = bpf_text.replace('MINBLOCK_US_VALUE', str(args.min_block_time)) bpf_text = bpf_text.replace('MAXBLOCK_US_VALUE', str(args.max_block_time)) # handle stack args kernel_stack_get = "stack_traces.get_stackid(ctx, 0)" user_stack_get = "stack_traces.get_stackid(ctx, BPF_F_USER_STACK)" stack_context = "" if args.user_stacks_only: stack_context = "user" kernel_stack_get = "-1" elif args.kernel_stacks_only: stack_context = "kernel" user_stack_get = "-1" else: stack_context = "user + kernel" bpf_text = bpf_text.replace('USER_STACK_GET', user_stack_get) bpf_text = bpf_text.replace('KERNEL_STACK_GET', kernel_stack_get) need_delimiter = args.delimited and not (args.kernel_stacks_only or args.user_stacks_only) # check for an edge case; the code below will handle this case correctly # but ultimately nothing will be displayed if args.kernel_threads_only and args.user_stacks_only: print("ERROR: Displaying user stacks for kernel threads " + "doesn't make sense.", file=stderr) exit(1) if debug or args.ebpf: print(bpf_text) if args.ebpf: exit() # initialize BPF b = BPF(text=bpf_text) b.attach_kprobe(event="finish_task_switch", fn_name="oncpu") matched = b.num_open_kprobes() if matched == 0: print("error: 0 functions traced. Exiting.", file=stderr) exit(1) # header if not folded: print("Tracing off-CPU time (us) of %s by %s stack" % (thread_context, stack_context), end="") if duration < 99999999: print(" for %d secs." % duration) else: print("... Hit Ctrl-C to end.") try: sleep(duration) except KeyboardInterrupt: # as cleanup can take many seconds, trap Ctrl-C: signal.signal(signal.SIGINT, signal_ignore) if not folded: print() missing_stacks = 0 has_enomem = False counts = b.get_table("counts") stack_traces = b.get_table("stack_traces") for k, v in sorted(counts.items(), key=lambda counts: counts[1].value): # handle get_stackid errors if not args.user_stacks_only and stack_id_err(k.kernel_stack_id): missing_stacks += 1 has_enomem = has_enomem or k.kernel_stack_id == -errno.ENOMEM if not args.kernel_stacks_only and stack_id_err(k.user_stack_id): missing_stacks += 1 has_enomem = has_enomem or k.user_stack_id == -errno.ENOMEM # user stacks will be symbolized by tgid, not pid, to avoid the overhead # of one symbol resolver per thread user_stack = [] if k.user_stack_id < 0 else \ stack_traces.walk(k.user_stack_id) kernel_stack = [] if k.kernel_stack_id < 0 else \ stack_traces.walk(k.kernel_stack_id) if folded: # print folded stack output user_stack = list(user_stack) kernel_stack = list(kernel_stack) line = [k.name.decode('utf-8', 'replace')] # if we failed to get the stack is, such as due to no space (-ENOMEM) or # hash collision (-EEXIST), we still print a placeholder for consistency if not args.kernel_stacks_only: if stack_id_err(k.user_stack_id): line.append("[Missed User Stack]") else: line.extend([b.sym(addr, k.tgid).decode('utf-8', 'replace') for addr in reversed(user_stack)]) if not args.user_stacks_only: line.extend(["-"] if (need_delimiter and k.kernel_stack_id >= 0 and k.user_stack_id >= 0) else []) if stack_id_err(k.kernel_stack_id): line.append("[Missed Kernel Stack]") else: line.extend([b.ksym(addr).decode('utf-8', 'replace') for addr in reversed(kernel_stack)]) print("%s %d" % (";".join(line), v.value)) else: # print default multi-line stack output if not args.user_stacks_only: if stack_id_err(k.kernel_stack_id): print(" [Missed Kernel Stack]") else: for addr in kernel_stack: print(" %s" % b.ksym(addr)) if not args.kernel_stacks_only: if need_delimiter and k.user_stack_id >= 0 and k.kernel_stack_id >= 0: print(" --") if stack_id_err(k.user_stack_id): print(" [Missed User Stack]") else: for addr in user_stack: print(" %s" % b.sym(addr, k.tgid)) print(" %-16s %s (%d)" % ("-", k.name.decode('utf-8', 'replace'), k.pid)) print(" %d\n" % v.value) if missing_stacks > 0: enomem_str = "" if not has_enomem else \ " Consider increasing --stack-storage-size." print("WARNING: %d stack traces lost and could not be displayed.%s" % (missing_stacks, enomem_str), file=stderr) bpfcc-0.12.0/tools/offcputime_example.txt000066400000000000000000000461311357404205000204310ustar00rootroot00000000000000Demonstrations of offcputime, the Linux eBPF/bcc version. This program shows stack traces that were blocked, and the total duration they were blocked. It works by tracing when threads block and when they return to CPU, measuring both the time they were blocked (aka the "off-CPU time") and the blocked stack trace and the task name. This data is summarized in kernel by summing the blocked time by unique stack trace and task name. Here is some example output. The -K option was used to only match kernel stacks. To explain what we are seeing: the very first stack trace looks like a page fault (do_page_fault() etc) from the "chmod" command, and in total was off-CPU for 13 microseconds. # ./offcputime -K Tracing off-CPU time (us) of all threads by kernel stack... Hit Ctrl-C to end. ^C schedule schedule_timeout io_schedule_timeout bit_wait_io __wait_on_bit wait_on_page_bit_killable __lock_page_or_retry filemap_fault __do_fault handle_mm_fault __do_page_fault do_page_fault page_fault chmod 13 schedule rcu_nocb_kthread kthread ret_from_fork ddebug_tables rcuos/0 22 schedule schedule_timeout io_schedule_timeout bit_wait_io __wait_on_bit_lock __lock_page lock_page __do_fault handle_mm_fault __do_page_fault do_page_fault page_fault run 27 schedule schedule_timeout io_schedule_timeout bit_wait_io __wait_on_bit wait_on_page_bit_killable __lock_page_or_retry filemap_fault __do_fault handle_mm_fault __do_page_fault do_page_fault page_fault clear_user padzero load_elf_binary search_binary_handler load_script search_binary_handler do_execveat_common.isra.27 run 28 schedule schedule_timeout io_schedule_timeout bit_wait_io __wait_on_bit wait_on_page_bit_killable __lock_page_or_retry filemap_fault __do_fault handle_mm_fault __do_page_fault do_page_fault page_fault run 82 schedule pipe_wait pipe_read __vfs_read vfs_read sys_read entry_SYSCALL_64_fastpath bash 94 schedule rcu_gp_kthread kthread ret_from_fork ddebug_tables rcu_sched 104 schedule schedule_timeout io_schedule_timeout bit_wait_io __wait_on_bit out_of_line_wait_on_bit __wait_on_buffer jbd2_journal_commit_transaction kjournald2 kthread ret_from_fork mb_cache_list jbd2/xvda1-8 986 schedule schedule_timeout io_schedule_timeout bit_wait_io __wait_on_bit out_of_line_wait_on_bit __wait_on_buffer jbd2_journal_commit_transaction kjournald2 kthread ret_from_fork mb_cache_list jbd2/xvda1-8 6630 schedule schedule_timeout io_schedule_timeout bit_wait_io __wait_on_bit out_of_line_wait_on_bit do_get_write_access jbd2_journal_get_write_access __ext4_journal_get_write_access ext4_mb_mark_diskspace_used ext4_mb_new_blocks ext4_ext_map_blocks ext4_map_blocks ext4_writepages do_writepages __filemap_fdatawrite_range filemap_flush ext4_alloc_da_blocks ext4_rename ext4_rename2 supervise 6645 schedule schedule_timeout io_schedule_timeout bit_wait_io __wait_on_bit out_of_line_wait_on_bit do_get_write_access jbd2_journal_get_write_access __ext4_journal_get_write_access __ext4_new_inode ext4_create vfs_create path_openat do_filp_open do_sys_open sys_open entry_SYSCALL_64_fastpath supervise 12702 schedule rcu_nocb_kthread kthread ret_from_fork rcuos/2 16036 schedule rcu_nocb_kthread kthread ret_from_fork rcuos/4 24085 schedule do_wait sys_wait4 entry_SYSCALL_64_fastpath run 233055 schedule schedule_timeout io_schedule_timeout bit_wait_io __wait_on_bit wait_on_page_bit truncate_inode_pages_range truncate_inode_pages_final ext4_evict_inode evict iput __dentry_kill dput sys_rename entry_SYSCALL_64_fastpath supervise 297113 schedule schedule_timeout wait_woken n_tty_read tty_read __vfs_read vfs_read sys_read entry_SYSCALL_64_fastpath bash 1789866 schedule schedule_timeout io_schedule_timeout do_blockdev_direct_IO __blockdev_direct_IO blkdev_direct_IO generic_file_read_iter blkdev_read_iter __vfs_read vfs_read sys_read entry_SYSCALL_64_fastpath dd 3310763 schedule smpboot_thread_fn kthread ret_from_fork watchdog/1 3999989 schedule smpboot_thread_fn kthread ret_from_fork watchdog/5 3999995 schedule smpboot_thread_fn kthread ret_from_fork watchdog/4 3999996 schedule smpboot_thread_fn kthread ret_from_fork watchdog/0 3999996 schedule smpboot_thread_fn kthread ret_from_fork watchdog/3 3999998 schedule smpboot_thread_fn kthread ret_from_fork watchdog/7 3999999 schedule smpboot_thread_fn kthread ret_from_fork watchdog/2 4000001 schedule smpboot_thread_fn kthread ret_from_fork watchdog/6 4000001 schedule do_wait sys_wait4 entry_SYSCALL_64_fastpath bash 4039675 schedule do_nanosleep hrtimer_nanosleep sys_nanosleep entry_SYSCALL_64_fastpath svscan 5000112 schedule schedule_hrtimeout_range_clock schedule_hrtimeout_range poll_schedule_timeout do_select core_sys_select sys_select entry_SYSCALL_64_fastpath snmpd 5998761 schedule smpboot_thread_fn kthread ret_from_fork migration/3 6149779 schedule schedule_hrtimeout_range_clock schedule_hrtimeout_range poll_schedule_timeout do_select core_sys_select sys_select entry_SYSCALL_64_fastpath ntpd 6999832 schedule worker_thread kthread ret_from_fork kworker/u16:2 7131941 schedule worker_thread kthread ret_from_fork kworker/3:0 7999844 schedule worker_thread kthread ret_from_fork kworker/1:1 7999872 schedule worker_thread kthread ret_from_fork kworker/2:1 7999889 schedule worker_thread kthread ret_from_fork kworker/5:1 7999936 schedule worker_thread kthread ret_from_fork kworker/7:1 7999938 schedule worker_thread kthread ret_from_fork kworker/6:1 7999940 schedule do_nanosleep hrtimer_nanosleep sys_nanosleep entry_SYSCALL_64_fastpath tail 8000905 schedule smpboot_thread_fn kthread ret_from_fork migration/7 8197046 schedule pipe_wait pipe_read __vfs_read vfs_read sys_read entry_SYSCALL_64_fastpath readproctitle 8197835 schedule smpboot_thread_fn kthread ret_from_fork migration/4 8201851 schedule smpboot_thread_fn kthread ret_from_fork migration/2 8203375 schedule smpboot_thread_fn kthread ret_from_fork migration/6 8208664 schedule smpboot_thread_fn kthread ret_from_fork migration/5 8209819 schedule smpboot_thread_fn kthread ret_from_fork ddebug_tables migration/0 8211292 schedule smpboot_thread_fn kthread ret_from_fork migration/1 8212100 schedule worker_thread kthread ret_from_fork kworker/0:2 8270305 schedule rcu_nocb_kthread kthread ret_from_fork rcuos/3 8349697 schedule rcu_nocb_kthread kthread ret_from_fork rcuos/2 8363357 schedule rcu_nocb_kthread kthread ret_from_fork rcuos/1 8365338 schedule schedule_timeout xfs_buf_terminate kthread ret_from_fork xfsaild/md0 8371514 schedule rcu_nocb_kthread kthread ret_from_fork rcuos/4 8384013 schedule rcu_nocb_kthread kthread ret_from_fork rcuos/5 8390016 schedule rcu_nocb_kthread kthread ret_from_fork ddebug_tables rcuos/0 8405428 schedule schedule_timeout rcu_gp_kthread kthread ret_from_fork ddebug_tables rcu_sched 8406930 schedule rcu_nocb_kthread kthread ret_from_fork rcuos/7 8409575 schedule rcu_nocb_kthread kthread ret_from_fork rcuos/6 8415062 schedule schedule_hrtimeout_range_clock schedule_hrtimeout_range poll_schedule_timeout do_select core_sys_select sys_select entry_SYSCALL_64_fastpath offcputime 8421478 schedule worker_thread kthread ret_from_fork kworker/4:0 8421492 schedule schedule_hrtimeout_range_clock schedule_hrtimeout_range poll_schedule_timeout do_select core_sys_select sys_select entry_SYSCALL_64_fastpath sshd 14249005 schedule schedule_hrtimeout_range_clock schedule_hrtimeout_range poll_schedule_timeout do_sys_poll sys_poll entry_SYSCALL_64_fastpath supervise 81670888 The last few stack traces aren't very interesting, since they are threads that are often blocked off-CPU waiting for work. Do be somewhat careful with overhead: this is tracing scheduler functions, which can be called very frequently. While this uses in-kernel summaries for efficiency, the rate of scheduler functions can be very high (> 1,000,000/sec), and this is performing stack walks when threads return to CPU. At some point the overhead will be measurable. A -p option can be used to filter (in-kernel) on a single process ID. For example, only matching PID 26651, which is a running "dd" command: # ./offcputime -K -p 26651 Tracing off-CPU time (us) of all threads by kernel stack... Hit Ctrl-C to end. ^C schedule schedule_timeout io_schedule_timeout do_blockdev_direct_IO __blockdev_direct_IO blkdev_direct_IO generic_file_read_iter blkdev_read_iter __vfs_read vfs_read sys_read entry_SYSCALL_64_fastpath dd 2405710 The stack trace shows "dd" is blocked waiting on disk I/O, as expected, for a total of 2.4 seconds during tracing. A duration can be added, for example, tracing for 5 seconds only: # ./offcputime -K -p 26651 5 Tracing off-CPU time (us) of all threads by kernel stack for 5 secs. schedule schedule_timeout io_schedule_timeout do_blockdev_direct_IO __blockdev_direct_IO blkdev_direct_IO generic_file_read_iter blkdev_read_iter __vfs_read vfs_read sys_read entry_SYSCALL_64_fastpath dd 4413909 Here, dd was blocked for 4.4 seconds out of 5. Or put differently, likely on-CPU for about 12% of the time. Which matches the ratio seen by time(1): # time dd if=/dev/md0 iflag=direct of=/dev/null bs=1k ^C108115+0 records in 108114+0 records out 110708736 bytes (111 MB) copied, 13.7565 s, 8.0 MB/s real 0m13.760s user 0m0.000s sys 0m1.739s A -f option will emit output using the "folded stacks" format, which can be read directly by flamegraph.pl from the FlameGraph open source software (https://github.com/brendangregg/FlameGraph). Eg: # ./offcputime -K -f 5 bash;entry_SYSCALL_64_fastpath;sys_read;vfs_read;__vfs_read;tty_read;n_tty_read;call_rwsem_down_read_failed;rwsem_down_read_failed;schedule 8 yes;entry_SYSCALL_64_fastpath;sys_write;vfs_write;__vfs_write;tty_write;n_tty_write;call_rwsem_down_read_failed;rwsem_down_read_failed;schedule 14 run;page_fault;do_page_fault;__do_page_fault;handle_mm_fault;__do_fault;filemap_fault;__lock_page_or_retry;wait_on_page_bit_killable;__wait_on_bit;bit_wait_io;io_schedule_timeout;schedule_timeout;schedule 33 rcuos/4;ret_from_fork;kthread;rcu_nocb_kthread;schedule 45 bash;entry_SYSCALL_64_fastpath;sys_read;vfs_read;__vfs_read;pipe_read;pipe_wait;schedule 88 run;page_fault;do_page_fault;__do_page_fault;handle_mm_fault;__do_fault;filemap_fault;__lock_page_or_retry;wait_on_page_bit_killable;__wait_on_bit;bit_wait_io;io_schedule_timeout;schedule_timeout;schedule 108 jbd2/xvda1-8;mb_cache_list;ret_from_fork;kthread;kjournald2;jbd2_journal_commit_transaction;__wait_on_buffer;out_of_line_wait_on_bit;__wait_on_bit;bit_wait_io;io_schedule_timeout;schedule_timeout;schedule 828 jbd2/xvda1-8;mb_cache_list;ret_from_fork;kthread;kjournald2;jbd2_journal_commit_transaction;__wait_on_buffer;out_of_line_wait_on_bit;__wait_on_bit;bit_wait_io;io_schedule_timeout;schedule_timeout;schedule 6201 supervise;entry_SYSCALL_64_fastpath;sys_rename;dput;__dentry_kill;iput;evict;ext4_evict_inode;truncate_inode_pages_final;truncate_inode_pages_range;wait_on_page_bit;__wait_on_bit;bit_wait_io;io_schedule_timeout;schedule_timeout;schedule 41049 run;entry_SYSCALL_64_fastpath;sys_wait4;do_wait;schedule 120709 bash;entry_SYSCALL_64_fastpath;sys_wait4;do_wait;schedule 699320 ksoftirqd/0;ret_from_fork;kthread;smpboot_thread_fn;schedule 1077529 bash;entry_SYSCALL_64_fastpath;sys_read;vfs_read;__vfs_read;tty_read;n_tty_read;wait_woken;schedule_timeout;schedule 1362045 sshd;entry_SYSCALL_64_fastpath;sys_select;core_sys_select;do_select;poll_schedule_timeout;schedule_hrtimeout_range;schedule_hrtimeout_range_clock;schedule 1377627 migration/0;ddebug_tables;ret_from_fork;kthread;smpboot_thread_fn;schedule 2040753 snmpd;entry_SYSCALL_64_fastpath;sys_select;core_sys_select;do_select;poll_schedule_timeout;schedule_hrtimeout_range;schedule_hrtimeout_range_clock;schedule 2197568 migration/5;ret_from_fork;kthread;smpboot_thread_fn;schedule 3079426 migration/7;ret_from_fork;kthread;smpboot_thread_fn;schedule 3084746 kworker/6:2;ret_from_fork;kthread;worker_thread;schedule 3940583 kworker/5:1;ret_from_fork;kthread;worker_thread;schedule 3944892 kworker/1:2;ret_from_fork;kthread;worker_thread;schedule 3999646 ntpd;entry_SYSCALL_64_fastpath;sys_select;core_sys_select;do_select;poll_schedule_timeout;schedule_hrtimeout_range;schedule_hrtimeout_range_clock;schedule 3999904 kworker/u16:0;ret_from_fork;kthread;worker_thread;schedule 3999967 kworker/7:0;ret_from_fork;kthread;worker_thread;schedule 3999987 tail;entry_SYSCALL_64_fastpath;sys_nanosleep;hrtimer_nanosleep;do_nanosleep;schedule 4000473 migration/1;ret_from_fork;kthread;smpboot_thread_fn;schedule 4091150 migration/4;ret_from_fork;kthread;smpboot_thread_fn;schedule 4095217 readproctitle;entry_SYSCALL_64_fastpath;sys_read;vfs_read;__vfs_read;pipe_read;pipe_wait;schedule 4108470 migration/3;ret_from_fork;kthread;smpboot_thread_fn;schedule 4109264 migration/2;ret_from_fork;kthread;smpboot_thread_fn;schedule 4109280 migration/6;ret_from_fork;kthread;smpboot_thread_fn;schedule 4111143 kworker/4:0;ret_from_fork;kthread;worker_thread;schedule 4402350 kworker/3:0;ret_from_fork;kthread;worker_thread;schedule 4433988 kworker/2:1;ret_from_fork;kthread;worker_thread;schedule 4636142 kworker/0:2;ret_from_fork;kthread;worker_thread;schedule 4832023 rcuos/1;ret_from_fork;kthread;rcu_nocb_kthread;schedule 4974186 rcuos/5;ret_from_fork;kthread;rcu_nocb_kthread;schedule 4977137 rcuos/6;ret_from_fork;kthread;rcu_nocb_kthread;schedule 4987769 rcuos/3;ret_from_fork;kthread;rcu_nocb_kthread;schedule 4992282 rcuos/4;ret_from_fork;kthread;rcu_nocb_kthread;schedule 4992364 rcuos/2;ret_from_fork;kthread;rcu_nocb_kthread;schedule 4992714 rcuos/0;ddebug_tables;ret_from_fork;kthread;rcu_nocb_kthread;schedule 4996504 rcuos/7;ret_from_fork;kthread;rcu_nocb_kthread;schedule 4998497 rcu_sched;ddebug_tables;ret_from_fork;kthread;rcu_gp_kthread;schedule_timeout;schedule 5000686 offcputime;entry_SYSCALL_64_fastpath;sys_select;core_sys_select;do_select;poll_schedule_timeout;schedule_hrtimeout_range;schedule_hrtimeout_range_clock;schedule 5005063 dd;entry_SYSCALL_64_fastpath;sys_read;vfs_read;__vfs_read;blkdev_read_iter;generic_file_read_iter;blkdev_direct_IO;__blockdev_direct_IO;do_blockdev_direct_IO;io_schedule_timeout;schedule_timeout;schedule 8025599 supervise;entry_SYSCALL_64_fastpath;sys_poll;do_sys_poll;poll_schedule_timeout;schedule_hrtimeout_range;schedule_hrtimeout_range_clock;schedule 40835611 The stack traces are shown as single lines, with functions separated by semicolons. The first entry is the task name. The 2nd column is the total off-CPU time. I'd save this output to a file, then move it to the system where you'll be creating your "off-CPU time flame graphs". USAGE message: # ./offcputime.py -h usage: offcputime.py [-h] [-p PID | -t TID | -u | -k] [-U | -K] [-d] [-f] [--stack-storage-size STACK_STORAGE_SIZE] [-m MIN_BLOCK_TIME] [-M MAX_BLOCK_TIME] [--state STATE] [duration] Summarize off-CPU time by stack trace positional arguments: duration duration of trace, in seconds optional arguments: -h, --help show this help message and exit -p PID, --pid PID trace this PID only -t TID, --tid TID trace this TID only -u, --user-threads-only user threads only (no kernel threads) -k, --kernel-threads-only kernel threads only (no user threads) -U, --user-stacks-only show stacks from user space only (no kernel space stacks) -K, --kernel-stacks-only show stacks from kernel space only (no user space stacks) -d, --delimited insert delimiter between kernel/user stacks -f, --folded output folded format --stack-storage-size STACK_STORAGE_SIZE the number of unique stack traces that can be stored and displayed (default 1024) -m MIN_BLOCK_TIME, --min-block-time MIN_BLOCK_TIME the amount of time in microseconds over which we store traces (default 1) -M MAX_BLOCK_TIME, --max-block-time MAX_BLOCK_TIME the amount of time in microseconds under which we store traces (default U64_MAX) --state STATE filter on this thread state bitmask (eg, 2 == TASK_UNINTERRUPTIBLE) see include/linux/sched.h examples: ./offcputime # trace off-CPU stack time until Ctrl-C ./offcputime 5 # trace for 5 seconds only ./offcputime -f 5 # 5 seconds, and output in folded format ./offcputime -m 1000 # trace only events that last more than 1000 usec ./offcputime -M 10000 # trace only events that last less than 10000 usec ./offcputime -p 185 # only trace threads for PID 185 ./offcputime -t 188 # only trace thread 188 ./offcputime -u # only trace user threads (no kernel) ./offcputime -k # only trace kernel threads (no user) ./offcputime -U # only show user space stacks (no kernel) ./offcputime -K # only show kernel space stacks (no user) bpfcc-0.12.0/tools/offwaketime.py000077500000000000000000000340371357404205000166740ustar00rootroot00000000000000#!/usr/bin/python # # offwaketime Summarize blocked time by kernel off-CPU stack + waker stack # For Linux, uses BCC, eBPF. # # USAGE: offwaketime [-h] [-p PID | -u | -k] [-U | -K] [-f] [duration] # # Copyright 2016 Netflix, Inc. # Licensed under the Apache License, Version 2.0 (the "License") # # 20-Jan-2016 Brendan Gregg Created this. from __future__ import print_function from bcc import BPF from time import sleep import argparse import signal import errno from sys import stderr # arg validation def positive_int(val): try: ival = int(val) except ValueError: raise argparse.ArgumentTypeError("must be an integer") if ival < 0: raise argparse.ArgumentTypeError("must be positive") return ival def positive_nonzero_int(val): ival = positive_int(val) if ival == 0: raise argparse.ArgumentTypeError("must be nonzero") return ival def stack_id_err(stack_id): # -EFAULT in get_stackid normally means the stack-trace is not availible, # Such as getting kernel stack trace in userspace code return (stack_id < 0) and (stack_id != -errno.EFAULT) # arguments examples = """examples: ./offwaketime # trace off-CPU + waker stack time until Ctrl-C ./offwaketime 5 # trace for 5 seconds only ./offwaketime -f 5 # 5 seconds, and output in folded format ./offwaketime -m 1000 # trace only events that last more than 1000 usec ./offwaketime -M 9000 # trace only events that last less than 9000 usec ./offwaketime -p 185 # only trace threads for PID 185 ./offwaketime -t 188 # only trace thread 188 ./offwaketime -u # only trace user threads (no kernel) ./offwaketime -k # only trace kernel threads (no user) ./offwaketime -U # only show user space stacks (no kernel) ./offwaketime -K # only show kernel space stacks (no user) """ parser = argparse.ArgumentParser( description="Summarize blocked time by kernel stack trace + waker stack", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) thread_group = parser.add_mutually_exclusive_group() # Note: this script provides --pid and --tid flags but their arguments are # referred to internally using kernel nomenclature: TGID and PID. thread_group.add_argument("-p", "--pid", metavar="PID", dest="tgid", help="trace this PID only", type=positive_int) thread_group.add_argument("-t", "--tid", metavar="TID", dest="pid", help="trace this TID only", type=positive_int) thread_group.add_argument("-u", "--user-threads-only", action="store_true", help="user threads only (no kernel threads)") thread_group.add_argument("-k", "--kernel-threads-only", action="store_true", help="kernel threads only (no user threads)") stack_group = parser.add_mutually_exclusive_group() stack_group.add_argument("-U", "--user-stacks-only", action="store_true", help="show stacks from user space only (no kernel space stacks)") stack_group.add_argument("-K", "--kernel-stacks-only", action="store_true", help="show stacks from kernel space only (no user space stacks)") parser.add_argument("-d", "--delimited", action="store_true", help="insert delimiter between kernel/user stacks") parser.add_argument("-f", "--folded", action="store_true", help="output folded format") parser.add_argument("--stack-storage-size", default=1024, type=positive_nonzero_int, help="the number of unique stack traces that can be stored and " "displayed (default 1024)") parser.add_argument("duration", nargs="?", default=99999999, type=positive_nonzero_int, help="duration of trace, in seconds") parser.add_argument("-m", "--min-block-time", default=1, type=positive_nonzero_int, help="the amount of time in microseconds over which we " + "store traces (default 1)") parser.add_argument("-M", "--max-block-time", default=(1 << 64) - 1, type=positive_nonzero_int, help="the amount of time in microseconds under which we " + "store traces (default U64_MAX)") parser.add_argument("--ebpf", action="store_true", help=argparse.SUPPRESS) args = parser.parse_args() folded = args.folded duration = int(args.duration) # signal handler def signal_ignore(signal, frame): print() # define BPF program bpf_text = """ #include #include #define MINBLOCK_US MINBLOCK_US_VALUEULL #define MAXBLOCK_US MAXBLOCK_US_VALUEULL struct key_t { char waker[TASK_COMM_LEN]; char target[TASK_COMM_LEN]; int w_k_stack_id; int w_u_stack_id; int t_k_stack_id; int t_u_stack_id; u32 t_pid; u32 t_tgid; u32 w_pid; u32 w_tgid; }; BPF_HASH(counts, struct key_t); // Key of this hash is PID of waiting Process, // value is timestamp when it went into waiting BPF_HASH(start, u32); struct wokeby_t { char name[TASK_COMM_LEN]; int k_stack_id; int u_stack_id; int w_pid; int w_tgid; }; // Key of the hash is PID of the Process to be waken, value is information // of the Process who wakes it BPF_HASH(wokeby, u32, struct wokeby_t); BPF_STACK_TRACE(stack_traces, 2); int waker(struct pt_regs *ctx, struct task_struct *p) { // PID and TGID of the target Process to be waken u32 pid = p->pid; u32 tgid = p->tgid; if (!(THREAD_FILTER)) { return 0; } // Construct information about current (the waker) Process struct wokeby_t woke = {}; bpf_get_current_comm(&woke.name, sizeof(woke.name)); woke.k_stack_id = KERNEL_STACK_GET; woke.u_stack_id = USER_STACK_GET; woke.w_pid = bpf_get_current_pid_tgid(); woke.w_tgid = bpf_get_current_pid_tgid() >> 32; wokeby.update(&pid, &woke); return 0; } int oncpu(struct pt_regs *ctx, struct task_struct *p) { // PID and TGID of the previous Process (Process going into waiting) u32 pid = p->pid; u32 tgid = p->tgid; u64 *tsp; u64 ts = bpf_ktime_get_ns(); // Record timestamp for the previous Process (Process going into waiting) if (THREAD_FILTER) { start.update(&pid, &ts); } // Calculate current Process's wait time by finding the timestamp of when // it went into waiting. // pid and tgid are now the PID and TGID of the current (waking) Process. pid = bpf_get_current_pid_tgid(); tgid = bpf_get_current_pid_tgid() >> 32; tsp = start.lookup(&pid); if (tsp == 0) { // Missed or filtered when the Process went into waiting return 0; } u64 delta = ts - *tsp; start.delete(&pid); delta = delta / 1000; if ((delta < MINBLOCK_US) || (delta > MAXBLOCK_US)) { return 0; } // create map key struct key_t key = {}; struct wokeby_t *woke; bpf_get_current_comm(&key.target, sizeof(key.target)); key.t_pid = pid; key.t_tgid = tgid; key.t_k_stack_id = KERNEL_STACK_GET; key.t_u_stack_id = USER_STACK_GET; woke = wokeby.lookup(&pid); if (woke) { key.w_k_stack_id = woke->k_stack_id; key.w_u_stack_id = woke->u_stack_id; key.w_pid = woke->w_pid; key.w_tgid = woke->w_tgid; __builtin_memcpy(&key.waker, woke->name, TASK_COMM_LEN); wokeby.delete(&pid); } counts.increment(key, delta); return 0; } """ # set thread filter thread_context = "" if args.tgid is not None: thread_context = "PID %d" % args.tgid thread_filter = 'tgid == %d' % args.tgid elif args.pid is not None: thread_context = "TID %d" % args.pid thread_filter = 'pid == %d' % args.pid elif args.user_threads_only: thread_context = "user threads" thread_filter = '!(p->flags & PF_KTHREAD)' elif args.kernel_threads_only: thread_context = "kernel threads" thread_filter = 'p->flags & PF_KTHREAD' else: thread_context = "all threads" thread_filter = '1' bpf_text = bpf_text.replace('THREAD_FILTER', thread_filter) # set stack storage size bpf_text = bpf_text.replace('STACK_STORAGE_SIZE', str(args.stack_storage_size)) bpf_text = bpf_text.replace('MINBLOCK_US_VALUE', str(args.min_block_time)) bpf_text = bpf_text.replace('MAXBLOCK_US_VALUE', str(args.max_block_time)) # handle stack args kernel_stack_get = "stack_traces.get_stackid(ctx, 0)" user_stack_get = "stack_traces.get_stackid(ctx, BPF_F_USER_STACK)" stack_context = "" if args.user_stacks_only: stack_context = "user" kernel_stack_get = "-1" elif args.kernel_stacks_only: stack_context = "kernel" user_stack_get = "-1" else: stack_context = "user + kernel" bpf_text = bpf_text.replace('USER_STACK_GET', user_stack_get) bpf_text = bpf_text.replace('KERNEL_STACK_GET', kernel_stack_get) if args.ebpf: print(bpf_text) exit() # initialize BPF b = BPF(text=bpf_text) b.attach_kprobe(event="finish_task_switch", fn_name="oncpu") b.attach_kprobe(event="try_to_wake_up", fn_name="waker") matched = b.num_open_kprobes() if matched == 0: print("0 functions traced. Exiting.") exit() # header if not folded: print("Tracing blocked time (us) by %s off-CPU and waker stack" % stack_context, end="") if duration < 99999999: print(" for %d secs." % duration) else: print("... Hit Ctrl-C to end.") try: sleep(duration) except KeyboardInterrupt: # as cleanup can take many seconds, trap Ctrl-C: # print a newline for folded output on Ctrl-C signal.signal(signal.SIGINT, signal_ignore) if not folded: print() missing_stacks = 0 has_enomem = False counts = b.get_table("counts") stack_traces = b.get_table("stack_traces") need_delimiter = args.delimited and not (args.kernel_stacks_only or args.user_stacks_only) for k, v in sorted(counts.items(), key=lambda counts: counts[1].value): # handle get_stackid errors if not args.user_stacks_only: missing_stacks += int(stack_id_err(k.w_k_stack_id)) missing_stacks += int(stack_id_err(k.t_k_stack_id)) has_enomem = has_enomem or (k.w_k_stack_id == -errno.ENOMEM) or \ (k.t_k_stack_id == -errno.ENOMEM) if not args.kernel_stacks_only: missing_stacks += int(stack_id_err(k.w_u_stack_id)) missing_stacks += int(stack_id_err(k.t_u_stack_id)) has_enomem = has_enomem or (k.w_u_stack_id == -errno.ENOMEM) or \ (k.t_u_stack_id == -errno.ENOMEM) waker_user_stack = [] if k.w_u_stack_id < 1 else \ reversed(list(stack_traces.walk(k.w_u_stack_id))[1:]) waker_kernel_stack = [] if k.w_k_stack_id < 1 else \ reversed(list(stack_traces.walk(k.w_k_stack_id))[1:]) target_user_stack = [] if k.t_u_stack_id < 1 else \ stack_traces.walk(k.t_u_stack_id) target_kernel_stack = [] if k.t_k_stack_id < 1 else \ stack_traces.walk(k.t_k_stack_id) if folded: # print folded stack output line = [k.target.decode('utf-8', 'replace')] if not args.kernel_stacks_only: if stack_id_err(k.t_u_stack_id): line.append("[Missed User Stack] %d" % k.t_u_stack_id) else: line.extend([b.sym(addr, k.t_tgid).decode('utf-8', 'replace') for addr in reversed(list(target_user_stack)[1:])]) if not args.user_stacks_only: line.extend(["-"] if (need_delimiter and k.t_k_stack_id > 0 and k.t_u_stack_id > 0) else []) if stack_id_err(k.t_k_stack_id): line.append("[Missed Kernel Stack]") else: line.extend([b.ksym(addr).decode('utf-8', 'replace') for addr in reversed(list(target_kernel_stack)[1:])]) line.append("--") if not args.user_stacks_only: if stack_id_err(k.w_k_stack_id): line.append("[Missed Kernel Stack]") else: line.extend([b.ksym(addr).decode('utf-8', 'replace') for addr in reversed(list(waker_kernel_stack))]) if not args.kernel_stacks_only: line.extend(["-"] if (need_delimiter and k.w_u_stack_id > 0 and k.w_k_stack_id > 0) else []) if stack_id_err(k.w_u_stack_id): line.append("[Missed User Stack]") else: line.extend([b.sym(addr, k.w_tgid).decode('utf-8', 'replace') for addr in reversed(list(waker_user_stack))]) line.append(k.waker.decode('utf-8', 'replace')) print("%s %d" % (";".join(line), v.value)) else: # print wakeup name then stack in reverse order print(" %-16s %s %s" % ("waker:", k.waker.decode('utf-8', 'replace'), k.w_pid)) if not args.kernel_stacks_only: if stack_id_err(k.w_u_stack_id): print(" [Missed User Stack] %d" % k.w_u_stack_id) else: for addr in waker_user_stack: print(" %s" % b.sym(addr, k.w_tgid)) if not args.user_stacks_only: if need_delimiter and k.w_u_stack_id > 0 and k.w_k_stack_id > 0: print(" -") if stack_id_err(k.w_k_stack_id): print(" [Missed Kernel Stack]") else: for addr in waker_kernel_stack: print(" %s" % b.ksym(addr)) # print waker/wakee delimiter print(" %-16s %s" % ("--", "--")) if not args.user_stacks_only: if stack_id_err(k.t_k_stack_id): print(" [Missed Kernel Stack]") else: for addr in target_kernel_stack: print(" %s" % b.ksym(addr)) if not args.kernel_stacks_only: if need_delimiter and k.t_u_stack_id > 0 and k.t_k_stack_id > 0: print(" -") if stack_id_err(k.t_u_stack_id): print(" [Missed User Stack]") else: for addr in target_user_stack: print(" %s" % b.sym(addr, k.t_tgid)) print(" %-16s %s %s" % ("target:", k.target.decode('utf-8', 'replace'), k.t_pid)) print(" %d\n" % v.value) if missing_stacks > 0: enomem_str = " Consider increasing --stack-storage-size." print("WARNING: %d stack traces lost and could not be displayed.%s" % (missing_stacks, (enomem_str if has_enomem else "")), file=stderr) bpfcc-0.12.0/tools/offwaketime_example.txt000066400000000000000000001121551357404205000205710ustar00rootroot00000000000000Demonstrations of offwaketime, the Linux eBPF/bcc version. This program shows kernel stack traces and task names that were blocked and "off-CPU", along with the stack traces and task names for the threads that woke them, and the total elapsed time from when they blocked to when they were woken up. This combines the summaries from both the offwaketime and wakeuptime tools. The time measurement will be very similar to off-CPU time, however, off-CPU time may include a little extra time spent waiting on a run queue to be scheduled. The combined stacks, task names, and total time is summarized in kernel context for efficiency, using an eBPF map. The output summary will further help you identify reasons why threads were blocking, and quantify the time from when they were blocked to woken up. This spans all types of blocking activity: disk I/O, network I/O, locks, page faults, swapping, sleeping, involuntary context switches, etc. Here is some sample output from a 5 second trace, truncated to highlight several stack pairs: # ./offwaketime 5 Tracing blocked time (us) by kernel off-CPU and waker stack for 5 secs. [...] waker: swapper/0 ffffffff8137897c blk_mq_complete_request ffffffff81378930 __blk_mq_complete_request ffffffff81378793 blk_mq_end_request ffffffff813778b9 blk_mq_free_request ffffffff8137782d __blk_mq_free_request ffffffff8137bc57 blk_mq_put_tag ffffffff8137b2c7 bt_clear_tag ffffffff810b54d9 __wake_up ffffffff810b5462 __wake_up_common ffffffff810b5b12 autoremove_wake_function - - ffffffff81785085 schedule ffffffff81787e16 schedule_timeout ffffffff81784634 __sched_text_start ffffffff8137b839 bt_get ffffffff8137bbf7 blk_mq_get_tag ffffffff8137761b __blk_mq_alloc_request ffffffff81379442 blk_mq_map_request ffffffff8137a445 blk_sq_make_request ffffffff8136ebc3 generic_make_request ffffffff8136ed07 submit_bio ffffffff81225adf submit_bh_wbc ffffffff81225b42 submit_bh ffffffff812721e0 __ext4_get_inode_loc ffffffff812751dd ext4_iget ffffffff81275c90 ext4_iget_normal ffffffff8127f45b ext4_lookup ffffffff811f94ed lookup_real ffffffff811fad43 __lookup_hash ffffffff811fc3fb walk_component ffffffff811fd050 link_path_walk target: cksum 56529 [...] waker: swapper/1 ffffffff81475cf0 xen_evtchn_do_upcall ffffffff81473e83 __xen_evtchn_do_upcall ffffffff814766f7 evtchn_2l_handle_events ffffffff810cb0c2 generic_handle_irq ffffffff810cf1ca handle_percpu_irq ffffffff810cb9c8 handle_irq_event_percpu ffffffff8100b9e1 xen_timer_interrupt ffffffff810dfba8 hrtimer_interrupt ffffffff810df494 __hrtimer_run_queues ffffffff810df082 hrtimer_wakeup - - ffffffff81785085 schedule ffffffff817880bf do_nanosleep ffffffff810e003d hrtimer_nanosleep ffffffff810e018c sys_nanosleep ffffffff81789076 entry_SYSCALL_64_fastpath target: vmstat 3000331 [...] waker: swapper/0 ffffffff81378930 __blk_mq_complete_request ffffffff8137875a blk_mq_end_request ffffffff8136f157 blk_update_request ffffffff8136836f bio_endio ffffffff812ba709 mpage_end_io ffffffff81176af9 unlock_page ffffffff810b5781 __wake_up_bit ffffffff810b54d9 __wake_up ffffffff810b5462 __wake_up_common ffffffff810b5b7e wake_bit_function - - ffffffff81785085 schedule ffffffff81787e16 schedule_timeout ffffffff81784634 __sched_text_start ffffffff8178586b bit_wait_io ffffffff8178563e __wait_on_bit_lock ffffffff8117616e __lock_page_killable ffffffff81177fce generic_file_read_iter ffffffff811ef9c7 __vfs_read ffffffff811f0206 vfs_read ffffffff811f0eb6 sys_read ffffffff81789076 entry_SYSCALL_64_fastpath target: cksum 4334521 [...] waker: kworker/u16:2 ffffffff8178940f ret_from_fork ffffffff81092979 kthread ffffffff8108caeb worker_thread ffffffff8108c80a process_one_work ffffffff81496df5 flush_to_ldisc ffffffff81494424 n_tty_receive_buf2 ffffffff814939fd n_tty_receive_buf_common ffffffff810b54d9 __wake_up ffffffff810b5462 __wake_up_common ffffffff812037b6 pollwake - - ffffffff81785085 schedule ffffffff81788234 schedule_hrtimeout_range_clock ffffffff81788253 schedule_hrtimeout_range ffffffff812035d4 poll_schedule_timeout ffffffff8120402a do_select ffffffff812042f0 core_sys_select ffffffff8120449b sys_select ffffffff81789076 entry_SYSCALL_64_fastpath target: sshd 6530897 [...] waker: swapper/0 ffffffff81475cf0 xen_evtchn_do_upcall ffffffff81473e83 __xen_evtchn_do_upcall ffffffff814766f7 evtchn_2l_handle_events ffffffff810cb0c2 generic_handle_irq ffffffff810cf1ca handle_percpu_irq ffffffff810cb9c8 handle_irq_event_percpu ffffffff8100b9e1 xen_timer_interrupt ffffffff810dfba8 hrtimer_interrupt ffffffff810df494 __hrtimer_run_queues ffffffff810df082 hrtimer_wakeup - - ffffffff81785085 schedule ffffffff81787fc3 schedule_hrtimeout_range_clock.part.23 ffffffff81788219 schedule_hrtimeout_range_clock ffffffff81788253 schedule_hrtimeout_range ffffffff812035d4 poll_schedule_timeout ffffffff81204b6d do_sys_poll ffffffff81204cf2 sys_poll ffffffff81789076 entry_SYSCALL_64_fastpath target: supervise 16332240 Detaching... The output includes two paths from the cksum(1) command, one for reading files via vfs_read() and the other doing a link_path_walk(). There is also a vmstat(8) stack showing it sleeping between intervals, and an sshd(8) stack showing it waiting on a file descriptor for input. The stack shown at the bottom is the off-CPU stack belonging to the task name shown after "target:". Then there is a separator, "-", and above it the waker stack and the waker task name after "waker:". The wakeup stack is printed in reverse order. The number beneath the stacks is the total time spent from the blocking event to the wakeup event. This is summed for all occurrences with the same stack pairs. The -u option will print user-mode target threads only, and the -f option will show the stacks in "folded stacks" format. Eg: # ./offwaketime -fu 5 supervise;entry_SYSCALL_64_fastpath;sys_rename;dput;__dentry_kill;iput;evict;ext4_evict_inode;ext4_truncate;ext4_ext_truncate;ext4_ext_remove_space;ext4_free_blocks;__ext4_handle_dirty_metadata;_cond_resched;preempt_schedule_common;-; 2 sshd;entry_SYSCALL_64_fastpath;sys_read;vfs_read;__vfs_read;tty_read;n_tty_read;down_read;_cond_resched;preempt_schedule_common;-; 2 cksum;entry_SYSCALL_64_fastpath;sys_open;do_sys_open;do_filp_open;path_openat;lookup_real;ext4_lookup;ext4_iget_normal;ext4_iget;iget_locked;alloc_inode;ext4_alloc_inode;kmem_cache_alloc;_cond_resched;preempt_schedule_common;-; 3 mkdir;page_fault;do_page_fault;__do_page_fault;handle_mm_fault;anon_vma_prepare;_cond_resched;preempt_schedule_common;-; 3 cksum;entry_SYSCALL_64_fastpath;sys_open;do_sys_open;do_filp_open;path_openat;link_path_walk;walk_component;__lookup_hash;lookup_real;ext4_lookup;ext4_iget_normal;ext4_iget;__ext4_get_inode_loc;__breadahead;ll_rw_block;submit_bh_wbc;bio_alloc_bioset;mempool_alloc;_cond_resched;preempt_schedule_common;-; 3 cksum;entry_SYSCALL_64_fastpath;sys_read;vfs_read;__vfs_read;generic_file_read_iter;page_cache_sync_readahead;ondemand_readahead;__do_page_cache_readahead;ext4_readpages;ext4_mpage_readpages;ext4_map_blocks;down_read;_cond_resched;preempt_schedule_common;-; 3 cksum;entry_SYSCALL_64_fastpath;sys_open;do_sys_open;fd_install;__fd_install;_cond_resched;preempt_schedule_common;-; 3 run;return_from_execve;sys_execve;do_execveat_common.isra.33;search_binary_handler;load_script;search_binary_handler;load_elf_binary;elf_map;vm_munmap;down_write;_cond_resched;preempt_schedule_common;-; 3 svscan;entry_SYSCALL_64_fastpath;sys_getdents;iterate_dir;ext4_readdir;ext4_htree_fill_tree;htree_dirblock_to_tree;ext4_htree_store_dirent;__kmalloc;_cond_resched;preempt_schedule_common;-; 4 cksum;entry_SYSCALL_64_fastpath;sys_open;do_sys_open;do_filp_open;path_openat;mutex_lock;_cond_resched;preempt_schedule_common;-; 4 run;entry_SYSCALL_64_fastpath;sys_mprotect;down_write;_cond_resched;preempt_schedule_common;-; 5 sshd;entry_SYSCALL_64_fastpath;sys_select;core_sys_select;do_select;_cond_resched;preempt_schedule_common;-; 5 run;return_from_execve;sys_execve;do_execveat_common.isra.33;search_binary_handler;load_script;search_binary_handler;load_elf_binary;set_brk;vm_brk;down_write;_cond_resched;preempt_schedule_common;-; 5 supervise;entry_SYSCALL_64_fastpath;sys_clone;_do_fork;copy_process;anon_vma_fork;anon_vma_clone;down_write;_cond_resched;preempt_schedule_common;-; 6 svscan;retint_user;prepare_exit_to_usermode;exit_to_usermode_loop;schedule;-;hrtimer_wakeup;__hrtimer_run_queues;hrtimer_interrupt;xen_timer_interrupt;handle_irq_event_percpu;handle_percpu_irq;generic_handle_irq;evtchn_2l_handle_events;__xen_evtchn_do_upcall;xen_evtchn_do_upcall;swapper/0 11 supervise;return_from_execve;sys_execve;do_execveat_common.isra.33;search_binary_handler;load_script;search_binary_handler;load_elf_binary;flush_old_exec;mmput;exit_mmap;free_pgtables;unlink_anon_vmas;down_write;_cond_resched;preempt_schedule_common;-; 12 run;return_from_execve;sys_execve;do_execveat_common.isra.33;search_binary_handler;load_elf_binary;flush_old_exec;mmput;_cond_resched;preempt_schedule_common;-; 13 sshd;entry_SYSCALL_64_fastpath;sys_write;vfs_write;__vfs_write;sock_write_iter;sock_sendmsg;inet_sendmsg;tcp_sendmsg;lock_sock_nested;_cond_resched;preempt_schedule_common;-; 14 cksum;entry_SYSCALL_64_fastpath;sys_write;vfs_write;__vfs_write;tty_write;n_tty_write;mutex_lock;_cond_resched;preempt_schedule_common;-; 19 sshd;retint_user;prepare_exit_to_usermode;exit_to_usermode_loop;schedule;-; 24 run;return_from_execve;sys_execve;do_execveat_common.isra.33;search_binary_handler;load_script;search_binary_handler;load_elf_binary;vm_brk;down_write;_cond_resched;preempt_schedule_common;-; 31 sshd;entry_SYSCALL_64_fastpath;sys_write;vfs_write;__vfs_write;sock_write_iter;sock_sendmsg;inet_sendmsg;tcp_sendmsg;sk_stream_alloc_skb;__alloc_skb;kmem_cache_alloc_node;_cond_resched;preempt_schedule_common;-; 32 run;page_fault;do_page_fault;__do_page_fault;_cond_resched;preempt_schedule_common;-; 33 run;page_fault;do_page_fault;__do_page_fault;handle_mm_fault;anon_vma_prepare;_cond_resched;preempt_schedule_common;-; 33 run;entry_SYSCALL_64_fastpath;sys_clone;_do_fork;copy_process;down_write;_cond_resched;preempt_schedule_common;-; 35 run;page_fault;do_page_fault;__do_page_fault;handle_mm_fault;__do_fault;filemap_fault;_cond_resched;preempt_schedule_common;-; 36 run;return_from_execve;sys_execve;do_execveat_common.isra.33;search_binary_handler;load_script;search_binary_handler;load_elf_binary;elf_map;vm_mmap;vm_mmap_pgoff;down_write;_cond_resched;preempt_schedule_common;-; 38 cksum;entry_SYSCALL_64_fastpath;sys_open;do_sys_open;do_filp_open;path_openat;lookup_real;ext4_lookup;ext4_iget_normal;ext4_iget;__ext4_get_inode_loc;__getblk_gfp;_cond_resched;preempt_schedule_common;-; 38 chmod;int_ret_from_sys_call;syscall_return_slowpath;exit_to_usermode_loop;schedule;-; 39 run;entry_SYSCALL_64_fastpath;sys_munmap;vm_munmap;do_munmap;unmap_region;unmap_vmas;unmap_single_vma;_cond_resched;preempt_schedule_common;-; 41 readproctitle;entry_SYSCALL_64_fastpath;sys_read;vfs_read;__vfs_read;pipe_read;mutex_lock;_cond_resched;preempt_schedule_common;-; 44 run;entry_SYSCALL_64_fastpath;sys_clone;_do_fork;copy_process;kmem_cache_alloc;_cond_resched;preempt_schedule_common;-; 48 run;return_from_execve;sys_execve;do_execveat_common.isra.33;search_binary_handler;load_elf_binary;flush_old_exec;mmput;exit_mmap;unmap_vmas;unmap_single_vma;_cond_resched;preempt_schedule_common;-; 49 sshd;entry_SYSCALL_64_fastpath;sys_select;core_sys_select;do_select;tty_poll;tty_ldisc_ref_wait;ldsem_down_read;_cond_resched;preempt_schedule_common;-; 50 supervise;return_from_execve;sys_execve;do_execveat_common.isra.33;search_binary_handler;load_script;remove_arg_zero;get_user_pages;__get_user_pages;_cond_resched;preempt_schedule_common;-; 50 readproctitle;int_ret_from_sys_call;syscall_return_slowpath;exit_to_usermode_loop;schedule;-; 51 mkdir;int_ret_from_sys_call;syscall_return_slowpath;exit_to_usermode_loop;schedule;-; 53 supervise;entry_SYSCALL_64_fastpath;sys_clone;_do_fork;copy_process;copy_creds;prepare_creds;kmem_cache_alloc;_cond_resched;preempt_schedule_common;-; 66 cksum;entry_SYSCALL_64_fastpath;sys_open;do_sys_open;do_filp_open;path_openat;lookup_real;ext4_lookup;ext4_find_entry;__ext4_read_dirblock;ext4_bread;ext4_getblk;__getblk_gfp;_cond_resched;preempt_schedule_common;-; 76 run;return_from_execve;sys_execve;do_execveat_common.isra.33;search_binary_handler;load_elf_binary;kernel_read;vfs_read;__vfs_read;generic_file_read_iter;_cond_resched;preempt_schedule_common;-; 96 chmod;entry_SYSCALL_64_fastpath;sys_exit_group;do_group_exit;do_exit;mmput;exit_mmap;unmap_vmas;unmap_single_vma;_cond_resched;preempt_schedule_common;-; 100 run;page_fault;do_page_fault;__do_page_fault;handle_mm_fault;__do_fault;filemap_fault;__lock_page_or_retry;wait_on_page_bit_killable;__wait_on_bit;bit_wait_io;__sched_text_start;schedule_timeout;schedule;-;wake_bit_function;__wake_up_common;__wake_up;__wake_up_bit;unlock_page;handle_mm_fault;__do_page_fault;do_page_fault;page_fault;;run 117 run;entry_SYSCALL_64_fastpath;sys_clone;_do_fork;copy_process;copy_page_range;_cond_resched;preempt_schedule_common;-; 117 cksum;entry_SYSCALL_64_fastpath;sys_read;vfs_read;__vfs_read;generic_file_read_iter;_cond_resched;preempt_schedule_common;-; 121 chown;entry_SYSCALL_64_fastpath;sys_mmap;sys_mmap_pgoff;vm_mmap_pgoff;down_write;_cond_resched;preempt_schedule_common;-; 137 chown;entry_SYSCALL_64_fastpath;sys_mmap;sys_mmap_pgoff;vm_mmap_pgoff;do_mmap;mmap_region;kmem_cache_alloc;_cond_resched;preempt_schedule_common;-; 138 run;return_from_execve;sys_execve;do_execveat_common.isra.33;count.isra.21.constprop.38;_cond_resched;preempt_schedule_common;-; 145 supervise;entry_SYSCALL_64_fastpath;sys_rename;dput;__dentry_kill;iput;evict;ext4_evict_inode;truncate_inode_pages_final;truncate_inode_pages_range;wait_on_page_bit;__wait_on_bit;bit_wait_io;__sched_text_start;schedule_timeout;schedule;-;wake_bit_function;__wake_up_common;__wake_up;__wake_up_bit;end_page_writeback;ext4_finish_bio;ext4_end_bio;bio_endio;blk_update_request;blk_mq_end_request;mkdir 147 chmod;return_from_execve;sys_execve;do_execveat_common.isra.33;search_binary_handler;load_elf_binary;clear_user;page_fault;do_page_fault;__do_page_fault;_cond_resched;preempt_schedule_common;-; 159 chown;return_from_execve;sys_execve;do_execveat_common.isra.33;search_binary_handler;load_elf_binary;setup_arg_pages;shift_arg_pages;vma_adjust;down_write;_cond_resched;preempt_schedule_common;-; 173 chown;retint_user;prepare_exit_to_usermode;exit_to_usermode_loop;schedule;-; 176 chmod;retint_user;prepare_exit_to_usermode;exit_to_usermode_loop;schedule;-; 191 chmod;entry_SYSCALL_64_fastpath;sys_fchmodat;chmod_common;notify_change;ext4_setattr;__mark_inode_dirty;ext4_dirty_inode;ext4_mark_inode_dirty;ext4_reserve_inode_write;__ext4_get_inode_loc;__getblk_gfp;_cond_resched;preempt_schedule_common;-; 221 cksum;entry_SYSCALL_64_fastpath;sys_open;do_sys_open;do_filp_open;path_openat;trailing_symlink;page_follow_link_light;page_getlink.isra.34.constprop.38;read_cache_page;do_read_cache_page;wait_on_page_read;wait_on_page_bit;__wait_on_bit;bit_wait_io;__sched_text_start;schedule_timeout;schedule;-;wake_bit_function;__wake_up_common;__wake_up;__wake_up_bit;unlock_page;mpage_end_io;bio_endio;blk_update_request;blk_mq_end_request;__blk_mq_complete_request;swapper/0 230 cksum;entry_SYSCALL_64_fastpath;sys_open;do_sys_open;do_filp_open;path_openat;lookup_real;ext4_lookup;ext4_find_entry;__ext4_read_dirblock;ext4_bread;__wait_on_buffer;out_of_line_wait_on_bit;__wait_on_bit;bit_wait_io;__sched_text_start;schedule_timeout;schedule;-;wake_bit_function;__wake_up_common;__wake_up;__wake_up_bit;wake_up_bit;unlock_buffer;__end_buffer_read_notouch;end_buffer_read_sync;end_bio_bh_io_sync;bio_endio;rcu_sched 231 supervise;return_from_execve;sys_execve;do_execveat_common.isra.33;search_binary_handler;load_script;search_binary_handler;load_elf_binary;flush_old_exec;mmput;_cond_resched;preempt_schedule_common;-; 234 chown;int_ret_from_sys_call;syscall_return_slowpath;exit_to_usermode_loop;schedule;-; 249 cksum;entry_SYSCALL_64_fastpath;sys_read;vfs_read;__vfs_read;generic_file_read_iter;__lock_page_killable;__wait_on_bit_lock;bit_wait_io;__sched_text_start;schedule_timeout;schedule;-;wake_bit_function;__wake_up_common;__wake_up;__wake_up_bit;unlock_page;mpage_end_io;bio_endio;blk_update_request;blk_mq_end_request;__blk_mq_complete_request;svscan 273 mkdir;entry_SYSCALL_64_fastpath;sys_exit_group;do_group_exit;do_exit;mmput;exit_mmap;unmap_vmas;unmap_single_vma;_cond_resched;preempt_schedule_common;-; 382 supervise;return_from_execve;sys_execve;do_execveat_common.isra.33;search_binary_handler;load_script;search_binary_handler;load_elf_binary;flush_old_exec;mmput;exit_mmap;unmap_vmas;unmap_single_vma;_cond_resched;preempt_schedule_common;-; 389 run;page_fault;do_page_fault;__do_page_fault;handle_mm_fault;do_wp_page;wp_page_copy.isra.57;anon_vma_prepare;_cond_resched;preempt_schedule_common;-; 390 supervise;return_from_execve;sys_execve;do_execveat_common.isra.33;sched_exec;stop_one_cpu;wait_for_completion;_cond_resched;preempt_schedule_common;-; 409 run;return_from_execve;sys_execve;do_execveat_common.isra.33;sched_exec;stop_one_cpu;wait_for_completion;_cond_resched;preempt_schedule_common;-; 419 mkdir;retint_user;prepare_exit_to_usermode;exit_to_usermode_loop;schedule;-; 457 cksum;entry_SYSCALL_64_fastpath;sys_read;vfs_read;__vfs_read;generic_file_read_iter;__lock_page_killable;__wait_on_bit_lock;bit_wait_io;__sched_text_start;schedule_timeout;schedule;-;wake_bit_function;__wake_up_common;__wake_up;__wake_up_bit;unlock_page;mpage_end_io;bio_endio;blk_update_request;blk_mq_end_request;__blk_mq_complete_request;rcuos/0 460 run;entry_SYSCALL_64_fastpath;sys_exit_group;do_group_exit;do_exit;mmput;exit_mmap;unmap_vmas;unmap_single_vma;_cond_resched;preempt_schedule_common;-; 481 sshd;int_ret_from_sys_call;syscall_return_slowpath;exit_to_usermode_loop;schedule;-; 495 cksum;do_filp_open;path_openat;lookup_real;ext4_lookup;ext4_find_entry;dx_probe;__ext4_read_dirblock;ext4_bread;ext4_getblk;ext4_map_blocks;ext4_ext_map_blocks;ext4_find_extent;__read_extent_tree_block;bh_submit_read;out_of_line_wait_on_bit;__wait_on_bit;bit_wait_io;__sched_text_start;schedule_timeout;schedule;-;wake_bit_function;__wake_up_common;__wake_up;__wake_up_bit;wake_up_bit;unlock_buffer;__end_buffer_read_notouch;end_buffer_read_sync;end_bio_bh_io_sync;bio_endio;swapper/0 495 cksum;entry_SYSCALL_64_fastpath;sys_open;do_sys_open;do_filp_open;path_openat;lookup_real;ext4_lookup;ext4_find_entry;dx_probe;__ext4_read_dirblock;ext4_bread;__wait_on_buffer;out_of_line_wait_on_bit;__wait_on_bit;bit_wait_io;__sched_text_start;schedule_timeout;schedule;-;wake_bit_function;__wake_up_common;__wake_up;__wake_up_bit;wake_up_bit;unlock_buffer;__end_buffer_read_notouch;end_buffer_read_sync;end_bio_bh_io_sync;bio_endio;swapper/0 514 run;entry_SYSCALL_64_fastpath;sys_clone;_do_fork;copy_process;alloc_pid;kmem_cache_alloc;_cond_resched;preempt_schedule_common;-; 572 run;return_from_execve;sys_execve;do_execveat_common.isra.33;search_binary_handler;load_script;search_binary_handler;load_elf_binary;clear_user;page_fault;do_page_fault;__do_page_fault;handle_mm_fault;__pte_alloc;pte_alloc_one;alloc_pages_current;__alloc_pages_nodemask;_cond_resched;preempt_schedule_common;-; 579 supervise;entry_SYSCALL_64_fastpath;sys_clone;_do_fork;copy_process;copy_page_range;_cond_resched;preempt_schedule_common;-; 590 cksum;int_ret_from_sys_call;syscall_return_slowpath;exit_to_usermode_loop;schedule;-; 592 chmod;return_from_execve;sys_execve;do_execveat_common.isra.33;search_binary_handler;load_elf_binary;setup_arg_pages;shift_arg_pages;vma_adjust;down_write;_cond_resched;preempt_schedule_common;-; 697 cksum;entry_SYSCALL_64_fastpath;sys_open;do_sys_open;do_filp_open;path_openat;lookup_real;ext4_lookup;ext4_find_entry;__ext4_read_dirblock;ext4_bread;__wait_on_buffer;out_of_line_wait_on_bit;__wait_on_bit;bit_wait_io;__sched_text_start;schedule_timeout;schedule;-;wake_bit_function;__wake_up_common;__wake_up;__wake_up_bit;wake_up_bit;unlock_buffer;__end_buffer_read_notouch;end_buffer_read_sync;end_bio_bh_io_sync;bio_endio;swapper/0 706 cksum;entry_SYSCALL_64_fastpath;sys_write;vfs_write;__vfs_write;tty_write;n_tty_write;mutex_lock;_cond_resched;preempt_schedule_common;-;woken_wake_function;__wake_up_common;__wake_up;n_tty_read;tty_read;__vfs_read;vfs_read;sys_read;entry_SYSCALL_64_fastpath;;sshd 804 supervise;retint_user;prepare_exit_to_usermode;exit_to_usermode_loop;schedule;-; 1101 run;return_from_execve;sys_execve;do_execveat_common.isra.33;search_binary_handler;load_elf_binary;flush_old_exec;mmput;exit_mmap;free_pgtables;unlink_anon_vmas;__put_anon_vma;_cond_resched;preempt_schedule_common;-; 1122 cksum;entry_SYSCALL_64_fastpath;sys_read;vfs_read;__vfs_read;generic_file_read_iter;__lock_page_killable;__wait_on_bit_lock;bit_wait_io;__sched_text_start;schedule_timeout;schedule;-;wake_bit_function;__wake_up_common;__wake_up;__wake_up_bit;unlock_page;mpage_end_io;bio_endio;blk_update_request;blk_mq_end_request;__blk_mq_complete_request;readproctitle 1319 run;int_ret_from_sys_call;syscall_return_slowpath;exit_to_usermode_loop;schedule;-; 1902 cksum;entry_SYSCALL_64_fastpath;sys_read;vfs_read;__vfs_read;generic_file_read_iter;__lock_page_killable;__wait_on_bit_lock;bit_wait_io;__sched_text_start;schedule_timeout;schedule;-;wake_bit_function;__wake_up_common;__wake_up;__wake_up_bit;unlock_page;mpage_end_io;bio_endio;blk_update_request;blk_mq_end_request;__blk_mq_complete_request;chown 1925 supervise;entry_SYSCALL_64_fastpath;sys_rename;dput;__dentry_kill;iput;evict;ext4_evict_inode;truncate_inode_pages_final;truncate_inode_pages_range;wait_on_page_bit;__wait_on_bit;bit_wait_io;__sched_text_start;schedule_timeout;schedule;-;wake_bit_function;__wake_up_common;__wake_up;__wake_up_bit;end_page_writeback;ext4_finish_bio;ext4_end_bio;bio_endio;blk_update_request;blk_mq_end_request;cksum 2181 cksum;entry_SYSCALL_64_fastpath;sys_open;do_sys_open;do_filp_open;path_openat;link_path_walk;walk_component;__lookup_hash;lookup_real;ext4_lookup;ext4_find_entry;__wait_on_buffer;out_of_line_wait_on_bit;__wait_on_bit;bit_wait_io;__sched_text_start;schedule_timeout;schedule;-;wake_bit_function;__wake_up_common;__wake_up;__wake_up_bit;wake_up_bit;unlock_buffer;__end_buffer_read_notouch;end_buffer_read_sync;end_bio_bh_io_sync;bio_endio;swapper/0 2599 cksum;retint_user;prepare_exit_to_usermode;exit_to_usermode_loop;schedule;-; 2816 supervise;ext4_rename2;ext4_rename;ext4_alloc_da_blocks;filemap_flush;__filemap_fdatawrite_range;do_writepages;ext4_writepages;ext4_map_blocks;ext4_ext_map_blocks;ext4_mb_new_blocks;ext4_mb_mark_diskspace_used;__ext4_journal_get_write_access;jbd2_journal_get_write_access;do_get_write_access;out_of_line_wait_on_bit;__wait_on_bit;bit_wait_io;__sched_text_start;schedule_timeout;schedule;-;wake_bit_function;__wake_up_common;__wake_up;__wake_up_bit;wake_up_bit;journal_end_buffer_io_sync;end_bio_bh_io_sync;bio_endio;blk_update_request;blk_mq_end_request;swapper/0 3393 supervise;entry_SYSCALL_64_fastpath;sys_rename;dput;__dentry_kill;iput;evict;ext4_evict_inode;truncate_inode_pages_final;truncate_inode_pages_range;wait_on_page_bit;__wait_on_bit;bit_wait_io;__sched_text_start;schedule_timeout;schedule;-;wake_bit_function;__wake_up_common;__wake_up;__wake_up_bit;end_page_writeback;ext4_finish_bio;ext4_end_bio;bio_endio;blk_update_request;blk_mq_end_request;supervise 5398 sshd;entry_SYSCALL_64_fastpath;sys_select;core_sys_select;do_select;poll_schedule_timeout;schedule_hrtimeout_range;schedule_hrtimeout_range_clock;schedule;-;pollwake;__wake_up_common;__wake_up_sync_key;sock_def_readable;tcp_data_queue;tcp_rcv_established;tcp_v4_do_rcv;tcp_v4_rcv;ip_local_deliver_finish;ip_local_deliver;mkdir 6582 cksum;entry_SYSCALL_64_fastpath;sys_read;vfs_read;__vfs_read;generic_file_read_iter;__lock_page_killable;__wait_on_bit_lock;bit_wait_io;__sched_text_start;schedule_timeout;schedule;-;wake_bit_function;__wake_up_common;__wake_up;__wake_up_bit;unlock_page;mpage_end_io;bio_endio;blk_update_request;blk_mq_end_request;__blk_mq_complete_request;chmod 8310 run;retint_user;prepare_exit_to_usermode;exit_to_usermode_loop;schedule;-; 8444 supervise;entry_SYSCALL_64_fastpath;sys_rename;dput;__dentry_kill;iput;evict;ext4_evict_inode;truncate_inode_pages_final;truncate_inode_pages_range;wait_on_page_bit;__wait_on_bit;bit_wait_io;__sched_text_start;schedule_timeout;schedule;-;wake_bit_function;__wake_up_common;__wake_up;__wake_up_bit;end_page_writeback;ext4_finish_bio;ext4_end_bio;bio_endio;blk_update_request;blk_mq_end_request;readproctitle 9768 supervise;entry_SYSCALL_64_fastpath;sys_rename;dput;__dentry_kill;iput;evict;ext4_evict_inode;truncate_inode_pages_final;truncate_inode_pages_range;wait_on_page_bit;__wait_on_bit;bit_wait_io;__sched_text_start;schedule_timeout;schedule;-;wake_bit_function;__wake_up_common;__wake_up;__wake_up_bit;end_page_writeback;ext4_finish_bio;ext4_end_bio;bio_endio;blk_update_request;blk_mq_end_request;run 9945 cksum;entry_SYSCALL_64_fastpath;sys_read;vfs_read;__vfs_read;generic_file_read_iter;__lock_page_killable;__wait_on_bit_lock;bit_wait_io;__sched_text_start;schedule_timeout;schedule;-;wake_bit_function;__wake_up_common;__wake_up;__wake_up_bit;unlock_page;mpage_end_io;bio_endio;blk_update_request;blk_mq_end_request;__blk_mq_complete_request;mkdir 11978 cksum;entry_SYSCALL_64_fastpath;sys_open;do_sys_open;do_filp_open;path_openat;link_path_walk;walk_component;__lookup_hash;lookup_real;ext4_lookup;ext4_iget_normal;ext4_iget;__ext4_get_inode_loc;__wait_on_buffer;out_of_line_wait_on_bit;__wait_on_bit;bit_wait_io;__sched_text_start;schedule_timeout;schedule;-;wake_bit_function;__wake_up_common;__wake_up;__wake_up_bit;wake_up_bit;unlock_buffer;__end_buffer_read_notouch;end_buffer_read_sync;end_bio_bh_io_sync;bio_endio;swapper/0 12120 cksum;link_path_walk;walk_component;__lookup_hash;lookup_real;ext4_lookup;ext4_iget_normal;ext4_iget;__ext4_get_inode_loc;submit_bh;submit_bh_wbc;submit_bio;generic_make_request;blk_sq_make_request;blk_mq_map_request;__blk_mq_alloc_request;blk_mq_get_tag;bt_get;__sched_text_start;schedule_timeout;schedule;-;autoremove_wake_function;__wake_up_common;__wake_up;bt_clear_tag;blk_mq_put_tag;__blk_mq_free_request;blk_mq_free_request;blk_mq_end_request;__blk_mq_complete_request;blk_mq_complete_request;swapper/0 23243 cksum;entry_SYSCALL_64_fastpath;sys_open;do_sys_open;do_filp_open;path_openat;lookup_real;ext4_lookup;ext4_find_entry;__wait_on_buffer;out_of_line_wait_on_bit;__wait_on_bit;bit_wait_io;__sched_text_start;schedule_timeout;schedule;-;wake_bit_function;__wake_up_common;__wake_up;__wake_up_bit;wake_up_bit;unlock_buffer;__end_buffer_read_notouch;end_buffer_read_sync;end_bio_bh_io_sync;bio_endio;swapper/0 24767 run;entry_SYSCALL_64_fastpath;sys_wait4;do_wait;schedule;-;child_wait_callback;__wake_up_common;__wake_up_sync_key;__wake_up_parent;do_notify_parent;do_exit;do_group_exit;sys_exit_group;entry_SYSCALL_64_fastpath;;chmod 33289 run;entry_SYSCALL_64_fastpath;sys_wait4;do_wait;schedule;-;child_wait_callback;__wake_up_common;__wake_up_sync_key;__wake_up_parent;do_notify_parent;do_exit;do_group_exit;sys_exit_group;entry_SYSCALL_64_fastpath;;mkdir 34991 cksum;entry_SYSCALL_64_fastpath;sys_read;vfs_read;__vfs_read;generic_file_read_iter;__lock_page_killable;__wait_on_bit_lock;bit_wait_io;__sched_text_start;schedule_timeout;schedule;-;wake_bit_function;__wake_up_common;__wake_up;__wake_up_bit;unlock_page;mpage_end_io;bio_endio;blk_update_request;blk_mq_end_request;__blk_mq_complete_request;supervise 35746 run;entry_SYSCALL_64_fastpath;sys_wait4;do_wait;schedule;-;child_wait_callback;__wake_up_common;__wake_up_sync_key;__wake_up_parent;do_notify_parent;do_exit;do_group_exit;sys_exit_group;entry_SYSCALL_64_fastpath;;chown 36942 supervise;entry_SYSCALL_64_fastpath;sys_rename;dput;__dentry_kill;iput;evict;ext4_evict_inode;truncate_inode_pages_final;truncate_inode_pages_range;wait_on_page_bit;__wait_on_bit;bit_wait_io;__sched_text_start;schedule_timeout;schedule;-;wake_bit_function;__wake_up_common;__wake_up;__wake_up_bit;end_page_writeback;ext4_finish_bio;ext4_end_bio;bio_endio;blk_update_request;blk_mq_end_request;swapper/0 42993 cksum;entry_SYSCALL_64_fastpath;sys_open;do_sys_open;do_filp_open;path_openat;lookup_real;ext4_lookup;ext4_iget_normal;ext4_iget;__ext4_get_inode_loc;__wait_on_buffer;out_of_line_wait_on_bit;__wait_on_bit;bit_wait_io;__sched_text_start;schedule_timeout;schedule;-;wake_bit_function;__wake_up_common;__wake_up;__wake_up_bit;wake_up_bit;unlock_buffer;__end_buffer_read_notouch;end_buffer_read_sync;end_bio_bh_io_sync;bio_endio;swapper/0 53348 sshd;entry_SYSCALL_64_fastpath;sys_select;core_sys_select;do_select;poll_schedule_timeout;schedule_hrtimeout_range;schedule_hrtimeout_range_clock;schedule;-;pollwake;__wake_up_common;__wake_up;n_tty_receive_buf_common;n_tty_receive_buf2;flush_to_ldisc;process_one_work;worker_thread;kthread;ret_from_fork;kworker/u16:2 86256 cksum;entry_SYSCALL_64_fastpath;sys_read;vfs_read;__vfs_read;generic_file_read_iter;__lock_page_killable;__wait_on_bit_lock;bit_wait_io;__sched_text_start;schedule_timeout;schedule;-;wake_bit_function;__wake_up_common;__wake_up;__wake_up_bit;unlock_page;mpage_end_io;bio_endio;blk_update_request;blk_mq_end_request;__blk_mq_complete_request;run 109480 ntpd;entry_SYSCALL_64_fastpath;sys_select;core_sys_select;do_select;poll_schedule_timeout;schedule_hrtimeout_range;schedule_hrtimeout_range_clock;schedule;-;signal_wake_up_state;complete_signal;__send_signal;send_signal;do_send_sig_info;group_send_sig_info;kill_pid_info;it_real_fn;__hrtimer_run_queues;hrtimer_interrupt;cksum 999975 ntpd;entry_SYSCALL_64_fastpath;sys_select;core_sys_select;do_select;poll_schedule_timeout;schedule_hrtimeout_range;schedule_hrtimeout_range_clock;schedule;-;signal_wake_up_state;complete_signal;__send_signal;send_signal;do_send_sig_info;group_send_sig_info;kill_pid_info;it_real_fn;__hrtimer_run_queues;hrtimer_interrupt;swapper/0 999976 supervise;entry_SYSCALL_64_fastpath;sys_poll;do_sys_poll;poll_schedule_timeout;schedule_hrtimeout_range;schedule_hrtimeout_range_clock;schedule_hrtimeout_range_clock.part.23;schedule;-;hrtimer_wakeup;__hrtimer_run_queues;hrtimer_interrupt;xen_timer_interrupt;handle_irq_event_percpu;handle_percpu_irq;generic_handle_irq;evtchn_2l_handle_events;__xen_evtchn_do_upcall;xen_evtchn_do_upcall;chmod 1021082 snmpd;entry_SYSCALL_64_fastpath;sys_select;core_sys_select;do_select;poll_schedule_timeout;schedule_hrtimeout_range;schedule_hrtimeout_range_clock;schedule_hrtimeout_range_clock.part.23;schedule;-;hrtimer_wakeup;__hrtimer_run_queues;hrtimer_interrupt;xen_timer_interrupt;handle_irq_event_percpu;handle_percpu_irq;generic_handle_irq;evtchn_2l_handle_events;__xen_evtchn_do_upcall;xen_evtchn_do_upcall;swapper/0 1726275 ntpd;entry_SYSCALL_64_fastpath;sys_select;core_sys_select;do_select;poll_schedule_timeout;schedule_hrtimeout_range;schedule_hrtimeout_range_clock;schedule;-;signal_wake_up_state;complete_signal;__send_signal;send_signal;do_send_sig_info;group_send_sig_info;kill_pid_info;it_real_fn;__hrtimer_run_queues;hrtimer_interrupt;swapper/1 1999944 supervise;entry_SYSCALL_64_fastpath;sys_poll;do_sys_poll;poll_schedule_timeout;schedule_hrtimeout_range;schedule_hrtimeout_range_clock;schedule_hrtimeout_range_clock.part.23;schedule;-;hrtimer_wakeup;__hrtimer_run_queues;hrtimer_interrupt;xen_timer_interrupt;handle_irq_event_percpu;handle_percpu_irq;generic_handle_irq;evtchn_2l_handle_events;__xen_evtchn_do_upcall;xen_evtchn_do_upcall;cksum 2041945 cksum;entry_SYSCALL_64_fastpath;sys_read;vfs_read;__vfs_read;generic_file_read_iter;__lock_page_killable;__wait_on_bit_lock;bit_wait_io;__sched_text_start;schedule_timeout;schedule;-;wake_bit_function;__wake_up_common;__wake_up;__wake_up_bit;unlock_page;mpage_end_io;bio_endio;blk_update_request;blk_mq_end_request;__blk_mq_complete_request;swapper/0 3720413 vmstat;entry_SYSCALL_64_fastpath;sys_nanosleep;hrtimer_nanosleep;do_nanosleep;schedule;-;hrtimer_wakeup;__hrtimer_run_queues;hrtimer_interrupt;xen_timer_interrupt;handle_irq_event_percpu;handle_percpu_irq;generic_handle_irq;evtchn_2l_handle_events;__xen_evtchn_do_upcall;xen_evtchn_do_upcall;swapper/0 4000402 tail;entry_SYSCALL_64_fastpath;sys_nanosleep;hrtimer_nanosleep;do_nanosleep;schedule;-;hrtimer_wakeup;__hrtimer_run_queues;hrtimer_interrupt;xen_timer_interrupt;handle_irq_event_percpu;handle_percpu_irq;generic_handle_irq;evtchn_2l_handle_events;__xen_evtchn_do_upcall;xen_evtchn_do_upcall;swapper/0 4000447 readproctitle;entry_SYSCALL_64_fastpath;sys_read;vfs_read;__vfs_read;pipe_read;pipe_wait;schedule;-;autoremove_wake_function;__wake_up_common;__wake_up_sync_key;pipe_write;__vfs_write;vfs_write;sys_write;entry_SYSCALL_64_fastpath;;run 4149862 offwaketime.py;entry_SYSCALL_64_fastpath;sys_select;core_sys_select;do_select;poll_schedule_timeout;schedule_hrtimeout_range;schedule_hrtimeout_range_clock;schedule_hrtimeout_range_clock.part.23;schedule;-;hrtimer_wakeup;__hrtimer_run_queues;hrtimer_interrupt;xen_timer_interrupt;handle_irq_event_percpu;handle_percpu_irq;generic_handle_irq;evtchn_2l_handle_events;__xen_evtchn_do_upcall;xen_evtchn_do_upcall;swapper/1 5005058 supervise;entry_SYSCALL_64_fastpath;sys_poll;do_sys_poll;poll_schedule_timeout;schedule_hrtimeout_range;schedule_hrtimeout_range_clock;schedule_hrtimeout_range_clock.part.23;schedule;-;hrtimer_wakeup;__hrtimer_run_queues;hrtimer_interrupt;xen_timer_interrupt;handle_irq_event_percpu;handle_percpu_irq;generic_handle_irq;evtchn_2l_handle_events;__xen_evtchn_do_upcall;xen_evtchn_do_upcall;swapper/1 8168600 sshd;entry_SYSCALL_64_fastpath;sys_select;core_sys_select;do_select;poll_schedule_timeout;schedule_hrtimeout_range;schedule_hrtimeout_range_clock;schedule;-;pollwake;__wake_up_common;__wake_up;n_tty_receive_buf_common;n_tty_receive_buf2;flush_to_ldisc;process_one_work;worker_thread;kthread;ret_from_fork;kworker/u16:1 8821767 supervise;entry_SYSCALL_64_fastpath;sys_poll;do_sys_poll;poll_schedule_timeout;schedule_hrtimeout_range;schedule_hrtimeout_range_clock;schedule_hrtimeout_range_clock.part.23;schedule;-;hrtimer_wakeup;__hrtimer_run_queues;hrtimer_interrupt;xen_timer_interrupt;handle_irq_event_percpu;handle_percpu_irq;generic_handle_irq;evtchn_2l_handle_events;__xen_evtchn_do_upcall;xen_evtchn_do_upcall;run 9186846 supervise;entry_SYSCALL_64_fastpath;sys_poll;do_sys_poll;poll_schedule_timeout;schedule_hrtimeout_range;schedule_hrtimeout_range_clock;schedule_hrtimeout_range_clock.part.23;schedule;-;hrtimer_wakeup;__hrtimer_run_queues;hrtimer_interrupt;xen_timer_interrupt;handle_irq_event_percpu;handle_percpu_irq;generic_handle_irq;evtchn_2l_handle_events;__xen_evtchn_do_upcall;xen_evtchn_do_upcall;swapper/0 20415299 This output format is suitable for feeding into the open source FlameGraph software, which visualizes these. USAGE message: # ./offwaketime -h usage: offwaketime [-h] [-p PID | -t TID | -u | -k] [-U | -K] [-d] [-f] [--stack-storage-size STACK_STORAGE_SIZE] [-m MIN_BLOCK_TIME] [-M MAX_BLOCK_TIME] [duration] Summarize blocked time by kernel stack trace + waker stack positional arguments: duration duration of trace, in seconds optional arguments: -h, --help show this help message and exit -p PID, --pid PID trace this PID only -t TID, --tid TID trace this TID only -u, --user-threads-only user threads only (no kernel threads) -k, --kernel-threads-only kernel threads only (no user threads) -U, --user-stacks-only show stacks from user space only (no kernel space stacks) -K, --kernel-stacks-only show stacks from kernel space only (no user space stacks) -d, --delimited insert delimiter between kernel/user stacks -f, --folded output folded format --stack-storage-size STACK_STORAGE_SIZE the number of unique stack traces that can be stored and displayed (default 1024) -m MIN_BLOCK_TIME, --min-block-time MIN_BLOCK_TIME the amount of time in microseconds over which we store traces (default 1) -M MAX_BLOCK_TIME, --max-block-time MAX_BLOCK_TIME the amount of time in microseconds under which we store traces (default U64_MAX) examples: ./offwaketime # trace off-CPU + waker stack time until Ctrl-C ./offwaketime 5 # trace for 5 seconds only ./offwaketime -f 5 # 5 seconds, and output in folded format ./offwaketime -m 1000 # trace only events that last more than 1000 usec ./offwaketime -M 10000 # trace only events that last less than 10000 usec ./offwaketime -p 185 # only trace threads for PID 185 ./offwaketime -t 188 # only trace thread 188 ./offwaketime -u # only trace user threads (no kernel) ./offwaketime -k # only trace kernel threads (no user) ./offwaketime -U # only show user space stacks (no kernel) ./offwaketime -K # only show kernel space stacks (no user) bpfcc-0.12.0/tools/old/000077500000000000000000000000001357404205000145655ustar00rootroot00000000000000bpfcc-0.12.0/tools/old/CMakeLists.txt000066400000000000000000000002731357404205000173270ustar00rootroot00000000000000file(GLOB PY_FILES *.py) foreach(FIL ${PY_FILES}) get_filename_component(FIL_WE ${FIL} NAME_WE) install(PROGRAMS ${FIL} DESTINATION share/bcc/tools/old RENAME ${FIL_WE}) endforeach() bpfcc-0.12.0/tools/old/bashreadline.py000077500000000000000000000021051357404205000175610ustar00rootroot00000000000000#!/usr/bin/python # # bashreadline Print entered bash commands from all running shells. # For Linux, uses BCC, eBPF. Embedded C. # # This works by tracing the readline() function using a uretprobe (uprobes). # # Copyright 2016 Netflix, Inc. # Licensed under the Apache License, Version 2.0 (the "License") # # 28-Jan-2016 Brendan Gregg Created this. from __future__ import print_function from bcc import BPF from time import strftime # load BPF program bpf_text = """ #include int printret(struct pt_regs *ctx) { if (!ctx->ax) return 0; char str[80] = {}; bpf_probe_read(&str, sizeof(str), (void *)PT_REGS_RC(ctx)); bpf_trace_printk("%s\\n", &str); return 0; }; """ b = BPF(text=bpf_text) b.attach_uretprobe(name="/bin/bash", sym="readline", fn_name="printret") # header print("%-9s %-6s %s" % ("TIME", "PID", "COMMAND")) # format output while 1: try: (task, pid, cpu, flags, ts, msg) = b.trace_fields() except ValueError: continue print("%-9s %-6d %s" % (strftime("%H:%M:%S"), pid, msg)) bpfcc-0.12.0/tools/old/biosnoop.py000077500000000000000000000065301357404205000167760ustar00rootroot00000000000000#!/usr/bin/python # @lint-avoid-python-3-compatibility-imports # # biosnoop Trace block device I/O and print details including issuing PID. # For Linux, uses BCC, eBPF. # # This uses in-kernel eBPF maps to cache process details (PID and comm) by I/O # request, as well as a starting timestamp for calculating I/O latency. # # Copyright (c) 2015 Brendan Gregg. # Licensed under the Apache License, Version 2.0 (the "License") # # 16-Sep-2015 Brendan Gregg Created this. from __future__ import print_function from bcc import BPF # load BPF program b = BPF(text=""" #include #include struct val_t { u32 pid; char name[TASK_COMM_LEN]; }; BPF_HASH(start, struct request *); BPF_HASH(infobyreq, struct request *, struct val_t); // cache PID and comm by-req int trace_pid_start(struct pt_regs *ctx, struct request *req) { struct val_t val = {}; if (bpf_get_current_comm(&val.name, sizeof(val.name)) == 0) { val.pid = bpf_get_current_pid_tgid(); infobyreq.update(&req, &val); } return 0; } // time block I/O int trace_req_start(struct pt_regs *ctx, struct request *req) { u64 ts; ts = bpf_ktime_get_ns(); start.update(&req, &ts); return 0; } // output int trace_req_completion(struct pt_regs *ctx, struct request *req) { u64 *tsp, delta; u32 *pidp = 0; struct val_t *valp; // fetch timestamp and calculate delta tsp = start.lookup(&req); if (tsp == 0) { // missed tracing issue return 0; } delta = bpf_ktime_get_ns() - *tsp; // // Fetch and output issuing pid and comm. // As bpf_trace_prink() is limited to a maximum of 1 string and 2 // integers, we'll use more than one to output the data. // valp = infobyreq.lookup(&req); if (valp == 0) { bpf_trace_printk("0 0 ? %d\\n", req->__data_len); } else { bpf_trace_printk("0 %d %s %d\\n", valp->pid, valp->name, req->__data_len); } // output remaining details if (req->cmd_flags & REQ_WRITE) { bpf_trace_printk("1 W %s %d %d ?\\n", req->rq_disk->disk_name, req->__sector, delta / 1000); } else { bpf_trace_printk("1 R %s %d %d ?\\n", req->rq_disk->disk_name, req->__sector, delta / 1000); } start.delete(&req); infobyreq.delete(&req); return 0; } """, debug=0) b.attach_kprobe(event="blk_account_io_start", fn_name="trace_pid_start") b.attach_kprobe(event="blk_start_request", fn_name="trace_req_start") b.attach_kprobe(event="blk_mq_start_request", fn_name="trace_req_start") b.attach_kprobe(event="blk_account_io_completion", fn_name="trace_req_completion") # header print("%-14s %-14s %-6s %-7s %-2s %-9s %-7s %7s" % ("TIME(s)", "COMM", "PID", "DISK", "T", "SECTOR", "BYTES", "LAT(ms)")) start_ts = 0 # format output while 1: (task, pid, cpu, flags, ts, msg) = b.trace_fields() args = msg.split(" ") if start_ts == 0: start_ts = ts if args[0] == "0": (real_pid, real_comm, bytes_s) = (args[1], args[2], args[3]) continue else: (type_s, disk_s, sector_s, us_s) = (args[1], args[2], args[3], args[4]) ms = float(int(us_s, 10)) / 1000 print("%-14.9f %-14.14s %-6s %-7s %-2s %-9s %-7s %7.2f" % ( ts - start_ts, real_comm, real_pid, disk_s, type_s, sector_s, bytes_s, ms)) bpfcc-0.12.0/tools/old/filelife.py000077500000000000000000000051641357404205000167270ustar00rootroot00000000000000#!/usr/bin/python # @lint-avoid-python-3-compatibility-imports # # filelife Trace the lifespan of short-lived files. # For Linux, uses BCC, eBPF. Embedded C. # # This traces the creation and deletion of files, providing information # on who deleted the file, the file age, and the file name. The intent is to # provide information on short-lived files, for debugging or performance # analysis. # # USAGE: filelife [-h] [-p PID] # # Copyright 2016 Netflix, Inc. # Licensed under the Apache License, Version 2.0 (the "License") # # 08-Feb-2015 Brendan Gregg Created this. from __future__ import print_function from bcc import BPF import argparse from time import strftime # arguments examples = """examples: ./filelife # trace all stat() syscalls ./filelife -p 181 # only trace PID 181 """ parser = argparse.ArgumentParser( description="Trace stat() syscalls", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("-p", "--pid", help="trace this PID only") args = parser.parse_args() debug = 0 # define BPF program bpf_text = """ #include #include BPF_HASH(birth, struct dentry *); // trace file creation time int trace_create(struct pt_regs *ctx, struct inode *dir, struct dentry *dentry) { u32 pid = bpf_get_current_pid_tgid(); FILTER u64 ts = bpf_ktime_get_ns(); birth.update(&dentry, &ts); return 0; }; // trace file deletion and output details int trace_unlink(struct pt_regs *ctx, struct inode *dir, struct dentry *dentry) { u32 pid = bpf_get_current_pid_tgid(); FILTER u64 *tsp, delta; tsp = birth.lookup(&dentry); if (tsp == 0) { return 0; // missed create } delta = (bpf_ktime_get_ns() - *tsp) / 1000000; birth.delete(&dentry); if (dentry->d_iname[0] == 0) return 0; bpf_trace_printk("%d %s\\n", delta, dentry->d_iname); return 0; } """ if args.pid: bpf_text = bpf_text.replace('FILTER', 'if (pid != %s) { return 0; }' % args.pid) else: bpf_text = bpf_text.replace('FILTER', '') if debug: print(bpf_text) # initialize BPF b = BPF(text=bpf_text) b.attach_kprobe(event="vfs_create", fn_name="trace_create") b.attach_kprobe(event="vfs_unlink", fn_name="trace_unlink") # header print("%-8s %-6s %-16s %-7s %s" % ("TIME", "PID", "COMM", "AGE(s)", "FILE")) start_ts = 0 # format output while 1: (task, pid, cpu, flags, ts, msg) = b.trace_fields() (delta, filename) = msg.split(" ", 1) # print columns print("%-8s %-6d %-16s %-7.2f %s" % (strftime("%H:%M:%S"), pid, task, float(delta) / 1000, filename)) bpfcc-0.12.0/tools/old/gethostlatency.py000077500000000000000000000043461357404205000202060ustar00rootroot00000000000000#!/usr/bin/python # # gethostlatency Show latency for getaddrinfo/gethostbyname[2] calls. # For Linux, uses BCC, eBPF. Embedded C. # # 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. # # Copyright 2016 Netflix, Inc. # Licensed under the Apache License, Version 2.0 (the "License") # # 28-Jan-2016 Brendan Gregg Created this. from __future__ import print_function from bcc import BPF from time import strftime # load BPF program bpf_text = """ #include struct val_t { char host[80]; u64 ts; }; BPF_HASH(start, u32, struct val_t); int do_entry(struct pt_regs *ctx) { if (!ctx->di) return 0; struct val_t val = {}; u32 pid = bpf_get_current_pid_tgid(); bpf_probe_read(&val.host, sizeof(val.host), (void *)ctx->di); val.ts = bpf_ktime_get_ns(); start.update(&pid, &val); return 0; } int do_return(struct pt_regs *ctx) { struct val_t *valp; u64 delta; u32 pid = bpf_get_current_pid_tgid(); valp = start.lookup(&pid); if (valp == 0) return 0; // missed start delta = (bpf_ktime_get_ns() - valp->ts) / 1000; bpf_trace_printk("%d %s\\n", delta, valp->host); start.delete(&pid); return 0; } """ b = BPF(text=bpf_text) b.attach_uprobe(name="c", sym="getaddrinfo", fn_name="do_entry") b.attach_uprobe(name="c", sym="gethostbyname", fn_name="do_entry") b.attach_uprobe(name="c", sym="gethostbyname2", fn_name="do_entry") b.attach_uretprobe(name="c", sym="getaddrinfo", fn_name="do_return") b.attach_uretprobe(name="c", sym="gethostbyname", fn_name="do_return") b.attach_uretprobe(name="c", sym="gethostbyname2", fn_name="do_return") # header print("%-9s %-6s %-12s %6s %s" % ("TIME", "PID", "COMM", "LATms", "HOST")) # format output while 1: try: (task, pid, cpu, flags, ts, msg) = b.trace_fields() except ValueError: continue (delta, host) = msg.split(" ") deltams = int(delta) / 1000 print("%-9s %-6d %-12.12s %6.2f %s" % (strftime("%H:%M:%S"), pid, task, deltams, host)) bpfcc-0.12.0/tools/old/killsnoop.py000077500000000000000000000052241357404205000171570ustar00rootroot00000000000000#!/usr/bin/python # @lint-avoid-python-3-compatibility-imports # # killsnoop Trace signals issued by the kill() syscall. # For Linux, uses BCC, eBPF. Embedded C. # # USAGE: killsnoop [-h] [-t] [-x] [-p PID] # # Copyright (c) 2015 Brendan Gregg. # Licensed under the Apache License, Version 2.0 (the "License") # # 20-Sep-2015 Brendan Gregg Created this. from __future__ import print_function from bcc import BPF import argparse # arguments examples = """examples: ./killsnoop # trace all kill() signals ./killsnoop -t # include timestamps ./killsnoop -x # only show failed kills ./killsnoop -p 181 # only trace PID 181 """ parser = argparse.ArgumentParser( description="Trace signals issued by the kill() syscall", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("-t", "--timestamp", action="store_true", help="include timestamp on output") parser.add_argument("-x", "--failed", action="store_true", help="only show failed opens") parser.add_argument("-p", "--pid", help="trace this PID only") args = parser.parse_args() debug = 0 # define BPF program bpf_text = """ #include BPF_HASH(args_pid, u32, int); BPF_HASH(args_sig, u32, int); int kprobe__sys_kill(struct pt_regs *ctx, int tpid, int sig) { u32 pid = bpf_get_current_pid_tgid(); FILTER args_pid.update(&pid, &tpid); args_sig.update(&pid, &sig); return 0; }; int kretprobe__sys_kill(struct pt_regs *ctx) { int *tpidp, *sigp, ret = ctx->ax; u32 pid = bpf_get_current_pid_tgid(); tpidp = args_pid.lookup(&pid); sigp = args_sig.lookup(&pid); if (tpidp == 0 || sigp == 0) { return 0; // missed entry } bpf_trace_printk("%d %d %d\\n", *tpidp, *sigp, ret); args_pid.delete(&pid); args_sig.delete(&pid); return 0; } """ if args.pid: bpf_text = bpf_text.replace('FILTER', 'if (pid != %s) { return 0; }' % args.pid) else: bpf_text = bpf_text.replace('FILTER', '') if debug: print(bpf_text) # initialize BPF b = BPF(text=bpf_text) # header if args.timestamp: print("%-14s" % ("TIME(s)"), end="") print("%-6s %-16s %-4s %-6s %s" % ("PID", "COMM", "SIG", "TPID", "RESULT")) start_ts = 0 # format output while 1: (task, pid, cpu, flags, ts, msg) = b.trace_fields() (tpid_s, sig_s, ret_s) = msg.split(" ") ret = int(ret_s) if (args.failed and (ret >= 0)): continue # print columns if args.timestamp: if start_ts == 0: start_ts = ts print("%-14.9f" % (ts - start_ts), end="") print("%-6d %-16s %-4s %-6s %s" % (pid, task, sig_s, tpid_s, ret_s)) bpfcc-0.12.0/tools/old/memleak.py000077500000000000000000000237231357404205000165640ustar00rootroot00000000000000#!/usr/bin/python # # memleak Trace and display outstanding allocations to detect # memory leaks in user-mode processes and the kernel. # # USAGE: memleak [-h] [-p PID] [-t] [-a] [-o OLDER] [-c COMMAND] # [-s SAMPLE_RATE] [-d STACK_DEPTH] [-T TOP] [-z MIN_SIZE] # [-Z MAX_SIZE] # [interval] [count] # # Licensed under the Apache License, Version 2.0 (the "License") # Copyright (C) 2016 Sasha Goldshtein. from bcc import BPF from time import sleep from datetime import datetime import argparse import subprocess import os def decode_stack(bpf, pid, info): stack = "" if info.num_frames <= 0: return "???" for i in range(0, info.num_frames): addr = info.callstack[i] stack += " %s ;" % bpf.sym(addr, pid, show_offset=True) return stack def run_command_get_output(command): p = subprocess.Popen(command.split(), stdout=subprocess.PIPE, stderr=subprocess.STDOUT) return iter(p.stdout.readline, b'') def run_command_get_pid(command): p = subprocess.Popen(command.split()) return p.pid examples = """ EXAMPLES: ./memleak -p $(pidof allocs) Trace allocations and display a summary of "leaked" (outstanding) allocations every 5 seconds ./memleak -p $(pidof allocs) -t Trace allocations and display each individual call to malloc/free ./memleak -ap $(pidof allocs) 10 Trace allocations and display allocated addresses, sizes, and stacks every 10 seconds for outstanding allocations ./memleak -c "./allocs" Run the specified command and trace its allocations ./memleak Trace allocations in kernel mode and display a summary of outstanding allocations every 5 seconds ./memleak -o 60000 Trace allocations in kernel mode and display a summary of outstanding allocations that are at least one minute (60 seconds) old ./memleak -s 5 Trace roughly every 5th allocation, to reduce overhead """ description = """ Trace outstanding memory allocations that weren't freed. Supports both user-mode allocations made with malloc/free and kernel-mode allocations made with kmalloc/kfree. """ parser = argparse.ArgumentParser(description=description, formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("-p", "--pid", type=int, default=-1, help="the PID to trace; if not specified, trace kernel allocs") parser.add_argument("-t", "--trace", action="store_true", help="print trace messages for each alloc/free call") parser.add_argument("interval", nargs="?", default=5, type=int, help="interval in seconds to print outstanding allocations") parser.add_argument("count", nargs="?", type=int, help="number of times to print the report before exiting") parser.add_argument("-a", "--show-allocs", default=False, action="store_true", help="show allocation addresses and sizes as well as call stacks") parser.add_argument("-o", "--older", default=500, type=int, help="prune allocations younger than this age in milliseconds") parser.add_argument("-c", "--command", help="execute and trace the specified command") parser.add_argument("-s", "--sample-rate", default=1, type=int, help="sample every N-th allocation to decrease the overhead") parser.add_argument("-d", "--stack-depth", default=10, type=int, help="maximum stack depth to capture") parser.add_argument("-T", "--top", type=int, default=10, help="display only this many top allocating stacks (by size)") parser.add_argument("-z", "--min-size", type=int, help="capture only allocations larger than this size") parser.add_argument("-Z", "--max-size", type=int, help="capture only allocations smaller than this size") args = parser.parse_args() pid = args.pid command = args.command kernel_trace = (pid == -1 and command is None) trace_all = args.trace interval = args.interval min_age_ns = 1e6 * args.older sample_every_n = args.sample_rate num_prints = args.count max_stack_size = args.stack_depth + 2 top_stacks = args.top min_size = args.min_size max_size = args.max_size if min_size is not None and max_size is not None and min_size > max_size: print("min_size (-z) can't be greater than max_size (-Z)") exit(1) if command is not None: print("Executing '%s' and tracing the resulting process." % command) pid = run_command_get_pid(command) bpf_source = """ #include struct alloc_info_t { u64 size; u64 timestamp_ns; int num_frames; u64 callstack[MAX_STACK_SIZE]; }; BPF_HASH(sizes, u64); BPF_HASH(allocs, u64, struct alloc_info_t); // Adapted from https://github.com/iovisor/bcc/tools/offcputime.py static u64 get_frame(u64 *bp) { if (*bp) { // The following stack walker is x86_64 specific u64 ret = 0; if (bpf_probe_read(&ret, sizeof(ret), (void *)(*bp+8))) return 0; if (bpf_probe_read(bp, sizeof(*bp), (void *)*bp)) *bp = 0; return ret; } return 0; } static int grab_stack(struct pt_regs *ctx, struct alloc_info_t *info) { int depth = 0; u64 bp = ctx->bp; GRAB_ONE_FRAME return depth; } int alloc_enter(struct pt_regs *ctx, size_t size) { SIZE_FILTER if (SAMPLE_EVERY_N > 1) { u64 ts = bpf_ktime_get_ns(); if (ts % SAMPLE_EVERY_N != 0) return 0; } u64 pid = bpf_get_current_pid_tgid(); u64 size64 = size; sizes.update(&pid, &size64); if (SHOULD_PRINT) bpf_trace_printk("alloc entered, size = %u\\n", size); return 0; } int alloc_exit(struct pt_regs *ctx) { u64 address = ctx->ax; u64 pid = bpf_get_current_pid_tgid(); u64* size64 = sizes.lookup(&pid); struct alloc_info_t info = {0}; if (size64 == 0) return 0; // missed alloc entry info.size = *size64; sizes.delete(&pid); info.timestamp_ns = bpf_ktime_get_ns(); info.num_frames = grab_stack(ctx, &info) - 2; allocs.update(&address, &info); if (SHOULD_PRINT) { bpf_trace_printk("alloc exited, size = %lu, result = %lx," "frames = %d\\n", info.size, address, info.num_frames); } return 0; } int free_enter(struct pt_regs *ctx, void *address) { u64 addr = (u64)address; struct alloc_info_t *info = allocs.lookup(&addr); if (info == 0) return 0; allocs.delete(&addr); if (SHOULD_PRINT) { bpf_trace_printk("free entered, address = %lx, size = %lu\\n", address, info->size); } return 0; } """ bpf_source = bpf_source.replace("SHOULD_PRINT", "1" if trace_all else "0") bpf_source = bpf_source.replace("SAMPLE_EVERY_N", str(sample_every_n)) bpf_source = bpf_source.replace("GRAB_ONE_FRAME", max_stack_size * "\tif (!(info->callstack[depth++] = get_frame(&bp))) return depth;\n") bpf_source = bpf_source.replace("MAX_STACK_SIZE", str(max_stack_size)) size_filter = "" if min_size is not None and max_size is not None: size_filter = "if (size < %d || size > %d) return 0;" % \ (min_size, max_size) elif min_size is not None: size_filter = "if (size < %d) return 0;" % min_size elif max_size is not None: size_filter = "if (size > %d) return 0;" % max_size bpf_source = bpf_source.replace("SIZE_FILTER", size_filter) bpf_program = BPF(text=bpf_source) if not kernel_trace: print("Attaching to malloc and free in pid %d, Ctrl+C to quit." % pid) bpf_program.attach_uprobe(name="c", sym="malloc", fn_name="alloc_enter", pid=pid) bpf_program.attach_uretprobe(name="c", sym="malloc", fn_name="alloc_exit", pid=pid) bpf_program.attach_uprobe(name="c", sym="free", fn_name="free_enter", pid=pid) else: print("Attaching to kmalloc and kfree, Ctrl+C to quit.") bpf_program.attach_kprobe(event="__kmalloc", fn_name="alloc_enter") bpf_program.attach_kretprobe(event="__kmalloc", fn_name="alloc_exit") bpf_program.attach_kprobe(event="kfree", fn_name="free_enter") def print_outstanding(): stacks = {} print("[%s] Top %d stacks with outstanding allocations:" % (datetime.now().strftime("%H:%M:%S"), top_stacks)) allocs = bpf_program.get_table("allocs") for address, info in sorted(allocs.items(), key=lambda a: a[1].size): if BPF.monotonic_time() - min_age_ns < info.timestamp_ns: continue stack = decode_stack(bpf_program, pid, info) if stack in stacks: stacks[stack] = (stacks[stack][0] + 1, stacks[stack][1] + info.size) else: stacks[stack] = (1, info.size) if args.show_allocs: print("\taddr = %x size = %s" % (address.value, info.size)) to_show = sorted(stacks.items(), key=lambda s: s[1][1])[-top_stacks:] for stack, (count, size) in to_show: print("\t%d bytes in %d allocations from stack\n\t\t%s" % (size, count, stack.replace(";", "\n\t\t"))) count_so_far = 0 while True: if trace_all: print(bpf_program.trace_fields()) else: try: sleep(interval) except KeyboardInterrupt: exit() print_outstanding() count_so_far += 1 if num_prints is not None and count_so_far >= num_prints: exit() bpfcc-0.12.0/tools/old/offcputime.py000077500000000000000000000144771357404205000173200ustar00rootroot00000000000000#!/usr/bin/python # # offcputime Summarize off-CPU time by kernel stack trace # For Linux, uses BCC, eBPF. # # USAGE: offcputime [-h] [-u] [-p PID] [-v] [-f] [duration] # # The current implementation uses an unrolled loop for x86_64, and was written # as a proof of concept. This implementation should be replaced in the future # with an appropriate bpf_ call, when available. # # Currently limited to a stack trace depth of 21 (maxdepth + 1). # # Copyright 2016 Netflix, Inc. # Licensed under the Apache License, Version 2.0 (the "License") # # 13-Jan-2016 Brendan Gregg Created this. from __future__ import print_function from bcc import BPF from time import sleep, strftime import argparse import signal # arguments examples = """examples: ./offcputime # trace off-CPU stack time until Ctrl-C ./offcputime 5 # trace for 5 seconds only ./offcputime -f 5 # 5 seconds, and output in folded format ./offcputime -u # don't include kernel threads (user only) ./offcputime -p 185 # trace fo PID 185 only """ parser = argparse.ArgumentParser( description="Summarize off-CPU time by kernel stack trace", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("-u", "--useronly", action="store_true", help="user threads only (no kernel threads)") parser.add_argument("-p", "--pid", help="trace this PID only") parser.add_argument("-v", "--verbose", action="store_true", help="show raw addresses") parser.add_argument("-f", "--folded", action="store_true", help="output folded format") parser.add_argument("duration", nargs="?", default=99999999, help="duration of trace, in seconds") args = parser.parse_args() folded = args.folded duration = int(args.duration) debug = 0 maxdepth = 20 # and MAXDEPTH if args.pid and args.useronly: print("ERROR: use either -p or -u.") exit() # signal handler def signal_ignore(signal, frame): print() # define BPF program bpf_text = """ #include #include #define MAXDEPTH 20 #define MINBLOCK_US 1 struct key_t { char name[TASK_COMM_LEN]; // Skip saving the ip u64 ret[MAXDEPTH]; }; BPF_HASH(counts, struct key_t); BPF_HASH(start, u32); static u64 get_frame(u64 *bp) { if (*bp) { // The following stack walker is x86_64 specific u64 ret = 0; if (bpf_probe_read(&ret, sizeof(ret), (void *)(*bp+8))) return 0; if (bpf_probe_read(bp, sizeof(*bp), (void *)*bp)) *bp = 0; if (ret < __START_KERNEL_map) return 0; return ret; } return 0; } int oncpu(struct pt_regs *ctx, struct task_struct *prev) { u32 pid = prev->pid; u64 ts, *tsp; // record previous thread sleep time if (FILTER) { ts = bpf_ktime_get_ns(); start.update(&pid, &ts); } // calculate current thread's delta time pid = bpf_get_current_pid_tgid(); tsp = start.lookup(&pid); if (tsp == 0) return 0; // missed start or filtered u64 delta = bpf_ktime_get_ns() - *tsp; start.delete(&pid); delta = delta / 1000; if (delta < MINBLOCK_US) return 0; // create map key u64 zero = 0, *val, bp = 0; int depth = 0; struct key_t key = {}; bpf_get_current_comm(&key.name, sizeof(key.name)); bp = ctx->bp; // unrolled loop (MAXDEPTH): if (!(key.ret[depth++] = get_frame(&bp))) goto out; if (!(key.ret[depth++] = get_frame(&bp))) goto out; if (!(key.ret[depth++] = get_frame(&bp))) goto out; if (!(key.ret[depth++] = get_frame(&bp))) goto out; if (!(key.ret[depth++] = get_frame(&bp))) goto out; if (!(key.ret[depth++] = get_frame(&bp))) goto out; if (!(key.ret[depth++] = get_frame(&bp))) goto out; if (!(key.ret[depth++] = get_frame(&bp))) goto out; if (!(key.ret[depth++] = get_frame(&bp))) goto out; if (!(key.ret[depth++] = get_frame(&bp))) goto out; if (!(key.ret[depth++] = get_frame(&bp))) goto out; if (!(key.ret[depth++] = get_frame(&bp))) goto out; if (!(key.ret[depth++] = get_frame(&bp))) goto out; if (!(key.ret[depth++] = get_frame(&bp))) goto out; if (!(key.ret[depth++] = get_frame(&bp))) goto out; if (!(key.ret[depth++] = get_frame(&bp))) goto out; if (!(key.ret[depth++] = get_frame(&bp))) goto out; if (!(key.ret[depth++] = get_frame(&bp))) goto out; if (!(key.ret[depth++] = get_frame(&bp))) goto out; if (!(key.ret[depth++] = get_frame(&bp))) goto out; out: val = counts.lookup_or_init(&key, &zero); if (val) { (*val) += delta; } return 0; } """ if args.pid: filter = 'pid == %s' % args.pid elif args.useronly: filter = '!(prev->flags & PF_KTHREAD)' else: filter = '1' bpf_text = bpf_text.replace('FILTER', filter) if debug: print(bpf_text) # initialize BPF b = BPF(text=bpf_text) b.attach_kprobe(event="finish_task_switch", fn_name="oncpu") matched = b.num_open_kprobes() if matched == 0: print("0 functions traced. Exiting.") exit() # header if not folded: print("Tracing off-CPU time (us) by kernel stack", end="") if duration < 99999999: print(" for %d secs." % duration) else: print("... Hit Ctrl-C to end.") # output while (1): try: sleep(duration) except KeyboardInterrupt: # as cleanup can take many seconds, trap Ctrl-C: signal.signal(signal.SIGINT, signal_ignore) if not folded: print() counts = b.get_table("counts") for k, v in sorted(counts.items(), key=lambda counts: counts[1].value): if folded: # print folded stack output line = k.name.decode('utf-8', 'replace') + ";" for i in reversed(range(0, maxdepth)): if k.ret[i] == 0: continue line = line + b.ksym(k.ret[i]) if i != 0: line = line + ";" print("%s %d" % (line, v.value)) else: # print default multi-line stack output for i in range(0, maxdepth): if k.ret[i] == 0: break print(" %-16x %s" % (k.ret[i], b.ksym(k.ret[i]))) print(" %-16s %s" % ("-", k.name)) print(" %d\n" % v.value) counts.clear() if not folded: print("Detaching...") exit() bpfcc-0.12.0/tools/old/offwaketime.py000077500000000000000000000212601357404205000174440ustar00rootroot00000000000000#!/usr/bin/python # # offwaketime Summarize blocked time by kernel off-CPU stack + waker stack # For Linux, uses BCC, eBPF. # # USAGE: offwaketime [-h] [-u] [-p PID] [-T] [duration] # # The current implementation uses an unrolled loop for x86_64, and was written # as a proof of concept. This implementation should be replaced in the future # with an appropriate bpf_ call, when available. # # The Off-CPU stack is currently limited to a stack trace depth of 20 # (maxtdepth), and the waker stack limited to 10 (maxwdepth). This is also # limited to kernel stacks, and x86_64 only. Check for future versions, where # these limitations should be removed. # # Copyright 2016 Netflix, Inc. # Licensed under the Apache License, Version 2.0 (the "License") # # 20-Jan-2016 Brendan Gregg Created this. from __future__ import print_function from bcc import BPF from time import sleep import argparse import signal # arguments examples = """examples: ./offwaketime # trace off-CPU + waker stack time until Ctrl-C ./offwaketime 5 # trace for 5 seconds only ./offwaketime -f 5 # 5 seconds, and output in folded format ./offwaketime -u # don't include kernel threads (user only) ./offwaketime -p 185 # trace fo PID 185 only """ parser = argparse.ArgumentParser( description="Summarize blocked time by kernel stack trace + waker stack", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("-u", "--useronly", action="store_true", help="user threads only (no kernel threads)") parser.add_argument("-p", "--pid", help="trace this PID only") parser.add_argument("-v", "--verbose", action="store_true", help="show raw addresses") parser.add_argument("-f", "--folded", action="store_true", help="output folded format") parser.add_argument("duration", nargs="?", default=99999999, help="duration of trace, in seconds") args = parser.parse_args() folded = args.folded duration = int(args.duration) debug = 0 maxwdepth = 10 # and MAXWDEPTH maxtdepth = 20 # and MAXTDEPTH if args.pid and args.useronly: print("ERROR: use either -p or -u.") exit() # signal handler def signal_ignore(signal, frame): print() # define BPF program bpf_text = """ #include #include #define MAXWDEPTH 10 #define MAXTDEPTH 20 #define MINBLOCK_US 1 struct key_t { char waker[TASK_COMM_LEN]; char target[TASK_COMM_LEN]; u64 wret[MAXWDEPTH]; u64 tret[MAXTDEPTH]; }; BPF_HASH(counts, struct key_t); BPF_HASH(start, u32); struct wokeby_t { char name[TASK_COMM_LEN]; u64 ret[MAXWDEPTH]; }; BPF_HASH(wokeby, u32, struct wokeby_t); static u64 get_frame(u64 *bp) { if (*bp) { // The following stack walker is x86_64 specific u64 ret = 0; if (bpf_probe_read(&ret, sizeof(ret), (void *)(*bp+8))) return 0; if (bpf_probe_read(bp, sizeof(*bp), (void *)*bp)) *bp = 0; if (ret < __START_KERNEL_map) return 0; return ret; } return 0; } int waker(struct pt_regs *ctx, struct task_struct *p) { u32 pid = p->pid; if (!(FILTER)) return 0; u64 bp = 0; struct wokeby_t woke = {}; int depth = 0; bpf_get_current_comm(&woke.name, sizeof(woke.name)); bp = ctx->bp; // unrolled loop (MAXWDEPTH): if (!(woke.ret[depth++] = get_frame(&bp))) goto out; if (!(woke.ret[depth++] = get_frame(&bp))) goto out; if (!(woke.ret[depth++] = get_frame(&bp))) goto out; if (!(woke.ret[depth++] = get_frame(&bp))) goto out; if (!(woke.ret[depth++] = get_frame(&bp))) goto out; if (!(woke.ret[depth++] = get_frame(&bp))) goto out; if (!(woke.ret[depth++] = get_frame(&bp))) goto out; if (!(woke.ret[depth++] = get_frame(&bp))) goto out; if (!(woke.ret[depth++] = get_frame(&bp))) goto out; woke.ret[depth] = get_frame(&bp); out: wokeby.update(&pid, &woke); return 0; } int oncpu(struct pt_regs *ctx, struct task_struct *p) { u32 pid = p->pid; u64 ts, *tsp; // record previous thread sleep time if (FILTER) { ts = bpf_ktime_get_ns(); start.update(&pid, &ts); } // calculate current thread's delta time pid = bpf_get_current_pid_tgid(); tsp = start.lookup(&pid); if (tsp == 0) return 0; // missed start or filtered u64 delta = bpf_ktime_get_ns() - *tsp; start.delete(&pid); delta = delta / 1000; if (delta < MINBLOCK_US) return 0; // create map key u64 zero = 0, *val, bp = 0; int depth = 0; struct key_t key = {}; struct wokeby_t *woke; bpf_get_current_comm(&key.target, sizeof(key.target)); bp = ctx->bp; // unrolled loop (MAXTDEPTH): if (!(key.tret[depth++] = get_frame(&bp))) goto out; if (!(key.tret[depth++] = get_frame(&bp))) goto out; if (!(key.tret[depth++] = get_frame(&bp))) goto out; if (!(key.tret[depth++] = get_frame(&bp))) goto out; if (!(key.tret[depth++] = get_frame(&bp))) goto out; if (!(key.tret[depth++] = get_frame(&bp))) goto out; if (!(key.tret[depth++] = get_frame(&bp))) goto out; if (!(key.tret[depth++] = get_frame(&bp))) goto out; if (!(key.tret[depth++] = get_frame(&bp))) goto out; if (!(key.tret[depth++] = get_frame(&bp))) goto out; if (!(key.tret[depth++] = get_frame(&bp))) goto out; if (!(key.tret[depth++] = get_frame(&bp))) goto out; if (!(key.tret[depth++] = get_frame(&bp))) goto out; if (!(key.tret[depth++] = get_frame(&bp))) goto out; if (!(key.tret[depth++] = get_frame(&bp))) goto out; if (!(key.tret[depth++] = get_frame(&bp))) goto out; if (!(key.tret[depth++] = get_frame(&bp))) goto out; if (!(key.tret[depth++] = get_frame(&bp))) goto out; if (!(key.tret[depth++] = get_frame(&bp))) goto out; key.tret[depth] = get_frame(&bp); out: woke = wokeby.lookup(&pid); if (woke) { __builtin_memcpy(&key.wret, woke->ret, sizeof(key.wret)); __builtin_memcpy(&key.waker, woke->name, TASK_COMM_LEN); wokeby.delete(&pid); } val = counts.lookup_or_init(&key, &zero); if (val) { (*val) += delta; } return 0; } """ if args.pid: filter = 'pid == %s' % args.pid elif args.useronly: filter = '!(p->flags & PF_KTHREAD)' else: filter = '1' bpf_text = bpf_text.replace('FILTER', filter) if debug: print(bpf_text) # initialize BPF b = BPF(text=bpf_text) b.attach_kprobe(event="finish_task_switch", fn_name="oncpu") b.attach_kprobe(event="try_to_wake_up", fn_name="waker") matched = b.num_open_kprobes() if matched == 0: print("0 functions traced. Exiting.") exit() # header if not folded: print("Tracing blocked time (us) by kernel off-CPU and waker stack", end="") if duration < 99999999: print(" for %d secs." % duration) else: print("... Hit Ctrl-C to end.") # output while (1): try: sleep(duration) except KeyboardInterrupt: # as cleanup can take many seconds, trap Ctrl-C: signal.signal(signal.SIGINT, signal_ignore) if not folded: print() counts = b.get_table("counts") for k, v in sorted(counts.items(), key=lambda counts: counts[1].value): if folded: # fold target stack line = k.target + ";" for i in reversed(range(0, maxtdepth)): if k.tret[i] == 0: continue line = line + b.ksym(k.tret[i]) if i != 0: line = line + ";" # add delimiter line = line + ";-" # fold waker stack for i in range(0, maxwdepth): line = line + ";" if k.wret[i] == 0: break line = line + b.ksym(k.wret[i]) if i != 0: line = line + ";" + k.waker # print as a line print("%s %d" % (line, v.value)) else: # print wakeup name then stack in reverse order print(" %-16s %s" % ("waker:", k.waker)) for i in reversed(range(0, maxwdepth)): if k.wret[i] == 0: continue print(" %-16x %s" % (k.wret[i], b.ksym(k.wret[i]))) # print delimiter print(" %-16s %s" % ("-", "-")) # print default multi-line stack output for i in range(0, maxtdepth): if k.tret[i] == 0: break print(" %-16x %s" % (k.tret[i], b.ksym(k.tret[i]))) print(" %-16s %s" % ("target:", k.target)) print(" %d\n" % v.value) counts.clear() if not folded: print("Detaching...") exit() bpfcc-0.12.0/tools/old/oomkill.py000077500000000000000000000045101357404205000166100ustar00rootroot00000000000000#!/usr/bin/python # # oomkill Trace oom_kill_process(). For Linux, uses BCC, 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 OOM. # # Copyright 2016 Netflix, Inc. # Licensed under the Apache License, Version 2.0 (the "License") # # 09-Feb-2016 Brendan Gregg Created this. from bcc import BPF from time import strftime import ctypes as ct # linux stats loadavg = "/proc/loadavg" # define BPF program bpf_text = """ #include #include struct data_t { u64 fpid; u64 tpid; u64 pages; char fcomm[TASK_COMM_LEN]; char tcomm[TASK_COMM_LEN]; }; BPF_PERF_OUTPUT(events); void kprobe__oom_kill_process(struct pt_regs *ctx, struct oom_control *oc, struct task_struct *p, unsigned int points, unsigned long totalpages) { struct data_t data = {}; u32 pid = bpf_get_current_pid_tgid(); data.fpid = pid; data.tpid = p->pid; data.pages = totalpages; bpf_get_current_comm(&data.fcomm, sizeof(data.fcomm)); bpf_probe_read(&data.tcomm, sizeof(data.tcomm), p->comm); events.perf_submit(ctx, &data, sizeof(data)); } """ # kernel->user event data: struct data_t TASK_COMM_LEN = 16 # linux/sched.h class Data(ct.Structure): _fields_ = [ ("fpid", ct.c_ulonglong), ("tpid", ct.c_ulonglong), ("pages", ct.c_ulonglong), ("fcomm", ct.c_char * TASK_COMM_LEN), ("tcomm", ct.c_char * TASK_COMM_LEN) ] # process event def print_event(cpu, data, size): event = ct.cast(data, ct.POINTER(Data)).contents with open(loadavg) as stats: avgline = stats.read().rstrip() print(("%s Triggered by PID %d (\"%s\"), OOM kill of PID %d (\"%s\")" ", %d pages, loadavg: %s") % (strftime("%H:%M:%S"), event.fpid, event.fcomm.decode('utf-8', 'replace'), event.tpid, event.tcomm.decode('utf-8', 'replace'), event.pages, avgline)) # initialize BPF b = BPF(text=bpf_text) print("Tracing OOM kills... Ctrl-C to stop.") b["events"].open_perf_buffer(print_event) while 1: b.perf_buffer_poll() bpfcc-0.12.0/tools/old/opensnoop.py000077500000000000000000000052611357404205000171660ustar00rootroot00000000000000#!/usr/bin/python # @lint-avoid-python-3-compatibility-imports # # opensnoop Trace open() syscalls. # For Linux, uses BCC, eBPF. Embedded C. # # USAGE: opensnoop [-h] [-t] [-x] [-p PID] # # Copyright (c) 2015 Brendan Gregg. # Licensed under the Apache License, Version 2.0 (the "License") # # 17-Sep-2015 Brendan Gregg Created this. from __future__ import print_function from bcc import BPF import argparse # arguments examples = """examples: ./opensnoop # trace all open() syscalls ./opensnoop -t # include timestamps ./opensnoop -x # only show failed opens ./opensnoop -p 181 # only trace PID 181 """ parser = argparse.ArgumentParser( description="Trace open() syscalls", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("-t", "--timestamp", action="store_true", help="include timestamp on output") parser.add_argument("-x", "--failed", action="store_true", help="only show failed opens") parser.add_argument("-p", "--pid", help="trace this PID only") args = parser.parse_args() debug = 0 # define BPF program bpf_text = """ #include BPF_HASH(args_filename, u32, const char *); int kprobe__sys_open(struct pt_regs *ctx, const char __user *filename) { u32 pid = bpf_get_current_pid_tgid(); FILTER args_filename.update(&pid, &filename); return 0; }; int kretprobe__sys_open(struct pt_regs *ctx) { const char **filenamep; int ret = ctx->ax; u32 pid = bpf_get_current_pid_tgid(); filenamep = args_filename.lookup(&pid); if (filenamep == 0) { // missed entry return 0; } bpf_trace_printk("%d %s\\n", ret, *filenamep); args_filename.delete(&pid); return 0; } """ if args.pid: bpf_text = bpf_text.replace('FILTER', 'if (pid != %s) { return 0; }' % args.pid) else: bpf_text = bpf_text.replace('FILTER', '') if debug: print(bpf_text) # initialize BPF b = BPF(text=bpf_text) # header if args.timestamp: print("%-14s" % ("TIME(s)"), end="") print("%-6s %-16s %4s %3s %s" % ("PID", "COMM", "FD", "ERR", "PATH")) start_ts = 0 # format output while 1: (task, pid, cpu, flags, ts, msg) = b.trace_fields() (ret_s, filename) = msg.split(" ", 1) ret = int(ret_s) if (args.failed and (ret >= 0)): continue # split return value into FD and errno columns if ret >= 0: fd_s = ret err = 0 else: fd_s = "-1" err = - ret # print columns if args.timestamp: if start_ts == 0: start_ts = ts print("%-14.9f" % (ts - start_ts), end="") print("%-6d %-16s %4s %3s %s" % (pid, task, fd_s, err, filename)) bpfcc-0.12.0/tools/old/profile.py000077500000000000000000000301171357404205000166040ustar00rootroot00000000000000#!/usr/bin/python # @lint-avoid-python-3-compatibility-imports # # profile Profile CPU usage by sampling stack traces at a timed interval. # For Linux, uses BCC, BPF, perf_events. Embedded C. # # This is an efficient profiler, as stack traces are frequency counted in # kernel context, rather than passing every stack to user space for frequency # counting there. Only the unique stacks and counts are passed to user space # at the end of the profile, greatly reducing the kernel<->user transfer. # # This uses perf_event_open to setup a timer which is instrumented by BPF, # and for efficiency it does not initialize the perf ring buffer, so the # redundant perf samples are not collected. # # Kernel stacks are post-process in user-land to skip the interrupt framework # frames. You can improve efficiency a little by specifying the exact number # of frames to skip with -s, provided you know what that is. If you get -s # wrong, note that the first line is the IP, and then the (skipped) stack. # # Note: if another perf-based sampling session is active, the output may become # polluted with their events. On older kernels, the ouptut may also become # polluted with tracing sessions (when the kprobe is used instead of the # tracepoint). If this becomes a problem, logic can be added to filter events. # # REQUIRES: Linux 4.6+ (BPF_MAP_TYPE_STACK_TRACE support), and the # perf_misc_flags() function symbol to exist. The latter may or may not # exist depending on your kernel build. Linux 4.9 provides a proper solution # to this (this tool will be updated). # # Copyright 2016 Netflix, Inc. # Licensed under the Apache License, Version 2.0 (the "License") # # THANKS: Sasha Goldshtein, Andrew Birchall, and Evgeny Vereshchagin, who wrote # much of the code here, borrowed from tracepoint.py and offcputime.py. # # 15-Jul-2016 Brendan Gregg Created this. from __future__ import print_function from bcc import BPF, Perf from sys import stderr from time import sleep import argparse import signal import os import errno import multiprocessing import ctypes as ct # # Process Arguments # # arg validation def positive_int(val): try: ival = int(val) except ValueError: raise argparse.ArgumentTypeError("must be an integer") if ival < 0: raise argparse.ArgumentTypeError("must be positive") return ival def positive_nonzero_int(val): ival = positive_int(val) if ival == 0: raise argparse.ArgumentTypeError("must be nonzero") return ival # arguments examples = """examples: ./profile # profile stack traces at 49 Hertz until Ctrl-C ./profile -F 99 # profile stack traces at 99 Hertz ./profile 5 # profile at 49 Hertz for 5 seconds only ./profile -f 5 # output in folded format for flame graphs ./profile -p 185 # only profile threads for PID 185 ./profile -U # only show user space stacks (no kernel) ./profile -K # only show kernel space stacks (no user) ./profile -S 11 # always skip 11 frames of kernel stack """ parser = argparse.ArgumentParser( description="Profile CPU stack traces at a timed interval", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) thread_group = parser.add_mutually_exclusive_group() thread_group.add_argument("-p", "--pid", type=positive_int, help="profile this PID only") # TODO: add options for user/kernel threads only stack_group = parser.add_mutually_exclusive_group() stack_group.add_argument("-U", "--user-stacks-only", action="store_true", help="show stacks from user space only (no kernel space stacks)") stack_group.add_argument("-K", "--kernel-stacks-only", action="store_true", help="show stacks from kernel space only (no user space stacks)") parser.add_argument("-F", "--frequency", type=positive_int, default=49, help="sample frequency, Hertz (default 49)") parser.add_argument("-d", "--delimited", action="store_true", help="insert delimiter between kernel/user stacks") parser.add_argument("-a", "--annotations", action="store_true", help="add _[k] annotations to kernel frames") parser.add_argument("-f", "--folded", action="store_true", help="output folded format, one line per stack (for flame graphs)") parser.add_argument("--stack-storage-size", default=2048, type=positive_nonzero_int, help="the number of unique stack traces that can be stored and " "displayed (default 2048)") parser.add_argument("-S", "--kernel-skip", type=positive_int, default=0, help="skip this many kernel frames (default 3)") parser.add_argument("duration", nargs="?", default=99999999, type=positive_nonzero_int, help="duration of trace, in seconds") # option logic args = parser.parse_args() skip = args.kernel_skip pid = int(args.pid) if args.pid is not None else -1 duration = int(args.duration) debug = 0 need_delimiter = args.delimited and not (args.kernel_stacks_only or args.user_stacks_only) # TODO: add stack depth, and interval # # Setup BPF # # define BPF program bpf_text = """ #include #include struct key_t { u32 pid; u64 kernel_ip; u64 kernel_ret_ip; int user_stack_id; int kernel_stack_id; char name[TASK_COMM_LEN]; }; BPF_HASH(counts, struct key_t); BPF_HASH(start, u32); BPF_STACK_TRACE(stack_traces, STACK_STORAGE_SIZE); // This code gets a bit complex. Probably not suitable for casual hacking. PERF_TRACE_EVENT { u32 pid = bpf_get_current_pid_tgid(); if (!(THREAD_FILTER)) return 0; // create map key u64 zero = 0, *val; struct key_t key = {.pid = pid}; bpf_get_current_comm(&key.name, sizeof(key.name)); // get stacks key.user_stack_id = USER_STACK_GET; key.kernel_stack_id = KERNEL_STACK_GET; if (key.kernel_stack_id >= 0) { // populate extras to fix the kernel stack struct pt_regs regs = {}; bpf_probe_read(®s, sizeof(regs), (void *)REGS_LOCATION); u64 ip = PT_REGS_IP(®s); // if ip isn't sane, leave key ips as zero for later checking #ifdef CONFIG_RANDOMIZE_MEMORY if (ip > __PAGE_OFFSET_BASE) { #else if (ip > PAGE_OFFSET) { #endif key.kernel_ip = ip; if (DO_KERNEL_RIP) { /* * User didn't specify a skip value (-s), so we will figure * out how many interrupt framework frames to skip by recording * the kernel rip, then later scanning for it on the stack. * This is likely x86_64 specific; can use -s as a workaround * until this supports your architecture. */ bpf_probe_read(&key.kernel_ret_ip, sizeof(key.kernel_ret_ip), (void *)(regs.bp + 8)); } } } val = counts.lookup_or_init(&key, &zero); if (val) { (*val)++; } return 0; } """ # set thread filter thread_context = "" perf_filter = "-a" if args.pid is not None: thread_context = "PID %s" % args.pid thread_filter = 'pid == %s' % args.pid perf_filter = '-p %s' % args.pid else: thread_context = "all threads" thread_filter = '1' bpf_text = bpf_text.replace('THREAD_FILTER', thread_filter) # set stack storage size bpf_text = bpf_text.replace('STACK_STORAGE_SIZE', str(args.stack_storage_size)) # handle stack args kernel_stack_get = "stack_traces.get_stackid(args, " \ "%d)" % skip user_stack_get = \ "stack_traces.get_stackid(args, BPF_F_USER_STACK)" stack_context = "" if args.user_stacks_only: stack_context = "user" kernel_stack_get = "-1" elif args.kernel_stacks_only: stack_context = "kernel" user_stack_get = "-1" else: stack_context = "user + kernel" bpf_text = bpf_text.replace('USER_STACK_GET', user_stack_get) bpf_text = bpf_text.replace('KERNEL_STACK_GET', kernel_stack_get) if skip: # don't record the rip, as we won't use it bpf_text = bpf_text.replace('DO_KERNEL_RIP', '0') else: # rip is used to skip interrupt infrastructure frames bpf_text = bpf_text.replace('DO_KERNEL_RIP', '1') # header if not args.folded: print("Sampling at %d Hertz of %s by %s stack" % (args.frequency, thread_context, stack_context), end="") if duration < 99999999: print(" for %d secs." % duration) else: print("... Hit Ctrl-C to end.") # kprobe perf_misc_flags() bpf_text = bpf_text.replace('PERF_TRACE_EVENT', 'int kprobe__perf_misc_flags(struct pt_regs *args)') bpf_text = bpf_text.replace('REGS_LOCATION', 'PT_REGS_PARM1(args)') if debug: print(bpf_text) # initialize BPF try: b = BPF(text=bpf_text) except: print("BPF initialization failed. perf_misc_flags() may be inlined in " + "your kernel build.\nThis tool will be updated in the future to " + "support Linux 4.9, which has reliable profiling support. Exiting.") exit() # signal handler def signal_ignore(signal, frame): print() # # Setup perf_events # # use perf_events to sample try: Perf.perf_event_open(0, pid=-1, ptype=Perf.PERF_TYPE_SOFTWARE, freq=args.frequency) except: print("ERROR: initializing perf_events for sampling.\n" "To debug this, try running the following command:\n" " perf record -F 49 -e cpu-clock %s -- sleep 1\n" "If that also doesn't work, fix it first." % perf_filter, file=stderr) exit(0) # # Output Report # # collect samples try: sleep(duration) except KeyboardInterrupt: # as cleanup can take some time, trap Ctrl-C: signal.signal(signal.SIGINT, signal_ignore) if not args.folded: print() def aksym(addr): if args.annotations: return b.ksym(addr) + "_[k]" else: return b.ksym(addr) # output stacks missing_stacks = 0 has_enomem = False counts = b.get_table("counts") stack_traces = b.get_table("stack_traces") for k, v in sorted(counts.items(), key=lambda counts: counts[1].value): # handle get_stackid erorrs if (not args.user_stacks_only and k.kernel_stack_id < 0 and k.kernel_stack_id != -errno.EFAULT) or \ (not args.kernel_stacks_only and k.user_stack_id < 0 and k.user_stack_id != -errno.EFAULT): missing_stacks += 1 # check for an ENOMEM error if k.kernel_stack_id == -errno.ENOMEM or \ k.user_stack_id == -errno.ENOMEM: has_enomem = True user_stack = [] if k.user_stack_id < 0 else \ stack_traces.walk(k.user_stack_id) kernel_tmp = [] if k.kernel_stack_id < 0 else \ stack_traces.walk(k.kernel_stack_id) # fix kernel stack kernel_stack = [] if k.kernel_stack_id >= 0: if skip: # fixed skip for addr in kernel_tmp: kernel_stack.append(addr) kernel_stack = kernel_stack[skip:] else: # skip the interrupt framework stack by searching for our RIP skipping = 1 for addr in kernel_tmp: if k.kernel_ret_ip == addr: skipping = 0 if not skipping: kernel_stack.append(addr) if k.kernel_ip: kernel_stack.insert(0, k.kernel_ip) do_delimiter = need_delimiter and kernel_stack if args.folded: # print folded stack output user_stack = list(user_stack) kernel_stack = list(kernel_stack) line = [k.name.decode('utf-8', 'replace')] + \ [b.sym(addr, k.pid) for addr in reversed(user_stack)] + \ (do_delimiter and ["-"] or []) + \ [aksym(addr) for addr in reversed(kernel_stack)] print("%s %d" % (";".join(line), v.value)) else: # print default multi-line stack output. for addr in kernel_stack: print(" %s" % aksym(addr)) if do_delimiter: print(" --") for addr in user_stack: print(" %s" % b.sym(addr, k.pid)) print(" %-16s %s (%d)" % ("-", k.name, k.pid)) print(" %d\n" % v.value) # check missing if missing_stacks > 0: enomem_str = "" if not has_enomem else \ " Consider increasing --stack-storage-size." print("WARNING: %d stack traces could not be displayed.%s" % (missing_stacks, enomem_str), file=stderr) bpfcc-0.12.0/tools/old/profile_example.txt000066400000000000000000001035171357404205000205100ustar00rootroot00000000000000Demonstrations of profile, the Linux eBPF/bcc version. This is a CPU profiler. It works by taking samples of stack traces at timed intervals, and frequency counting them in kernel context for efficiency. Example output: # ./profile Sampling at 49 Hertz of all threads by user + kernel stack... Hit Ctrl-C to end. ^C ffffffff81189249 filemap_map_pages ffffffff811bd3f5 handle_mm_fault ffffffff81065990 __do_page_fault ffffffff81065caf do_page_fault ffffffff817ce228 page_fault 00007fed989afcc0 [unknown] - cp (9036) 1 00007f31d76c3251 [unknown] 47a2c1e752bf47f7 [unknown] - sign-file (8877) 1 ffffffff813d0af8 __clear_user ffffffff813d5277 iov_iter_zero ffffffff814ec5f2 read_iter_zero ffffffff8120be9d __vfs_read ffffffff8120c385 vfs_read ffffffff8120d786 sys_read ffffffff817cc076 entry_SYSCALL_64_fastpath 00007fc5652ad9b0 read - dd (25036) 4 0000000000400542 func_a 0000000000400598 main 00007f12a133e830 __libc_start_main 083e258d4c544155 [unknown] - func_ab (13549) 5 [...] ffffffff8105eb66 native_safe_halt ffffffff8103659e default_idle ffffffff81036d1f arch_cpu_idle ffffffff810bba5a default_idle_call ffffffff810bbd07 cpu_startup_entry ffffffff817bf4a7 rest_init ffffffff81d65f58 start_kernel ffffffff81d652db x86_64_start_reservations ffffffff81d65418 x86_64_start_kernel - swapper/0 (0) 72 ffffffff8105eb66 native_safe_halt ffffffff8103659e default_idle ffffffff81036d1f arch_cpu_idle ffffffff810bba5a default_idle_call ffffffff810bbd07 cpu_startup_entry ffffffff8104df55 start_secondary - swapper/1 (0) 75 The output was long; I truncated some lines ("[...]"). This default output prints stack traces as two columns (raw addresses, and then translated symbol names), followed by a line to describe the process (a dash, the process name, and a PID in parenthesis), and then an integer count of how many times this stack trace was sampled. The output above shows the most frequent stack was from the "swapper/1" process (PID 0), running the native_safe_halt() function, which was called by default_idle(), which was called by arch_cpu_idle(), and so on. This is the idle thread. Stacks can be read top-down, to follow ancestry: child, parent, grandparent, etc. The func_ab process is running the func_a() function, called by main(), called by __libc_start_main(), and called by "[unknown]" with what looks like a bogus address (1st column). That's evidence of a broken stack trace. It's common for user-level software that hasn't been compiled with frame pointers (in this case, libc). The dd process has called read(), and then enters the kernel via entry_SYSCALL_64_fastpath(), calling sys_read(), and so on. Yes, I'm now reading it bottom up. That way follows the code flow. The dd process is actually "dd if=/dev/zero of=/dev/null": it's a simple workload to analyze that just moves bytes from /dev/zero to /dev/null. Profiling just that process: # ./profile -p 25036 Sampling at 49 Hertz of PID 25036 by user + kernel stack... Hit Ctrl-C to end. ^C 0000000000402748 [unknown] 00007fc56561422c [unknown] - dd (25036) 1 00007fc5652ada0e __write - dd (25036) 1 00007fc5652ad9b0 read - dd (25036) 1 [...] 00000000004047b2 [unknown] 00007fc56561422c [unknown] - dd (25036) 2 ffffffff817cc060 entry_SYSCALL_64_fastpath 00007fc5652ada10 __write 00007fc56561422c [unknown] - dd (25036) 3 ffffffff817cc060 entry_SYSCALL_64_fastpath 00007fc5652ad9b0 read - dd (25036) 3 ffffffff813d0af8 __clear_user ffffffff813d5277 iov_iter_zero ffffffff814ec5f2 read_iter_zero ffffffff8120be9d __vfs_read ffffffff8120c385 vfs_read ffffffff8120d786 sys_read ffffffff817cc076 entry_SYSCALL_64_fastpath 00007fc5652ad9b0 read 00007fc56561422c [unknown] - dd (25036) 3 ffffffff813d0af8 __clear_user ffffffff813d5277 iov_iter_zero ffffffff814ec5f2 read_iter_zero ffffffff8120be9d __vfs_read ffffffff8120c385 vfs_read ffffffff8120d786 sys_read ffffffff817cc076 entry_SYSCALL_64_fastpath 00007fc5652ad9b0 read - dd (25036) 7 Again, I've truncated some lines. Now we're just analyzing the dd process. The filtering is performed in kernel context, for efficiency. This output has some "[unknown]" frames that probably have valid addresses, but we're lacking the symbol translation. This is a common for all profilers on Linux, and is usually fixable. See the DEBUGGING section of the profile(8) man page. Lets add delimiters between the user and kernel stacks, using -d: # ./profile -p 25036 -d ^C ffffffff8120b385 __vfs_write ffffffff8120d826 sys_write ffffffff817cc076 entry_SYSCALL_64_fastpath -- 00007fc5652ada10 __write - dd (25036) 1 -- 00007fc565255ef3 [unknown] 00007fc56561422c [unknown] - dd (25036) 1 ffffffff813d4569 iov_iter_init ffffffff8120be8e __vfs_read ffffffff8120c385 vfs_read ffffffff8120d786 sys_read ffffffff817cc076 entry_SYSCALL_64_fastpath -- 00007fc5652ad9b0 read - dd (25036) 1 [...] ffffffff813d0af8 __clear_user ffffffff813d5277 iov_iter_zero ffffffff814ec5f2 read_iter_zero ffffffff8120be9d __vfs_read ffffffff8120c385 vfs_read ffffffff8120d786 sys_read ffffffff817cc076 entry_SYSCALL_64_fastpath -- 00007fc5652ad9b0 read - dd (25036) 9 In this mode, the delimiters are "--". Here's another example, a func_ab program that runs two functions, func_a() and func_b(). Profiling it for 5 seconds: # ./profile -p `pgrep -n func_ab` 5 Sampling at 49 Hertz of PID 2930 by user + kernel stack for 5 secs. 000000000040053e func_a 0000000000400598 main 00007f0458819830 __libc_start_main 083e258d4c544155 [unknown] - func_ab (2930) 2 0000000000400566 func_b 00000000004005ac main 00007f0458819830 __libc_start_main 083e258d4c544155 [unknown] - func_ab (2930) 3 000000000040053a func_a 0000000000400598 main 00007f0458819830 __libc_start_main 083e258d4c544155 [unknown] - func_ab (2930) 5 0000000000400562 func_b 00000000004005ac main 00007f0458819830 __libc_start_main 083e258d4c544155 [unknown] - func_ab (2930) 12 000000000040056a func_b 00000000004005ac main 00007f0458819830 __libc_start_main 083e258d4c544155 [unknown] - func_ab (2930) 19 0000000000400542 func_a 0000000000400598 main 00007f0458819830 __libc_start_main 083e258d4c544155 [unknown] - func_ab (2930) 22 0000000000400571 func_b 00000000004005ac main 00007f0458819830 __libc_start_main 083e258d4c544155 [unknown] - func_ab (2930) 64 0000000000400549 func_a 0000000000400598 main 00007f0458819830 __libc_start_main 083e258d4c544155 [unknown] - func_ab (2930) 72 Note that the same stack (2nd column) seems to be repeated. Weren't we doing frequency counting and only printing unique stacks? We are, but in terms of the raw addresses, not the symbols. See the 1st column: those stacks are all unique. We can output in "folded format", which puts the stack trace on one line, separating frames with semi-colons. Eg: # ./profile -f -p `pgrep -n func_ab` 5 func_ab;[unknown];__libc_start_main;main;func_a 2 func_ab;[unknown];__libc_start_main;main;func_b 2 func_ab;[unknown];__libc_start_main;main;func_a 11 func_ab;[unknown];__libc_start_main;main;func_b 12 func_ab;[unknown];__libc_start_main;main;func_a 23 func_ab;[unknown];__libc_start_main;main;func_b 28 func_ab;[unknown];__libc_start_main;main;func_b 57 func_ab;[unknown];__libc_start_main;main;func_a 64 I find this pretty useful for writing to files and later grepping. Folded format can also be used by flame graph stack visualizers, including the original implementation: https://github.com/brendangregg/FlameGraph I'd include delimiters, -d. For example: # ./profile -df -p `pgrep -n func_ab` 5 > out.profile # git clone https://github.com/brendangregg/FlameGraph # ./FlameGraph/flamegraph.pl < out.profile > out.svg (Yes, I could pipe profile directly into flamegraph.pl, however, I like to keep the raw folded profiles around: can be useful for regenerating flamegraphs with different options, and, for differential flame graphs.) Some flamegraph.pl palettes recognize kernel annotations, which can be added with -a. It simply adds a "_[k]" at the end of kernel function names. For example: # ./profile -adf -p `pgrep -n dd` 10 dd;[unknown] 1 dd;[unknown];[unknown] 1 dd;[unknown];[unknown] 1 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];rw_verify_area_[k];security_file_permission_[k];__fsnotify_parent_[k] 1 dd;[unknown];__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k];__fsnotify_parent_[k] 1 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];__fdget_pos_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];rw_verify_area_[k];apparmor_file_permission_[k] 1 dd;[unknown] 1 dd;[unknown];[unknown] 1 dd;__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k] 1 dd;[unknown] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];__fget_light_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];rw_verify_area_[k];__fsnotify_parent_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k];iov_iter_zero_[k] 1 dd;[unknown];__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];__fget_light_[k] 1 dd;[unknown];[unknown] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k] 1 dd;[unknown];[unknown] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k];iov_iter_zero_[k] 1 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k];iov_iter_zero_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];read_iter_zero_[k] 1 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__fsnotify_parent_[k] 1 dd;[unknown];[unknown] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];rw_verify_area_[k];security_file_permission_[k];apparmor_file_permission_[k];common_file_perm_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];__fsnotify_parent_[k] 1 dd;__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k];fsnotify_[k] 1 dd;__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k];rw_verify_area_[k];security_file_permission_[k] 1 dd;[unknown];__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];__fdget_pos_[k] 1 dd;[unknown];__write;-;entry_SYSCALL_64_fastpath_[k] 1 dd;[unknown];[unknown] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];rw_verify_area_[k];security_file_permission_[k] 1 dd;__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k] 1 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];__fget_light_[k] 1 dd;[unknown] 1 dd;[unknown];__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];rw_verify_area_[k];security_file_permission_[k];apparmor_file_permission_[k] 1 dd;[unknown];[unknown] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];rw_verify_area_[k];security_file_permission_[k] 1 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];rw_verify_area_[k];__fsnotify_parent_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k];iov_iter_zero_[k] 1 dd;__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k];rw_verify_area_[k];security_file_permission_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k];iov_iter_zero_[k];__clear_user_[k] 1 dd;[unknown];[unknown] 1 dd;__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k] 1 dd;[unknown];[unknown] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];rw_verify_area_[k];security_file_permission_[k];apparmor_file_permission_[k];common_file_perm_[k] 1 dd;read 1 dd;__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k];security_file_permission_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];fsnotify_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];fsnotify_[k] 1 dd;__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k];rw_verify_area_[k];apparmor_file_permission_[k] 1 dd;[unknown];__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k];__fsnotify_parent_[k] 1 dd;[unknown];__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k];rw_verify_area_[k];apparmor_file_permission_[k] 1 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];iov_iter_init_[k] 1 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];rw_verify_area_[k];security_file_permission_[k];__fsnotify_parent_[k] 1 dd;[unknown];__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k];__vfs_write_[k];write_null_[k] 1 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k];__clear_user_[k] 1 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];security_file_permission_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];rw_verify_area_[k];security_file_permission_[k];apparmor_file_permission_[k];common_file_perm_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k] 1 dd;[unknown];__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k] 1 dd;__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k] 1 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];rw_verify_area_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];__fget_light_[k] 1 dd;[unknown];__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k];rw_verify_area_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k] 1 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];__vfs_read_[k] 1 dd;[unknown];__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k];__vfs_write_[k] 1 dd;[unknown] 1 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k];iov_iter_zero_[k] 1 dd;[unknown] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k] 1 dd;__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];__fsnotify_parent_[k] 1 dd;__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k] 1 dd;[unknown];[unknown] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k];iov_iter_zero_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k];iov_iter_zero_[k] 1 dd;[unknown];__write;-;sys_write_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];__fsnotify_parent_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];rw_verify_area_[k];security_file_permission_[k];common_file_perm_[k] 1 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k];iov_iter_zero_[k] 1 dd;[unknown];[unknown] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k];iov_iter_zero_[k];__clear_user_[k] 1 dd;__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k];rw_verify_area_[k];security_file_permission_[k];apparmor_file_permission_[k] 1 dd;__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];__fget_light_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];vfs_read_[k] 1 dd;__write 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];vfs_read_[k] 1 dd;__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k];rw_verify_area_[k];security_file_permission_[k];apparmor_file_permission_[k];common_file_perm_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k];iov_iter_zero_[k] 1 dd;[unknown];__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];__fget_light_[k] 1 dd;[unknown];[unknown] 1 dd;[unknown] 1 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k];iov_iter_zero_[k] 1 dd;[unknown] 1 dd;[unknown] 1 dd;[unknown];[unknown] 1 dd;__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k] 1 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k];iov_iter_zero_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k] 1 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k] 1 dd;__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k];rw_verify_area_[k];security_file_permission_[k];apparmor_file_permission_[k];common_file_perm_[k] 1 dd;__write 1 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];__fget_light_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];rw_verify_area_[k];security_file_permission_[k] 1 dd;__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k] 1 dd;[unknown] 1 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];__fget_light_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k] 1 dd;[unknown];[unknown] 1 dd;__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];__fdget_pos_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k] 1 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];_cond_resched_[k] 1 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];iov_iter_init_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];rw_verify_area_[k];security_file_permission_[k];__fsnotify_parent_[k] 1 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k] 1 dd;__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];rw_verify_area_[k] 1 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];rw_verify_area_[k];apparmor_file_permission_[k] 1 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k];iov_iter_zero_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k] 1 dd;[unknown] 1 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];rw_verify_area_[k];security_file_permission_[k];fsnotify_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k] 1 dd;__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];__fdget_pos_[k] 1 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];rw_verify_area_[k];security_file_permission_[k] 1 dd;__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k] 1 dd;[unknown];__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k];__vfs_write_[k] 1 dd;__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k];rw_verify_area_[k];apparmor_file_permission_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];rw_verify_area_[k];security_file_permission_[k] 1 dd;__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k];rw_verify_area_[k];security_file_permission_[k];apparmor_file_permission_[k];common_file_perm_[k] 1 dd;__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k] 1 dd;__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];__fget_light_[k] 1 dd;[unknown] 1 dd;[unknown];__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k];fsnotify_[k] 1 dd;[unknown];__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k];rw_verify_area_[k];security_file_permission_[k];apparmor_file_permission_[k];common_file_perm_[k] 1 dd;__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k];rw_verify_area_[k] 1 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k];iov_iter_zero_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];fsnotify_[k] 1 dd;[unknown];__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k] 1 dd;__write;-;entry_SYSCALL_64_fastpath_[k];vfs_write_[k] 1 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];rw_verify_area_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];rw_verify_area_[k];security_file_permission_[k];apparmor_file_permission_[k];common_file_perm_[k] 1 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k] 1 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];rw_verify_area_[k];fsnotify_[k] 1 dd;[unknown];__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k];rw_verify_area_[k];apparmor_file_permission_[k] 2 dd;read;-;entry_SYSCALL_64_fastpath_[k];__fdget_pos_[k] 2 dd;[unknown];[unknown] 2 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];__fdget_pos_[k] 2 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];rw_verify_area_[k];security_file_permission_[k];apparmor_file_permission_[k];common_file_perm_[k] 2 dd;[unknown];[unknown] 2 dd;[unknown];[unknown] 2 dd;[unknown];[unknown] 2 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k] 2 dd;[unknown];[unknown] 2 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k];__clear_user_[k] 2 dd;__write;-;entry_SYSCALL_64_fastpath_[k];__fdget_pos_[k] 2 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k] 2 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k] 2 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k];iov_iter_zero_[k];__clear_user_[k] 2 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k];iov_iter_zero_[k];__clear_user_[k] 2 dd;[unknown];[unknown] 2 dd;__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];__fget_light_[k] 2 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];rw_verify_area_[k];security_file_permission_[k];fsnotify_[k] 2 dd;__write;-;sys_write_[k] 2 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];fsnotify_[k] 2 dd;[unknown];[unknown] 2 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k] 2 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k];iov_iter_zero_[k] 2 dd;read;-;SyS_read_[k] 2 dd;[unknown] 2 dd;__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k];rw_verify_area_[k] 2 dd;[unknown];__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];__fget_light_[k] 2 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k] 2 dd;__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k];rw_verify_area_[k];security_file_permission_[k];apparmor_file_permission_[k] 2 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k];__clear_user_[k] 2 dd;[unknown];__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];rw_verify_area_[k] 2 dd;[unknown];[unknown] 3 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];rw_verify_area_[k] 3 dd;[unknown];[unknown] 3 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k];iov_iter_zero_[k];__clear_user_[k] 3 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k];iov_iter_zero_[k] 3 dd;[unknown];[unknown] 3 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k];iov_iter_zero_[k] 3 dd;[unknown];[unknown] 3 dd;[unknown];[unknown] 3 dd;__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k] 3 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k] 3 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k];iov_iter_zero_[k];__clear_user_[k] 3 dd;[unknown] 4 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k] 4 dd;[unknown];[unknown] 4 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k] 4 dd;[unknown] 4 dd;[unknown];[unknown] 4 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k] 4 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k];iov_iter_zero_[k];__clear_user_[k] 5 dd;[unknown];__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k] 5 dd;[unknown];[unknown] 5 dd;[unknown];[unknown] 5 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k];iov_iter_zero_[k] 6 dd;read 15 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k];iov_iter_zero_[k];__clear_user_[k] 19 dd;[unknown];__write;-;entry_SYSCALL_64_fastpath_[k] 20 dd;read;-;entry_SYSCALL_64_fastpath_[k] 23 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k];iov_iter_zero_[k];__clear_user_[k] 24 dd;__write;-;entry_SYSCALL_64_fastpath_[k] 25 dd;__write 29 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k] 31 This can be made into a flamegraph. Eg: # ./profile -adf -p `pgrep -n func_ab` 10 > out.profile # git clone https://github.com/brendangregg/FlameGraph # ./FlameGraph/flamegraph.pl --color=java < out.profile > out.svg It will highlight the kernel frames in orange, and user-level in red (and Java in green, and C++ in yellow). If you copy-n-paste the above output into a out.profile file, you can try it out. You can increase or decrease the sample frequency. Eg, sampling at 9 Hertz: # ./profile -F 9 Sampling at 9 Hertz of all threads by user + kernel stack... Hit Ctrl-C to end. ^C 000000000040056a func_b 00000000004005ac main 00007f0458819830 __libc_start_main 083e258d4c544155 [unknown] - func_ab (2930) 1 [...] ffffffff8105eb66 native_safe_halt ffffffff8103659e default_idle ffffffff81036d1f arch_cpu_idle ffffffff810bba5a default_idle_call ffffffff810bbd07 cpu_startup_entry ffffffff8104df55 start_secondary - swapper/3 (0) 8 ffffffff8105eb66 native_safe_halt ffffffff8103659e default_idle ffffffff81036d1f arch_cpu_idle ffffffff810bba5a default_idle_call ffffffff810bbd07 cpu_startup_entry ffffffff817bf497 rest_init ffffffff81d65f58 start_kernel ffffffff81d652db x86_64_start_reservations ffffffff81d65418 x86_64_start_kernel - swapper/0 (0) 8 You can also restrict profiling to just kernel stacks (-K) or user stacks (-U). For example, just user stacks: # ./profile -U Sampling at 49 Hertz of all threads by user stack... Hit Ctrl-C to end. ^C 0000000000402ccc [unknown] 00007f45a624422c [unknown] - dd (2931) 1 0000000000404b80 [unknown] 00007f45a624422c [unknown] - dd (2931) 1 0000000000404d77 [unknown] 00007f45a624422c [unknown] - dd (2931) 1 00007f45a5e85e5e [unknown] 00007f45a624422c [unknown] - dd (2931) 1 0000000000402d12 [unknown] 00007f45a624422c [unknown] - dd (2931) 1 0000000000400562 func_b 00000000004005ac main 00007f0458819830 __libc_start_main 083e258d4c544155 [unknown] - func_ab (2930) 1 0000000000404805 [unknown] - dd (2931) 1 00000000004047de [unknown] - dd (2931) 1 0000000000400542 func_a 0000000000400598 main 00007f0458819830 __libc_start_main 083e258d4c544155 [unknown] - func_ab (2930) 3 00007f45a5edda10 __write 00007f45a624422c [unknown] - dd (2931) 3 000000000040053a func_a 0000000000400598 main 00007f0458819830 __libc_start_main 083e258d4c544155 [unknown] - func_ab (2930) 4 000000000040056a func_b 00000000004005ac main 00007f0458819830 __libc_start_main 083e258d4c544155 [unknown] - func_ab (2930) 7 - swapper/6 (0) 10 0000000000400571 func_b 00000000004005ac main 00007f0458819830 __libc_start_main 083e258d4c544155 [unknown] - func_ab (2930) 10 00007f45a5edda10 __write - dd (2931) 10 0000000000400549 func_a 0000000000400598 main 00007f0458819830 __libc_start_main 083e258d4c544155 [unknown] - func_ab (2930) 11 00007f45a5edd9b0 read - dd (2931) 12 00007f45a5edd9b0 read 00007f45a624422c [unknown] - dd (2931) 14 - swapper/7 (0) 46 - swapper/0 (0) 46 - swapper/2 (0) 46 - swapper/1 (0) 46 - swapper/3 (0) 46 - swapper/4 (0) 46 If there are too many unique stack traces for the kernel to save, a warning will be printed. Eg: # ./profile [...] WARNING: 8 stack traces could not be displayed. Consider increasing --stack-storage-size. Run ./profile -h to see the default. There is a -S option to skip kernel frames. You probably don't need to mess with this. Here's why it exists: consider the following kernel stack trace, and IP: ffffffff81174e78 perf_swevent_hrtimer ffffffff810e6984 __hrtimer_run_queues ffffffff810e70f8 hrtimer_interrupt ffffffff81022c69 xen_timer_interrupt ffffffff810d2942 handle_irq_event_percpu ffffffff810d62da handle_percpu_irq ffffffff810d1f52 generic_handle_irq ffffffff814a5137 evtchn_2l_handle_events ffffffff814a2853 __xen_evtchn_do_upcall ffffffff814a4740 xen_evtchn_do_upcall ffffffff817cd50c xen_hvm_callback_vector ffffffff8103663e default_idle ffffffff81036dbf arch_cpu_idle ffffffff810bb8ea default_idle_call ffffffff810bbb97 cpu_startup_entry ffffffff8104df85 start_secondary IP: ffffffff8105eb66 native_safe_halt This is the idle thread. The first function is native_safe_halt(), and its parent is default_idle(). But what you see there is really what we are profiling. All that stuff above default_idle()? Interrupt framework stack. So we have to exclude those interrupt frames. I do this by fetching the ret IP from the kernel stack, and then scanning for it in user-level: in this case it would be default_idle(). Ok. If this doesn't work on your architecture (and your kernel stacks are a single line, the IP), then you might consider setting a fixed skip count, which avoids this ret IP logic. For the above stack, I'd set "-S 11", and it would slice off those 11 interrupt frames nicely. It also does this in kernel context for efficiency. So how do you figure out what number to use? 11? 14? 5? Well.. Try "-S 1", and then see how much higher you need to set it. Remember on the real profile output that the IP line is printed on top of the sliced stack. USAGE message: # ./profile -h usage: profile [-h] [-p PID] [-U | -K] [-F FREQUENCY] [-d] [-a] [-f] [--stack-storage-size STACK_STORAGE_SIZE] [-S KERNEL_SKIP] [duration] Profile CPU stack traces at a timed interval positional arguments: duration duration of trace, in seconds optional arguments: -h, --help show this help message and exit -p PID, --pid PID profile this PID only -U, --user-stacks-only show stacks from user space only (no kernel space stacks) -K, --kernel-stacks-only show stacks from kernel space only (no user space stacks) -F FREQUENCY, --frequency FREQUENCY sample frequency, Hertz (default 49) -d, --delimited insert delimiter between kernel/user stacks -a, --annotations add _[k] annotations to kernel frames -f, --folded output folded format, one line per stack (for flame graphs) --stack-storage-size STACK_STORAGE_SIZE the number of unique stack traces that can be stored and displayed (default 2048) -S KERNEL_SKIP, --kernel-skip KERNEL_SKIP skip this many kernel frames (default 3) examples: ./profile # profile stack traces at 49 Hertz until Ctrl-C ./profile -F 99 # profile stack traces at 99 Hertz ./profile 5 # profile at 49 Hertz for 5 seconds only ./profile -f 5 # output in folded format for flame graphs ./profile -p 185 # only profile threads for PID 185 ./profile -U # only show user space stacks (no kernel) ./profile -K # only show kernel space stacks (no user) ./profile -S 11 # always skip 11 frames of kernel stack bpfcc-0.12.0/tools/old/softirqs.py000077500000000000000000000134561357404205000170250ustar00rootroot00000000000000#!/usr/bin/python # @lint-avoid-python-3-compatibility-imports # # softirqs Summarize soft IRQ (interrupt) event time. # For Linux, uses BCC, eBPF. # # USAGE: softirqs [-h] [-T] [-N] [-d] [interval] [count] # # Copyright (c) 2015 Brendan Gregg. # Licensed under the Apache License, Version 2.0 (the "License") # # 20-Oct-2015 Brendan Gregg Created this. from __future__ import print_function from bcc import BPF from time import sleep, strftime import argparse # arguments examples = """examples: ./softirqs # sum soft irq event time ./softirqs -d # show soft irq event time as histograms ./softirqs 1 10 # print 1 second summaries, 10 times ./softirqs -NT 1 # 1s summaries, nanoseconds, and timestamps """ parser = argparse.ArgumentParser( description="Summarize soft irq event time as histograms", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("-T", "--timestamp", action="store_true", help="include timestamp on output") parser.add_argument("-N", "--nanoseconds", action="store_true", help="output in nanoseconds") parser.add_argument("-d", "--dist", action="store_true", help="show distributions as histograms") parser.add_argument("-C", "--bycpu", action="store_true", help="break down softirqs to individual cpus") parser.add_argument("interval", nargs="?", default=99999999, help="output interval, in seconds") parser.add_argument("count", nargs="?", default=99999999, help="number of outputs") args = parser.parse_args() countdown = int(args.count) if args.nanoseconds: factor = 1 label = "nsecs" else: factor = 1000 label = "usecs" debug = 0 # define BPF program bpf_text = "" if args.bycpu: bpf_text = """ #include typedef struct irq_cpu_key { s64 cpu; u64 slot; } irq_key_t; BPF_HASH(start, u32); BPF_HISTOGRAM(dist, irq_key_t); // time IRQ int trace_start_cpu(struct pt_regs *ctx) { int curr_cpu = bpf_get_smp_processor_id(); u64 ts = bpf_ktime_get_ns(); start.update(&curr_cpu, &ts); return 0; } int trace_completion_cpu(struct pt_regs *ctx) { u64 *tsp, delta; int curr_cpu = bpf_get_smp_processor_id(); // fetch timestamp and calculate delta tsp = start.lookup(&curr_cpu); COMMON // store as sum or histogram irq_key_t key = {.cpu = curr_cpu, STORE start.delete(&curr_cpu); return 0; } """ else: bpf_text = """ #include typedef struct irq_key { u64 ip; u64 slot; } irq_key_t; BPF_HASH(start, u32); BPF_HASH(iptr, u32); BPF_HISTOGRAM(dist, irq_key_t); // time IRQ int trace_start(struct pt_regs *ctx) { u32 pid = bpf_get_current_pid_tgid(); u64 ip = PT_REGS_IP(ctx), ts = bpf_ktime_get_ns(); start.update(&pid, &ts); iptr.update(&pid, &ip); return 0; } int trace_completion(struct pt_regs *ctx) { u64 *tsp, delta, ip, *ipp; u32 pid = bpf_get_current_pid_tgid(); // fetch timestamp and calculate delta tsp = start.lookup(&pid); ipp = iptr.lookup(&pid); COMMON // store as sum or histogram irq_key_t key = { STORE start.delete(&pid); iptr.delete(&pid); return 0; } """ # code substitutions bpf_text = bpf_text.replace('COMMON', """if (tsp == 0) { return 0; // missed start } delta = bpf_ktime_get_ns() - *tsp; """) if args.dist: bpf_text = bpf_text.replace('STORE', '.slot = bpf_log2l(delta)};' + 'dist.increment(key);') else: bpf_text = bpf_text.replace('STORE', ' .ip = ip, .slot = 0 /* ignore */};' + 'u64 zero = 0, *vp = dist.lookup_or_init(&key, &zero);' + 'if (vp) { (*vp) += delta; }') if debug: print(bpf_text) # load BPF program b = BPF(text=bpf_text) # this should really use irq:softirq_entry/exit tracepoints; for now the # soft irq functions are individually traced (search your kernel for # open_softirq() calls, and adjust the following list as needed). for softirqfunc in ("blk_iopoll_softirq", "blk_done_softirq", "rcu_process_callbacks", "run_rebalance_domains", "tasklet_action", "tasklet_hi_action", "run_timer_softirq", "net_tx_action", "net_rx_action"): if args.bycpu: b.attach_kprobe(event=softirqfunc, fn_name="trace_start_cpu") b.attach_kretprobe(event=softirqfunc, fn_name="trace_completion_cpu") else: b.attach_kprobe(event=softirqfunc, fn_name="trace_start") b.attach_kretprobe(event=softirqfunc, fn_name="trace_completion") print("Tracing soft irq event time... Hit Ctrl-C to end.") # output exiting = 0 if args.interval else 1 dist = b.get_table("dist") while (1): try: sleep(int(args.interval)) except KeyboardInterrupt: exiting = 1 print() if args.timestamp: print("%-8s\n" % strftime("%H:%M:%S"), end="") if args.dist: if args.bycpu: dist.print_log2_hist(label, "CPU") else: dist.print_log2_hist(label, "softirq", section_print_fn=b.ksym) else: if args.bycpu: print("%-26s %11s %11s" % ("SOFTIRQ", "CPU", "TOTAL_" + label)) for k, v in sorted(dist.items(), key=lambda dist: dist[1].value): print("%-26s %11d %11d" % (b.ksym(k.ip), k.cpu, v.value / factor)) else: print("%-26s %11s" % ("SOFTIRQ", "TOTAL_" + label)) for k, v in sorted(dist.items(), key=lambda dist: dist[1].value): print("%-26s %11d" % (b.ksym(k.ip), v.value / factor)) dist.clear() countdown -= 1 if exiting or countdown == 0: exit() bpfcc-0.12.0/tools/old/stackcount.py000077500000000000000000000126531357404205000173270ustar00rootroot00000000000000#!/usr/bin/python # # stackcount Count kernel function calls and their stack traces. # For Linux, uses BCC, eBPF. # # USAGE: stackcount [-h] [-p PID] [-i INTERVAL] [-T] [-r] pattern # # The pattern is a string with optional '*' wildcards, similar to file # globbing. If you'd prefer to use regular expressions, use the -r option. # # The current implementation uses an unrolled loop for x86_64, and was written # as a proof of concept. This implementation should be replaced in the future # with an appropriate bpf_ call, when available. # # Currently limited to a stack trace depth of 11 (maxdepth + 1). # # Copyright 2016 Netflix, Inc. # Licensed under the Apache License, Version 2.0 (the "License") # # 12-Jan-2016 Brendan Gregg Created this. from __future__ import print_function from bcc import BPF from time import sleep, strftime import argparse import signal # arguments examples = """examples: ./stackcount submit_bio # count kernel stack traces for submit_bio ./stackcount ip_output # count kernel stack traces for ip_output ./stackcount -s ip_output # show symbol offsets ./stackcount -sv ip_output # show offsets and raw addresses (verbose) ./stackcount 'tcp_send*' # count stacks for funcs matching tcp_send* ./stackcount -r '^tcp_send.*' # same as above, using regular expressions ./stackcount -Ti 5 ip_output # output every 5 seconds, with timestamps ./stackcount -p 185 ip_output # count ip_output stacks for PID 185 only """ parser = argparse.ArgumentParser( description="Count kernel function calls and their stack traces", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("-p", "--pid", help="trace this PID only") parser.add_argument("-i", "--interval", default=99999999, help="summary interval, seconds") parser.add_argument("-T", "--timestamp", action="store_true", help="include timestamp on output") parser.add_argument("-r", "--regexp", action="store_true", help="use regular expressions. Default is \"*\" wildcards only.") parser.add_argument("-s", "--offset", action="store_true", help="show address offsets") parser.add_argument("-v", "--verbose", action="store_true", help="show raw addresses") parser.add_argument("pattern", help="search expression for kernel functions") args = parser.parse_args() pattern = args.pattern if not args.regexp: pattern = pattern.replace('*', '.*') pattern = '^' + pattern + '$' offset = args.offset verbose = args.verbose debug = 0 maxdepth = 10 # and MAXDEPTH # signal handler def signal_ignore(signal, frame): print() # load BPF program bpf_text = """ #include #define MAXDEPTH 10 struct key_t { u64 ip; u64 ret[MAXDEPTH]; }; BPF_HASH(counts, struct key_t); static u64 get_frame(u64 *bp) { if (*bp) { // The following stack walker is x86_64 specific u64 ret = 0; if (bpf_probe_read(&ret, sizeof(ret), (void *)(*bp+8))) return 0; if (bpf_probe_read(bp, sizeof(*bp), (void *)*bp)) *bp = 0; if (ret < __START_KERNEL_map) return 0; return ret; } return 0; } int trace_count(struct pt_regs *ctx) { FILTER struct key_t key = {}; u64 zero = 0, *val, bp = 0; int depth = 0; key.ip = ctx->ip; bp = ctx->bp; // unrolled loop, 10 (MAXDEPTH) frames deep: if (!(key.ret[depth++] = get_frame(&bp))) goto out; if (!(key.ret[depth++] = get_frame(&bp))) goto out; if (!(key.ret[depth++] = get_frame(&bp))) goto out; if (!(key.ret[depth++] = get_frame(&bp))) goto out; if (!(key.ret[depth++] = get_frame(&bp))) goto out; if (!(key.ret[depth++] = get_frame(&bp))) goto out; if (!(key.ret[depth++] = get_frame(&bp))) goto out; if (!(key.ret[depth++] = get_frame(&bp))) goto out; if (!(key.ret[depth++] = get_frame(&bp))) goto out; if (!(key.ret[depth++] = get_frame(&bp))) goto out; out: val = counts.lookup_or_init(&key, &zero); if (val) { (*val)++; } return 0; } """ if args.pid: bpf_text = bpf_text.replace('FILTER', ('u32 pid; pid = bpf_get_current_pid_tgid(); ' + 'if (pid != %s) { return 0; }') % (args.pid)) else: bpf_text = bpf_text.replace('FILTER', '') if debug: print(bpf_text) b = BPF(text=bpf_text) b.attach_kprobe(event_re=pattern, fn_name="trace_count") matched = b.num_open_kprobes() if matched == 0: print("0 functions matched by \"%s\". Exiting." % args.pattern) exit() # header print("Tracing %d functions for \"%s\"... Hit Ctrl-C to end." % (matched, args.pattern)) def print_frame(addr): print(" ", end="") if verbose: print("%-16x " % addr, end="") print(b.ksym(addr, show_offset=offset)) # output exiting = 0 if args.interval else 1 while (1): try: sleep(int(args.interval)) except KeyboardInterrupt: exiting = 1 # as cleanup can take many seconds, trap Ctrl-C: signal.signal(signal.SIGINT, signal_ignore) print() if args.timestamp: print("%-8s\n" % strftime("%H:%M:%S"), end="") counts = b.get_table("counts") for k, v in sorted(counts.items(), key=lambda counts: counts[1].value): print_frame(k.ip) for i in range(0, maxdepth): if k.ret[i] == 0: break print_frame(k.ret[i]) print(" %d\n" % v.value) counts.clear() if exiting: print("Detaching...") exit() bpfcc-0.12.0/tools/old/stacksnoop.py000077500000000000000000000075311357404205000173340ustar00rootroot00000000000000#!/usr/bin/python # # stacksnoop Trace a kernel function and print all kernel stack traces. # For Linux, uses BCC, eBPF, and currently x86_64 only. Inline C. # # USAGE: stacksnoop [-h] [-p PID] [-s] [-v] function # # The current implementation uses an unrolled loop for x86_64, and was written # as a proof of concept. This implementation should be replaced in the future # with an appropriate bpf_ call, when available. # # The stack depth is limited to 10 (+1 for the current instruction pointer). # This could be tunable in a future version. # # Copyright 2016 Netflix, Inc. # Licensed under the Apache License, Version 2.0 (the "License") # # 12-Jan-2016 Brendan Gregg Created this. from __future__ import print_function from bcc import BPF import argparse # arguments examples = """examples: ./stacksnoop ext4_sync_fs # print kernel stack traces for ext4_sync_fs ./stacksnoop -s ext4_sync_fs # ... also show symbol offsets ./stacksnoop -v ext4_sync_fs # ... show extra columns ./stacksnoop -p 185 ext4_sync_fs # ... only when PID 185 is on-CPU """ parser = argparse.ArgumentParser( description="Trace and print kernel stack traces for a kernel function", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("-p", "--pid", help="trace this PID only") parser.add_argument("-s", "--offset", action="store_true", help="show address offsets") parser.add_argument("-v", "--verbose", action="store_true", help="print more fields") parser.add_argument("function", help="kernel function name") args = parser.parse_args() function = args.function offset = args.offset verbose = args.verbose debug = 0 # define BPF program bpf_text = """ #include static int print_frame(u64 *bp, int *depth) { if (*bp) { // The following stack walker is x86_64 specific u64 ret = 0; if (bpf_probe_read(&ret, sizeof(ret), (void *)(*bp+8))) return 0; if (ret < __START_KERNEL_map) return 0; bpf_trace_printk("r%d: %llx\\n", *depth, ret); if (bpf_probe_read(bp, sizeof(*bp), (void *)*bp)) return 0; *depth += 1; return 1; } return 0; } void trace_stack(struct pt_regs *ctx) { FILTER u64 bp = 0; int depth = 0; bpf_trace_printk("\\n"); if (ctx->ip) bpf_trace_printk("ip: %llx\\n", ctx->ip); bp = ctx->bp; // unrolled loop, 10 frames deep: if (!print_frame(&bp, &depth)) return; if (!print_frame(&bp, &depth)) return; if (!print_frame(&bp, &depth)) return; if (!print_frame(&bp, &depth)) return; if (!print_frame(&bp, &depth)) return; if (!print_frame(&bp, &depth)) return; if (!print_frame(&bp, &depth)) return; if (!print_frame(&bp, &depth)) return; if (!print_frame(&bp, &depth)) return; if (!print_frame(&bp, &depth)) return; }; """ if args.pid: bpf_text = bpf_text.replace('FILTER', ('u32 pid; pid = bpf_get_current_pid_tgid(); ' + 'if (pid != %s) { return; }') % (args.pid)) else: bpf_text = bpf_text.replace('FILTER', '') if debug: print(bpf_text) # initialize BPF b = BPF(text=bpf_text) b.attach_kprobe(event=function, fn_name="trace_stack") matched = b.num_open_kprobes() if matched == 0: print("Function \"%s\" not found. Exiting." % function) exit() # header if verbose: print("%-18s %-12s %-6s %-3s %s" % ("TIME(s)", "COMM", "PID", "CPU", "STACK")) else: print("%-18s %s" % ("TIME(s)", "STACK")) # format output while 1: (task, pid, cpu, flags, ts, msg) = b.trace_fields() if msg != "": (reg, addr) = msg.split(" ") ip = b.ksym(int(addr, 16), show_offset=offset) msg = msg + " " + ip if verbose: print("%-18.9f %-12.12s %-6d %-3d %s" % (ts, task, pid, cpu, msg)) else: print("%-18.9f %s" % (ts, msg)) bpfcc-0.12.0/tools/old/statsnoop.py000077500000000000000000000060141357404205000171750ustar00rootroot00000000000000#!/usr/bin/python # @lint-avoid-python-3-compatibility-imports # # statsnoop Trace stat() syscalls. # For Linux, uses BCC, eBPF. Embedded C. # # USAGE: statsnoop [-h] [-t] [-x] [-p PID] # # Copyright 2016 Netflix, Inc. # Licensed under the Apache License, Version 2.0 (the "License") # # 08-Feb-2016 Brendan Gregg Created this. from __future__ import print_function from bcc import BPF import argparse # arguments examples = """examples: ./statsnoop # trace all stat() syscalls ./statsnoop -t # include timestamps ./statsnoop -x # only show failed stats ./statsnoop -p 181 # only trace PID 181 """ parser = argparse.ArgumentParser( description="Trace stat() syscalls", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("-t", "--timestamp", action="store_true", help="include timestamp on output") parser.add_argument("-x", "--failed", action="store_true", help="only show failed stats") parser.add_argument("-p", "--pid", help="trace this PID only") args = parser.parse_args() debug = 0 # define BPF program bpf_text = """ #include BPF_HASH(args_filename, u32, const char *); int trace_entry(struct pt_regs *ctx, const char __user *filename) { u32 pid = bpf_get_current_pid_tgid(); FILTER args_filename.update(&pid, &filename); return 0; }; int trace_return(struct pt_regs *ctx) { const char **filenamep; int ret = ctx->ax; u32 pid = bpf_get_current_pid_tgid(); filenamep = args_filename.lookup(&pid); if (filenamep == 0) { // missed entry return 0; } bpf_trace_printk("%d %s\\n", ret, *filenamep); args_filename.delete(&pid); return 0; } """ if args.pid: bpf_text = bpf_text.replace('FILTER', 'if (pid != %s) { return 0; }' % args.pid) else: bpf_text = bpf_text.replace('FILTER', '') if debug: print(bpf_text) # initialize BPF b = BPF(text=bpf_text) b.attach_kprobe(event="sys_stat", fn_name="trace_entry") b.attach_kprobe(event="sys_statfs", fn_name="trace_entry") b.attach_kprobe(event="sys_newstat", fn_name="trace_entry") b.attach_kretprobe(event="sys_stat", fn_name="trace_return") b.attach_kretprobe(event="sys_statfs", fn_name="trace_return") b.attach_kretprobe(event="sys_newstat", fn_name="trace_return") # header if args.timestamp: print("%-14s" % ("TIME(s)"), end="") print("%-6s %-16s %4s %3s %s" % ("PID", "COMM", "FD", "ERR", "PATH")) start_ts = 0 # format output while 1: (task, pid, cpu, flags, ts, msg) = b.trace_fields() (ret_s, filename) = msg.split(" ", 1) ret = int(ret_s) if (args.failed and (ret >= 0)): continue # split return value into FD and errno columns if ret >= 0: fd_s = ret err = 0 else: fd_s = "-1" err = - ret # print columns if args.timestamp: if start_ts == 0: start_ts = ts print("%-14.9f" % (ts - start_ts), end="") print("%-6d %-16s %4s %3s %s" % (pid, task, fd_s, err, filename)) bpfcc-0.12.0/tools/old/syncsnoop.py000077500000000000000000000014061357404205000171760ustar00rootroot00000000000000#!/usr/bin/python # @lint-avoid-python-3-compatibility-imports # # syncsnoop Trace sync() syscall. # For Linux, uses BCC, eBPF. Embedded C. # # Written as a basic example of BCC trace & reformat. See # examples/hello_world.py for a BCC trace with default output example. # # Copyright (c) 2015 Brendan Gregg. # Licensed under the Apache License, Version 2.0 (the "License") # # 13-Aug-2015 Brendan Gregg Created this. from __future__ import print_function from bcc import BPF # load BPF program b = BPF(text=""" void kprobe__sys_sync(void *ctx) { bpf_trace_printk("sync()\\n"); }; """) # header print("%-18s %s" % ("TIME(s)", "CALL")) # format output while 1: (task, pid, cpu, flags, ts, msg) = b.trace_fields() print("%-18.9f %s" % (ts, msg)) bpfcc-0.12.0/tools/old/tcpaccept.py000077500000000000000000000076061357404205000171210ustar00rootroot00000000000000#!/usr/bin/python # @lint-avoid-python-3-compatibility-imports # # tcpaccept Trace TCP accept()s. # For Linux, uses BCC, eBPF. Embedded C. # # USAGE: tcpaccept [-h] [-t] [-p PID] # # 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. # # IPv4 addresses are printed as dotted quads. For IPv6 addresses, the last four # bytes are printed after "..."; check for future versions with better IPv6 # support. # # Copyright (c) 2015 Brendan Gregg. # Licensed under the Apache License, Version 2.0 (the "License") # # 13-Oct-2015 Brendan Gregg Created this. from __future__ import print_function from bcc import BPF import argparse # arguments examples = """examples: ./tcpaccept # trace all TCP accept()s ./tcpaccept -t # include timestamps ./tcpaccept -p 181 # only trace PID 181 """ parser = argparse.ArgumentParser( description="Trace TCP accepts", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("-t", "--timestamp", action="store_true", help="include timestamp on output") parser.add_argument("-p", "--pid", help="trace this PID only") args = parser.parse_args() debug = 0 # define BPF program bpf_text = """ #include #include #include int kretprobe__inet_csk_accept(struct pt_regs *ctx) { struct sock *newsk = (struct sock *)PT_REGS_RC(ctx); u32 pid = bpf_get_current_pid_tgid(); if (newsk == NULL) return 0; // check this is TCP u8 protocol = 0; // workaround for reading the sk_protocol bitfield: bpf_probe_read(&protocol, 1, (void *)((long)&newsk->sk_wmem_queued) - 3); if (protocol != IPPROTO_TCP) return 0; // pull in details u16 family = 0, lport = 0; u32 saddr = 0, daddr = 0; bpf_probe_read(&family, sizeof(family), &newsk->__sk_common.skc_family); bpf_probe_read(&lport, sizeof(lport), &newsk->__sk_common.skc_num); if (family == AF_INET) { bpf_probe_read(&saddr, sizeof(saddr), &newsk->__sk_common.skc_rcv_saddr); bpf_probe_read(&daddr, sizeof(daddr), &newsk->__sk_common.skc_daddr); // output bpf_trace_printk("4 %x %x %d\\n", daddr, saddr, lport); } else if (family == AF_INET6) { // just grab the last 4 bytes for now bpf_probe_read(&saddr, sizeof(saddr), &newsk->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32[3]); bpf_probe_read(&daddr, sizeof(daddr), &newsk->__sk_common.skc_v6_daddr.in6_u.u6_addr32[3]); // output and flip byte order of addresses bpf_trace_printk("6 %x %x %d\\n", bpf_ntohl(daddr), bpf_ntohl(saddr), lport); } // else drop return 0; } """ # code substitutions if args.pid: bpf_text = bpf_text.replace('FILTER', 'if (pid != %s) { return 0; }' % args.pid) else: bpf_text = bpf_text.replace('FILTER', '') if debug: print(bpf_text) # initialize BPF b = BPF(text=bpf_text) # header if args.timestamp: print("%-9s" % ("TIME(s)"), end="") print("%-6s %-12s %-2s %-16s %-16s %-4s" % ("PID", "COMM", "IP", "RADDR", "LADDR", "LPORT")) start_ts = 0 def inet_ntoa(addr): dq = '' for i in range(0, 4): dq = dq + str(addr & 0xff) if (i != 3): dq = dq + '.' addr = addr >> 8 return dq # format output while 1: (task, pid, cpu, flags, ts, msg) = b.trace_fields() (ip_s, raddr_hs, laddr_hs, lport_s) = msg.split(" ") if args.timestamp: if start_ts == 0: start_ts = ts print("%-9.3f" % (ts - start_ts), end="") print("%-6d %-12.12s %-2s %-16s %-16s %-4s" % (pid, task, ip_s, inet_ntoa(int(raddr_hs, 16)) if ip_s == "4" else "..." + raddr_hs, inet_ntoa(int(laddr_hs, 16)) if ip_s == "4" else "..." + laddr_hs, lport_s)) bpfcc-0.12.0/tools/old/tcpconnect.py000077500000000000000000000103351357404205000173040ustar00rootroot00000000000000#!/usr/bin/python # @lint-avoid-python-3-compatibility-imports # # tcpconnect Trace TCP connect()s. # For Linux, uses BCC, eBPF. Embedded C. # # USAGE: tcpconnect [-h] [-t] [-p PID] # # All connection attempts are traced, even if they ultimately fail. # # Copyright (c) 2015 Brendan Gregg. # Licensed under the Apache License, Version 2.0 (the "License") # # 25-Sep-2015 Brendan Gregg Created this. from __future__ import print_function from bcc import BPF import argparse # arguments examples = """examples: ./tcpconnect # trace all TCP connect()s ./tcpconnect -t # include timestamps ./tcpconnect -p 181 # only trace PID 181 """ parser = argparse.ArgumentParser( description="Trace TCP connects", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("-t", "--timestamp", action="store_true", help="include timestamp on output") parser.add_argument("-p", "--pid", help="trace this PID only") args = parser.parse_args() debug = 0 # define BPF program bpf_text = """ #include #include #include BPF_HASH(currsock, u32, struct sock *); int trace_connect_entry(struct pt_regs *ctx, struct sock *sk) { u32 pid = bpf_get_current_pid_tgid(); FILTER // stash the sock ptr for lookup on return currsock.update(&pid, &sk); return 0; }; static int trace_connect_return(struct pt_regs *ctx, short ipver) { int ret = PT_REGS_RC(ctx); u32 pid = bpf_get_current_pid_tgid(); struct sock **skpp; skpp = currsock.lookup(&pid); if (skpp == 0) { return 0; // missed entry } if (ret != 0) { // failed to send SYNC packet, may not have populated // socket __sk_common.{skc_rcv_saddr, ...} currsock.delete(&pid); return 0; } // pull in details struct sock *skp = *skpp; u32 saddr = 0, daddr = 0; u16 dport = 0; dport = skp->__sk_common.skc_dport; if (ipver == 4) { saddr = skp->__sk_common.skc_rcv_saddr; daddr = skp->__sk_common.skc_daddr; // output bpf_trace_printk("4 %x %x %d\\n", saddr, daddr, ntohs(dport)); } else /* 6 */ { // just grab the last 4 bytes for now bpf_probe_read(&saddr, sizeof(saddr), &skp->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32[3]); bpf_probe_read(&daddr, sizeof(daddr), &skp->__sk_common.skc_v6_daddr.in6_u.u6_addr32[3]); // output and flip byte order of addresses bpf_trace_printk("6 %x %x %d\\n", bpf_ntohl(saddr), bpf_ntohl(daddr), ntohs(dport)); } currsock.delete(&pid); return 0; } int trace_connect_v4_return(struct pt_regs *ctx) { return trace_connect_return(ctx, 4); } int trace_connect_v6_return(struct pt_regs *ctx) { return trace_connect_return(ctx, 6); } """ # code substitutions if args.pid: bpf_text = bpf_text.replace('FILTER', 'if (pid != %s) { return 0; }' % args.pid) else: bpf_text = bpf_text.replace('FILTER', '') if debug: print(bpf_text) # initialize BPF b = BPF(text=bpf_text) b.attach_kprobe(event="tcp_v4_connect", fn_name="trace_connect_entry") b.attach_kprobe(event="tcp_v6_connect", fn_name="trace_connect_entry") b.attach_kretprobe(event="tcp_v4_connect", fn_name="trace_connect_v4_return") b.attach_kretprobe(event="tcp_v6_connect", fn_name="trace_connect_v6_return") # header if args.timestamp: print("%-9s" % ("TIME(s)"), end="") print("%-6s %-12s %-2s %-16s %-16s %-4s" % ("PID", "COMM", "IP", "SADDR", "DADDR", "DPORT")) start_ts = 0 def inet_ntoa(addr): dq = '' for i in range(0, 4): dq = dq + str(addr & 0xff) if (i != 3): dq = dq + '.' addr = addr >> 8 return dq # format output while 1: (task, pid, cpu, flags, ts, msg) = b.trace_fields() (ip_s, saddr_hs, daddr_hs, dport_s) = msg.split(" ") if args.timestamp: if start_ts == 0: start_ts = ts print("%-9.3f" % (ts - start_ts), end="") print("%-6d %-12.12s %-2s %-16s %-16s %-4s" % (pid, task, ip_s, inet_ntoa(int(saddr_hs, 16)) if ip_s == "4" else "..." + saddr_hs, inet_ntoa(int(daddr_hs, 16)) if ip_s == "4" else "..." + daddr_hs, dport_s)) bpfcc-0.12.0/tools/old/wakeuptime.py000066400000000000000000000154401357404205000173160ustar00rootroot00000000000000#!/usr/bin/python # # wakeuptime Summarize sleep to wakeup time by waker kernel stack # For Linux, uses BCC, eBPF. # # USAGE: wakeuptime [-h] [-u] [-p PID] [-v] [-f] [duration] # # The current implementation uses an unrolled loop for x86_64, and was written # as a proof of concept. This implementation should be replaced in the future # with an appropriate bpf_ call, when available. # # Currently limited to a stack trace depth of 21 (maxdepth + 1). # # Copyright 2016 Netflix, Inc. # Licensed under the Apache License, Version 2.0 (the "License") # # 14-Jan-2016 Brendan Gregg Created this. from __future__ import print_function from bcc import BPF from time import sleep, strftime import argparse import signal # arguments examples = """examples: ./wakeuptime # trace blocked time with waker stacks ./wakeuptime 5 # trace for 5 seconds only ./wakeuptime -f 5 # 5 seconds, and output in folded format ./wakeuptime -u # don't include kernel threads (user only) ./wakeuptime -p 185 # trace fo PID 185 only """ parser = argparse.ArgumentParser( description="Summarize sleep to wakeup time by waker kernel stack", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("-u", "--useronly", action="store_true", help="user threads only (no kernel threads)") parser.add_argument("-p", "--pid", help="trace this PID only") parser.add_argument("-v", "--verbose", action="store_true", help="show raw addresses") parser.add_argument("-f", "--folded", action="store_true", help="output folded format") parser.add_argument("duration", nargs="?", default=99999999, help="duration of trace, in seconds") args = parser.parse_args() folded = args.folded duration = int(args.duration) debug = 0 maxdepth = 20 # and MAXDEPTH if args.pid and args.useronly: print("ERROR: use either -p or -u.") exit() # signal handler def signal_ignore(signal, frame): print() # define BPF program bpf_text = """ #include #include #define MAXDEPTH 20 #define MINBLOCK_US 1 struct key_t { char waker[TASK_COMM_LEN]; char target[TASK_COMM_LEN]; // Skip saving the ip u64 ret[MAXDEPTH]; }; BPF_HASH(counts, struct key_t); BPF_HASH(start, u32); static u64 get_frame(u64 *bp) { if (*bp) { // The following stack walker is x86_64/arm64 specific u64 ret = 0; if (bpf_probe_read(&ret, sizeof(ret), (void *)(*bp+8))) return 0; if (bpf_probe_read(bp, sizeof(*bp), (void *)*bp)) return 0; #ifdef __x86_64__ if (ret < __START_KERNEL_map) #elif __aarch64__ if (ret < VA_START) #else #error "Unsupported architecture for stack walker" #endif return 0; return ret; } return 0; } int offcpu(struct pt_regs *ctx) { u32 pid = bpf_get_current_pid_tgid(); u64 ts = bpf_ktime_get_ns(); // XXX: should filter here too, but need task_struct start.update(&pid, &ts); return 0; } int waker(struct pt_regs *ctx, struct task_struct *p) { u32 pid = p->pid; u64 delta, *tsp, ts; tsp = start.lookup(&pid); if (tsp == 0) return 0; // missed start start.delete(&pid); if (FILTER) return 0; // calculate delta time delta = bpf_ktime_get_ns() - *tsp; delta = delta / 1000; if (delta < MINBLOCK_US) return 0; struct key_t key = {}; u64 zero = 0, *val, bp = 0; int depth = 0; bpf_probe_read(&key.target, sizeof(key.target), p->comm); bpf_get_current_comm(&key.waker, sizeof(key.waker)); bp = PT_REGS_FP(ctx); // unrolled loop (MAXDEPTH): if (!(key.ret[depth++] = get_frame(&bp))) goto out; if (!(key.ret[depth++] = get_frame(&bp))) goto out; if (!(key.ret[depth++] = get_frame(&bp))) goto out; if (!(key.ret[depth++] = get_frame(&bp))) goto out; if (!(key.ret[depth++] = get_frame(&bp))) goto out; if (!(key.ret[depth++] = get_frame(&bp))) goto out; if (!(key.ret[depth++] = get_frame(&bp))) goto out; if (!(key.ret[depth++] = get_frame(&bp))) goto out; if (!(key.ret[depth++] = get_frame(&bp))) goto out; if (!(key.ret[depth++] = get_frame(&bp))) goto out; if (!(key.ret[depth++] = get_frame(&bp))) goto out; if (!(key.ret[depth++] = get_frame(&bp))) goto out; if (!(key.ret[depth++] = get_frame(&bp))) goto out; if (!(key.ret[depth++] = get_frame(&bp))) goto out; if (!(key.ret[depth++] = get_frame(&bp))) goto out; if (!(key.ret[depth++] = get_frame(&bp))) goto out; if (!(key.ret[depth++] = get_frame(&bp))) goto out; if (!(key.ret[depth++] = get_frame(&bp))) goto out; if (!(key.ret[depth++] = get_frame(&bp))) goto out; if (!(key.ret[depth++] = get_frame(&bp))) goto out; out: val = counts.lookup_or_init(&key, &zero); if (val) { (*val) += delta; } return 0; } """ if args.pid: filter = 'pid != %s' % args.pid elif args.useronly: filter = 'p->flags & PF_KTHREAD' else: filter = '0' bpf_text = bpf_text.replace('FILTER', filter) if debug: print(bpf_text) # initialize BPF b = BPF(text=bpf_text) b.attach_kprobe(event="schedule", fn_name="offcpu") b.attach_kprobe(event="try_to_wake_up", fn_name="waker") matched = b.num_open_kprobes() if matched == 0: print("0 functions traced. Exiting.") exit() # header if not folded: print("Tracing blocked time (us) by kernel stack", end="") if duration < 99999999: print(" for %d secs." % duration) else: print("... Hit Ctrl-C to end.") # output while (1): try: sleep(duration) except KeyboardInterrupt: # as cleanup can take many seconds, trap Ctrl-C: signal.signal(signal.SIGINT, signal_ignore) if not folded: print() counts = b.get_table("counts") for k, v in sorted(counts.items(), key=lambda counts: counts[1].value): if folded: # print folded stack output line = k.waker.decode('utf-8', 'replace') + ";" for i in reversed(range(0, maxdepth)): if k.ret[i] == 0: continue line = line + b.ksym(k.ret[i]) if i != 0: line = line + ";" print("%s;%s %d" % (line, k.target.decode('utf-8', 'replace'), v.value)) else: # print default multi-line stack output print(" %-16s %s" % ("target:", k.target.decode('utf-8', 'replace'))) for i in range(0, maxdepth): if k.ret[i] == 0: break print(" %-16x %s" % (k.ret[i], b.ksym(k.ret[i]))) print(" %-16s %s" % ("waker:", k.waker.decode('utf-8', 'replace'))) print(" %d\n" % v.value) counts.clear() if not folded: print("Detaching...") exit() bpfcc-0.12.0/tools/oomkill.py000077500000000000000000000040611357404205000160330ustar00rootroot00000000000000#!/usr/bin/python # # oomkill Trace oom_kill_process(). For Linux, uses BCC, 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 OOM. # # Copyright 2016 Netflix, Inc. # Licensed under the Apache License, Version 2.0 (the "License") # # 09-Feb-2016 Brendan Gregg Created this. from bcc import BPF from time import strftime # linux stats loadavg = "/proc/loadavg" # define BPF program bpf_text = """ #include #include struct data_t { u64 fpid; u64 tpid; u64 pages; char fcomm[TASK_COMM_LEN]; char tcomm[TASK_COMM_LEN]; }; BPF_PERF_OUTPUT(events); void kprobe__oom_kill_process(struct pt_regs *ctx, struct oom_control *oc, const char *message) { unsigned long totalpages; struct task_struct *p = oc->chosen; struct data_t data = {}; u32 pid = bpf_get_current_pid_tgid(); data.fpid = pid; data.tpid = p->pid; data.pages = oc->totalpages; bpf_get_current_comm(&data.fcomm, sizeof(data.fcomm)); bpf_probe_read(&data.tcomm, sizeof(data.tcomm), p->comm); events.perf_submit(ctx, &data, sizeof(data)); } """ # process event def print_event(cpu, data, size): event = b["events"].event(data) with open(loadavg) as stats: avgline = stats.read().rstrip() print(("%s Triggered by PID %d (\"%s\"), OOM kill of PID %d (\"%s\")" ", %d pages, loadavg: %s") % (strftime("%H:%M:%S"), event.fpid, event.fcomm.decode('utf-8', 'replace'), event.tpid, event.tcomm.decode('utf-8', 'replace'), event.pages, avgline)) # initialize BPF b = BPF(text=bpf_text) print("Tracing OOM kills... Ctrl-C to stop.") b["events"].open_perf_buffer(print_event) while 1: try: b.perf_buffer_poll() except KeyboardInterrupt: exit() bpfcc-0.12.0/tools/oomkill_example.txt000066400000000000000000000036051357404205000177350ustar00rootroot00000000000000Demonstrations of oomkill, the Linux eBPF/bcc 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 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. The following commands can be used to test this program, and invoke a memory consuming process that exhausts system memory and is OOM killed: sysctl -w vm.overcommit_memory=1 # always overcommit perl -e 'while (1) { $a .= "A" x 1024; }' # eat all memory WARNING: This exhausts system memory after disabling some overcommit checks. Only test in a lab environment. bpfcc-0.12.0/tools/opensnoop.py000077500000000000000000000160631357404205000164120ustar00rootroot00000000000000#!/usr/bin/python # @lint-avoid-python-3-compatibility-imports # # opensnoop Trace open() syscalls. # For Linux, uses BCC, eBPF. Embedded C. # # USAGE: opensnoop [-h] [-T] [-x] [-p PID] [-d DURATION] [-t TID] [-n NAME] # # Copyright (c) 2015 Brendan Gregg. # Licensed under the Apache License, Version 2.0 (the "License") # # 17-Sep-2015 Brendan Gregg Created this. # 29-Apr-2016 Allan McAleavy Updated for BPF_PERF_OUTPUT. # 08-Oct-2016 Dina Goldshtein Support filtering by PID and TID. # 28-Dec-2018 Tim Douglas Print flags argument, enable filtering # 06-Jan-2019 Takuma Kume Support filtering by UID from __future__ import print_function from bcc import ArgString, BPF from bcc.utils import printb import argparse from datetime import datetime, timedelta import os # arguments examples = """examples: ./opensnoop # trace all open() syscalls ./opensnoop -T # include timestamps ./opensnoop -U # include UID ./opensnoop -x # only show failed opens ./opensnoop -p 181 # only trace PID 181 ./opensnoop -t 123 # only trace TID 123 ./opensnoop -u 1000 # only trace UID 1000 ./opensnoop -d 10 # trace for 10 seconds only ./opensnoop -n main # only print process names containing "main" ./opensnoop -e # show extended fields ./opensnoop -f O_WRONLY -f O_RDWR # only print calls for writing """ parser = argparse.ArgumentParser( description="Trace open() syscalls", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("-T", "--timestamp", action="store_true", help="include timestamp on output") parser.add_argument("-U", "--print-uid", action="store_true", help="print UID column") parser.add_argument("-x", "--failed", action="store_true", help="only show failed opens") parser.add_argument("-p", "--pid", help="trace this PID only") parser.add_argument("-t", "--tid", help="trace this TID only") parser.add_argument("-u", "--uid", help="trace this UID only") parser.add_argument("-d", "--duration", help="total duration of trace in seconds") parser.add_argument("-n", "--name", type=ArgString, help="only print process names containing this name") parser.add_argument("--ebpf", action="store_true", help=argparse.SUPPRESS) parser.add_argument("-e", "--extended_fields", action="store_true", help="show extended fields") parser.add_argument("-f", "--flag_filter", action="append", help="filter on flags argument (e.g., O_WRONLY)") args = parser.parse_args() debug = 0 if args.duration: args.duration = timedelta(seconds=int(args.duration)) flag_filter_mask = 0 for flag in args.flag_filter or []: if not flag.startswith('O_'): exit("Bad flag: %s" % flag) try: flag_filter_mask |= getattr(os, flag) except AttributeError: exit("Bad flag: %s" % flag) # define BPF program bpf_text = """ #include #include #include struct val_t { u64 id; char comm[TASK_COMM_LEN]; const char *fname; int flags; // EXTENDED_STRUCT_MEMBER }; struct data_t { u64 id; u64 ts; u32 uid; int ret; char comm[TASK_COMM_LEN]; char fname[NAME_MAX]; int flags; // EXTENDED_STRUCT_MEMBER }; BPF_HASH(infotmp, u64, struct val_t); BPF_PERF_OUTPUT(events); int trace_entry(struct pt_regs *ctx, int dfd, const char __user *filename, int flags) { struct val_t val = {}; u64 id = bpf_get_current_pid_tgid(); u32 pid = id >> 32; // PID is higher part u32 tid = id; // Cast and get the lower part u32 uid = bpf_get_current_uid_gid(); PID_TID_FILTER UID_FILTER FLAGS_FILTER if (bpf_get_current_comm(&val.comm, sizeof(val.comm)) == 0) { val.id = id; val.fname = filename; val.flags = flags; // EXTENDED_STRUCT_MEMBER infotmp.update(&id, &val); } return 0; }; int trace_return(struct pt_regs *ctx) { u64 id = bpf_get_current_pid_tgid(); struct val_t *valp; struct data_t data = {}; u64 tsp = bpf_ktime_get_ns(); valp = infotmp.lookup(&id); if (valp == 0) { // missed entry return 0; } bpf_probe_read(&data.comm, sizeof(data.comm), valp->comm); bpf_probe_read(&data.fname, sizeof(data.fname), (void *)valp->fname); data.id = valp->id; data.ts = tsp / 1000; data.uid = bpf_get_current_uid_gid(); data.flags = valp->flags; // EXTENDED_STRUCT_MEMBER data.ret = PT_REGS_RC(ctx); events.perf_submit(ctx, &data, sizeof(data)); infotmp.delete(&id); return 0; } """ if args.tid: # TID trumps PID bpf_text = bpf_text.replace('PID_TID_FILTER', 'if (tid != %s) { return 0; }' % args.tid) elif args.pid: bpf_text = bpf_text.replace('PID_TID_FILTER', 'if (pid != %s) { return 0; }' % args.pid) else: bpf_text = bpf_text.replace('PID_TID_FILTER', '') if args.uid: bpf_text = bpf_text.replace('UID_FILTER', 'if (uid != %s) { return 0; }' % args.uid) else: bpf_text = bpf_text.replace('UID_FILTER', '') if args.flag_filter: bpf_text = bpf_text.replace('FLAGS_FILTER', 'if (!(flags & %d)) { return 0; }' % flag_filter_mask) else: bpf_text = bpf_text.replace('FLAGS_FILTER', '') if not (args.extended_fields or args.flag_filter): bpf_text = '\n'.join(x for x in bpf_text.split('\n') if 'EXTENDED_STRUCT_MEMBER' not in x) if debug or args.ebpf: print(bpf_text) if args.ebpf: exit() # initialize BPF b = BPF(text=bpf_text) b.attach_kprobe(event="do_sys_open", fn_name="trace_entry") b.attach_kretprobe(event="do_sys_open", fn_name="trace_return") initial_ts = 0 # header if args.timestamp: print("%-14s" % ("TIME(s)"), end="") if args.print_uid: print("%-6s" % ("UID"), end="") print("%-6s %-16s %4s %3s " % ("TID" if args.tid else "PID", "COMM", "FD", "ERR"), end="") if args.extended_fields: print("%-9s" % ("FLAGS"), end="") print("PATH") # process event def print_event(cpu, data, size): event = b["events"].event(data) global initial_ts # split return value into FD and errno columns if event.ret >= 0: fd_s = event.ret err = 0 else: fd_s = -1 err = - event.ret if not initial_ts: initial_ts = event.ts if args.failed and (event.ret >= 0): return if args.name and bytes(args.name) not in event.comm: return if args.timestamp: delta = event.ts - initial_ts printb(b"%-14.9f" % (float(delta) / 1000000), nl="") if args.print_uid: printb(b"%-6d" % event.uid, nl="") printb(b"%-6d %-16s %4d %3d " % (event.id & 0xffffffff if args.tid else event.id >> 32, event.comm, fd_s, err), nl="") if args.extended_fields: printb(b"%08o " % event.flags, nl="") printb(b'%s' % event.fname) # loop with callback to print_event b["events"].open_perf_buffer(print_event, page_cnt=64) start_time = datetime.now() while not args.duration or datetime.now() - start_time < args.duration: try: b.perf_buffer_poll() except KeyboardInterrupt: exit() bpfcc-0.12.0/tools/opensnoop_example.txt000066400000000000000000000223571357404205000203140ustar00rootroot00000000000000Demonstrations of opensnoop, the Linux eBPF/bcc version. opensnoop traces the open() syscall system-wide, and prints various details. Example output: # ./opensnoop PID COMM FD ERR PATH 17326 <...> 7 0 /sys/kernel/debug/tracing/trace_pipe 1576 snmpd 9 0 /proc/net/dev 1576 snmpd 11 0 /proc/net/if_inet6 1576 snmpd 11 0 /proc/sys/net/ipv4/neigh/eth0/retrans_time_ms 1576 snmpd 11 0 /proc/sys/net/ipv6/neigh/eth0/retrans_time_ms 1576 snmpd 11 0 /proc/sys/net/ipv6/conf/eth0/forwarding 1576 snmpd 11 0 /proc/sys/net/ipv6/neigh/eth0/base_reachable_time_ms 1576 snmpd 11 0 /proc/sys/net/ipv4/neigh/lo/retrans_time_ms 1576 snmpd 11 0 /proc/sys/net/ipv6/neigh/lo/retrans_time_ms 1576 snmpd 11 0 /proc/sys/net/ipv6/conf/lo/forwarding 1576 snmpd 11 0 /proc/sys/net/ipv6/neigh/lo/base_reachable_time_ms 1576 snmpd 9 0 /proc/diskstats 1576 snmpd 9 0 /proc/stat 1576 snmpd 9 0 /proc/vmstat 1956 supervise 9 0 supervise/status.new 1956 supervise 9 0 supervise/status.new 17358 run 3 0 /etc/ld.so.cache 17358 run 3 0 /lib/x86_64-linux-gnu/libtinfo.so.5 17358 run 3 0 /lib/x86_64-linux-gnu/libdl.so.2 17358 run 3 0 /lib/x86_64-linux-gnu/libc.so.6 17358 run -1 6 /dev/tty 17358 run 3 0 /proc/meminfo 17358 run 3 0 /etc/nsswitch.conf 17358 run 3 0 /etc/ld.so.cache 17358 run 3 0 /lib/x86_64-linux-gnu/libnss_compat.so.2 17358 run 3 0 /lib/x86_64-linux-gnu/libnsl.so.1 17358 run 3 0 /etc/ld.so.cache 17358 run 3 0 /lib/x86_64-linux-gnu/libnss_nis.so.2 17358 run 3 0 /lib/x86_64-linux-gnu/libnss_files.so.2 17358 run 3 0 /etc/passwd 17358 run 3 0 ./run ^C While tracing, the snmpd process opened various /proc files (reading metrics), and a "run" process read various libraries and config files (looks like it was starting up: a new process). opensnoop can be useful for discovering configuration and log files, if used during application startup. The -p option can be used to filter on a PID, which is filtered in-kernel. Here I've used it with -T to print timestamps: ./opensnoop -Tp 1956 TIME(s) PID COMM FD ERR PATH 0.000000000 1956 supervise 9 0 supervise/status.new 0.000289999 1956 supervise 9 0 supervise/status.new 1.023068000 1956 supervise 9 0 supervise/status.new 1.023381997 1956 supervise 9 0 supervise/status.new 2.046030000 1956 supervise 9 0 supervise/status.new 2.046363000 1956 supervise 9 0 supervise/status.new 3.068203997 1956 supervise 9 0 supervise/status.new 3.068544999 1956 supervise 9 0 supervise/status.new This shows the supervise process is opening the status.new file twice every second. The -U option include UID on output: # ./opensnoop -U UID PID COMM FD ERR PATH 0 27063 vminfo 5 0 /var/run/utmp 103 628 dbus-daemon -1 2 /usr/local/share/dbus-1/system-services 103 628 dbus-daemon 18 0 /usr/share/dbus-1/system-services 103 628 dbus-daemon -1 2 /lib/dbus-1/system-services The -u option filtering UID: # ./opensnoop -Uu 1000 UID PID COMM FD ERR PATH 1000 30240 ls 3 0 /etc/ld.so.cache 1000 30240 ls 3 0 /lib/x86_64-linux-gnu/libselinux.so.1 1000 30240 ls 3 0 /lib/x86_64-linux-gnu/libc.so.6 1000 30240 ls 3 0 /lib/x86_64-linux-gnu/libpcre.so.3 1000 30240 ls 3 0 /lib/x86_64-linux-gnu/libdl.so.2 1000 30240 ls 3 0 /lib/x86_64-linux-gnu/libpthread.so.0 The -x option only prints failed opens: # ./opensnoop -x PID COMM FD ERR PATH 18372 run -1 6 /dev/tty 18373 run -1 6 /dev/tty 18373 multilog -1 13 lock 18372 multilog -1 13 lock 18384 df -1 2 /usr/share/locale/en_US.UTF-8/LC_MESSAGES/coreutils.mo 18384 df -1 2 /usr/share/locale/en_US.utf8/LC_MESSAGES/coreutils.mo 18384 df -1 2 /usr/share/locale/en_US/LC_MESSAGES/coreutils.mo 18384 df -1 2 /usr/share/locale/en.UTF-8/LC_MESSAGES/coreutils.mo 18384 df -1 2 /usr/share/locale/en.utf8/LC_MESSAGES/coreutils.mo 18384 df -1 2 /usr/share/locale/en/LC_MESSAGES/coreutils.mo 18385 run -1 6 /dev/tty 18386 run -1 6 /dev/tty This caught a df command failing to open a coreutils.mo file, and trying from different directories. The ERR column is the system error number. Error number 2 is ENOENT: no such file or directory. A maximum tracing duration can be set with the -d option. For example, to trace for 2 seconds: # ./opensnoop -d 2 PID COMM FD ERR PATH 2191 indicator-multi 11 0 /sys/block 2191 indicator-multi 11 0 /sys/block 2191 indicator-multi 11 0 /sys/block 2191 indicator-multi 11 0 /sys/block 2191 indicator-multi 11 0 /sys/block The -n option can be used to filter on process name using partial matches: # ./opensnoop -n ed PID COMM FD ERR PATH 2679 sed 3 0 /etc/ld.so.cache 2679 sed 3 0 /lib/x86_64-linux-gnu/libselinux.so.1 2679 sed 3 0 /lib/x86_64-linux-gnu/libc.so.6 2679 sed 3 0 /lib/x86_64-linux-gnu/libpcre.so.3 2679 sed 3 0 /lib/x86_64-linux-gnu/libdl.so.2 2679 sed 3 0 /lib/x86_64-linux-gnu/libpthread.so.0 2679 sed 3 0 /proc/filesystems 2679 sed 3 0 /usr/lib/locale/locale-archive 2679 sed -1 2 2679 sed 3 0 /usr/lib/x86_64-linux-gnu/gconv/gconv-modules.cache 2679 sed 3 0 /dev/null 2680 sed 3 0 /etc/ld.so.cache 2680 sed 3 0 /lib/x86_64-linux-gnu/libselinux.so.1 2680 sed 3 0 /lib/x86_64-linux-gnu/libc.so.6 2680 sed 3 0 /lib/x86_64-linux-gnu/libpcre.so.3 2680 sed 3 0 /lib/x86_64-linux-gnu/libdl.so.2 2680 sed 3 0 /lib/x86_64-linux-gnu/libpthread.so.0 2680 sed 3 0 /proc/filesystems 2680 sed 3 0 /usr/lib/locale/locale-archive 2680 sed -1 2 ^C This caught the 'sed' command because it partially matches 'ed' that's passed to the '-n' option. The -e option prints out extra columns; for example, the following output contains the flags passed to open(2), in octal: # ./opensnoop -e PID COMM FD ERR FLAGS PATH 28512 sshd 10 0 00101101 /proc/self/oom_score_adj 28512 sshd 3 0 02100000 /etc/ld.so.cache 28512 sshd 3 0 02100000 /lib/x86_64-linux-gnu/libwrap.so.0 28512 sshd 3 0 02100000 /lib/x86_64-linux-gnu/libaudit.so.1 28512 sshd 3 0 02100000 /lib/x86_64-linux-gnu/libpam.so.0 28512 sshd 3 0 02100000 /lib/x86_64-linux-gnu/libselinux.so.1 28512 sshd 3 0 02100000 /lib/x86_64-linux-gnu/libsystemd.so.0 28512 sshd 3 0 02100000 /usr/lib/x86_64-linux-gnu/libcrypto.so.1.0.2 28512 sshd 3 0 02100000 /lib/x86_64-linux-gnu/libutil.so.1 The -f option filters based on flags to the open(2) call, for example: # ./opensnoop -e -f O_WRONLY -f O_RDWR PID COMM FD ERR FLAGS PATH 28084 clear_console 3 0 00100002 /dev/tty 28084 clear_console -1 13 00100002 /dev/tty0 28084 clear_console -1 13 00100001 /dev/tty0 28084 clear_console -1 13 00100002 /dev/console 28084 clear_console -1 13 00100001 /dev/console 28051 sshd 8 0 02100002 /var/run/utmp 28051 sshd 7 0 00100001 /var/log/wtmp USAGE message: # ./opensnoop -h usage: opensnoop [-h] [-T] [-x] [-p PID] [-t TID] [-d DURATION] [-n NAME] [-e] [-f FLAG_FILTER] Trace open() syscalls optional arguments: -h, --help show this help message and exit -T, --timestamp include timestamp on output -U, --print-uid include UID on output -x, --failed only show failed opens -p PID, --pid PID trace this PID only -t TID, --tid TID trace this TID only -u UID, --uid UID trace this UID only -d DURATION, --duration DURATION total duration of trace in seconds -n NAME, --name NAME only print process names containing this name -e, --extended_fields show extended fields -f FLAG_FILTER, --flag_filter FLAG_FILTER filter on flags argument (e.g., O_WRONLY) examples: ./opensnoop # trace all open() syscalls ./opensnoop -T # include timestamps ./opensnoop -U # include UID ./opensnoop -x # only show failed opens ./opensnoop -p 181 # only trace PID 181 ./opensnoop -t 123 # only trace TID 123 ./opensnoop -u 1000 # only trace UID 1000 ./opensnoop -d 10 # trace for 10 seconds only ./opensnoop -n main # only print process names containing "main" ./opensnoop -e # show extended fields ./opensnoop -f O_WRONLY -f O_RDWR # only print calls for writing bpfcc-0.12.0/tools/perlcalls.sh000077500000000000000000000000761357404205000163320ustar00rootroot00000000000000#!/bin/bash lib=$(dirname $0)/lib $lib/ucalls.py -l perl "$@" bpfcc-0.12.0/tools/perlcalls_example.txt000077700000000000000000000000001357404205000245442lib/ucalls_example.txtustar00rootroot00000000000000bpfcc-0.12.0/tools/perlflow.sh000077500000000000000000000000751357404205000162020ustar00rootroot00000000000000#!/bin/bash lib=$(dirname $0)/lib $lib/uflow.py -l perl "$@" bpfcc-0.12.0/tools/perlflow_example.txt000077700000000000000000000000001357404205000242662lib/uflow_example.txtustar00rootroot00000000000000bpfcc-0.12.0/tools/perlstat.sh000077500000000000000000000000751357404205000162060ustar00rootroot00000000000000#!/bin/bash lib=$(dirname $0)/lib $lib/ustat.py -l perl "$@" bpfcc-0.12.0/tools/perlstat_example.txt000077700000000000000000000000001357404205000242762lib/ustat_example.txtustar00rootroot00000000000000bpfcc-0.12.0/tools/phpcalls.sh000077500000000000000000000000751357404205000161560ustar00rootroot00000000000000#!/bin/bash lib=$(dirname $0)/lib $lib/ucalls.py -l php "$@" bpfcc-0.12.0/tools/phpcalls_example.txt000077700000000000000000000000001357404205000243712lib/ucalls_example.txtustar00rootroot00000000000000bpfcc-0.12.0/tools/phpflow.sh000077500000000000000000000000741357404205000160260ustar00rootroot00000000000000#!/bin/bash lib=$(dirname $0)/lib $lib/uflow.py -l php "$@" bpfcc-0.12.0/tools/phpflow_example.txt000077700000000000000000000000001357404205000241132lib/uflow_example.txtustar00rootroot00000000000000bpfcc-0.12.0/tools/phpstat.sh000077500000000000000000000000741357404205000160320ustar00rootroot00000000000000#!/bin/bash lib=$(dirname $0)/lib $lib/ustat.py -l php "$@" bpfcc-0.12.0/tools/phpstat_example.txt000077700000000000000000000000001357404205000241232lib/ustat_example.txtustar00rootroot00000000000000bpfcc-0.12.0/tools/pidpersec.py000077500000000000000000000021611357404205000163420ustar00rootroot00000000000000#!/usr/bin/python # @lint-avoid-python-3-compatibility-imports # # pidpersec Count new processes (via fork). # For Linux, uses BCC, eBPF. See .c file. # # USAGE: pidpersec # # Written as a basic example of counting an event. # # Copyright (c) 2015 Brendan Gregg. # Licensed under the Apache License, Version 2.0 (the "License") # # 11-Aug-2015 Brendan Gregg Created this. from bcc import BPF from ctypes import c_int from time import sleep, strftime # load BPF program b = BPF(text=""" #include enum stat_types { S_COUNT = 1, S_MAXSTAT }; BPF_ARRAY(stats, u64, S_MAXSTAT); static void stats_increment(int key) { u64 *leaf = stats.lookup(&key); if (leaf) (*leaf)++; } void do_count(struct pt_regs *ctx) { stats_increment(S_COUNT); } """) b.attach_kprobe(event="sched_fork", fn_name="do_count") # stat indexes S_COUNT = c_int(1) # header print("Tracing... Ctrl-C to end.") # output while (1): try: sleep(1) except KeyboardInterrupt: exit() print("%s: PIDs/sec: %d" % (strftime("%H:%M:%S"), b["stats"][S_COUNT].value)) b["stats"].clear() bpfcc-0.12.0/tools/pidpersec_example.txt000066400000000000000000000012451357404205000202430ustar00rootroot00000000000000Demonstrations of pidpersec, the Linux eBPF/bcc version. This shows the number of new processes created per second, measured by tracing the kernel fork() routine: # ./pidpersec Tracing... Ctrl-C to end. 18:33:06: PIDs/sec: 4 18:33:07: PIDs/sec: 5 18:33:08: PIDs/sec: 4 18:33:09: PIDs/sec: 4 18:33:10: PIDs/sec: 21 18:33:11: PIDs/sec: 5 18:33:12: PIDs/sec: 4 18:33:13: PIDs/sec: 4 Each second there are four new processes (this happens to be caused by a launcher script that is retrying in a loop, and encountering errors). At 18:33:10, I typed "man ls" in another server session, which caused an increase in the number of new processes as the necessary commands were run. bpfcc-0.12.0/tools/profile.py000077500000000000000000000307201357404205000160260ustar00rootroot00000000000000#!/usr/bin/python # @lint-avoid-python-3-compatibility-imports # # profile Profile CPU usage by sampling stack traces at a timed interval. # For Linux, uses BCC, BPF, perf_events. Embedded C. # # This is an efficient profiler, as stack traces are frequency counted in # kernel context, rather than passing every stack to user space for frequency # counting there. Only the unique stacks and counts are passed to user space # at the end of the profile, greatly reducing the kernel<->user transfer. # # By default CPU idle stacks are excluded by simply excluding PID 0. # # REQUIRES: Linux 4.9+ (BPF_PROG_TYPE_PERF_EVENT support). Under tools/old is # a version of this tool that may work on Linux 4.6 - 4.8. # # Copyright 2016 Netflix, Inc. # Licensed under the Apache License, Version 2.0 (the "License") # # THANKS: Alexei Starovoitov, who added proper BPF profiling support to Linux; # Sasha Goldshtein, Andrew Birchall, and Evgeny Vereshchagin, who wrote much # of the code here, borrowed from tracepoint.py and offcputime.py; and # Teng Qin, who added perf support in bcc. # # 15-Jul-2016 Brendan Gregg Created this. # 20-Oct-2016 " " Switched to use the new 4.9 support. # 26-Jan-2019 " " Changed to exclude CPU idle by default. from __future__ import print_function from bcc import BPF, PerfType, PerfSWConfig from sys import stderr from time import sleep import argparse import signal import os import errno # # Process Arguments # # arg validation def positive_int(val): try: ival = int(val) except ValueError: raise argparse.ArgumentTypeError("must be an integer") if ival < 0: raise argparse.ArgumentTypeError("must be positive") return ival def positive_nonzero_int(val): ival = positive_int(val) if ival == 0: raise argparse.ArgumentTypeError("must be nonzero") return ival def stack_id_err(stack_id): # -EFAULT in get_stackid normally means the stack-trace is not availible, # Such as getting kernel stack trace in userspace code return (stack_id < 0) and (stack_id != -errno.EFAULT) # arguments examples = """examples: ./profile # profile stack traces at 49 Hertz until Ctrl-C ./profile -F 99 # profile stack traces at 99 Hertz ./profile -c 1000000 # profile stack traces every 1 in a million events ./profile 5 # profile at 49 Hertz for 5 seconds only ./profile -f 5 # output in folded format for flame graphs ./profile -p 185 # only profile process with PID 185 ./profile -L 185 # only profile thread with TID 185 ./profile -U # only show user space stacks (no kernel) ./profile -K # only show kernel space stacks (no user) """ parser = argparse.ArgumentParser( description="Profile CPU stack traces at a timed interval", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) thread_group = parser.add_mutually_exclusive_group() thread_group.add_argument("-p", "--pid", type=positive_int, help="profile process with this PID only") thread_group.add_argument("-L", "--tid", type=positive_int, help="profile thread with this TID only") # TODO: add options for user/kernel threads only stack_group = parser.add_mutually_exclusive_group() stack_group.add_argument("-U", "--user-stacks-only", action="store_true", help="show stacks from user space only (no kernel space stacks)") stack_group.add_argument("-K", "--kernel-stacks-only", action="store_true", help="show stacks from kernel space only (no user space stacks)") sample_group = parser.add_mutually_exclusive_group() sample_group.add_argument("-F", "--frequency", type=positive_int, help="sample frequency, Hertz") sample_group.add_argument("-c", "--count", type=positive_int, help="sample period, number of events") parser.add_argument("-d", "--delimited", action="store_true", help="insert delimiter between kernel/user stacks") parser.add_argument("-a", "--annotations", action="store_true", help="add _[k] annotations to kernel frames") parser.add_argument("-I", "--include-idle", action="store_true", help="include CPU idle stacks") parser.add_argument("-f", "--folded", action="store_true", help="output folded format, one line per stack (for flame graphs)") parser.add_argument("--stack-storage-size", default=16384, type=positive_nonzero_int, help="the number of unique stack traces that can be stored and " "displayed (default %(default)s)") parser.add_argument("duration", nargs="?", default=99999999, type=positive_nonzero_int, help="duration of trace, in seconds") parser.add_argument("-C", "--cpu", type=int, default=-1, help="cpu number to run profile on") parser.add_argument("--ebpf", action="store_true", help=argparse.SUPPRESS) # option logic args = parser.parse_args() pid = int(args.pid) if args.pid is not None else -1 duration = int(args.duration) debug = 0 need_delimiter = args.delimited and not (args.kernel_stacks_only or args.user_stacks_only) # TODO: add stack depth, and interval # # Setup BPF # # define BPF program bpf_text = """ #include #include #include struct key_t { u32 pid; u64 kernel_ip; u64 kernel_ret_ip; int user_stack_id; int kernel_stack_id; char name[TASK_COMM_LEN]; }; BPF_HASH(counts, struct key_t); BPF_STACK_TRACE(stack_traces, STACK_STORAGE_SIZE); // This code gets a bit complex. Probably not suitable for casual hacking. int do_perf_event(struct bpf_perf_event_data *ctx) { u64 id = bpf_get_current_pid_tgid(); u32 tgid = id >> 32; u32 pid = id; if (IDLE_FILTER) return 0; if (!(THREAD_FILTER)) return 0; // create map key struct key_t key = {.pid = tgid}; bpf_get_current_comm(&key.name, sizeof(key.name)); // get stacks key.user_stack_id = USER_STACK_GET; key.kernel_stack_id = KERNEL_STACK_GET; if (key.kernel_stack_id >= 0) { // populate extras to fix the kernel stack u64 ip = PT_REGS_IP(&ctx->regs); u64 page_offset; // if ip isn't sane, leave key ips as zero for later checking #if defined(CONFIG_X86_64) && defined(__PAGE_OFFSET_BASE) // x64, 4.16, ..., 4.11, etc., but some earlier kernel didn't have it page_offset = __PAGE_OFFSET_BASE; #elif defined(CONFIG_X86_64) && defined(__PAGE_OFFSET_BASE_L4) // x64, 4.17, and later #if defined(CONFIG_DYNAMIC_MEMORY_LAYOUT) && defined(CONFIG_X86_5LEVEL) page_offset = __PAGE_OFFSET_BASE_L5; #else page_offset = __PAGE_OFFSET_BASE_L4; #endif #else // earlier x86_64 kernels, e.g., 4.6, comes here // arm64, s390, powerpc, x86_32 page_offset = PAGE_OFFSET; #endif if (ip > page_offset) { key.kernel_ip = ip; } } counts.increment(key); return 0; } """ # set idle filter idle_filter = "pid == 0" if args.include_idle: idle_filter = "0" bpf_text = bpf_text.replace('IDLE_FILTER', idle_filter) # set process/thread filter thread_context = "" if args.pid is not None: thread_context = "PID %s" % args.pid thread_filter = 'tgid == %s' % args.pid elif args.tid is not None: thread_context = "TID %s" % args.tid thread_filter = 'pid == %s' % args.tid else: thread_context = "all threads" thread_filter = '1' bpf_text = bpf_text.replace('THREAD_FILTER', thread_filter) # set stack storage size bpf_text = bpf_text.replace('STACK_STORAGE_SIZE', str(args.stack_storage_size)) # handle stack args kernel_stack_get = "stack_traces.get_stackid(&ctx->regs, 0)" user_stack_get = "stack_traces.get_stackid(&ctx->regs, BPF_F_USER_STACK)" stack_context = "" if args.user_stacks_only: stack_context = "user" kernel_stack_get = "-1" elif args.kernel_stacks_only: stack_context = "kernel" user_stack_get = "-1" else: stack_context = "user + kernel" bpf_text = bpf_text.replace('USER_STACK_GET', user_stack_get) bpf_text = bpf_text.replace('KERNEL_STACK_GET', kernel_stack_get) sample_freq = 0 sample_period = 0 if args.frequency: sample_freq = args.frequency elif args.count: sample_period = args.count else: # If user didn't specify anything, use default 49Hz sampling sample_freq = 49 sample_context = "%s%d %s" % (("", sample_freq, "Hertz") if sample_freq else ("every ", sample_period, "events")) # header if not args.folded: print("Sampling at %s of %s by %s stack" % (sample_context, thread_context, stack_context), end="") if args.cpu >= 0: print(" on CPU#{}".format(args.cpu), end="") if duration < 99999999: print(" for %d secs." % duration) else: print("... Hit Ctrl-C to end.") if debug or args.ebpf: print(bpf_text) if args.ebpf: exit() # initialize BPF & perf_events b = BPF(text=bpf_text) b.attach_perf_event(ev_type=PerfType.SOFTWARE, ev_config=PerfSWConfig.CPU_CLOCK, fn_name="do_perf_event", sample_period=sample_period, sample_freq=sample_freq, cpu=args.cpu) # signal handler def signal_ignore(signal, frame): print() # # Output Report # # collect samples try: sleep(duration) except KeyboardInterrupt: # as cleanup can take some time, trap Ctrl-C: signal.signal(signal.SIGINT, signal_ignore) if not args.folded: print() def aksym(addr): if args.annotations: return b.ksym(addr) + "_[k]".encode() else: return b.ksym(addr) # output stacks missing_stacks = 0 has_enomem = False counts = b.get_table("counts") stack_traces = b.get_table("stack_traces") need_delimiter = args.delimited and not (args.kernel_stacks_only or args.user_stacks_only) for k, v in sorted(counts.items(), key=lambda counts: counts[1].value): # handle get_stackid errors if not args.user_stacks_only and stack_id_err(k.kernel_stack_id): missing_stacks += 1 has_enomem = has_enomem or k.kernel_stack_id == -errno.ENOMEM if not args.kernel_stacks_only and stack_id_err(k.user_stack_id): missing_stacks += 1 has_enomem = has_enomem or k.user_stack_id == -errno.ENOMEM user_stack = [] if k.user_stack_id < 0 else \ stack_traces.walk(k.user_stack_id) kernel_tmp = [] if k.kernel_stack_id < 0 else \ stack_traces.walk(k.kernel_stack_id) # fix kernel stack kernel_stack = [] if k.kernel_stack_id >= 0: for addr in kernel_tmp: kernel_stack.append(addr) # the later IP checking if k.kernel_ip: kernel_stack.insert(0, k.kernel_ip) if args.folded: # print folded stack output user_stack = list(user_stack) kernel_stack = list(kernel_stack) line = [k.name] # if we failed to get the stack is, such as due to no space (-ENOMEM) or # hash collision (-EEXIST), we still print a placeholder for consistency if not args.kernel_stacks_only: if stack_id_err(k.user_stack_id): line.append("[Missed User Stack]") else: line.extend([b.sym(addr, k.pid) for addr in reversed(user_stack)]) if not args.user_stacks_only: line.extend(["-"] if (need_delimiter and k.kernel_stack_id >= 0 and k.user_stack_id >= 0) else []) if stack_id_err(k.kernel_stack_id): line.append("[Missed Kernel Stack]") else: line.extend([aksym(addr) for addr in reversed(kernel_stack)]) print("%s %d" % (b";".join(line).decode('utf-8', 'replace'), v.value)) else: # print default multi-line stack output if not args.user_stacks_only: if stack_id_err(k.kernel_stack_id): print(" [Missed Kernel Stack]") else: for addr in kernel_stack: print(" %s" % aksym(addr)) if not args.kernel_stacks_only: if need_delimiter and k.user_stack_id >= 0 and k.kernel_stack_id >= 0: print(" --") if stack_id_err(k.user_stack_id): print(" [Missed User Stack]") else: for addr in user_stack: print(" %s" % b.sym(addr, k.pid).decode('utf-8', 'replace')) print(" %-16s %s (%d)" % ("-", k.name.decode('utf-8', 'replace'), k.pid)) print(" %d\n" % v.value) # check missing if missing_stacks > 0: enomem_str = "" if not has_enomem else \ " Consider increasing --stack-storage-size." print("WARNING: %d stack traces could not be displayed.%s" % (missing_stacks, enomem_str), file=stderr) bpfcc-0.12.0/tools/profile_example.txt000066400000000000000000000727031357404205000177340ustar00rootroot00000000000000Demonstrations of profile, the Linux eBPF/bcc version. This is a CPU profiler. It works by taking samples of stack traces at timed intervals, and frequency counting them in kernel context for efficiency. Example output: # ./profile Sampling at 49 Hertz of all threads by user + kernel stack... Hit Ctrl-C to end. ^C filemap_map_pages handle_mm_fault __do_page_fault do_page_fault page_fault [unknown] - cp (9036) 1 [unknown] [unknown] - sign-file (8877) 1 __clear_user iov_iter_zero read_iter_zero __vfs_read vfs_read sys_read entry_SYSCALL_64_fastpath read - dd (25036) 4 func_a main __libc_start_main [unknown] - func_ab (13549) 5 The output was long; I truncated some lines ("[...]"). This default output prints stack traces, followed by a line to describe the process (a dash, the process name, and a PID in parenthesis), and then an integer count of how many times this stack trace was sampled. The func_ab process is running the func_a() function, called by main(), called by __libc_start_main(), and called by "[unknown]" with what looks like a bogus address (1st column). That's evidence of a broken stack trace. It's common for user-level software that hasn't been compiled with frame pointers (in this case, libc). The dd process has called read(), and then enters the kernel via entry_SYSCALL_64_fastpath(), calling sys_read(), and so on. Yes, I'm now reading it bottom up. That way follows the code flow. By default, CPU idle stacks are excluded. They can be included with -I: # ./profile -I [...] native_safe_halt default_idle arch_cpu_idle default_idle_call cpu_startup_entry rest_init start_kernel x86_64_start_reservations x86_64_start_kernel - swapper/0 (0) 72 native_safe_halt default_idle arch_cpu_idle default_idle_call cpu_startup_entry start_secondary - swapper/1 (0) 75 The output above shows the most frequent stack was from the "swapper/1" process (PID 0), running the native_safe_halt() function, which was called by default_idle(), which was called by arch_cpu_idle(), and so on. This is the idle thread. Stacks can be read top-down, to follow ancestry: child, parent, grandparent, etc. The dd process profiled ealrier is actually "dd if=/dev/zero of=/dev/null": it's a simple workload to analyze that just moves bytes from /dev/zero to /dev/null. Profiling just that process: # ./profile -p 25036 Sampling at 49 Hertz of PID 25036 by user + kernel stack... Hit Ctrl-C to end. ^C [unknown] [unknown] - dd (25036) 1 __write - dd (25036) 1 read - dd (25036) 1 [...] [unknown] [unknown] - dd (25036) 2 entry_SYSCALL_64_fastpath __write [unknown] - dd (25036) 3 entry_SYSCALL_64_fastpath read - dd (25036) 3 __clear_user iov_iter_zero read_iter_zero __vfs_read vfs_read sys_read entry_SYSCALL_64_fastpath read [unknown] - dd (25036) 3 __clear_user iov_iter_zero read_iter_zero __vfs_read vfs_read sys_read entry_SYSCALL_64_fastpath read - dd (25036) 7 Again, I've truncated some lines. Now we're just analyzing the dd process. The filtering is performed in kernel context, for efficiency. This output has some "[unknown]" frames that probably have valid addresses, but we're lacking the symbol translation. This is a common for all profilers on Linux, and is usually fixable. See the DEBUGGING section of the profile(8) man page. Lets add delimiters between the user and kernel stacks, using -d: # ./profile -p 25036 -d ^C __vfs_write sys_write entry_SYSCALL_64_fastpath -- __write - dd (25036) 1 -- [unknown] [unknown] - dd (25036) 1 iov_iter_init __vfs_read vfs_read sys_read entry_SYSCALL_64_fastpath -- read - dd (25036) 1 [...] __clear_user iov_iter_zero read_iter_zero __vfs_read vfs_read sys_read entry_SYSCALL_64_fastpath -- read - dd (25036) 9 In this mode, the delimiters are "--". Here's another example, a func_ab program that runs two functions, func_a() and func_b(). Profiling it for 5 seconds: # ./profile -p `pgrep -n func_ab` 5 Sampling at 49 Hertz of PID 2930 by user + kernel stack for 5 secs. func_a main __libc_start_main [unknown] - func_ab (2930) 2 func_b main __libc_start_main [unknown] - func_ab (2930) 3 func_a main __libc_start_main [unknown] - func_ab (2930) 5 func_b main __libc_start_main [unknown] - func_ab (2930) 12 func_b main __libc_start_main [unknown] - func_ab (2930) 19 func_a main __libc_start_main [unknown] - func_ab (2930) 22 func_b main __libc_start_main [unknown] - func_ab (2930) 64 func_a main __libc_start_main [unknown] - func_ab (2930) 72 Note that the same stack (2nd column) seems to be repeated. Weren't we doing frequency counting and only printing unique stacks? We are, but in terms of the raw addresses, not the symbols. See the 1st column: those stacks are all unique. We can output in "folded format", which puts the stack trace on one line, separating frames with semi-colons. Eg: # ./profile -f -p `pgrep -n func_ab` 5 func_ab;[unknown];__libc_start_main;main;func_a 2 func_ab;[unknown];__libc_start_main;main;func_b 2 func_ab;[unknown];__libc_start_main;main;func_a 11 func_ab;[unknown];__libc_start_main;main;func_b 12 func_ab;[unknown];__libc_start_main;main;func_a 23 func_ab;[unknown];__libc_start_main;main;func_b 28 func_ab;[unknown];__libc_start_main;main;func_b 57 func_ab;[unknown];__libc_start_main;main;func_a 64 I find this pretty useful for writing to files and later grepping. Folded format can also be used by flame graph stack visualizers, including the original implementation: https://github.com/brendangregg/FlameGraph I'd include delimiters, -d. For example: # ./profile -df -p `pgrep -n func_ab` 5 > out.profile # git clone https://github.com/brendangregg/FlameGraph # ./FlameGraph/flamegraph.pl < out.profile > out.svg (Yes, I could pipe profile directly into flamegraph.pl, however, I like to keep the raw folded profiles around: can be useful for regenerating flamegraphs with different options, and, for differential flame graphs.) Some flamegraph.pl palettes recognize kernel annotations, which can be added with -a. It simply adds a "_[k]" at the end of kernel function names. For example: # ./profile -adf -p `pgrep -n dd` 10 dd;[unknown] 1 dd;[unknown];[unknown] 1 dd;[unknown];[unknown] 1 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];rw_verify_area_[k];security_file_permission_[k];__fsnotify_parent_[k] 1 dd;[unknown];__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k];__fsnotify_parent_[k] 1 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];__fdget_pos_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];rw_verify_area_[k];apparmor_file_permission_[k] 1 dd;[unknown] 1 dd;[unknown];[unknown] 1 dd;__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k] 1 dd;[unknown] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];__fget_light_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];rw_verify_area_[k];__fsnotify_parent_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k];iov_iter_zero_[k] 1 dd;[unknown];__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];__fget_light_[k] 1 dd;[unknown];[unknown] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k] 1 dd;[unknown];[unknown] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k];iov_iter_zero_[k] 1 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k];iov_iter_zero_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];read_iter_zero_[k] 1 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__fsnotify_parent_[k] 1 dd;[unknown];[unknown] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];rw_verify_area_[k];security_file_permission_[k];apparmor_file_permission_[k];common_file_perm_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];__fsnotify_parent_[k] 1 dd;__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k];fsnotify_[k] 1 dd;__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k];rw_verify_area_[k];security_file_permission_[k] 1 dd;[unknown];__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];__fdget_pos_[k] 1 dd;[unknown];__write;-;entry_SYSCALL_64_fastpath_[k] 1 dd;[unknown];[unknown] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];rw_verify_area_[k];security_file_permission_[k] 1 dd;__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k] 1 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];__fget_light_[k] 1 dd;[unknown] 1 dd;[unknown];__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];rw_verify_area_[k];security_file_permission_[k];apparmor_file_permission_[k] 1 dd;[unknown];[unknown] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];rw_verify_area_[k];security_file_permission_[k] 1 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];rw_verify_area_[k];__fsnotify_parent_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k];iov_iter_zero_[k] 1 dd;__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k];rw_verify_area_[k];security_file_permission_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k];iov_iter_zero_[k];__clear_user_[k] 1 dd;[unknown];[unknown] 1 dd;__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k] 1 dd;[unknown];[unknown] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];rw_verify_area_[k];security_file_permission_[k];apparmor_file_permission_[k];common_file_perm_[k] 1 dd;read 1 dd;__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k];security_file_permission_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];fsnotify_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];fsnotify_[k] 1 dd;__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k];rw_verify_area_[k];apparmor_file_permission_[k] 1 dd;[unknown];__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k];__fsnotify_parent_[k] 1 dd;[unknown];__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k];rw_verify_area_[k];apparmor_file_permission_[k] 1 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];iov_iter_init_[k] 1 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];rw_verify_area_[k];security_file_permission_[k];__fsnotify_parent_[k] 1 dd;[unknown];__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k];__vfs_write_[k];write_null_[k] 1 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k];__clear_user_[k] 1 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];security_file_permission_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];rw_verify_area_[k];security_file_permission_[k];apparmor_file_permission_[k];common_file_perm_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k] 1 dd;[unknown];__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k] 1 dd;__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k] 1 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];rw_verify_area_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];__fget_light_[k] 1 dd;[unknown];__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k];rw_verify_area_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k] 1 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];__vfs_read_[k] 1 dd;[unknown];__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k];__vfs_write_[k] 1 dd;[unknown] 1 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k];iov_iter_zero_[k] 1 dd;[unknown] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k] 1 dd;__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];__fsnotify_parent_[k] 1 dd;__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k] 1 dd;[unknown];[unknown] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k];iov_iter_zero_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k];iov_iter_zero_[k] 1 dd;[unknown];__write;-;sys_write_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];__fsnotify_parent_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];rw_verify_area_[k];security_file_permission_[k];common_file_perm_[k] 1 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k];iov_iter_zero_[k] 1 dd;[unknown];[unknown] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k];iov_iter_zero_[k];__clear_user_[k] 1 dd;__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k];rw_verify_area_[k];security_file_permission_[k];apparmor_file_permission_[k] 1 dd;__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];__fget_light_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];vfs_read_[k] 1 dd;__write 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];vfs_read_[k] 1 dd;__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k];rw_verify_area_[k];security_file_permission_[k];apparmor_file_permission_[k];common_file_perm_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k];iov_iter_zero_[k] 1 dd;[unknown];__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];__fget_light_[k] 1 dd;[unknown];[unknown] 1 dd;[unknown] 1 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k];iov_iter_zero_[k] 1 dd;[unknown] 1 dd;[unknown] 1 dd;[unknown];[unknown] 1 dd;__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k] 1 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k];iov_iter_zero_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k] 1 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k] 1 dd;__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k];rw_verify_area_[k];security_file_permission_[k];apparmor_file_permission_[k];common_file_perm_[k] 1 dd;__write 1 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];__fget_light_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];rw_verify_area_[k];security_file_permission_[k] 1 dd;__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k] 1 dd;[unknown] 1 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];__fget_light_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k] 1 dd;[unknown];[unknown] 1 dd;__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];__fdget_pos_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k] 1 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];_cond_resched_[k] 1 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];iov_iter_init_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];rw_verify_area_[k];security_file_permission_[k];__fsnotify_parent_[k] 1 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k] 1 dd;__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];rw_verify_area_[k] 1 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];rw_verify_area_[k];apparmor_file_permission_[k] 1 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k];iov_iter_zero_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k] 1 dd;[unknown] 1 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];rw_verify_area_[k];security_file_permission_[k];fsnotify_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k] 1 dd;__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];__fdget_pos_[k] 1 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];rw_verify_area_[k];security_file_permission_[k] 1 dd;__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k] 1 dd;[unknown];__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k];__vfs_write_[k] 1 dd;__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k];rw_verify_area_[k];apparmor_file_permission_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];rw_verify_area_[k];security_file_permission_[k] 1 dd;__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k];rw_verify_area_[k];security_file_permission_[k];apparmor_file_permission_[k];common_file_perm_[k] 1 dd;__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k] 1 dd;__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];__fget_light_[k] 1 dd;[unknown] 1 dd;[unknown];__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k];fsnotify_[k] 1 dd;[unknown];__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k];rw_verify_area_[k];security_file_permission_[k];apparmor_file_permission_[k];common_file_perm_[k] 1 dd;__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k];rw_verify_area_[k] 1 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k];iov_iter_zero_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];fsnotify_[k] 1 dd;[unknown];__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k] 1 dd;__write;-;entry_SYSCALL_64_fastpath_[k];vfs_write_[k] 1 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];rw_verify_area_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k] 1 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];rw_verify_area_[k];security_file_permission_[k];apparmor_file_permission_[k];common_file_perm_[k] 1 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k] 1 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];rw_verify_area_[k];fsnotify_[k] 1 dd;[unknown];__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k];rw_verify_area_[k];apparmor_file_permission_[k] 2 dd;read;-;entry_SYSCALL_64_fastpath_[k];__fdget_pos_[k] 2 dd;[unknown];[unknown] 2 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];__fdget_pos_[k] 2 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];rw_verify_area_[k];security_file_permission_[k];apparmor_file_permission_[k];common_file_perm_[k] 2 dd;[unknown];[unknown] 2 dd;[unknown];[unknown] 2 dd;[unknown];[unknown] 2 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k] 2 dd;[unknown];[unknown] 2 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k];__clear_user_[k] 2 dd;__write;-;entry_SYSCALL_64_fastpath_[k];__fdget_pos_[k] 2 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k] 2 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k] 2 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k];iov_iter_zero_[k];__clear_user_[k] 2 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k];iov_iter_zero_[k];__clear_user_[k] 2 dd;[unknown];[unknown] 2 dd;__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];__fget_light_[k] 2 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];rw_verify_area_[k];security_file_permission_[k];fsnotify_[k] 2 dd;__write;-;sys_write_[k] 2 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];fsnotify_[k] 2 dd;[unknown];[unknown] 2 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k] 2 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k];iov_iter_zero_[k] 2 dd;read;-;SyS_read_[k] 2 dd;[unknown] 2 dd;__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k];rw_verify_area_[k] 2 dd;[unknown];__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];__fget_light_[k] 2 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k] 2 dd;__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k];rw_verify_area_[k];security_file_permission_[k];apparmor_file_permission_[k] 2 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k];__clear_user_[k] 2 dd;[unknown];__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];rw_verify_area_[k] 2 dd;[unknown];[unknown] 3 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];rw_verify_area_[k] 3 dd;[unknown];[unknown] 3 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k];iov_iter_zero_[k];__clear_user_[k] 3 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k];iov_iter_zero_[k] 3 dd;[unknown];[unknown] 3 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k];iov_iter_zero_[k] 3 dd;[unknown];[unknown] 3 dd;[unknown];[unknown] 3 dd;__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k] 3 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k] 3 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k];iov_iter_zero_[k];__clear_user_[k] 3 dd;[unknown] 4 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k] 4 dd;[unknown];[unknown] 4 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k] 4 dd;[unknown] 4 dd;[unknown];[unknown] 4 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k] 4 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k];iov_iter_zero_[k];__clear_user_[k] 5 dd;[unknown];__write;-;entry_SYSCALL_64_fastpath_[k];sys_write_[k];vfs_write_[k] 5 dd;[unknown];[unknown] 5 dd;[unknown];[unknown] 5 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k];iov_iter_zero_[k] 6 dd;read 15 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k];iov_iter_zero_[k];__clear_user_[k] 19 dd;[unknown];__write;-;entry_SYSCALL_64_fastpath_[k] 20 dd;read;-;entry_SYSCALL_64_fastpath_[k] 23 dd;read;-;entry_SYSCALL_64_fastpath_[k];SyS_read_[k];vfs_read_[k];__vfs_read_[k];read_iter_zero_[k];iov_iter_zero_[k];__clear_user_[k] 24 dd;__write;-;entry_SYSCALL_64_fastpath_[k] 25 dd;__write 29 dd;[unknown];read;-;entry_SYSCALL_64_fastpath_[k] 31 This can be made into a flamegraph. Eg: # ./profile -adf -p `pgrep -n func_ab` 10 > out.profile # git clone https://github.com/brendangregg/FlameGraph # ./FlameGraph/flamegraph.pl --color=java < out.profile > out.svg It will highlight the kernel frames in orange, and user-level in red (and Java in green, and C++ in yellow). If you copy-n-paste the above output into a out.profile file, you can try it out. You can increase or decrease the sample frequency. Eg, sampling at 9 Hertz: # ./profile -F 9 Sampling at 9 Hertz of all threads by user + kernel stack... Hit Ctrl-C to end. ^C [...] func_b main __libc_start_main [unknown] - func_ab (2930) 1 [...] You can also restrict profiling to just kernel stacks (-K) or user stacks (-U). For example, just user stacks: # ./profile -C 7 2 Sampling at 49 Hertz of all threads by user + kernel stack on CPU#7 for 2 secs. PyEval_EvalFrameEx [unknown] [unknown] - python (2827439) 1 PyDict_GetItem [unknown] - python (2827439) 1 [unknown] - python (2827439) 1 PyEval_EvalFrameEx [unknown] [unknown] - python (2827439) 1 PyEval_EvalFrameEx - python (2827439) 1 [unknown] [unknown] - python (2827439) in this example python application was busylooping on a single core/cpu (#7) we were collecting stack traces only from it # ./profile -U Sampling at 49 Hertz of all threads by user stack... Hit Ctrl-C to end. ^C [unknown] [unknown] - dd (2931) 1 [unknown] [unknown] - dd (2931) 1 [unknown] [unknown] - dd (2931) 1 [unknown] [unknown] - dd (2931) 1 [unknown] [unknown] - dd (2931) 1 func_b main __libc_start_main [unknown] - func_ab (2930) 1 [unknown] - dd (2931) 1 [unknown] - dd (2931) 1 func_a main __libc_start_main [unknown] - func_ab (2930) 3 __write [unknown] - dd (2931) 3 func_a main __libc_start_main [unknown] - func_ab (2930) 4 func_b main __libc_start_main [unknown] - func_ab (2930) 7 - swapper/6 (0) 10 func_b main __libc_start_main [unknown] - func_ab (2930) 10 __write - dd (2931) 10 func_a main __libc_start_main [unknown] - func_ab (2930) 11 read - dd (2931) 12 read [unknown] - dd (2931) 14 If there are too many unique stack traces for the kernel to save, a warning will be printed. Eg: # ./profile [...] WARNING: 8 stack traces could not be displayed. Consider increasing --stack-storage-size. Run ./profile -h to see the default. USAGE message: # ./profile -h usage: profile.py [-h] [-p PID | -L TID] [-U | -K] [-F FREQUENCY | -c COUNT] [-d] [-a] [-I] [-f] [--stack-storage-size STACK_STORAGE_SIZE] [-C CPU] [duration] Profile CPU stack traces at a timed interval positional arguments: duration duration of trace, in seconds optional arguments: -h, --help show this help message and exit -p PID, --pid PID profile process with this PID only -L TID, --tid TID profile thread with this TID only -U, --user-stacks-only show stacks from user space only (no kernel space stacks) -K, --kernel-stacks-only show stacks from kernel space only (no user space stacks) -F FREQUENCY, --frequency FREQUENCY sample frequency, Hertz -c COUNT, --count COUNT sample period, number of events -d, --delimited insert delimiter between kernel/user stacks -a, --annotations add _[k] annotations to kernel frames -I, --include-idle include CPU idle stacks -f, --folded output folded format, one line per stack (for flame graphs) --stack-storage-size STACK_STORAGE_SIZE the number of unique stack traces that can be stored and displayed (default 16384) -C CPU, --cpu CPU cpu number to run profile on examples: ./profile # profile stack traces at 49 Hertz until Ctrl-C ./profile -F 99 # profile stack traces at 99 Hertz ./profile -c 1000000 # profile stack traces every 1 in a million events ./profile 5 # profile at 49 Hertz for 5 seconds only ./profile -f 5 # output in folded format for flame graphs ./profile -p 185 # only profile process with PID 185 ./profile -L 185 # only profile thread with TID 185 ./profile -U # only show user space stacks (no kernel) ./profile -K # only show kernel space stacks (no user) bpfcc-0.12.0/tools/pythoncalls.sh000077500000000000000000000001001357404205000166750ustar00rootroot00000000000000#!/bin/bash lib=$(dirname $0)/lib $lib/ucalls.py -l python "$@" bpfcc-0.12.0/tools/pythoncalls_example.txt000077700000000000000000000000001357404205000251232lib/ucalls_example.txtustar00rootroot00000000000000bpfcc-0.12.0/tools/pythonflow.sh000077500000000000000000000000771357404205000165630ustar00rootroot00000000000000#!/bin/bash lib=$(dirname $0)/lib $lib/uflow.py -l python "$@" bpfcc-0.12.0/tools/pythonflow_example.txt000077700000000000000000000000001357404205000246452lib/uflow_example.txtustar00rootroot00000000000000bpfcc-0.12.0/tools/pythongc.sh000077500000000000000000000000751357404205000162030ustar00rootroot00000000000000#!/bin/bash lib=$(dirname $0)/lib $lib/ugc.py -l python "$@" bpfcc-0.12.0/tools/pythongc_example.txt000077700000000000000000000000001357404205000237112lib/ugc_example.txtustar00rootroot00000000000000bpfcc-0.12.0/tools/pythonstat.sh000077500000000000000000000000771357404205000165670ustar00rootroot00000000000000#!/bin/bash lib=$(dirname $0)/lib $lib/ustat.py -l python "$@" bpfcc-0.12.0/tools/pythonstat_example.txt000077700000000000000000000000001357404205000246552lib/ustat_example.txtustar00rootroot00000000000000bpfcc-0.12.0/tools/reset-trace.sh000077500000000000000000000066501357404205000165730ustar00rootroot00000000000000#!/bin/bash # # reset-trace - reset state of tracing, disabling all tracing. # Written for Linux. # # If a bcc tool crashed and you suspect tracing is partially enabled, you # can use this tool to reset the state of tracing, disabling anything still # enabled. Only use this tool in the case of error, and, consider filing a # bcc ticket so we can fix the error. # # bcc-used tracing facilities are reset. Other tracing facilities (ftrace) are # checked, and if not in an expected state, a note is printed. All tracing # files can be reset with -F for force, but this will interfere with any other # running tracing sessions (eg, ftrace). # # USAGE: ./reset-trace [-Fhqv] # # REQUIREMENTS: debugfs mounted on /sys/kernel/debug # # COPYRIGHT: Copyright (c) 2016 Brendan Gregg. # Licensed under the Apache License, Version 2.0 (the "License") # # 20-Jul-2014 Brendan Gregg Created this. # 18-Oct-2016 " " Updated for bcc use. tracing=/sys/kernel/debug/tracing opt_force=0; opt_verbose=0; opt_quiet=0 function usage { cat <<-END >&2 USAGE: reset-trace [-Fhqv] -F # force: reset all tracing files -v # verbose: print details while working -h # this usage message -q # quiet: no output eg, reset-trace # disable semi-enabled tracing END exit } function die { echo >&2 "$@" exit 1 } function vecho { (( ! opt_verbose )) && return echo "$@" } function writefile { file=$1 write=$2 if [[ ! -w $file ]]; then echo >&2 "WARNING: file $file not writable/exists. Skipping." return fi vecho "Checking $PWD/$file" contents=$(grep -v '^#' $file) if [[ "$contents" != "$expected" ]]; then (( ! opt_quiet )) && echo "Needed to reset $PWD/$file" vecho "$file, before (line enumerated):" (( opt_verbose )) && cat -nv $file cmd="echo $write > $file" if ! eval "$cmd"; then echo >&2 "WARNING: command failed \"$cmd\"." \ "bcc still running? Continuing." fi vecho "$file, after (line enumerated):" (( opt_verbose )) && cat -nv $file vecho fi } # only write when force is used function checkfile { file=$1 write=$2 expected=$3 if [[ ! -e $file ]]; then echo >&2 "WARNING: file $file doesn't exist. Skipping." return fi if (( opt_force )); then writefile $file $write return fi (( opt_quiet )) && return vecho "Checking $PWD/$file" contents=$(grep -v '^#' $file) if [[ "$contents" != "$expected" ]]; then echo "Noticed unrelated tracing file $PWD/$file isn't set as" \ "expected. Not reseting (-F to force, -v for verbose)." vecho "Contents of $file is (line enumerated):" (( opt_verbose )) && cat -nv $file vecho "Expected \"$expected\"." fi } ### process options while getopts Fhqv opt do case $opt in F) opt_force=1 ;; q) opt_quiet=1 ;; v) opt_verbose=1 ;; h|?) usage ;; esac done shift $(( $OPTIND - 1 )) ### reset tracing state vecho "Reseting tracing state..." vecho cd $tracing || die "ERROR: accessing tracing. Root user? /sys/kernel/debug?" # files bcc uses writefile kprobe_events "" "" writefile uprobe_events "" "" writefile trace "" "" # clears trace_pipe # non-bcc files checkfile current_tracer nop nop checkfile set_ftrace_filter "" "" checkfile set_graph_function "" "" checkfile set_ftrace_pid "" "no pid" checkfile events/enable 0 0 checkfile tracing_thresh 0 0 checkfile tracing_on 1 1 vecho vecho "Done." bpfcc-0.12.0/tools/reset-trace_example.txt000066400000000000000000000222201357404205000204770ustar00rootroot00000000000000Demonstrations of reset-trace, for Linux bcc/BPF. You will probably never need this tool. If you kill -9 a bcc tool (plus other signals, like SIGTERM), or if a bcc tool crashes, then kernel tracing can be left in a semi-enabled state. It's not as bad as it sounds: there may just be overhead for writing to ring buffers that are never read. This tool can be used to clean up the tracing state, and reset and disable active tracing. WARNING: Make sure no other tracing sessions are active, as it will likely stop them from functioning (perhaps ungracefully). This specifically clears the state in at least the following files in /sys/kernel/debug/tracing: kprobe_events, uprobe_events, trace_pipe. Other tracing facilities (ftrace) are checked, and if not in an expected state, a note is printed. All tracing files can be reset with -F for force, but this will interfere with any other running tracing sessions (eg, ftrace). Here's an example: # ./reset-trace.sh # That's it. You can use -v to see what it does: # ./reset-trace.sh -v Reseting tracing state... Checking /sys/kernel/debug/tracing/kprobe_events Checking /sys/kernel/debug/tracing/uprobe_events Checking /sys/kernel/debug/tracing/trace Checking /sys/kernel/debug/tracing/current_tracer Checking /sys/kernel/debug/tracing/set_ftrace_filter Checking /sys/kernel/debug/tracing/set_graph_function Checking /sys/kernel/debug/tracing/set_ftrace_pid Checking /sys/kernel/debug/tracing/events/enable Checking /sys/kernel/debug/tracing/tracing_thresh Checking /sys/kernel/debug/tracing/tracing_on Done. In this example, no resetting was necessary. Here's an example of actually needing it: # ./funccount 'bash:r*' Tracing 317 functions for "bash:r*"... Hit Ctrl-C to end. ^C FUNC COUNT rl_free_undo_list 1 rl_deprep_terminal 1 readline_internal_teardown 1 rl_on_new_line 1 rl_crlf 1 rl_clear_signals 1 rl_prep_terminal 1 rl_reset_line_state 1 rl_initialize 1 rl_newline 1 readline_internal_setup 1 rl_set_screen_size 1 readline 1 rl_set_signals 1 rl_expand_prompt 1 replace_history_data 1 rl_set_prompt 1 rl_add_undo 1 rl_insert_text 2 rl_insert 2 rl_redisplay 3 rl_read_key 3 rl_getc 3 readline_internal_char 3 restore_parser_state 6 reap_dead_jobs 6 reset_parser 6 restore_input_line_state 6 realloc 7 read_octal 10 read_tty_modified 13 run_exit_trap 13 redirection_expand 13 restore_pipestatus_array 18 reader_loop 20 run_return_trap 21 remember_args 25 reset_signal_handlers 30 remove_quoted_escapes 60 run_unwind_frame 102 reset_terminating_signals 125 restore_original_signals 139 reset_internal_getopt 405 run_debug_trap 719 read_command 940 remove_quoted_nulls 1830 run_pending_traps 3207 ^C ^C ^C I've traced 317 functions using funccount, and when I hit Ctrl-C, funccount is not exiting (it can normally take many seconds, but this really looks stuck): # pidstat 1 Linux 4.9.0-rc1-virtual (bgregg-xenial-bpf-i-xxx) 10/18/2016 _x86_64_ (8 CPU) 10:00:33 PM UID PID %usr %system %guest %CPU CPU Command 10:00:34 PM 60004 3277 0.00 0.98 0.00 0.98 0 redis-server 10:00:34 PM 0 27980 87.25 10.78 0.00 98.04 3 funccount.py 10:00:34 PM 0 29965 0.00 0.98 0.00 0.98 6 pidstat 10:00:34 PM UID PID %usr %system %guest %CPU CPU Command 10:00:35 PM 65534 3276 0.00 1.00 0.00 1.00 2 multilog 10:00:35 PM 0 27980 77.00 23.00 0.00 100.00 3 funccount.py 10:00:35 PM 0 29965 0.00 1.00 0.00 1.00 6 pidstat 10:00:35 PM 60004 29990 0.00 1.00 0.00 1.00 6 catalina.sh funccount looks a lot like it's in an infinite loop (I can use a stack-sampling profiler to confirm). This is a known bug (#665) and may be fixed by the time you read this. But right now it's a good example of needing reset-trace. I'll send a SIGTERM, before resorting to a SIGKILL: # kill 27980 Terminated Ok, so the process is now gone, but it did leave tracing in a semi-enabled state. Using reset-trace: # ./reset-trace.sh -v Reseting tracing state... Checking /sys/kernel/debug/tracing/kprobe_events Checking /sys/kernel/debug/tracing/uprobe_events Needed to reset /sys/kernel/debug/tracing/uprobe_events uprobe_events, before (line enumerated): 1 p:uprobes/p__bin_bash_0xa2540 /bin/bash:0x00000000000a2540 2 p:uprobes/p__bin_bash_0x21220 /bin/bash:0x0000000000021220 3 p:uprobes/p__bin_bash_0x78530 /bin/bash:0x0000000000078530 4 p:uprobes/p__bin_bash_0xa3840 /bin/bash:0x00000000000a3840 5 p:uprobes/p__bin_bash_0x9c550 /bin/bash:0x000000000009c550 6 p:uprobes/p__bin_bash_0x5e360 /bin/bash:0x000000000005e360 7 p:uprobes/p__bin_bash_0xb2630 /bin/bash:0x00000000000b2630 8 p:uprobes/p__bin_bash_0xb1e70 /bin/bash:0x00000000000b1e70 9 p:uprobes/p__bin_bash_0xb2540 /bin/bash:0x00000000000b2540 10 p:uprobes/p__bin_bash_0xb16e0 /bin/bash:0x00000000000b16e0 [...] 312 p:uprobes/p__bin_bash_0xa80b0 /bin/bash:0x00000000000a80b0 313 p:uprobes/p__bin_bash_0x9e280 /bin/bash:0x000000000009e280 314 p:uprobes/p__bin_bash_0x9e100 /bin/bash:0x000000000009e100 315 p:uprobes/p__bin_bash_0xb2bd0 /bin/bash:0x00000000000b2bd0 316 p:uprobes/p__bin_bash_0x9d9c0 /bin/bash:0x000000000009d9c0 317 p:uprobes/p__bin_bash_0x4a930 /bin/bash:0x000000000004a930 uprobe_events, after (line enumerated): Checking /sys/kernel/debug/tracing/trace Checking /sys/kernel/debug/tracing/current_tracer Checking /sys/kernel/debug/tracing/set_ftrace_filter Checking /sys/kernel/debug/tracing/set_graph_function Checking /sys/kernel/debug/tracing/set_ftrace_pid Checking /sys/kernel/debug/tracing/events/enable Checking /sys/kernel/debug/tracing/tracing_thresh Checking /sys/kernel/debug/tracing/tracing_on Done. Now looks clean. I did truncate the output here: there were a few hundred lines from uprobe_events. Here's the same situation, but without the verbose option: # ./reset-trace.sh Needed to reset /sys/kernel/debug/tracing/uprobe_events # And again with quiet: # ./reset-trace.sh -q # Here is an example of reset-trace detecting an unrelated tracing session: # ./reset-trace.sh Noticed unrelated tracing file /sys/kernel/debug/tracing/set_ftrace_filter isn't set as expected. Not reseting (-F to force, -v for verbose). And verbose: # ./reset-trace.sh -v Reseting tracing state... Checking /sys/kernel/debug/tracing/kprobe_events Checking /sys/kernel/debug/tracing/uprobe_events Checking /sys/kernel/debug/tracing/trace Checking /sys/kernel/debug/tracing/current_tracer Checking /sys/kernel/debug/tracing/set_ftrace_filter Noticed unrelated tracing file /sys/kernel/debug/tracing/set_ftrace_filter isn't set as expected. Not reseting (-F to force, -v for verbose). Contents of set_ftrace_filter is (line enumerated): 1 tcp_send_mss 2 tcp_sendpage 3 tcp_sendmsg 4 tcp_send_dupack 5 tcp_send_challenge_ack.isra.53 6 tcp_send_rcvq 7 tcp_send_ack 8 tcp_send_loss_probe 9 tcp_send_fin 10 tcp_send_active_reset 11 tcp_send_synack 12 tcp_send_delayed_ack 13 tcp_send_window_probe 14 tcp_send_probe0 Expected "". Checking /sys/kernel/debug/tracing/set_graph_function Checking /sys/kernel/debug/tracing/set_ftrace_pid Checking /sys/kernel/debug/tracing/events/enable Checking /sys/kernel/debug/tracing/tracing_thresh Checking /sys/kernel/debug/tracing/tracing_on Done. So this file is not currently used by bcc, but it may be useful to know that it's not in the default state -- something is either using it or has left it enabled. These files can be reset with -F, but that may break other tools that are currently using them. Use -h to print the USAGE message: # ./reset-trace.sh -h USAGE: reset-trace [-Fhqv] -F # force: reset all tracing files -v # verbose: print details while working -h # this usage message -q # quiet: no output eg, reset-trace # disable semi-enabled tracing bpfcc-0.12.0/tools/rubycalls.sh000077500000000000000000000000761357404205000163510ustar00rootroot00000000000000#!/bin/bash lib=$(dirname $0)/lib $lib/ucalls.py -l ruby "$@" bpfcc-0.12.0/tools/rubycalls_example.txt000077700000000000000000000000001357404205000245632lib/ucalls_example.txtustar00rootroot00000000000000bpfcc-0.12.0/tools/rubyflow.sh000077500000000000000000000000751357404205000162210ustar00rootroot00000000000000#!/bin/bash lib=$(dirname $0)/lib $lib/uflow.py -l ruby "$@" bpfcc-0.12.0/tools/rubyflow_example.txt000077700000000000000000000000001357404205000243052lib/uflow_example.txtustar00rootroot00000000000000bpfcc-0.12.0/tools/rubygc.sh000077500000000000000000000000731357404205000156410ustar00rootroot00000000000000#!/bin/bash lib=$(dirname $0)/lib $lib/ugc.py -l ruby "$@" bpfcc-0.12.0/tools/rubygc_example.txt000077700000000000000000000000001357404205000233512lib/ugc_example.txtustar00rootroot00000000000000bpfcc-0.12.0/tools/rubyobjnew.sh000077500000000000000000000000771357404205000165400ustar00rootroot00000000000000#!/bin/bash lib=$(dirname $0)/lib $lib/uobjnew.py -l ruby "$@" bpfcc-0.12.0/tools/rubyobjnew_example.txt000077700000000000000000000000001357404205000251372lib/uobjnew_example.txtustar00rootroot00000000000000bpfcc-0.12.0/tools/rubystat.sh000077500000000000000000000000751357404205000162250ustar00rootroot00000000000000#!/bin/bash lib=$(dirname $0)/lib $lib/ustat.py -l ruby "$@" bpfcc-0.12.0/tools/rubystat_example.txt000077700000000000000000000000001357404205000243152lib/ustat_example.txtustar00rootroot00000000000000bpfcc-0.12.0/tools/runqlat.py000077500000000000000000000175441357404205000160650ustar00rootroot00000000000000#!/usr/bin/python # @lint-avoid-python-3-compatibility-imports # # runqlat Run queue (scheduler) latency as a histogram. # For Linux, uses BCC, eBPF. # # USAGE: runqlat [-h] [-T] [-m] [-P] [-L] [-p PID] [interval] [count] # # This measures the time a task spends waiting on a run queue for a turn # on-CPU, and shows this time as a histogram. This time should be small, but a # task may need to wait its turn due to CPU load. # # This measures two types of run queue latency: # 1. The time from a task being enqueued on a run queue to its context switch # and execution. This traces ttwu_do_wakeup(), wake_up_new_task() -> # finish_task_switch() with either raw tracepoints (if supported) or kprobes # and instruments the run queue latency after a voluntary context switch. # 2. The time from when a task was involuntary context switched and still # in the runnable state, to when it next executed. This is instrumented # from finish_task_switch() alone. # # Copyright 2016 Netflix, Inc. # Licensed under the Apache License, Version 2.0 (the "License") # # 07-Feb-2016 Brendan Gregg Created this. from __future__ import print_function from bcc import BPF from time import sleep, strftime import argparse # arguments examples = """examples: ./runqlat # summarize run queue latency as a histogram ./runqlat 1 10 # print 1 second summaries, 10 times ./runqlat -mT 1 # 1s summaries, milliseconds, and timestamps ./runqlat -P # show each PID separately ./runqlat -p 185 # trace PID 185 only """ parser = argparse.ArgumentParser( description="Summarize run queue (scheduler) latency as a histogram", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("-T", "--timestamp", action="store_true", help="include timestamp on output") parser.add_argument("-m", "--milliseconds", action="store_true", help="millisecond histogram") parser.add_argument("-P", "--pids", action="store_true", help="print a histogram per process ID") # PID options are --pid and --pids, so namespaces should be --pidns (not done # yet) and --pidnss: parser.add_argument("--pidnss", action="store_true", help="print a histogram per PID namespace") parser.add_argument("-L", "--tids", action="store_true", help="print a histogram per thread ID") parser.add_argument("-p", "--pid", help="trace this PID only") parser.add_argument("interval", nargs="?", default=99999999, help="output interval, in seconds") parser.add_argument("count", nargs="?", default=99999999, help="number of outputs") parser.add_argument("--ebpf", action="store_true", help=argparse.SUPPRESS) args = parser.parse_args() countdown = int(args.count) debug = 0 # define BPF program bpf_text = """ #include #include #include #include typedef struct pid_key { u64 id; // work around u64 slot; } pid_key_t; typedef struct pidns_key { u64 id; // work around u64 slot; } pidns_key_t; BPF_HASH(start, u32); STORAGE struct rq; // record enqueue timestamp static int trace_enqueue(u32 tgid, u32 pid) { if (FILTER || pid == 0) return 0; u64 ts = bpf_ktime_get_ns(); start.update(&pid, &ts); return 0; } """ bpf_text_kprobe = """ int trace_wake_up_new_task(struct pt_regs *ctx, struct task_struct *p) { return trace_enqueue(p->tgid, p->pid); } int trace_ttwu_do_wakeup(struct pt_regs *ctx, struct rq *rq, struct task_struct *p, int wake_flags) { return trace_enqueue(p->tgid, p->pid); } // calculate latency int trace_run(struct pt_regs *ctx, struct task_struct *prev) { u32 pid, tgid; // ivcsw: treat like an enqueue event and store timestamp if (prev->state == TASK_RUNNING) { tgid = prev->tgid; pid = prev->pid; if (!(FILTER || pid == 0)) { u64 ts = bpf_ktime_get_ns(); start.update(&pid, &ts); } } tgid = bpf_get_current_pid_tgid() >> 32; pid = bpf_get_current_pid_tgid(); if (FILTER || pid == 0) return 0; u64 *tsp, delta; // fetch timestamp and calculate delta tsp = start.lookup(&pid); if (tsp == 0) { return 0; // missed enqueue } delta = bpf_ktime_get_ns() - *tsp; FACTOR // store as histogram STORE start.delete(&pid); return 0; } """ bpf_text_raw_tp = """ RAW_TRACEPOINT_PROBE(sched_wakeup) { // TP_PROTO(struct task_struct *p) struct task_struct *p = (struct task_struct *)ctx->args[0]; return trace_enqueue(p->tgid, p->pid); } RAW_TRACEPOINT_PROBE(sched_wakeup_new) { // TP_PROTO(struct task_struct *p) struct task_struct *p = (struct task_struct *)ctx->args[0]; return trace_enqueue(p->tgid, p->pid); } RAW_TRACEPOINT_PROBE(sched_switch) { // TP_PROTO(bool preempt, struct task_struct *prev, struct task_struct *next) struct task_struct *prev = (struct task_struct *)ctx->args[1]; struct task_struct *next = (struct task_struct *)ctx->args[2]; u32 pid, tgid; // ivcsw: treat like an enqueue event and store timestamp if (prev->state == TASK_RUNNING) { tgid = prev->tgid; pid = prev->pid; if (!(FILTER || pid == 0)) { u64 ts = bpf_ktime_get_ns(); start.update(&pid, &ts); } } tgid = next->tgid; pid = next->pid; if (FILTER || pid == 0) return 0; u64 *tsp, delta; // fetch timestamp and calculate delta tsp = start.lookup(&pid); if (tsp == 0) { return 0; // missed enqueue } delta = bpf_ktime_get_ns() - *tsp; FACTOR // store as histogram STORE start.delete(&pid); return 0; } """ is_support_raw_tp = BPF.support_raw_tracepoint() if is_support_raw_tp: bpf_text += bpf_text_raw_tp else: bpf_text += bpf_text_kprobe # code substitutions if args.pid: # pid from userspace point of view is thread group from kernel pov bpf_text = bpf_text.replace('FILTER', 'tgid != %s' % args.pid) else: bpf_text = bpf_text.replace('FILTER', '0') if args.milliseconds: bpf_text = bpf_text.replace('FACTOR', 'delta /= 1000000;') label = "msecs" else: bpf_text = bpf_text.replace('FACTOR', 'delta /= 1000;') label = "usecs" if args.pids or args.tids: section = "pid" pid = "tgid" if args.tids: pid = "pid" section = "tid" bpf_text = bpf_text.replace('STORAGE', 'BPF_HISTOGRAM(dist, pid_key_t);') bpf_text = bpf_text.replace('STORE', 'pid_key_t key = {.id = ' + pid + ', .slot = bpf_log2l(delta)}; ' + 'dist.increment(key);') elif args.pidnss: section = "pidns" bpf_text = bpf_text.replace('STORAGE', 'BPF_HISTOGRAM(dist, pidns_key_t);') bpf_text = bpf_text.replace('STORE', 'pidns_key_t key = ' + '{.id = prev->nsproxy->pid_ns_for_children->ns.inum, ' + '.slot = bpf_log2l(delta)}; dist.increment(key);') else: section = "" bpf_text = bpf_text.replace('STORAGE', 'BPF_HISTOGRAM(dist);') bpf_text = bpf_text.replace('STORE', 'dist.increment(bpf_log2l(delta));') if debug or args.ebpf: print(bpf_text) if args.ebpf: exit() # load BPF program b = BPF(text=bpf_text) if not is_support_raw_tp: b.attach_kprobe(event="ttwu_do_wakeup", fn_name="trace_ttwu_do_wakeup") b.attach_kprobe(event="wake_up_new_task", fn_name="trace_wake_up_new_task") b.attach_kprobe(event="finish_task_switch", fn_name="trace_run") print("Tracing run queue latency... Hit Ctrl-C to end.") # output exiting = 0 if args.interval else 1 dist = b.get_table("dist") while (1): try: sleep(int(args.interval)) except KeyboardInterrupt: exiting = 1 print() if args.timestamp: print("%-8s\n" % strftime("%H:%M:%S"), end="") dist.print_log2_hist(label, section, section_print_fn=int) dist.clear() countdown -= 1 if exiting or countdown == 0: exit() bpfcc-0.12.0/tools/runqlat_example.txt000066400000000000000000000764631357404205000177710ustar00rootroot00000000000000Demonstrations of runqlat, the Linux eBPF/bcc version. This program summarizes scheduler run queue latency as a histogram, showing how long tasks spent waiting their turn to run on-CPU. Here is a heavily loaded system: # ./runqlat Tracing run queue latency... Hit Ctrl-C to end. ^C usecs : count distribution 0 -> 1 : 233 |*********** | 2 -> 3 : 742 |************************************ | 4 -> 7 : 203 |********** | 8 -> 15 : 173 |******** | 16 -> 31 : 24 |* | 32 -> 63 : 0 | | 64 -> 127 : 30 |* | 128 -> 255 : 6 | | 256 -> 511 : 3 | | 512 -> 1023 : 5 | | 1024 -> 2047 : 27 |* | 2048 -> 4095 : 30 |* | 4096 -> 8191 : 20 | | 8192 -> 16383 : 29 |* | 16384 -> 32767 : 809 |****************************************| 32768 -> 65535 : 64 |*** | The distribution is bimodal, with one mode between 0 and 15 microseconds, and another between 16 and 65 milliseconds. These modes are visible as the spikes in the ASCII distribution (which is merely a visual representation of the "count" column). As an example of reading one line: 809 events fell into the 16384 to 32767 microsecond range (16 to 32 ms) while tracing. I would expect the two modes to be due the workload: 16 hot CPU-bound threads, and many other mostly idle threads doing occasional work. I suspect the mostly idle threads will run with a higher priority when they wake up, and are the reason for the low latency mode. The high latency mode will be the CPU-bound threads. More analysis with this and other tools can confirm. A -m option can be used to show milliseconds instead, as well as an interval and a count. For example, showing three x five second summary in milliseconds: # ./runqlat -m 5 3 Tracing run queue latency... Hit Ctrl-C to end. msecs : count distribution 0 -> 1 : 3818 |****************************************| 2 -> 3 : 39 | | 4 -> 7 : 39 | | 8 -> 15 : 62 | | 16 -> 31 : 2214 |*********************** | 32 -> 63 : 226 |** | msecs : count distribution 0 -> 1 : 3775 |****************************************| 2 -> 3 : 52 | | 4 -> 7 : 37 | | 8 -> 15 : 65 | | 16 -> 31 : 2230 |*********************** | 32 -> 63 : 212 |** | msecs : count distribution 0 -> 1 : 3816 |****************************************| 2 -> 3 : 49 | | 4 -> 7 : 40 | | 8 -> 15 : 53 | | 16 -> 31 : 2228 |*********************** | 32 -> 63 : 221 |** | This shows a similar distribution across the three summaries. A -p option can be used to show one PID only, which is filtered in kernel for efficiency. For example, PID 4505, and one second summaries: # ./runqlat -mp 4505 1 Tracing run queue latency... Hit Ctrl-C to end. msecs : count distribution 0 -> 1 : 1 |* | 2 -> 3 : 2 |*** | 4 -> 7 : 1 |* | 8 -> 15 : 0 | | 16 -> 31 : 25 |****************************************| 32 -> 63 : 3 |**** | msecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 2 |** | 4 -> 7 : 0 | | 8 -> 15 : 1 |* | 16 -> 31 : 30 |****************************************| 32 -> 63 : 1 |* | msecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 28 |****************************************| 32 -> 63 : 2 |** | msecs : count distribution 0 -> 1 : 1 |* | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 27 |****************************************| 32 -> 63 : 4 |***** | [...] For comparison, here is pidstat(1) for that process: # pidstat -p 4505 1 Linux 4.4.0-virtual (bgregg-xxxxxxxx) 02/08/2016 _x86_64_ (8 CPU) 08:56:11 AM UID PID %usr %system %guest %CPU CPU Command 08:56:12 AM 0 4505 9.00 3.00 0.00 12.00 0 bash 08:56:13 AM 0 4505 7.00 5.00 0.00 12.00 0 bash 08:56:14 AM 0 4505 10.00 2.00 0.00 12.00 0 bash 08:56:15 AM 0 4505 11.00 2.00 0.00 13.00 0 bash 08:56:16 AM 0 4505 9.00 3.00 0.00 12.00 0 bash [...] This is a synthetic workload that is CPU bound. It's only spending 12% on-CPU each second because of high CPU demand on this server: the remaining time is spent waiting on a run queue, as visualized by runqlat. Here is the same system, but when it is CPU idle: # ./runqlat 5 1 Tracing run queue latency... Hit Ctrl-C to end. usecs : count distribution 0 -> 1 : 2250 |******************************** | 2 -> 3 : 2340 |********************************** | 4 -> 7 : 2746 |****************************************| 8 -> 15 : 418 |****** | 16 -> 31 : 93 |* | 32 -> 63 : 28 | | 64 -> 127 : 119 |* | 128 -> 255 : 9 | | 256 -> 511 : 4 | | 512 -> 1023 : 20 | | 1024 -> 2047 : 22 | | 2048 -> 4095 : 5 | | 4096 -> 8191 : 2 | | Back to a microsecond scale, this time there is little run queue latency past 1 millisecond, as would be expected. Now 16 threads are performing heavy disk I/O: # ./runqlat 5 1 Tracing run queue latency... Hit Ctrl-C to end. usecs : count distribution 0 -> 1 : 204 | | 2 -> 3 : 944 |* | 4 -> 7 : 16315 |********************* | 8 -> 15 : 29897 |****************************************| 16 -> 31 : 1044 |* | 32 -> 63 : 23 | | 64 -> 127 : 128 | | 128 -> 255 : 24 | | 256 -> 511 : 5 | | 512 -> 1023 : 13 | | 1024 -> 2047 : 15 | | 2048 -> 4095 : 13 | | 4096 -> 8191 : 10 | | The distribution hasn't changed too much. While the disks are 100% busy, there is still plenty of CPU headroom, and threads still don't spend much time waiting their turn. A -P option will print a distribution for each PID: # ./runqlat -P Tracing run queue latency... Hit Ctrl-C to end. ^C pid = 0 usecs : count distribution 0 -> 1 : 351 |******************************** | 2 -> 3 : 96 |******** | 4 -> 7 : 437 |****************************************| 8 -> 15 : 12 |* | 16 -> 31 : 10 | | 32 -> 63 : 0 | | 64 -> 127 : 16 |* | 128 -> 255 : 0 | | 256 -> 511 : 0 | | 512 -> 1023 : 0 | | 1024 -> 2047 : 0 | | 2048 -> 4095 : 0 | | 4096 -> 8191 : 0 | | 8192 -> 16383 : 1 | | pid = 12929 usecs : count distribution 0 -> 1 : 1 |****************************************| 2 -> 3 : 0 | | 4 -> 7 : 1 |****************************************| pid = 12930 usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 1 |****************************************| 32 -> 63 : 0 | | 64 -> 127 : 1 |****************************************| pid = 12931 usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 1 |******************** | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 0 | | 128 -> 255 : 0 | | 256 -> 511 : 0 | | 512 -> 1023 : 2 |****************************************| pid = 12932 usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 0 | | 128 -> 255 : 1 |****************************************| 256 -> 511 : 0 | | 512 -> 1023 : 1 |****************************************| pid = 7 usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 426 |************************************* | 4 -> 7 : 457 |****************************************| 8 -> 15 : 16 |* | pid = 9 usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 425 |****************************************| 8 -> 15 : 16 |* | pid = 11 usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 10 |****************************************| pid = 14 usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 8 |****************************************| 4 -> 7 : 2 |********** | pid = 18 usecs : count distribution 0 -> 1 : 414 |****************************************| 2 -> 3 : 0 | | 4 -> 7 : 20 |* | 8 -> 15 : 8 | | pid = 12928 usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 1 |****************************************| 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 1 |****************************************| pid = 1867 usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 15 |****************************************| 16 -> 31 : 1 |** | 32 -> 63 : 0 | | 64 -> 127 : 0 | | 128 -> 255 : 4 |********** | pid = 1871 usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 2 |****************************************| 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 0 | | 128 -> 255 : 0 | | 256 -> 511 : 0 | | 512 -> 1023 : 1 |******************** | pid = 1876 usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 1 |****************************************| 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 0 | | 128 -> 255 : 0 | | 256 -> 511 : 1 |****************************************| pid = 1878 usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 3 |****************************************| pid = 1880 usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 3 |****************************************| pid = 9307 usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 1 |****************************************| pid = 1886 usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 1 |******************** | 8 -> 15 : 2 |****************************************| pid = 1888 usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 3 |****************************************| pid = 3297 usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 1 |****************************************| pid = 1892 usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 1 |******************** | 32 -> 63 : 0 | | 64 -> 127 : 0 | | 128 -> 255 : 0 | | 256 -> 511 : 0 | | 512 -> 1023 : 2 |****************************************| pid = 7024 usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 4 |****************************************| pid = 16468 usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 3 |****************************************| pid = 12922 usecs : count distribution 0 -> 1 : 1 |****************************************| 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 1 |****************************************| 16 -> 31 : 1 |****************************************| 32 -> 63 : 0 | | 64 -> 127 : 1 |****************************************| pid = 12923 usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 1 |******************** | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 2 |****************************************| 128 -> 255 : 0 | | 256 -> 511 : 0 | | 512 -> 1023 : 1 |******************** | 1024 -> 2047 : 1 |******************** | pid = 12924 usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 2 |******************** | 8 -> 15 : 4 |****************************************| 16 -> 31 : 1 |********** | 32 -> 63 : 0 | | 64 -> 127 : 0 | | 128 -> 255 : 0 | | 256 -> 511 : 0 | | 512 -> 1023 : 0 | | 1024 -> 2047 : 1 |********** | pid = 12925 usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 1 |****************************************| pid = 12926 usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 1 |****************************************| 4 -> 7 : 0 | | 8 -> 15 : 1 |****************************************| 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 0 | | 128 -> 255 : 0 | | 256 -> 511 : 0 | | 512 -> 1023 : 1 |****************************************| pid = 12927 usecs : count distribution 0 -> 1 : 1 |****************************************| 2 -> 3 : 0 | | 4 -> 7 : 1 |****************************************| A -L option will print a distribution for each TID: # ./runqlat -L Tracing run queue latency... Hit Ctrl-C to end. ^C tid = 0 usecs : count distribution 0 -> 1 : 593 |**************************** | 2 -> 3 : 829 |****************************************| 4 -> 7 : 300 |************** | 8 -> 15 : 321 |*************** | 16 -> 31 : 132 |****** | 32 -> 63 : 58 |** | 64 -> 127 : 0 | | 128 -> 255 : 0 | | 256 -> 511 : 13 | | tid = 7 usecs : count distribution 0 -> 1 : 8 |******** | 2 -> 3 : 19 |******************** | 4 -> 7 : 37 |****************************************| [...] And a --pidnss option (short for PID namespaces) will print for each PID namespace, for analyzing container performance: # ./runqlat --pidnss -m Tracing run queue latency... Hit Ctrl-C to end. ^C pidns = 4026532870 msecs : count distribution 0 -> 1 : 40 |****************************************| 2 -> 3 : 1 |* | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 2 |** | 64 -> 127 : 5 |***** | pidns = 4026532809 msecs : count distribution 0 -> 1 : 67 |****************************************| pidns = 4026532748 msecs : count distribution 0 -> 1 : 63 |****************************************| pidns = 4026532687 msecs : count distribution 0 -> 1 : 7 |****************************************| pidns = 4026532626 msecs : count distribution 0 -> 1 : 45 |****************************************| 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 3 |** | pidns = 4026531836 msecs : count distribution 0 -> 1 : 314 |****************************************| 2 -> 3 : 1 | | 4 -> 7 : 11 |* | 8 -> 15 : 28 |*** | 16 -> 31 : 137 |***************** | 32 -> 63 : 86 |********** | 64 -> 127 : 1 | | pidns = 4026532382 msecs : count distribution 0 -> 1 : 285 |****************************************| 2 -> 3 : 5 | | 4 -> 7 : 16 |** | 8 -> 15 : 9 |* | 16 -> 31 : 69 |********* | 32 -> 63 : 25 |*** | Many of these distributions have two modes: the second, in this case, is caused by capping CPU usage via CPU shares. USAGE message: # ./runqlat -h usage: runqlat.py [-h] [-T] [-m] [-P] [--pidnss] [-L] [-p PID] [interval] [count] Summarize run queue (scheduler) latency as a histogram positional arguments: interval output interval, in seconds count number of outputs optional arguments: -h, --help show this help message and exit -T, --timestamp include timestamp on output -m, --milliseconds millisecond histogram -P, --pids print a histogram per process ID --pidnss print a histogram per PID namespace -L, --tids print a histogram per thread ID -p PID, --pid PID trace this PID only examples: ./runqlat # summarize run queue latency as a histogram ./runqlat 1 10 # print 1 second summaries, 10 times ./runqlat -mT 1 # 1s summaries, milliseconds, and timestamps ./runqlat -P # show each PID separately ./runqlat -p 185 # trace PID 185 only bpfcc-0.12.0/tools/runqlen.py000077500000000000000000000171671357404205000160640ustar00rootroot00000000000000#!/usr/bin/python # @lint-avoid-python-3-compatibility-imports # # runqlen Summarize scheduler run queue length as a histogram. # For Linux, uses BCC, eBPF. # # This counts the length of the run queue, excluding the currently running # thread, and shows it as a histogram. # # Also answers run queue occupancy. # # USAGE: runqlen [-h] [-T] [-Q] [-m] [-D] [interval] [count] # # REQUIRES: Linux 4.9+ (BPF_PROG_TYPE_PERF_EVENT support). Under tools/old is # a version of this tool that may work on Linux 4.6 - 4.8. # # Copyright 2016 Netflix, Inc. # Licensed under the Apache License, Version 2.0 (the "License") # # 12-Dec-2016 Brendan Gregg Created this. from __future__ import print_function from bcc import BPF, PerfType, PerfSWConfig from time import sleep, strftime from tempfile import NamedTemporaryFile from os import open, close, dup, unlink, O_WRONLY import argparse # arguments examples = """examples: ./runqlen # summarize run queue length as a histogram ./runqlen 1 10 # print 1 second summaries, 10 times ./runqlen -T 1 # 1s summaries and timestamps ./runqlen -O # report run queue occupancy ./runqlen -C # show each CPU separately """ parser = argparse.ArgumentParser( description="Summarize scheduler run queue length as a histogram", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("-T", "--timestamp", action="store_true", help="include timestamp on output") parser.add_argument("-O", "--runqocc", action="store_true", help="report run queue occupancy") parser.add_argument("-C", "--cpus", action="store_true", help="print output for each CPU separately") parser.add_argument("interval", nargs="?", default=99999999, help="output interval, in seconds") parser.add_argument("count", nargs="?", default=99999999, help="number of outputs") parser.add_argument("--ebpf", action="store_true", help=argparse.SUPPRESS) args = parser.parse_args() countdown = int(args.count) debug = 0 frequency = 99 # Linux 4.15 introduced a new field runnable_weight # in linux_src:kernel/sched/sched.h as # struct cfs_rq { # struct load_weight load; # unsigned long runnable_weight; # unsigned int nr_running, h_nr_running; # ...... # } # and this tool requires to access nr_running to get # runqueue len information. # # The commit which introduces cfs_rq->runnable_weight # field also introduces the field sched_entity->runnable_weight # where sched_entity is defined in linux_src:include/linux/sched.h. # # To cope with pre-4.15 and 4.15/post-4.15 releases, # we run a simple BPF program to detect whether # field sched_entity->runnable_weight exists. The existence of # this field should infer the existence of cfs_rq->runnable_weight. # # This will need maintenance as the relationship between these # two fields may change in the future. # def check_runnable_weight_field(): # Define the bpf program for checking purpose bpf_check_text = """ #include unsigned long dummy(struct sched_entity *entity) { return entity->runnable_weight; } """ # Get a temporary file name tmp_file = NamedTemporaryFile(delete=False) tmp_file.close(); # Duplicate and close stderr (fd = 2) old_stderr = dup(2) close(2) # Open a new file, should get fd number 2 # This will avoid printing llvm errors on the screen fd = open(tmp_file.name, O_WRONLY) try: t = BPF(text=bpf_check_text) success_compile = True except: success_compile = False # Release the fd 2, and next dup should restore old stderr close(fd) dup(old_stderr) close(old_stderr) # remove the temporary file and return unlink(tmp_file.name) return success_compile # define BPF program bpf_text = """ #include #include // Declare enough of cfs_rq to find nr_running, since we can't #import the // header. This will need maintenance. It is from kernel/sched/sched.h: struct cfs_rq_partial { struct load_weight load; RUNNABLE_WEIGHT_FIELD unsigned int nr_running, h_nr_running; }; typedef struct cpu_key { int cpu; unsigned int slot; } cpu_key_t; STORAGE int do_perf_event() { unsigned int len = 0; pid_t pid = 0; struct task_struct *task = NULL; struct cfs_rq_partial *my_q = NULL; // Fetch the run queue length from task->se.cfs_rq->nr_running. This is an // unstable interface and may need maintenance. Perhaps a future version // of BPF will support task_rq(p) or something similar as a more reliable // interface. task = (struct task_struct *)bpf_get_current_task(); my_q = (struct cfs_rq_partial *)task->se.cfs_rq; len = my_q->nr_running; // Calculate run queue length by subtracting the currently running task, // if present. len 0 == idle, len 1 == one running task. if (len > 0) len--; STORE return 0; } """ # code substitutions if args.cpus: bpf_text = bpf_text.replace('STORAGE', 'BPF_HISTOGRAM(dist, cpu_key_t);') bpf_text = bpf_text.replace('STORE', 'cpu_key_t key = {.slot = len}; ' + 'key.cpu = bpf_get_smp_processor_id(); ' + 'dist.increment(key);') else: bpf_text = bpf_text.replace('STORAGE', 'BPF_HISTOGRAM(dist, unsigned int);') bpf_text = bpf_text.replace('STORE', 'dist.increment(len);') if check_runnable_weight_field(): bpf_text = bpf_text.replace('RUNNABLE_WEIGHT_FIELD', 'unsigned long runnable_weight;') else: bpf_text = bpf_text.replace('RUNNABLE_WEIGHT_FIELD', '') if debug or args.ebpf: print(bpf_text) if args.ebpf: exit() # initialize BPF & perf_events b = BPF(text=bpf_text) b.attach_perf_event(ev_type=PerfType.SOFTWARE, ev_config=PerfSWConfig.CPU_CLOCK, fn_name="do_perf_event", sample_period=0, sample_freq=frequency) print("Sampling run queue length... Hit Ctrl-C to end.") # output exiting = 0 if args.interval else 1 dist = b.get_table("dist") while (1): try: sleep(int(args.interval)) except KeyboardInterrupt: exiting = 1 print() if args.timestamp: print("%-8s\n" % strftime("%H:%M:%S"), end="") if args.runqocc: if args.cpus: # run queue occupancy, per-CPU summary idle = {} queued = {} cpumax = 0 for k, v in dist.items(): if k.cpu > cpumax: cpumax = k.cpu for c in range(0, cpumax + 1): idle[c] = 0 queued[c] = 0 for k, v in dist.items(): if k.slot == 0: idle[k.cpu] += v.value else: queued[k.cpu] += v.value for c in range(0, cpumax + 1): samples = idle[c] + queued[c] if samples: runqocc = float(queued[c]) / samples else: runqocc = 0 print("runqocc, CPU %-3d %6.2f%%" % (c, 100 * runqocc)) else: # run queue occupancy, system-wide summary idle = 0 queued = 0 for k, v in dist.items(): if k.value == 0: idle += v.value else: queued += v.value samples = idle + queued if samples: runqocc = float(queued) / samples else: runqocc = 0 print("runqocc: %0.2f%%" % (100 * runqocc)) else: # run queue length histograms dist.print_linear_hist("runqlen", "cpu") dist.clear() countdown -= 1 if exiting or countdown == 0: exit() bpfcc-0.12.0/tools/runqlen_example.txt000066400000000000000000000275511357404205000177610ustar00rootroot00000000000000Demonstrations of runqlen, the Linux eBPF/bcc version. 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. As an example, here is an idle system: # ./runqlen.py Sampling run queue length... Hit Ctrl-C to end. ^C runqlen : count distribution 0 : 1776 |****************************************| This shows a zero run queue length each time it was sampled. And now a heavily loaded system: # ./runqlen.py Sampling run queue length... Hit Ctrl-C to end. ^C runqlen : count distribution 0 : 1068 |****************************************| 1 : 642 |************************ | 2 : 369 |************* | 3 : 183 |****** | 4 : 104 |*** | 5 : 42 |* | 6 : 13 | | 7 : 2 | | 8 : 1 | | Now there is often threads queued, with one sample reaching a queue length of 8. This will cause run queue latency, which can be measured by the bcc runqlat tool. Here's an example of an issue that runqlen can indentify. Starting with the system-wide summary: # ./runqlen.py Sampling run queue length... Hit Ctrl-C to end. ^C runqlen : count distribution 0 : 1209 |****************************************| 1 : 372 |************ | 2 : 73 |** | 3 : 3 | | 4 : 1 | | 5 : 0 | | 6 : 0 | | 7 : 237 |******* | This shows there is often a run queue length of 7. Now using the -C option to see per-CPU histograms: # ./runqlen.py -C Sampling run queue length... Hit Ctrl-C to end. ^C cpu = 0 runqlen : count distribution 0 : 257 |****************************************| 1 : 64 |********* | 2 : 5 | | 3 : 0 | | 4 : 0 | | 5 : 0 | | 6 : 1 | | cpu = 1 runqlen : count distribution 0 : 226 |****************************************| 1 : 90 |*************** | 2 : 11 |* | cpu = 2 runqlen : count distribution 0 : 264 |****************************************| 1 : 52 |******* | 2 : 8 |* | 3 : 1 | | 4 : 0 | | 5 : 0 | | 6 : 1 | | 7 : 0 | | 8 : 1 | | cpu = 3 runqlen : count distribution 0 : 0 | | 1 : 0 | | 2 : 0 | | 3 : 0 | | 4 : 0 | | 5 : 0 | | 6 : 0 | | 7 : 327 |****************************************| cpu = 4 runqlen : count distribution 0 : 255 |****************************************| 1 : 63 |********* | 2 : 9 |* | cpu = 5 runqlen : count distribution 0 : 244 |****************************************| 1 : 78 |************ | 2 : 3 | | 3 : 2 | | cpu = 6 runqlen : count distribution 0 : 253 |****************************************| 1 : 66 |********** | 2 : 6 | | 3 : 1 | | 4 : 1 | | cpu = 7 runqlen : count distribution 0 : 243 |****************************************| 1 : 74 |************ | 2 : 6 | | 3 : 1 | | 4 : 0 | | 5 : 1 | | 6 : 2 | | The run queue length of 7 is isolated to CPU 3. It was caused by CPU binding (taskset). This can sometimes happen by applications that try to auto-bind to CPUs, leaving other CPUs idle while work is queued. runqlat accepts an interval and a count. For example, with -T for timestamps: # ./runqlen.py -T 1 5 Sampling run queue length... Hit Ctrl-C to end. 19:51:34 runqlen : count distribution 0 : 635 |****************************************| 1 : 142 |******** | 2 : 13 | | 3 : 0 | | 4 : 1 | | 19:51:35 runqlen : count distribution 0 : 640 |****************************************| 1 : 136 |******** | 2 : 13 | | 3 : 1 | | 4 : 0 | | 5 : 0 | | 6 : 0 | | 7 : 0 | | 8 : 0 | | 9 : 0 | | 10 : 1 | | 19:51:36 runqlen : count distribution 0 : 603 |****************************************| 1 : 170 |*********** | 2 : 16 |* | 3 : 1 | | 4 : 0 | | 5 : 0 | | 6 : 0 | | 7 : 0 | | 8 : 0 | | 9 : 1 | | 19:51:37 runqlen : count distribution 0 : 617 |****************************************| 1 : 154 |********* | 2 : 20 |* | 3 : 0 | | 4 : 0 | | 5 : 0 | | 6 : 0 | | 7 : 0 | | 8 : 0 | | 9 : 0 | | 10 : 0 | | 11 : 1 | | 19:51:38 runqlen : count distribution 0 : 603 |****************************************| 1 : 161 |********** | 2 : 24 |* | 3 : 4 | | The spikes in run queue length of 11 are likely threads waking up at the same time (a thundering herd), and then are scheduled and complete their execution quickly. The -O option prints run queue occupancy: the percentage of time that there was work queued waiting its turn. Eg: # ./runqlen.py -OT 1 Sampling run queue length... Hit Ctrl-C to end. 19:54:53 runqocc: 41.09% 19:54:54 runqocc: 41.85% 19:54:55 runqocc: 41.47% 19:54:56 runqocc: 42.35% 19:54:57 runqocc: 40.83% [...] This can also be examined per-CPU: # ./runqlen.py -COT 1 Sampling run queue length... Hit Ctrl-C to end. 19:55:03 runqocc, CPU 0 32.32% runqocc, CPU 1 26.26% runqocc, CPU 2 38.38% runqocc, CPU 3 100.00% runqocc, CPU 4 26.26% runqocc, CPU 5 32.32% runqocc, CPU 6 39.39% runqocc, CPU 7 46.46% 19:55:04 runqocc, CPU 0 35.00% runqocc, CPU 1 32.32% runqocc, CPU 2 37.00% runqocc, CPU 3 100.00% runqocc, CPU 4 43.43% runqocc, CPU 5 31.31% runqocc, CPU 6 28.00% runqocc, CPU 7 31.31% 19:55:05 runqocc, CPU 0 43.43% runqocc, CPU 1 32.32% runqocc, CPU 2 45.45% runqocc, CPU 3 100.00% runqocc, CPU 4 29.29% runqocc, CPU 5 36.36% runqocc, CPU 6 36.36% runqocc, CPU 7 30.30% 19:55:06 runqocc, CPU 0 40.00% runqocc, CPU 1 38.00% runqocc, CPU 2 31.31% runqocc, CPU 3 100.00% runqocc, CPU 4 31.31% runqocc, CPU 5 28.28% runqocc, CPU 6 31.00% runqocc, CPU 7 29.29% [...] USAGE message: # ./runqlen -h usage: runqlen [-h] [-T] [-O] [-C] [interval] [count] Summarize scheduler run queue length as a histogram positional arguments: interval output interval, in seconds count number of outputs optional arguments: -h, --help show this help message and exit -T, --timestamp include timestamp on output -O, --runqocc report run queue occupancy -C, --cpus print output for each CPU separately examples: ./runqlen # summarize run queue length as a histogram ./runqlen 1 10 # print 1 second summaries, 10 times ./runqlen -T 1 # 1s summaries and timestamps ./runqlen -O # report run queue occupancy ./runqlen -C # show each CPU separately bpfcc-0.12.0/tools/runqslower.py000077500000000000000000000161561357404205000166160ustar00rootroot00000000000000#!/usr/bin/python # @lint-avoid-python-3-compatibility-imports # # runqslower Trace long process scheduling delays. # For Linux, uses BCC, eBPF. # # This script traces high scheduling delays between tasks being # ready to run and them running on CPU after that. # # USAGE: runqslower [-p PID] [min_us] # # REQUIRES: Linux 4.9+ (BPF_PROG_TYPE_PERF_EVENT support). # # This measures the time a task spends waiting on a run queue for a turn # on-CPU, and shows this time as a individual events. This time should be small, # but a task may need to wait its turn due to CPU load. # # This measures two types of run queue latency: # 1. The time from a task being enqueued on a run queue to its context switch # and execution. This traces ttwu_do_wakeup(), wake_up_new_task() -> # finish_task_switch() with either raw tracepoints (if supported) or kprobes # and instruments the run queue latency after a voluntary context switch. # 2. The time from when a task was involuntary context switched and still # in the runnable state, to when it next executed. This is instrumented # from finish_task_switch() alone. # # Copyright 2016 Cloudflare, Inc. # Licensed under the Apache License, Version 2.0 (the "License") # # 02-May-2018 Ivan Babrou Created this. # 18-Nov-2019 Gergely Bod BUG fix: Use bpf_probe_read_str() to extract the # process name from 'task_struct* next' in raw tp code. # bpf_get_current_comm() operates on the current task # which might already be different than 'next'. from __future__ import print_function from bcc import BPF import argparse from time import strftime # arguments examples = """examples: ./runqslower # trace run queue latency higher than 10000 us (default) ./runqslower 1000 # trace run queue latency higher than 1000 us ./runqslower -p 123 # trace pid 123 only """ parser = argparse.ArgumentParser( description="Trace high run queue latency", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("-p", "--pid", type=int, metavar="PID", dest="pid", help="trace this PID only") parser.add_argument("min_us", nargs="?", default='10000', help="minimum run queue latecy to trace, in ms (default 10000)") parser.add_argument("--ebpf", action="store_true", help=argparse.SUPPRESS) args = parser.parse_args() min_us = int(args.min_us) debug = 0 # define BPF program bpf_text = """ #include #include #include #include BPF_HASH(start, u32); struct rq; struct data_t { u32 pid; char task[TASK_COMM_LEN]; u64 delta_us; }; BPF_PERF_OUTPUT(events); // record enqueue timestamp static int trace_enqueue(u32 tgid, u32 pid) { if (FILTER_PID || pid == 0) return 0; u64 ts = bpf_ktime_get_ns(); start.update(&pid, &ts); return 0; } """ bpf_text_kprobe = """ int trace_wake_up_new_task(struct pt_regs *ctx, struct task_struct *p) { return trace_enqueue(p->tgid, p->pid); } int trace_ttwu_do_wakeup(struct pt_regs *ctx, struct rq *rq, struct task_struct *p, int wake_flags) { return trace_enqueue(p->tgid, p->pid); } // calculate latency int trace_run(struct pt_regs *ctx, struct task_struct *prev) { u32 pid, tgid; // ivcsw: treat like an enqueue event and store timestamp if (prev->state == TASK_RUNNING) { tgid = prev->tgid; pid = prev->pid; if (!(FILTER_PID || pid == 0)) { u64 ts = bpf_ktime_get_ns(); start.update(&pid, &ts); } } tgid = bpf_get_current_pid_tgid() >> 32; pid = bpf_get_current_pid_tgid(); u64 *tsp, delta_us; // fetch timestamp and calculate delta tsp = start.lookup(&pid); if (tsp == 0) { return 0; // missed enqueue } delta_us = (bpf_ktime_get_ns() - *tsp) / 1000; if (FILTER_US) return 0; struct data_t data = {}; data.pid = pid; data.delta_us = delta_us; bpf_get_current_comm(&data.task, sizeof(data.task)); // output events.perf_submit(ctx, &data, sizeof(data)); start.delete(&pid); return 0; } """ bpf_text_raw_tp = """ RAW_TRACEPOINT_PROBE(sched_wakeup) { // TP_PROTO(struct task_struct *p) struct task_struct *p = (struct task_struct *)ctx->args[0]; return trace_enqueue(p->tgid, p->pid); } RAW_TRACEPOINT_PROBE(sched_wakeup_new) { // TP_PROTO(struct task_struct *p) struct task_struct *p = (struct task_struct *)ctx->args[0]; u32 tgid, pid; bpf_probe_read(&tgid, sizeof(tgid), &p->tgid); bpf_probe_read(&pid, sizeof(pid), &p->pid); return trace_enqueue(tgid, pid); } RAW_TRACEPOINT_PROBE(sched_switch) { // TP_PROTO(bool preempt, struct task_struct *prev, struct task_struct *next) struct task_struct *prev = (struct task_struct *)ctx->args[1]; struct task_struct *next= (struct task_struct *)ctx->args[2]; u32 pid; long state; // ivcsw: treat like an enqueue event and store timestamp bpf_probe_read(&state, sizeof(long), (const void *)&prev->state); if (state == TASK_RUNNING) { bpf_probe_read(&pid, sizeof(prev->pid), &prev->pid); if (!(FILTER_PID || pid == 0)) { u64 ts = bpf_ktime_get_ns(); start.update(&pid, &ts); } } bpf_probe_read(&pid, sizeof(next->pid), &next->pid); u64 *tsp, delta_us; // fetch timestamp and calculate delta tsp = start.lookup(&pid); if (tsp == 0) { return 0; // missed enqueue } delta_us = (bpf_ktime_get_ns() - *tsp) / 1000; if (FILTER_US) return 0; struct data_t data = {}; data.pid = pid; data.delta_us = delta_us; bpf_probe_read_str(&data.task, sizeof(data.task), next->comm); // output events.perf_submit(ctx, &data, sizeof(data)); start.delete(&pid); return 0; } """ is_support_raw_tp = BPF.support_raw_tracepoint() if is_support_raw_tp: bpf_text += bpf_text_raw_tp else: bpf_text += bpf_text_kprobe # code substitutions if min_us == 0: bpf_text = bpf_text.replace('FILTER_US', '0') else: bpf_text = bpf_text.replace('FILTER_US', 'delta_us <= %s' % str(min_us)) if args.pid: bpf_text = bpf_text.replace('FILTER_PID', 'pid != %s' % args.pid) else: bpf_text = bpf_text.replace('FILTER_PID', '0') if debug or args.ebpf: print(bpf_text) if args.ebpf: exit() # process event def print_event(cpu, data, size): event = b["events"].event(data) print("%-8s %-16s %-6s %14s" % (strftime("%H:%M:%S"), event.task, event.pid, event.delta_us)) # load BPF program b = BPF(text=bpf_text) if not is_support_raw_tp: b.attach_kprobe(event="ttwu_do_wakeup", fn_name="trace_ttwu_do_wakeup") b.attach_kprobe(event="wake_up_new_task", fn_name="trace_wake_up_new_task") b.attach_kprobe(event="finish_task_switch", fn_name="trace_run") print("Tracing run queue latency higher than %d us" % min_us) print("%-8s %-16s %-6s %14s" % ("TIME", "COMM", "PID", "LAT(us)")) # read events b["events"].open_perf_buffer(print_event, page_cnt=64) while 1: try: b.perf_buffer_poll() except KeyboardInterrupt: exit() bpfcc-0.12.0/tools/runqslower_example.txt000066400000000000000000000033111357404205000205020ustar00rootroot00000000000000Demonstrations of runqslower, the Linux eBPF/bcc version. runqslower shows high latency scheduling times between tasks being ready to run and them running on CPU after that. For example: # runqslower Tracing run queue latency higher than 10000 us TIME COMM PID LAT(us) 04:16:32 cc1 12924 12739 04:16:32 sh 13640 12118 04:16:32 make 13639 12730 04:16:32 bash 13655 12047 04:16:32 bash 13657 12744 04:16:32 bash 13656 12880 04:16:32 sh 13660 10846 04:16:32 gcc 13663 12681 04:16:32 make 13668 10814 04:16:32 make 13670 12988 04:16:32 gcc 13677 11770 04:16:32 gcc 13678 23519 04:16:32 as 12999 20541 [...] This shows various processes waiting for available CPU during a Linux kernel build. By default the output contains delays for more than 10ms. These delays can be analyzed in depth with "perf sched" tool, see: * http://www.brendangregg.com/blog/2017-03-16/perf-sched.html USAGE message: # ./runqslower -h usage: runqslower.py [-h] [-p PID] [min_us] Trace high run queue latency positional arguments: min_us minimum run queue latecy to trace, in us (default 10000) optional arguments: -h, --help show this help message and exit -p PID, --pid PID trace this PID only examples: ./runqslower # trace run queue latency higher than 10000 us (default) ./runqslower 1000 # trace run queue latency higher than 1000 us ./runqslower -p 123 # trace pid 123 only bpfcc-0.12.0/tools/shmsnoop.py000077500000000000000000000174571357404205000162500ustar00rootroot00000000000000#!/usr/bin/python # @lint-avoid-python-3-compatibility-imports # # shmsnoop Trace shm*() syscalls. # For Linux, uses BCC, eBPF. Embedded C. # # USAGE: shmsnoop [-h] [-T] [-x] [-p PID] [-d DURATION] [-t TID] [-n NAME] # # Copyright (c) 2018 Jiri Olsa. # Licensed under the Apache License, Version 2.0 (the "License") # # 08-Oct-2018 Jiri Olsa Created this. from __future__ import print_function from bcc import ArgString, BPF import argparse from datetime import datetime, timedelta # arguments examples = """examples: ./shmsnoop # trace all shm*() syscalls ./shmsnoop -T # include timestamps ./shmsnoop -p 181 # only trace PID 181 ./shmsnoop -t 123 # only trace TID 123 ./shmsnoop -d 10 # trace for 10 seconds only ./shmsnoop -n main # only print process names containing "main" """ parser = argparse.ArgumentParser( description="Trace shm*() syscalls", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("-T", "--timestamp", action="store_true", help="include timestamp on output") parser.add_argument("-p", "--pid", help="trace this PID only") parser.add_argument("-t", "--tid", help="trace this TID only") parser.add_argument("-d", "--duration", help="total duration of trace in seconds") parser.add_argument("-n", "--name", type=ArgString, help="only print process names containing this name") parser.add_argument("--ebpf", action="store_true", help=argparse.SUPPRESS) args = parser.parse_args() debug = 0 if args.duration: args.duration = timedelta(seconds=int(args.duration)) # define BPF program bpf_text = """ #include #include #include struct val_t { u64 id; u64 ts; int sys; unsigned long key; unsigned long size; unsigned long shmflg; unsigned long shmid; unsigned long cmd; unsigned long buf; unsigned long shmaddr; unsigned long ret; char comm[TASK_COMM_LEN]; }; BPF_HASH(infotmp, u64, struct val_t); BPF_PERF_OUTPUT(events); enum { SYS_SHMGET, SYS_SHMAT, SYS_SHMDT, SYS_SHMCTL, }; static int enter(struct val_t *val) { u64 id = bpf_get_current_pid_tgid(); u32 pid = id >> 32; // PID is higher part u32 tid = id; // Cast and get the lower part FILTER val->id = id; infotmp.update(&id, val); return 0; } int trace_return(struct pt_regs *ctx) { u64 id = bpf_get_current_pid_tgid(); u64 tsp = bpf_ktime_get_ns(); struct val_t *val; val = infotmp.lookup(&id); if (val == 0) return 0; if (bpf_get_current_comm(&val->comm, sizeof(val->comm)) != 0) goto out; val->ts = tsp / 1000; val->ret = PT_REGS_RC(ctx); events.perf_submit(ctx, val, sizeof(*val)); out: infotmp.delete(&id); return 0; } int syscall__shmget(struct pt_regs *ctx, u64 key, u64 size, u64 shmflg) { struct val_t val = { .sys = SYS_SHMGET, }; val.key = key; val.size = size; val.shmflg = shmflg; return enter(&val); }; int syscall__shmat(struct pt_regs *ctx, u64 shmid, u64 shmaddr, u64 shmflg) { struct val_t val = { .sys = SYS_SHMAT, }; val.shmid = shmid; val.shmaddr = shmaddr; val.shmflg = shmflg; return enter(&val); }; int syscall__shmdt(struct pt_regs *ctx, u64 shmaddr) { struct val_t val = { .sys = SYS_SHMDT, }; val.shmaddr = shmaddr; return enter(&val); }; int syscall__shmctl(struct pt_regs *ctx, u64 shmid, u64 cmd, u64 buf) { struct val_t val = { .sys = SYS_SHMCTL, }; val.shmid = shmid; val.cmd = cmd; val.buf = buf; return enter(&val); }; """ if args.tid: # TID trumps PID bpf_text = bpf_text.replace('FILTER', 'if (tid != %s) { return 0; }' % args.tid) elif args.pid: bpf_text = bpf_text.replace('FILTER', 'if (pid != %s) { return 0; }' % args.pid) else: bpf_text = bpf_text.replace('FILTER', '') if debug or args.ebpf: print(bpf_text) if args.ebpf: exit() # initialize BPF b = BPF(text=bpf_text) syscall_fnname = b.get_syscall_fnname("shmget") if BPF.ksymname(syscall_fnname) != -1: b.attach_kprobe(event=syscall_fnname, fn_name="syscall__shmget") b.attach_kretprobe(event=syscall_fnname, fn_name="trace_return") syscall_fnname = b.get_syscall_fnname("shmat") if BPF.ksymname(syscall_fnname) != -1: b.attach_kprobe(event=syscall_fnname, fn_name="syscall__shmat") b.attach_kretprobe(event=syscall_fnname, fn_name="trace_return") syscall_fnname = b.get_syscall_fnname("shmdt") if BPF.ksymname(syscall_fnname) != -1: b.attach_kprobe(event=syscall_fnname, fn_name="syscall__shmdt") b.attach_kretprobe(event=syscall_fnname, fn_name="trace_return") syscall_fnname = b.get_syscall_fnname("shmctl") if BPF.ksymname(syscall_fnname) != -1: b.attach_kprobe(event=syscall_fnname, fn_name="syscall__shmctl") b.attach_kretprobe(event=syscall_fnname, fn_name="trace_return") TASK_COMM_LEN = 16 # linux/sched.h SYS_SHMGET = 0 SYS_SHMAT = 1 SYS_SHMDT = 2 SYS_SHMCTL = 3 initial_ts = 0 # header if args.timestamp: print("%-14s" % ("TIME(s)"), end="") print("%-6s %-16s %6s %16s ARGs" % ("TID" if args.tid else "PID", "COMM", "SYS", "RET")) def sys_name(sys): switcher = { SYS_SHMGET: "SHMGET", SYS_SHMAT: "SHMAT", SYS_SHMDT: "SHMDT", SYS_SHMCTL: "SHMCTL", } return switcher.get(sys, "N/A") shmget_flags = [ { 'name' : 'IPC_CREAT', 'value' : 0o1000 }, { 'name' : 'IPC_EXCL', 'value' : 0o2000 }, { 'name' : 'SHM_HUGETLB', 'value' : 0o4000 }, { 'name' : 'SHM_HUGE_2MB', 'value' : 21 << 26 }, { 'name' : 'SHM_HUGE_1GB', 'value' : 30 << 26 }, { 'name' : 'SHM_NORESERVE', 'value' : 0o10000 }, { 'name' : 'SHM_EXEC', 'value' : 0o100000 } ] shmat_flags = [ { 'name' : 'SHM_RDONLY', 'value' : 0o10000 }, { 'name' : 'SHM_RND', 'value' : 0o20000 }, { 'name' : 'SHM_REMAP', 'value' : 0o40000 }, { 'name' : 'SHM_EXEC', 'value' : 0o100000 }, ] def shmflg_str(val, flags): cur = filter(lambda x : x['value'] & val, flags) str = "0x%x" % val if (not val): return str str += " (" cnt = 0 for x in cur: if cnt: str += "|" str += x['name'] val &= ~x['value'] cnt += 1 if val != 0 or not cnt: if cnt: str += "|" str += "0%o" % val str += ")" return str # process event def print_event(cpu, data, size): event = b["events"].event(data) global initial_ts if not initial_ts: initial_ts = event.ts if args.name and bytes(args.name) not in event.comm: return if args.timestamp: delta = event.ts - initial_ts print("%-14.9f" % (float(delta) / 1000000), end="") print("%-6d %-16s %6s %16lx " % (event.id & 0xffffffff if args.tid else event.id >> 32, event.comm.decode(), sys_name(event.sys), event.ret), end = '') if event.sys == SYS_SHMGET: print("key: 0x%lx, size: %lu, shmflg: %s" % (event.key, event.size, shmflg_str(event.shmflg, shmget_flags))) if event.sys == SYS_SHMAT: print("shmid: 0x%lx, shmaddr: 0x%lx, shmflg: %s" % (event.shmid, event.shmaddr, shmflg_str(event.shmflg, shmat_flags))) if event.sys == SYS_SHMDT: print("shmaddr: 0x%lx" % (event.shmaddr)) if event.sys == SYS_SHMCTL: print("shmid: 0x%lx, cmd: %lu, buf: 0x%x" % (event.shmid, event.cmd, event.buf)) # loop with callback to print_event b["events"].open_perf_buffer(print_event, page_cnt=64) start_time = datetime.now() while not args.duration or datetime.now() - start_time < args.duration: try: b.perf_buffer_poll(timeout=1000) except KeyboardInterrupt: exit() bpfcc-0.12.0/tools/shmsnoop_example.txt000066400000000000000000000053561357404205000201420ustar00rootroot00000000000000Demonstrations of shmsnoop, the Linux eBPF/bcc version. shmsnoop traces shm*() syscalls, for example: # ./shmsnoop.py PID COMM SYS RET ARGs 19813 server SHMGET 10000 key: 0x78020001, size: 20, shmflg: 0x3b6 (IPC_CREAT|0666) 19813 server SHMAT 7f1cf8b1f000 shmid: 0x10000, shmaddr: 0x0, shmflg: 0x0 19816 client SHMGET 10000 key: 0x78020001, size: 20, shmflg: 0x1b6 (0666) 19816 client SHMAT 7f4fd8ee7000 shmid: 0x10000, shmaddr: 0x0, shmflg: 0x0 19816 client SHMDT 0 shmaddr: 0x7f4fd8ee7000 19813 server SHMDT 0 shmaddr: 0x7f1cf8b1f000 19813 server SHMCTL 0 shmid: 0x10000, cmd: 0, buf: 0x0 Every call the shm* syscall (SHM column) is displayed on separate line together with process info (PID/COMM columns) and argument details: return value (RET column) and syscall arguments (ARGs column). The ARGs column contains 'arg: value' couples that represent given syscall arguments as described in their manpage. This works by tracing shm* system calls and sending argument details to the python script. A -T option can be used to include a timestamp column, and a -n option to match on a command name. Regular expressions are allowed. For example, matching commands containing "server" with timestamps: # ./shmsnoop.py -T -n server TIME(s) PID COMM SYS RET ARGs 0.563194000 19825 server SHMDT 0 shmaddr: 0x7f74362e4000 0.563237000 19825 server SHMCTL 0 shmid: 0x18000, cmd: 0, buf: 0x0 A -p option can be used to trace only selected process: # ./shmsnoop.py -p 19855 PID COMM SYS RET ARGs 19855 server SHMDT 0 shmaddr: 0x7f4329ff8000 19855 server SHMCTL 0 shmid: 0x20000, cmd: 0, buf: 0x0 USAGE message: # ./shmsnoop.py -h usage: shmsnoop.py [-h] [-T] [-p PID] [-t TID] [-d DURATION] [-n NAME] Trace shm*() syscalls optional arguments: -h, --help show this help message and exit -T, --timestamp include timestamp on output -p PID, --pid PID trace this PID only -t TID, --tid TID trace this TID only -d DURATION, --duration DURATION total duration of trace in seconds -n NAME, --name NAME only print process names containing this name examples: ./shmsnoop # trace all shm*() syscalls ./shmsnoop -T # include timestamps ./shmsnoop -p 181 # only trace PID 181 ./shmsnoop -t 123 # only trace TID 123 ./shmsnoop -d 10 # trace for 10 seconds only ./shmsnoop -n main # only print process names containing "main" bpfcc-0.12.0/tools/slabratetop.py000077500000000000000000000071251357404205000167110ustar00rootroot00000000000000#!/usr/bin/python # @lint-avoid-python-3-compatibility-imports # # slabratetop Summarize kmem_cache_alloc() calls. # For Linux, uses BCC, eBPF. # # USAGE: slabratetop [-h] [-C] [-r MAXROWS] [interval] [count] # # This uses in-kernel BPF maps to store cache summaries for efficiency. # # SEE ALSO: slabtop(1), which shows the cache volumes. # # Copyright 2016 Netflix, Inc. # Licensed under the Apache License, Version 2.0 (the "License") # # 15-Oct-2016 Brendan Gregg Created this. from __future__ import print_function from bcc import BPF from bcc.utils import printb from time import sleep, strftime import argparse import signal from subprocess import call # arguments examples = """examples: ./slabratetop # kmem_cache_alloc() top, 1 second refresh ./slabratetop -C # don't clear the screen ./slabratetop 5 # 5 second summaries ./slabratetop 5 10 # 5 second summaries, 10 times only """ parser = argparse.ArgumentParser( description="Kernel SLAB/SLUB memory cache allocation rate top", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("-C", "--noclear", action="store_true", help="don't clear the screen") parser.add_argument("-r", "--maxrows", default=20, help="maximum rows to print, default 20") parser.add_argument("interval", nargs="?", default=1, help="output interval, in seconds") parser.add_argument("count", nargs="?", default=99999999, help="number of outputs") parser.add_argument("--ebpf", action="store_true", help=argparse.SUPPRESS) args = parser.parse_args() interval = int(args.interval) countdown = int(args.count) maxrows = int(args.maxrows) clear = not int(args.noclear) debug = 0 # linux stats loadavg = "/proc/loadavg" # signal handler def signal_ignore(signal, frame): print() # define BPF program bpf_text = """ #include #include #include #ifdef CONFIG_SLUB #include #else #include #endif #define CACHE_NAME_SIZE 32 // the key for the output summary struct info_t { char name[CACHE_NAME_SIZE]; }; // the value of the output summary struct val_t { u64 count; u64 size; }; BPF_HASH(counts, struct info_t, struct val_t); int kprobe__kmem_cache_alloc(struct pt_regs *ctx, struct kmem_cache *cachep) { struct info_t info = {}; const char *name = cachep->name; bpf_probe_read(&info.name, sizeof(info.name), name); struct val_t *valp, zero = {}; valp = counts.lookup_or_try_init(&info, &zero); if (valp) { valp->count++; valp->size += cachep->size; } return 0; } """ if debug or args.ebpf: print(bpf_text) if args.ebpf: exit() # initialize BPF b = BPF(text=bpf_text) print('Tracing... Output every %d secs. Hit Ctrl-C to end' % interval) # output exiting = 0 while 1: try: sleep(interval) except KeyboardInterrupt: exiting = 1 # header if clear: call("clear") else: print() with open(loadavg) as stats: print("%-8s loadavg: %s" % (strftime("%H:%M:%S"), stats.read())) print("%-32s %6s %10s" % ("CACHE", "ALLOCS", "BYTES")) # by-TID output counts = b.get_table("counts") line = 0 for k, v in reversed(sorted(counts.items(), key=lambda counts: counts[1].size)): printb(b"%-32s %6d %10d" % (k.name, v.count, v.size)) line += 1 if line >= maxrows: break counts.clear() countdown -= 1 if exiting or countdown == 0: print("Detaching...") exit() bpfcc-0.12.0/tools/slabratetop_example.txt000066400000000000000000000123431357404205000206060ustar00rootroot00000000000000Demonstrations of slabratetop, the Linux eBPF/bcc version. slabratetop shows the rate of allocations and total bytes from the kernel memory allocation caches (SLAB or SLUB), in a top-like display that refreshes. For example: # ./slabratetop 07:01:35 loadavg: 0.38 0.21 0.12 1/342 13297 CACHE ALLOCS BYTES kmalloc-4096 3554 14557184 kmalloc-256 2382 609536 cred_jar 2568 493056 anon_vma_chain 2007 128448 anon_vma 972 77760 sighand_cache 24 50688 mm_struct 49 50176 RAW 52 49920 proc_inode_cache 59 38232 signal_cache 24 26112 dentry 135 25920 sock_inode_cache 29 18560 files_cache 24 16896 inode_cache 13 7696 TCP 2 3840 pid 24 3072 sigqueue 17 2720 ext4_inode_cache 2 2160 buffer_head 16 1664 xfs_trans 5 1160 By default the screen refreshes every one second, and only the top 20 caches are shown. These can be tuned with options: see USAGE (-h). The output above showed that the kmalloc-4096 cache allocated the most, about 14 Mbytes during this interval. This is a generic cache; other caches have more meaningful names ("dentry", "TCP", "pid", etc). slabtop(1) is a similar tool that shows the current static volume and usage of the caches. slabratetop shows the active call rates and total size of the allocations. Since "kmalloc-4096" isn't very descriptive, I'm interested in seeing the kernel stacks that led to this allocation. In the future (maybe by now) the bcc trace tool could do this. As I'm writing this, it can't, so I'll use my older ftrace-based kprobe tool as a workarond. This is from my perf-tools collection: https://github.com/brendangregg/perf-tools. # ./perf-tools/bin/kprobe -s 'p:kmem_cache_alloc name=+0(+96(%di)):string' 'name == "kmalloc-4096' | head -100 Tracing kprobe kmem_cache_alloc. Ctrl-C to end. kprobe-3892 [002] d... 7888274.478331: kmem_cache_alloc: (kmem_cache_alloc+0x0/0x1b0) name="kmalloc-4096" kprobe-3892 [002] d... 7888274.478333: => kmem_cache_alloc => user_path_at_empty => vfs_fstatat => SYSC_newstat => SyS_newstat => entry_SYSCALL_64_fastpath kprobe-3892 [002] d... 7888274.478340: kmem_cache_alloc: (kmem_cache_alloc+0x0/0x1b0) name="kmalloc-4096" kprobe-3892 [002] d... 7888274.478341: => kmem_cache_alloc => user_path_at_empty => vfs_fstatat => SYSC_newstat => SyS_newstat => entry_SYSCALL_64_fastpath kprobe-3892 [002] d... 7888274.478345: kmem_cache_alloc: (kmem_cache_alloc+0x0/0x1b0) name="kmalloc-4096" kprobe-3892 [002] d... 7888274.478346: => kmem_cache_alloc => user_path_at_empty => vfs_fstatat => SYSC_newstat => SyS_newstat => entry_SYSCALL_64_fastpath kprobe-3892 [002] d... 7888274.478350: kmem_cache_alloc: (kmem_cache_alloc+0x0/0x1b0) name="kmalloc-4096" kprobe-3892 [002] d... 7888274.478351: => kmem_cache_alloc => user_path_at_empty => vfs_fstatat => SYSC_newstat => SyS_newstat => entry_SYSCALL_64_fastpath kprobe-3892 [002] d... 7888274.478355: kmem_cache_alloc: (kmem_cache_alloc+0x0/0x1b0) name="kmalloc-4096" kprobe-3892 [002] d... 7888274.478355: => kmem_cache_alloc => user_path_at_empty => vfs_fstatat => SYSC_newstat => SyS_newstat => entry_SYSCALL_64_fastpath kprobe-3892 [002] d... 7888274.478359: kmem_cache_alloc: (kmem_cache_alloc+0x0/0x1b0) name="kmalloc-4096" kprobe-3892 [002] d... 7888274.478359: => kmem_cache_alloc => user_path_at_empty => vfs_fstatat => SYSC_newstat => SyS_newstat => entry_SYSCALL_64_fastpath [...] This is just an example so that you can see it's possible to dig further. Please don't copy-n-paste that kprobe command, as it's unlikely to work (the "+0(+96(%di))" text is specific to a kernel version and architecture). So these allocations are coming from user_path_at_empty(), which calls other functions (not seen in the stack: I suspect it's a tail-call compiler optimization). USAGE: # ./slabratetop -h usage: slabratetop [-h] [-C] [-r MAXROWS] [interval] [count] Kernel SLAB/SLUB memory cache allocation rate top positional arguments: interval output interval, in seconds count number of outputs optional arguments: -h, --help show this help message and exit -C, --noclear don't clear the screen -r MAXROWS, --maxrows MAXROWS maximum rows to print, default 20 examples: ./slabratetop # kmem_cache_alloc() top, 1 second refresh ./slabratetop -C # don't clear the screen ./slabratetop 5 # 5 second summaries ./slabratetop 5 10 # 5 second summaries, 10 times only bpfcc-0.12.0/tools/sofdsnoop.py000077500000000000000000000200661357404205000164020ustar00rootroot00000000000000#!/usr/bin/python # @lint-avoid-python-3-compatibility-imports # # sofdsnoop traces file descriptors passed via socket # For Linux, uses BCC, eBPF. Embedded C. # # USAGE: sofdsnoop # # Copyright (c) 2018 Jiri Olsa. # Licensed under the Apache License, Version 2.0 (the "License") # # 30-Jul-2018 Jiri Olsa Created this. from __future__ import print_function from bcc import ArgString, BPF import os import argparse from datetime import datetime, timedelta # arguments examples = """examples: ./sofdsnoop # trace passed file descriptors ./sofdsnoop -T # include timestamps ./sofdsnoop -p 181 # only trace PID 181 ./sofdsnoop -t 123 # only trace TID 123 ./sofdsnoop -d 10 # trace for 10 seconds only ./sofdsnoop -n main # only print process names containing "main" """ parser = argparse.ArgumentParser( description="Trace file descriptors passed via socket", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("-T", "--timestamp", action="store_true", help="include timestamp on output") parser.add_argument("-p", "--pid", help="trace this PID only") parser.add_argument("-t", "--tid", help="trace this TID only") parser.add_argument("-n", "--name", type=ArgString, help="only print process names containing this name") parser.add_argument("-d", "--duration", help="total duration of trace in seconds") args = parser.parse_args() debug = 0 ACTION_SEND=0 ACTION_RECV=1 MAX_FD=10 if args.duration: args.duration = timedelta(seconds=int(args.duration)) # define BPF program bpf_text = """ #include #include #include #include #include #define MAX_FD 10 #define ACTION_SEND 0 #define ACTION_RECV 1 struct val_t { u64 id; u64 ts; int action; int sock_fd; int fd_cnt; int fd[MAX_FD]; char comm[TASK_COMM_LEN]; }; BPF_HASH(detach_ptr, u64, struct cmsghdr *); BPF_HASH(sock_fd, u64, int); BPF_PERF_OUTPUT(events); static void set_fd(int fd) { u64 id = bpf_get_current_pid_tgid(); sock_fd.update(&id, &fd); } static int get_fd(void) { u64 id = bpf_get_current_pid_tgid(); int *fd; fd = sock_fd.lookup(&id); return fd ? *fd : -1; } static void put_fd(void) { u64 id = bpf_get_current_pid_tgid(); sock_fd.delete(&id); } static int sent_1(struct pt_regs *ctx, struct val_t *val, int num, void *data) { val->fd_cnt = min(num, MAX_FD); if (bpf_probe_read(&val->fd[0], MAX_FD * sizeof(int), data)) return -1; events.perf_submit(ctx, val, sizeof(*val)); return 0; } #define SEND_1 \ if (sent_1(ctx, &val, num, (void *) data)) \ return 0; \ \ num -= MAX_FD; \ if (num < 0) \ return 0; \ \ data += MAX_FD; #define SEND_2 SEND_1 SEND_1 #define SEND_4 SEND_2 SEND_2 #define SEND_8 SEND_4 SEND_4 #define SEND_260 SEND_8 SEND_8 SEND_8 SEND_2 static int send(struct pt_regs *ctx, struct cmsghdr *cmsg, int action) { struct val_t val = { 0 }; int *data, num, fd; u64 tsp = bpf_ktime_get_ns(); data = (void *) ((char *) cmsg + sizeof(struct cmsghdr)); num = (cmsg->cmsg_len - sizeof(struct cmsghdr)) / sizeof(int); val.id = bpf_get_current_pid_tgid(); val.action = action; val.sock_fd = get_fd(); val.ts = tsp / 1000; if (bpf_get_current_comm(&val.comm, sizeof(val.comm)) != 0) return 0; SEND_260 return 0; } static bool allow_pid(u64 id) { u32 pid = id >> 32; // PID is higher part u32 tid = id; // Cast and get the lower part FILTER return 1; } int trace_scm_send_entry(struct pt_regs *ctx, struct socket *sock, struct msghdr *hdr) { struct cmsghdr *cmsg = NULL; if (!allow_pid(bpf_get_current_pid_tgid())) return 0; if (hdr->msg_controllen >= sizeof(struct cmsghdr)) cmsg = hdr->msg_control; if (!cmsg || (cmsg->cmsg_type != SCM_RIGHTS)) return 0; return send(ctx, cmsg, ACTION_SEND); }; int trace_scm_detach_fds_entry(struct pt_regs *ctx, struct msghdr *hdr) { struct cmsghdr *cmsg = NULL; u64 id = bpf_get_current_pid_tgid(); if (!allow_pid(id)) return 0; if (hdr->msg_controllen >= sizeof(struct cmsghdr)) cmsg = hdr->msg_control; if (!cmsg) return 0; detach_ptr.update(&id, &cmsg); return 0; }; int trace_scm_detach_fds_return(struct pt_regs *ctx) { struct cmsghdr **cmsgp; u64 id = bpf_get_current_pid_tgid(); if (!allow_pid(id)) return 0; cmsgp = detach_ptr.lookup(&id); if (!cmsgp) return 0; return send(ctx, *cmsgp, ACTION_RECV); } int syscall__sendmsg(struct pt_regs *ctx, u64 fd, u64 msg, u64 flags) { struct pt_regs p; if (!allow_pid(bpf_get_current_pid_tgid())) return 0; set_fd(fd); return 0; } int trace_sendmsg_return(struct pt_regs *ctx) { if (!allow_pid(bpf_get_current_pid_tgid())) return 0; put_fd(); return 0; } int syscall__recvmsg(struct pt_regs *ctx, u64 fd, u64 msg, u64 flags) { struct pt_regs p; if (!allow_pid(bpf_get_current_pid_tgid())) return 0; fd = fd; set_fd(fd); return 0; } int trace_recvmsg_return(struct pt_regs *ctx) { if (!allow_pid(bpf_get_current_pid_tgid())) return 0; put_fd(); return 0; } """ if args.tid: # TID trumps PID bpf_text = bpf_text.replace('FILTER', 'if (tid != %s) { return 0; }' % args.tid) elif args.pid: bpf_text = bpf_text.replace('FILTER', 'if (pid != %s) { return 0; }' % args.pid) else: bpf_text = bpf_text.replace('FILTER', '') # initialize BPF b = BPF(text=bpf_text) syscall_fnname = b.get_syscall_fnname("sendmsg") if BPF.ksymname(syscall_fnname) != -1: b.attach_kprobe(event=syscall_fnname, fn_name="syscall__sendmsg") b.attach_kretprobe(event=syscall_fnname, fn_name="trace_sendmsg_return") syscall_fnname = b.get_syscall_fnname("recvmsg") if BPF.ksymname(syscall_fnname) != -1: b.attach_kprobe(event=syscall_fnname, fn_name="syscall__recvmsg") b.attach_kretprobe(event=syscall_fnname, fn_name="trace_recvmsg_return") b.attach_kprobe(event="__scm_send", fn_name="trace_scm_send_entry") b.attach_kprobe(event="scm_detach_fds", fn_name="trace_scm_detach_fds_entry") b.attach_kretprobe(event="scm_detach_fds", fn_name="trace_scm_detach_fds_return") initial_ts = 0 # header if args.timestamp: print("%-14s" % ("TIME(s)"), end="") print("%-6s %-6s %-16s %-25s %-5s %s" % ("ACTION", "TID", "COMM", "SOCKET", "FD", "NAME")) def get_file(pid, fd): proc = "/proc/%d/fd/%d" % (pid, fd) try: return os.readlink(proc) except OSError as err: return "N/A" # process event def print_event(cpu, data, size): event = b["events"].event(data) tid = event.id & 0xffffffff; cnt = min(MAX_FD, event.fd_cnt); if args.name and bytes(args.name) not in event.comm: return for i in range(0, cnt): global initial_ts if not initial_ts: initial_ts = event.ts if args.timestamp: delta = event.ts - initial_ts print("%-14.9f" % (float(delta) / 1000000), end="") print("%-6s %-6d %-16s " % ("SEND" if event.action == ACTION_SEND else "RECV", tid, event.comm.decode()), end = '') sock = "%d:%s" % (event.sock_fd, get_file(tid, event.sock_fd)) print("%-25s " % sock, end = '') fd = event.fd[i] fd_file = get_file(tid, fd) if event.action == ACTION_SEND else "" print("%-5d %s" % (fd, fd_file)) # loop with callback to print_event b["events"].open_perf_buffer(print_event, page_cnt=64) start_time = datetime.now() while not args.duration or datetime.now() - start_time < args.duration: try: b.perf_buffer_poll(timeout=1000) except KeyboardInterrupt: exit() bpfcc-0.12.0/tools/sofdsnoop_example.txt000066400000000000000000000062131357404205000202770ustar00rootroot00000000000000Demonstrations of sofdsnoop, the Linux eBPF/bcc version. sofdsnoop traces FDs passed through unix sockets # ./sofdsnoop.py ACTION TID COMM SOCKET FD NAME SEND 2576 Web Content 24:socket:[39763] 51 /dev/shm/org.mozilla.ipc.2576.23874 RECV 2576 Web Content 49:socket:[809997] 51 SEND 2576 Web Content 24:socket:[39763] 58 N/A RECV 2464 Gecko_IOThread 75:socket:[39753] 55 Every file descriptor that is passed via unix sockets os displayed on separate line together with process info (TID/COMM columns), ACTION details (SEND/RECV), file descriptor number (FD) and its translation to file if available (NAME). The file descriptor (fd) value is bound to a process. The SEND lines display the fd value within the sending process. The RECV lines display the fd value of the sending process. That's why there's translation to name only on SEND lines, where we are able to find it in task proc records. This works by tracing sendmsg/recvmsg system calls to provide the socket fds, and scm_send_entry/scm_detach_fds to provide the file descriptor details. A -T option can be used to include a timestamp column, and a -n option to match on a command name. Regular expressions are allowed. For example, matching commands containing "server" with timestamps: # ./sofdsnoop.py -T -n Web TIME(s) ACTION TID COMM SOCKET FD NAME 0.000000000 SEND 2576 Web Content 24:socket:[39763] 51 /dev/shm/org.mozilla.ipc.2576.25404 (deleted) 0.000413000 RECV 2576 Web Content 49:/dev/shm/org.mozilla.ipc.2576.25404 (deleted) 51 0.000558000 SEND 2576 Web Content 24:socket:[39763] 58 N/A 0.000952000 SEND 2576 Web Content 24:socket:[39763] 58 socket:[817962] A -p option can be used to trace only selected process: # ./sofdsnoop.py -p 2576 -T TIME(s) ACTION TID COMM SOCKET FD NAME 0.000000000 SEND 2576 Web Content 24:socket:[39763] 51 N/A 0.000138000 RECV 2576 Web Content 49:N/A 5 0.000191000 SEND 2576 Web Content 24:socket:[39763] 58 N/A 0.000424000 RECV 2576 Web Content 51:/dev/shm/org.mozilla.ipc.2576.25319 (deleted) 49 USAGE message: usage: sofdsnoop.py [-h] [-T] [-p PID] [-t TID] [-n NAME] [-d DURATION] Trace file descriptors passed via socket optional arguments: -h, --help show this help message and exit -T, --timestamp include timestamp on output -p PID, --pid PID trace this PID only -t TID, --tid TID trace this TID only -n NAME, --name NAME only print process names containing this name -d DURATION, --duration DURATION total duration of trace in seconds examples: ./sofdsnoop # trace passed file descriptors ./sofdsnoop -T # include timestamps ./sofdsnoop -p 181 # only trace PID 181 ./sofdsnoop -t 123 # only trace TID 123 ./sofdsnoop -d 10 # trace for 10 seconds only ./sofdsnoop -n main # only print process names containing "main" bpfcc-0.12.0/tools/softirqs.py000077500000000000000000000100241357404205000162330ustar00rootroot00000000000000#!/usr/bin/python # @lint-avoid-python-3-compatibility-imports # # softirqs Summarize soft IRQ (interrupt) event time. # For Linux, uses BCC, eBPF. # # USAGE: softirqs [-h] [-T] [-N] [-d] [interval] [count] # # Copyright (c) 2015 Brendan Gregg. # Licensed under the Apache License, Version 2.0 (the "License") # # 20-Oct-2015 Brendan Gregg Created this. # 03-Apr-2017 Sasha Goldshtein Migrated to kernel tracepoints. from __future__ import print_function from bcc import BPF from time import sleep, strftime import argparse # arguments examples = """examples: ./softirqs # sum soft irq event time ./softirqs -d # show soft irq event time as histograms ./softirqs 1 10 # print 1 second summaries, 10 times ./softirqs -NT 1 # 1s summaries, nanoseconds, and timestamps """ parser = argparse.ArgumentParser( description="Summarize soft irq event time as histograms.", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("-T", "--timestamp", action="store_true", help="include timestamp on output") parser.add_argument("-N", "--nanoseconds", action="store_true", help="output in nanoseconds") parser.add_argument("-d", "--dist", action="store_true", help="show distributions as histograms") parser.add_argument("interval", nargs="?", default=99999999, help="output interval, in seconds") parser.add_argument("count", nargs="?", default=99999999, help="number of outputs") parser.add_argument("--ebpf", action="store_true", help=argparse.SUPPRESS) args = parser.parse_args() countdown = int(args.count) if args.nanoseconds: factor = 1 label = "nsecs" else: factor = 1000 label = "usecs" debug = 0 # define BPF program bpf_text = """ #include typedef struct irq_key { u32 vec; u64 slot; } irq_key_t; typedef struct account_val { u64 ts; u32 vec; } account_val_t; BPF_HASH(start, u32, account_val_t); BPF_HASH(iptr, u32); BPF_HISTOGRAM(dist, irq_key_t); TRACEPOINT_PROBE(irq, softirq_entry) { u32 pid = bpf_get_current_pid_tgid(); account_val_t val = {}; val.ts = bpf_ktime_get_ns(); val.vec = args->vec; start.update(&pid, &val); return 0; } TRACEPOINT_PROBE(irq, softirq_exit) { u64 delta; u32 vec; u32 pid = bpf_get_current_pid_tgid(); account_val_t *valp; irq_key_t key = {0}; // fetch timestamp and calculate delta valp = start.lookup(&pid); if (valp == 0) { return 0; // missed start } delta = bpf_ktime_get_ns() - valp->ts; vec = valp->vec; // store as sum or histogram STORE start.delete(&pid); return 0; } """ # code substitutions if args.dist: bpf_text = bpf_text.replace('STORE', 'key.vec = vec; key.slot = bpf_log2l(delta / %d); ' % factor + 'dist.increment(key);') else: bpf_text = bpf_text.replace('STORE', 'key.vec = valp->vec; ' + 'dist.increment(key, delta);') if debug or args.ebpf: print(bpf_text) if args.ebpf: exit() # load BPF program b = BPF(text=bpf_text) def vec_to_name(vec): # copied from softirq_to_name() in kernel/softirq.c # may need updates if new softirq handlers are added return ["hi", "timer", "net_tx", "net_rx", "block", "irq_poll", "tasklet", "sched", "hrtimer", "rcu"][vec] print("Tracing soft irq event time... Hit Ctrl-C to end.") # output exiting = 0 if args.interval else 1 dist = b.get_table("dist") while (1): try: sleep(int(args.interval)) except KeyboardInterrupt: exiting = 1 print() if args.timestamp: print("%-8s\n" % strftime("%H:%M:%S"), end="") if args.dist: dist.print_log2_hist(label, "softirq", section_print_fn=vec_to_name) else: print("%-16s %11s" % ("SOFTIRQ", "TOTAL_" + label)) for k, v in sorted(dist.items(), key=lambda dist: dist[1].value): print("%-16s %11d" % (vec_to_name(k.vec), v.value / factor)) dist.clear() countdown -= 1 if exiting or countdown == 0: exit() bpfcc-0.12.0/tools/softirqs_example.txt000066400000000000000000000246271357404205000201500ustar00rootroot00000000000000Demonstrations of softirqs, the Linux eBPF/bcc version. This program traces soft interrupts (irqs), and stores timing statistics in-kernel for efficiency. For example: # ./softirqs Tracing soft irq event time... Hit Ctrl-C to end. ^C SOFTIRQ TOTAL_usecs rcu_process_callbacks 974 run_rebalance_domains 1809 run_timer_softirq 2615 net_tx_action 14605 tasklet_action 38692 net_rx_action 88188 The SOFTIRQ column prints the interrupt action function name. While tracing, the net_rx_action() soft interrupt ran for 20199 microseconds (20 milliseconds) in total. This tool works by dynamic tracing the individual softirq functions, and will need to be adjusted to match kernel/module changes. Future versions should use the softirq tracepoints instead. An interval can be provided, and also optionally a count. Eg, printing output every 1 second, and including timestamps (-T): # ./softirqs -T 1 3 Tracing soft irq event time... Hit Ctrl-C to end. 22:29:16 SOFTIRQ TOTAL_usecs rcu_process_callbacks 456 run_rebalance_domains 1005 run_timer_softirq 1196 net_tx_action 2796 tasklet_action 5534 net_rx_action 15075 22:29:17 SOFTIRQ TOTAL_usecs rcu_process_callbacks 456 run_rebalance_domains 839 run_timer_softirq 1142 net_tx_action 1912 tasklet_action 4428 net_rx_action 14652 22:29:18 SOFTIRQ TOTAL_usecs rcu_process_callbacks 502 run_rebalance_domains 840 run_timer_softirq 1192 net_tx_action 2341 tasklet_action 5496 net_rx_action 15656 This can be useful for quantifying where CPU cycles are spent among the soft interrupts (summarized as the %softirq column from mpstat(1), and shown as event counts in /proc/softirqs). The output above shows that most time was spent processing net_rx_action(), which was around 15 milliseconds per second (total time across all CPUs). The distribution of interrupt run time can be printed as a histogram with the -d option. Eg: # ./softirqs -d Tracing soft irq event time... Hit Ctrl-C to end. ^C softirq = net_tx_action usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 0 | | 128 -> 255 : 0 | | 256 -> 511 : 440 | | 512 -> 1023 : 27613 |****************************************| 1024 -> 2047 : 5728 |******** | 2048 -> 4095 : 439 | | 4096 -> 8191 : 53 | | 8192 -> 16383 : 2 | | softirq = net_rx_action usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 0 | | 128 -> 255 : 0 | | 256 -> 511 : 0 | | 512 -> 1023 : 6 | | 1024 -> 2047 : 35 | | 2048 -> 4095 : 3562 |**************** | 4096 -> 8191 : 7023 |******************************** | 8192 -> 16383 : 8770 |****************************************| 16384 -> 32767 : 1780 |******** | 32768 -> 65535 : 216 | | 65536 -> 131071 : 4 | | softirq = tasklet_action usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 0 | | 128 -> 255 : 0 | | 256 -> 511 : 95 | | 512 -> 1023 : 12521 |****************************************| 1024 -> 2047 : 1068 |*** | 2048 -> 4095 : 1077 |*** | 4096 -> 8191 : 12349 |*************************************** | 8192 -> 16383 : 464 |* | 16384 -> 32767 : 1 | | softirq = rcu_process_callbacks usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 0 | | 128 -> 255 : 0 | | 256 -> 511 : 0 | | 512 -> 1023 : 708 |****************************************| 1024 -> 2047 : 495 |*************************** | 2048 -> 4095 : 98 |***** | 4096 -> 8191 : 62 |*** | 8192 -> 16383 : 4 | | softirq = run_timer_softirq usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 0 | | 128 -> 255 : 0 | | 256 -> 511 : 2 | | 512 -> 1023 : 366 |********* | 1024 -> 2047 : 1525 |****************************************| 2048 -> 4095 : 629 |**************** | 4096 -> 8191 : 87 |** | 8192 -> 16383 : 1 | | softirq = run_rebalance_domains usecs : count distribution 0 -> 1 : 0 | | 2 -> 3 : 0 | | 4 -> 7 : 0 | | 8 -> 15 : 0 | | 16 -> 31 : 0 | | 32 -> 63 : 0 | | 64 -> 127 : 0 | | 128 -> 255 : 0 | | 256 -> 511 : 3 | | 512 -> 1023 : 18 |* | 1024 -> 2047 : 80 |******** | 2048 -> 4095 : 374 |****************************************| 4096 -> 8191 : 257 |*************************** | 8192 -> 16383 : 50 |***** | 16384 -> 32767 : 24 |** | USAGE message: # ./softirqs -h usage: softirqs [-h] [-T] [-N] [-d] [interval] [count] Summarize soft irq event time as histograms positional arguments: interval output interval, in seconds count number of outputs optional arguments: -h, --help show this help message and exit -T, --timestamp include timestamp on output -N, --nanoseconds output in nanoseconds -d, --dist show distributions as histograms examples: ./softirqs # sum soft irq event time ./softirqs -d # show soft irq event time as histograms ./softirqs 1 10 # print 1 second summaries, 10 times ./softirqs -NT 1 # 1s summaries, nanoseconds, and timestamps bpfcc-0.12.0/tools/solisten.py000077500000000000000000000137511357404205000162330ustar00rootroot00000000000000#!/usr/bin/python # # solisten Trace TCP listen events # For Linux, uses BCC, eBPF. Embedded C. # # USAGE: solisten.py [-h] [-p PID] [--show-netns] # # This is provided as a basic example of TCP connection & socket tracing. # It could be useful in scenarios where load balancers needs to be updated # dynamically as application is fully initialized. # # All IPv4 and IPv6 listen attempts are traced, even if they ultimately fail # or the the listening program is not willing to accept(). # # Copyright (c) 2016 Jean-Tiare Le Bigot. # Licensed under the Apache License, Version 2.0 (the "License") # # 04-Mar-2016 Jean-Tiare Le Bigot Created this. import os from socket import inet_ntop, AF_INET, AF_INET6, SOCK_STREAM, SOCK_DGRAM from struct import pack import argparse from bcc import BPF from bcc.utils import printb # Arguments examples = """Examples: ./solisten.py # Stream socket listen ./solisten.py -p 1234 # Stream socket listen for specified PID only ./solisten.py --netns 4242 # " for the specified network namespace ID only ./solisten.py --show-netns # Show network ns ID (useful for containers) """ parser = argparse.ArgumentParser( description="Stream sockets listen", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("--show-netns", action="store_true", help="show network namespace") parser.add_argument("-p", "--pid", default=0, type=int, help="trace this PID only") parser.add_argument("-n", "--netns", default=0, type=int, help="trace this Network Namespace only") parser.add_argument("--ebpf", action="store_true", help=argparse.SUPPRESS) # BPF Program bpf_text = """ #include #include #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wenum-conversion" #include #pragma clang diagnostic pop // Common structure for UDP/TCP IPv4/IPv6 struct listen_evt_t { u64 ts_us; u64 pid_tgid; u64 backlog; u64 netns; u64 proto; // familiy << 16 | type u64 lport; // use only 16 bits u64 laddr[2]; // IPv4: store in laddr[0] char task[TASK_COMM_LEN]; }; BPF_PERF_OUTPUT(listen_evt); // Send an event for each IPv4 listen with PID, bound address and port int kprobe__inet_listen(struct pt_regs *ctx, struct socket *sock, int backlog) { // cast types. Intermediate cast not needed, kept for readability struct sock *sk = sock->sk; struct inet_sock *inet = (struct inet_sock *)sk; // Built event for userland struct listen_evt_t evt = { .ts_us = bpf_ktime_get_ns() / 1000, .backlog = backlog, }; // Get process comm. Needs LLVM >= 3.7.1 // see https://github.com/iovisor/bcc/issues/393 bpf_get_current_comm(evt.task, TASK_COMM_LEN); // Get socket IP family u16 family = sk->__sk_common.skc_family; evt.proto = family << 16 | SOCK_STREAM; // Get PID evt.pid_tgid = bpf_get_current_pid_tgid(); ##FILTER_PID## // Get port evt.lport = inet->inet_sport; evt.lport = ntohs(evt.lport); // Get network namespace id, if kernel supports it #ifdef CONFIG_NET_NS evt.netns = sk->__sk_common.skc_net.net->ns.inum; #else evt.netns = 0; #endif ##FILTER_NETNS## // Get IP if (family == AF_INET) { evt.laddr[0] = inet->inet_rcv_saddr; } else if (family == AF_INET6) { bpf_probe_read(evt.laddr, sizeof(evt.laddr), sk->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32); } // Send event to userland listen_evt.perf_submit(ctx, &evt, sizeof(evt)); return 0; }; """ # TODO: properties to unpack protocol / ip / pid / tgid ... # Format output def event_printer(show_netns): def print_event(cpu, data, size): # Decode event event = b["listen_evt"].event(data) pid = event.pid_tgid & 0xffffffff proto_family = event.proto & 0xff proto_type = event.proto >> 16 & 0xff if proto_family == SOCK_STREAM: protocol = "TCP" elif proto_family == SOCK_DGRAM: protocol = "UDP" else: protocol = "UNK" address = "" if proto_type == AF_INET: protocol += "v4" address = inet_ntop(AF_INET, pack("I", event.laddr[0])) elif proto_type == AF_INET6: address = inet_ntop(AF_INET6, event.laddr) protocol += "v6" # Display if show_netns: printb(b"%-6d %-12.12s %-12d %-6s %-8d %-5d %-39s" % ( pid, event.task, event.netns, protocol.encode(), event.backlog, event.lport, address.encode(), )) else: printb(b"%-6d %-12.12s %-6s %-8d %-5d %-39s" % ( pid, event.task, protocol.encode(), event.backlog, event.lport, address.encode(), )) return print_event if __name__ == "__main__": # Parse arguments args = parser.parse_args() pid_filter = "" netns_filter = "" if args.pid: pid_filter = "if (evt.pid_tgid != %d) return 0;" % args.pid if args.netns: netns_filter = "if (evt.netns != %d) return 0;" % args.netns bpf_text = bpf_text.replace("##FILTER_PID##", pid_filter) bpf_text = bpf_text.replace("##FILTER_NETNS##", netns_filter) if args.ebpf: print(bpf_text) exit() # Initialize BPF b = BPF(text=bpf_text) b["listen_evt"].open_perf_buffer(event_printer(args.show_netns)) # Print headers if args.show_netns: print("%-6s %-12s %-12s %-6s %-8s %-5s %-39s" % ("PID", "COMM", "NETNS", "PROTO", "BACKLOG", "PORT", "ADDR")) else: print("%-6s %-12s %-6s %-8s %-5s %-39s" % ("PID", "COMM", "PROTO", "BACKLOG", "PORT", "ADDR")) # Read events while 1: try: b.perf_buffer_poll() except KeyboardInterrupt: exit() bpfcc-0.12.0/tools/solisten_example.txt000066400000000000000000000044621357404205000201310ustar00rootroot00000000000000Demonstrations of solisten.py, the Linux eBPF/bcc version. This tool traces the kernel function called when a program wants to listen for TCP connections. It will not see UDP neither UNIX domain sockets. It can be used to dynamically update a load balancer as a program is actually ready to accept connexion, hence avoiding the "downtime" while it is initializing. # ./solisten.py --show-netns PID COMM NETNS PROTO BACKLOG ADDR PORT 3643 nc 4026531957 TCPv4 1 0.0.0.0 4242 3659 nc 4026531957 TCPv6 1 2001:f0d0:1002:51::4 4242 4221 redis-server 4026532165 TCPv6 128 :: 6379 4221 redis-server 4026532165 TCPv4 128 0.0.0.0 6379 6067 nginx 4026531957 TCPv4 128 0.0.0.0 80 6067 nginx 4026531957 TCPv6 128 :: 80 6069 nginx 4026531957 TCPv4 128 0.0.0.0 80 6069 nginx 4026531957 TCPv6 128 :: 80 6069 nginx 4026531957 TCPv4 128 0.0.0.0 80 6069 nginx 4026531957 TCPv6 128 :: 80 This output show the listen event from 3 programs. Netcat was started twice as shown by the 2 different PIDs. The first time on the wilcard IPv4, the second time on an IPv6. Netcat being a "one shot" program. It can accept a single connection, hence the backlog of "1". The next program is redis-server. As the netns column shows, it is in a different network namespace than netcat and nginx. In this specific case it was launched in a docker container. It listens both on IPv4 and IPv4 with up to 128 pending connections. Determining the actual container is out if the scope of this tool. It could be derived by scrapping /proc//cgroup. Note that this is racy. The overhead of this tool is negligeable as it traces listen() calls which are invoked in the initialization path of a program. The operation part will remain unaffected. In particular, accept() calls will not be affected. Neither individual read() and write(). bpfcc-0.12.0/tools/sslsniff.py000077500000000000000000000157201357404205000162200ustar00rootroot00000000000000#!/usr/bin/python # # sslsniff Captures data on read/recv or write/send functions of OpenSSL, # GnuTLS and NSS # For Linux, uses BCC, eBPF. # # USAGE: sslsniff.py [-h] [-p PID] [-c COMM] [-o] [-g] [-d] # # Licensed under the Apache License, Version 2.0 (the "License") # # 12-Aug-2016 Adrian Lopez Created this. # 13-Aug-2016 Mark Drayton Fix SSL_Read # 17-Aug-2016 Adrian Lopez Capture GnuTLS and add options # from __future__ import print_function from bcc import BPF import argparse # arguments examples = """examples: ./sslsniff # sniff OpenSSL and GnuTLS functions ./sslsniff -p 181 # sniff PID 181 only ./sslsniff -c curl # sniff curl command only ./sslsniff --no-openssl # don't show OpenSSL calls ./sslsniff --no-gnutls # don't show GnuTLS calls ./sslsniff --no-nss # don't show NSS calls """ parser = argparse.ArgumentParser( description="Sniff SSL data", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("-p", "--pid", type=int, help="sniff this PID only.") parser.add_argument("-c", "--comm", help="sniff only commands matching string.") parser.add_argument("-o", "--no-openssl", action="store_false", dest="openssl", help="do not show OpenSSL calls.") parser.add_argument("-g", "--no-gnutls", action="store_false", dest="gnutls", help="do not show GnuTLS calls.") parser.add_argument("-n", "--no-nss", action="store_false", dest="nss", help="do not show NSS calls.") parser.add_argument('-d', '--debug', dest='debug', action='count', default=0, help='debug mode.') parser.add_argument("--ebpf", action="store_true", help=argparse.SUPPRESS) args = parser.parse_args() prog = """ #include #include /* For TASK_COMM_LEN */ struct probe_SSL_data_t { u64 timestamp_ns; u32 pid; char comm[TASK_COMM_LEN]; char v0[464]; u32 len; }; BPF_PERF_OUTPUT(perf_SSL_write); int probe_SSL_write(struct pt_regs *ctx, void *ssl, void *buf, int num) { u32 pid = bpf_get_current_pid_tgid(); FILTER struct probe_SSL_data_t __data = {0}; __data.timestamp_ns = bpf_ktime_get_ns(); __data.pid = pid; __data.len = num; bpf_get_current_comm(&__data.comm, sizeof(__data.comm)); if ( buf != 0) { bpf_probe_read(&__data.v0, sizeof(__data.v0), buf); } perf_SSL_write.perf_submit(ctx, &__data, sizeof(__data)); return 0; } BPF_PERF_OUTPUT(perf_SSL_read); BPF_HASH(bufs, u32, u64); int probe_SSL_read_enter(struct pt_regs *ctx, void *ssl, void *buf, int num) { u32 pid = bpf_get_current_pid_tgid(); FILTER bufs.update(&pid, (u64*)&buf); return 0; } int probe_SSL_read_exit(struct pt_regs *ctx, void *ssl, void *buf, int num) { u32 pid = bpf_get_current_pid_tgid(); FILTER u64 *bufp = bufs.lookup(&pid); if (bufp == 0) { return 0; } struct probe_SSL_data_t __data = {0}; __data.timestamp_ns = bpf_ktime_get_ns(); __data.pid = pid; __data.len = PT_REGS_RC(ctx); bpf_get_current_comm(&__data.comm, sizeof(__data.comm)); if (bufp != 0) { bpf_probe_read(&__data.v0, sizeof(__data.v0), (char *)*bufp); } bufs.delete(&pid); perf_SSL_read.perf_submit(ctx, &__data, sizeof(__data)); return 0; } """ if args.pid: prog = prog.replace('FILTER', 'if (pid != %d) { return 0; }' % args.pid) else: prog = prog.replace('FILTER', '') if args.debug or args.ebpf: print(prog) if args.ebpf: exit() b = BPF(text=prog) # It looks like SSL_read's arguments aren't available in a return probe so you # need to stash the buffer address in a map on the function entry and read it # on its exit (Mark Drayton) # if args.openssl: b.attach_uprobe(name="ssl", sym="SSL_write", fn_name="probe_SSL_write", pid=args.pid or -1) b.attach_uprobe(name="ssl", sym="SSL_read", fn_name="probe_SSL_read_enter", pid=args.pid or -1) b.attach_uretprobe(name="ssl", sym="SSL_read", fn_name="probe_SSL_read_exit", pid=args.pid or -1) if args.gnutls: b.attach_uprobe(name="gnutls", sym="gnutls_record_send", fn_name="probe_SSL_write", pid=args.pid or -1) b.attach_uprobe(name="gnutls", sym="gnutls_record_recv", fn_name="probe_SSL_read_enter", pid=args.pid or -1) b.attach_uretprobe(name="gnutls", sym="gnutls_record_recv", fn_name="probe_SSL_read_exit", pid=args.pid or -1) if args.nss: b.attach_uprobe(name="nspr4", sym="PR_Write", fn_name="probe_SSL_write", pid=args.pid or -1) b.attach_uprobe(name="nspr4", sym="PR_Send", fn_name="probe_SSL_write", pid=args.pid or -1) b.attach_uprobe(name="nspr4", sym="PR_Read", fn_name="probe_SSL_read_enter", pid=args.pid or -1) b.attach_uretprobe(name="nspr4", sym="PR_Read", fn_name="probe_SSL_read_exit", pid=args.pid or -1) b.attach_uprobe(name="nspr4", sym="PR_Recv", fn_name="probe_SSL_read_enter", pid=args.pid or -1) b.attach_uretprobe(name="nspr4", sym="PR_Recv", fn_name="probe_SSL_read_exit", pid=args.pid or -1) # define output data structure in Python TASK_COMM_LEN = 16 # linux/sched.h MAX_BUF_SIZE = 464 # Limited by the BPF stack # header print("%-12s %-18s %-16s %-6s %-6s" % ("FUNC", "TIME(s)", "COMM", "PID", "LEN")) # process event start = 0 def print_event_write(cpu, data, size): print_event(cpu, data, size, "WRITE/SEND", "perf_SSL_write") def print_event_read(cpu, data, size): print_event(cpu, data, size, "READ/RECV", "perf_SSL_read") def print_event(cpu, data, size, rw, evt): global start event = b[evt].event(data) # Filter events by command if args.comm: if not args.comm == event.comm: return if start == 0: start = event.timestamp_ns time_s = (float(event.timestamp_ns - start)) / 1000000000 s_mark = "-" * 5 + " DATA " + "-" * 5 e_mark = "-" * 5 + " END DATA " + "-" * 5 truncated_bytes = event.len - MAX_BUF_SIZE if truncated_bytes > 0: e_mark = "-" * 5 + " END DATA (TRUNCATED, " + str(truncated_bytes) + \ " bytes lost) " + "-" * 5 fmt = "%-12s %-18.9f %-16s %-6d %-6d\n%s\n%s\n%s\n\n" print(fmt % (rw, time_s, event.comm.decode('utf-8', 'replace'), event.pid, event.len, s_mark, event.v0.decode('utf-8', 'replace'), e_mark)) b["perf_SSL_write"].open_perf_buffer(print_event_write) b["perf_SSL_read"].open_perf_buffer(print_event_read) while 1: try: b.perf_buffer_poll() except KeyboardInterrupt: exit() bpfcc-0.12.0/tools/sslsniff_example.txt000066400000000000000000000045361357404205000201220ustar00rootroot00000000000000Demonstrations of sslsniff.py This tool traces the write/send and read/recv functions of OpenSSL, GnuTLS and NSS. Data passed to this functions is printed as plain text. Useful, for example, to sniff HTTP before encrypted with SSL. Output of tool executing in other shell "curl https://example.com" % sudo python sslsniff.py FUNC TIME(s) COMM PID LEN WRITE/SEND 0.000000000 curl 12915 75 ----- DATA ----- GET / HTTP/1.1 Host: example.com User-Agent: curl/7.50.1 Accept: */* ----- END DATA ----- READ/RECV 0.127144585 curl 12915 333 ----- DATA ----- HTTP/1.1 200 OK Cache-Control: max-age=604800 Content-Type: text/html Date: Tue, 16 Aug 2016 15:42:12 GMT Etag: "359670651+gzip+ident" Expires: Tue, 23 Aug 2016 15:42:12 GMT Last-Modified: Fri, 09 Aug 2013 23:54:35 GMT Server: ECS (iad/18CB) Vary: Accept-Encoding X-Cache: HIT x-ec-custom-error: 1 Content-Length: 1270 ----- END DATA ----- READ/RECV 0.129967972 curl 12915 1270 ----- DATA ----- Example Domain