Commit 3aa8bf4c authored by Martin KaFai Lau's avatar Martin KaFai Lau

Merge remote-tracking branch 'origin/master' into iamkafai

parents f9ada2cb bc3c1e48
---
BasedOnStyle: Google
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
IndentCaseLabels: false
AccessModifierOffset: -2
AccessModifierOffset: -1
......@@ -2,6 +2,10 @@
*.swp
*.swo
*.pyc
.idea
# Build artefacts
# Build artifacts
/build/
cmake-build-debug
debian/**/*.log
obj-x86_64-linux-gnu
language: generic
install:
- sudo apt-get install -y python-pip
- sudo pip install pep8
script:
- find tools/ -type f -name "*.py" | xargs pep8 -r --show-source --ignore=E123,E125,E126,E127,E128,E302
......@@ -3,21 +3,31 @@
cmake_minimum_required(VERSION 2.8.7)
project(bcc)
set(CMAKE_BUILD_TYPE Release)
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif()
enable_testing()
include(cmake/GetGitRevisionDescription.cmake)
include(cmake/version.cmake)
include(CMakeDependentOption)
include(GNUInstallDirs)
include(CheckCXXCompilerFlag)
include(cmake/FindCompilerFlag.cmake)
option(ENABLE_CLANG_JIT "Enable Loading BPF through Clang Frontend" ON)
option(ENABLE_USDT "Enable User-level Statically Defined Tracing" 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(NOT PYTHON_ONLY)
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}")
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
......@@ -47,30 +57,35 @@ if(NOT DEFINED BCC_KERNEL_MODULES_DIR)
set(BCC_KERNEL_MODULES_DIR "/lib/modules")
endif()
find_package(LibElf REQUIRED)
# Set to non-zero if system installs kernel headers with split source and build
# directories in /lib/modules/`uname -r`/. This is the case for debian and
# suse, to the best of my knowledge.
if(BCC_KERNEL_HAS_SOURCE_DIR)
set(BCC_KERNEL_HAS_SOURCE_DIR 1)
set(BCC_KERNEL_MODULES_SUFFIX "source")
else()
set(BCC_KERNEL_HAS_SOURCE_DIR 0)
if(NOT DEFINED BCC_PROG_TAG_DIR)
set(BCC_PROG_TAG_DIR "/var/tmp/bcc")
endif()
# Similar to above, set to custom value if kernel headers in
# /lib/modules/`uname -r` sit in a different location than build/.
if(NOT DEFINED BCC_KERNEL_MODULES_SUFFIX)
set(BCC_KERNEL_MODULES_SUFFIX "build")
# 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 11)
endif(NOT PYTHON_ONLY AND ENABLE_CLANG_JIT)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall")
endif()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall ${CXX_ISYSTEM_DIRS}")
add_subdirectory(src)
if(ENABLE_CLANG_JIT)
add_subdirectory(examples)
add_subdirectory(man)
add_subdirectory(src)
add_subdirectory(tests)
add_subdirectory(tools)
endif(ENABLE_CLANG_JIT)
# 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
......@@ -15,7 +15,7 @@ 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/bitehist.*).
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).
......@@ -31,11 +31,14 @@ A checklist for bcc tool development:
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.". Also, try hard to keep the output less than 80 characters wide, especially the default output of the tool. That way, the output not only fits on the smallest reasonable terminal, it also fits well in slide decks, blog posts, articles, and printed material, all of which help education and adoption. Publishers of technical books often have templates they require books to conform to: it may not be an option to shrink or narrow the font to fit your output.
1. **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!
FROM debian:jessie
FROM debian:stretch
RUN apt-key adv --keyserver ha.pool.sks-keyservers.net --recv 15CF4D18AF4F7421 && \
echo "deb http://llvm.org/apt/jessie/ llvm-toolchain-jessie-3.8 main" > /etc/apt/sources.list.d/llvm.list && \
apt-get update && \
apt-get install -y --no-install-recommends build-essential fakeroot bison cmake debhelper devscripts flex git libedit-dev python zlib1g-dev libllvm3.8 llvm-3.8-dev libclang-3.8-dev libelf-dev luajit libluajit-5.1-dev && \
mkdir -p /usr/share/llvm-3.8 && \
ln -s /usr/lib/llvm-3.8/share/llvm/cmake /usr/share/llvm-3.8/cmake
MAINTAINER Brenden Blanco <bblanco@gmail.com>
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 ./scripts/build-deb.sh
RUN /usr/lib/pbuilder/pbuilder-satisfydepends && \
./scripts/build-deb.sh
# File to be used for building an Ubuntu .deb
FROM ubuntu:xenial
FROM ubuntu:trusty
MAINTAINER Brenden Blanco <bblanco@gmail.com>
MAINTAINER Brenden Blanco <bblanco@plumgrid.com>
RUN apt-get -qq update && \
apt-get -y install pbuilder aptitude
RUN apt-get -y install wget
RUN printf "deb http://llvm.org/apt/trusty/ llvm-toolchain-trusty main\ndeb-src http://llvm.org/apt/trusty/ llvm-toolchain-trusty main\n" > /etc/apt/sources.list.d/llvm.list
RUN wget -q -O - http://llvm.org/apt/llvm-snapshot.gpg.key | apt-key add -
RUN apt-get -y update
COPY ./ /root/bcc
RUN apt-get -y install bison build-essential cmake debhelper devscripts flex git libedit-dev python zlib1g-dev
RUN apt-get -y install libllvm3.8 llvm-3.8-dev libclang-3.8-dev
RUN mkdir -p /root/bcc/build
COPY ./ /root/bcc/
WORKDIR /root
RUN tar zcf bcc_0.1.1.orig.tar.gz bcc/
WORKDIR /root/bcc
RUN DEB_BUILD_OPTIONS="nocheck parallel=4" debuild -us -uc
RUN /usr/lib/pbuilder/pbuilder-satisfydepends && \
./scripts/build-deb.sh
......@@ -5,9 +5,13 @@
- [Ubuntu](#ubuntu-xenial---binary)
- [Fedora](#fedora---binary)
- [Arch](#arch---aur)
- [Gentoo](#gentoo---portage)
- [openSUSE](#opensuse---binary)
* [Source](#source)
- [Debian](#debian---source)
- [Ubuntu](#ubuntu---source)
- [Fedora](#fedora---source)
- [openSUSE](#opensuse---source)
* [Older Instructions](#older-instructions)
## Kernel Configuration
......@@ -29,6 +33,16 @@ CONFIG_HAVE_BPF_JIT=y
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-<kernel-version>`.
......@@ -41,7 +55,7 @@ Only the nightly packages are built for Ubuntu 16.04, but the steps are very str
```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
sudo apt-get install bcc-tools libbcc-examples linux-headers-$(uname -r)
```
## Ubuntu Trusty - Binary
......@@ -74,7 +88,7 @@ To install:
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys D4284CDD
echo "deb https://repo.iovisor.org/apt trusty main" | sudo tee /etc/apt/sources.list.d/iovisor.list
sudo apt-get update
sudo apt-get install binutils bcc bcc-tools libbcc-examples python-bcc
sudo apt-get install binutils bcc bcc-tools libbcc-examples python-bcc linux-headers-$(uname -r)
```
**Nightly Packages**
......@@ -82,7 +96,7 @@ sudo apt-get install binutils bcc bcc-tools libbcc-examples python-bcc
```bash
echo "deb [trusted=yes] https://repo.iovisor.org/apt/trusty trusty-nightly main" | sudo tee /etc/apt/sources.list.d/iovisor.list
sudo apt-get update
sudo apt-get install bcc-tools
sudo apt-get install bcc-tools libbcc-examples
```
Test it:
......@@ -95,12 +109,12 @@ sudo python /usr/share/bcc/examples/tracing/task_switch.py
```bash
git clone https://github.com/svinota/pyroute2
cd pyroute2; sudo make install
sudo python /usr/share/bcc/examples/simple_tc.py
sudo python /usr/share/bcc/examples/networking/simple_tc.py
```
## Fedora - Binary
Install a 4.2+ kernel from
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
......@@ -109,13 +123,13 @@ sudo dnf update
# reboot
```
Nightly bcc binary packages are built for Fedora 23 and 24, hosted at
`https://repo.iovisor.org/yum/nightly/f{23,24}`.
Nightly bcc binary packages for Fedora 23, 24, and 25 are hosted at
`https://repo.iovisor.org/yum/nightly/f{23,24,25}`.
To install (change 'f23' to 'f24' for rawhide):
To install:
```bash
echo -e '[iovisor]\nbaseurl=https://repo.iovisor.org/yum/nightly/f23/$basearch\nenabled=1\ngpgcheck=0' | sudo tee /etc/yum.repos.d/iovisor.repo
sudo dnf install bcc-tools
echo -e '[iovisor]\nbaseurl=https://repo.iovisor.org/yum/nightly/f25/$basearch\nenabled=1\ngpgcheck=0' | sudo tee /etc/yum.repos.d/iovisor.repo
sudo dnf install bcc-tools kernel-devel-$(uname -r) kernel-headers-$(uname -r)
```
## Arch - AUR
......@@ -128,9 +142,124 @@ 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
```
# Source
## 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
```
#### 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 <preferred development directory>
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:
......@@ -183,8 +312,16 @@ 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
......@@ -196,6 +333,34 @@ 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 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
```
# Older Instructions
## Build LLVM and Clang development libs
......
- 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)
# 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).
This diff is collapsed.
%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
%define debug_package %{nil}
Name: bcc
......@@ -10,22 +17,38 @@ License: ASL 2.0
URL: https://github.com/iovisor/bcc
Source0: bcc.tar.gz
BuildArch: x86_64
BuildRequires: bison, cmake >= 2.8.7, flex, gcc, gcc-c++, python2-devel, elfutils-libelf-devel-static
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_lua}
BuildRequires: luajit luajit-devel
%endif
%if %{without local_clang_static}
BuildRequires: llvm-devel llvm-static
BuildRequires: clang-devel
%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 -n bcc
%setup -q -n bcc
%build
mkdir build
pushd build
cmake .. -DREVISION_LAST=%{version} -DREVISION=%{version} -DCMAKE_INSTALL_PREFIX=/usr
cmake .. -DREVISION_LAST=%{version} -DREVISION=%{version} \
-DCMAKE_INSTALL_PREFIX=/usr \
%{?lua_config}
make %{?_smp_mflags}
popd
......@@ -33,56 +56,53 @@ popd
pushd build
make install/strip DESTDIR=%{buildroot}
%changelog
* Mon Apr 04 2016 Vicent Marti <vicent@github.com> - 0.1.4-1
- Add bcc-lua package
* Sun Nov 29 2015 Brenden Blanco <bblanco@plumgrid.com> - 0.1.3-1
- Add bcc-tools package
* Mon Oct 12 2015 Brenden Blanco <bblanco@plumgrid.com> - 0.1.2-1
- Add better version numbering into libbcc.so
* Fri Jul 03 2015 Brenden Blanco <bblanco@plumgrid.com> - 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
Requires: libbcc = %{version}-%{release}
%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)
%if %{with_lua}
%package -n bcc-lua
Summary: Standalone tool to run BCC tracers written in Lua
Requires: libbcc
Requires: libbcc = %{version}-%{release}
%description -n bcc-lua
Standalone tool to run BCC tracers written in Lua
%endif
%files -n python-bcc
%{python_sitelib}/bcc*
%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 python-bcc
%{python_sitelib}/bcc*
%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
......@@ -96,5 +116,22 @@ Standalone tool to run BCC tracers written in Lua
/usr/share/bcc/tools/*
/usr/share/bcc/man/*
%files -n bcc-lua
/usr/bin/bcc-lua
%post -n libbcc -p /sbin/ldconfig
%postun -n libbcc -p /sbin/ldconfig
%changelog
* Mon Nov 21 2016 William Cohen <wcohen@redhat.com> - 0.2.0-1
- Revise bcc.spec to address rpmlint issues and build properly in Fedora koji.
* Mon Apr 04 2016 Vicent Marti <vicent@github.com> - 0.1.4-1
- Add bcc-lua package
* Sun Nov 29 2015 Brenden Blanco <bblanco@plumgrid.com> - 0.1.3-1
- Add bcc-tools package
* Mon Oct 12 2015 Brenden Blanco <bblanco@plumgrid.com> - 0.1.2-1
- Add better version numbering into libbcc.so
* Fri Jul 03 2015 Brenden Blanco <bblanco@plumgrid.com> - 0.1.1-2
- Initial RPM Release
# 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()
......@@ -31,7 +31,7 @@
FIND_PATH(LUAJIT_INCLUDE_DIR lua.h
HINTS
$ENV{LUAJIT_DIR}
PATH_SUFFIXES include/luajit-2.0 include/luajit2.0 include/luajit include
PATH_SUFFIXES luajit-2.0 luajit2.0 luajit luajit-2.1
PATHS
~/Library/Frameworks
/Library/Frameworks
......@@ -44,7 +44,7 @@ FIND_PATH(LUAJIT_INCLUDE_DIR lua.h
)
FIND_LIBRARY(LUAJIT_LIBRARY
NAMES libluajit-51.a libluajit-5.1.a libluajit.a
NAMES libluajit-51.a libluajit-5.1.a libluajit.a libluajit-5.1.so
HINTS
$ENV{LUAJIT_DIR}
PATH_SUFFIXES lib64 lib
......
set(llvm_raw_libs bitwriter bpfcodegen debuginfodwarf irreader linker
mcjit objcarcopts option passes nativecodegen lto)
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()
if (${LLVM_PACKAGE_VERSION} VERSION_GREATER "5")
list(APPEND llvm_raw_libs bpfdisassembler)
endif()
llvm_map_components_to_libnames(_llvm_libs ${llvm_raw_libs})
llvm_expand_dependencies(llvm_libs ${_llvm_libs})
# order is important
set(clang_libs
${libclangFrontend}
${libclangSerialization}
${libclangDriver}
${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}")
# 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()
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 <bblanco@gmail.com> 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 <bblanco@plumgrid.com> Thu, 08 Sep 2016 17:05:28 -0700
bcc (0.1.8-1) unstable; urgency=low
* Add many new utilities in tools/
......
......@@ -3,7 +3,12 @@ Maintainer: Brenden Blanco <bblanco@plumgrid.com>
Section: misc
Priority: optional
Standards-Version: 3.9.5
Build-Depends: debhelper (>= 9), cmake, libllvm3.7 | libllvm3.8, llvm-3.7-dev | llvm-3.8-dev, libclang-3.7-dev | libclang-3.8-dev, libelf-dev, bison, flex, libedit-dev, clang-format | clang-format-3.7, python-netaddr, python-pyroute2, luajit, libluajit-5.1-dev
Build-Depends: debhelper (>= 9), cmake, libllvm3.7 | libllvm3.8,
llvm-3.7-dev | llvm-3.8-dev, libclang-3.7-dev | libclang-3.8-dev,
libelf-dev, bison, flex, libfl-dev, libedit-dev, zlib1g-dev, git,
clang-format | clang-format-3.7 | clang-format-3.8, python (>= 2.7),
python-netaddr, python-pyroute2, luajit, libluajit-5.1-dev, arping,
inetutils-ping | iputils-ping, iperf, netperf, ethtool, devscripts
Homepage: https://github.com/iovisor/bcc
Package: libbcc
......
usr/include/bcc/*
usr/lib/x86_64-linux-gnu/libbcc*
usr/lib/*/libbcc*
usr/lib/*/pkgconfig/libbcc.pc
......@@ -11,6 +11,10 @@ UPSTREAM_VERSION := $(shell dpkg-parsechangelog | sed -rne "s,^Version: ([0-9.]+
%:
dh $@ --buildsystem=cmake --parallel
# 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"
This diff is collapsed.
This diff is collapsed.
# 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. Snippits are taken from various programs in bcc: see their files for licences.
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.
......@@ -36,7 +36,7 @@ There are six things to learn from this:
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).
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.
......@@ -88,7 +88,7 @@ while 1:
print("%-18.9f %-16s %-6d %s" % (ts, task, pid, msg))
```
This is simalar to hello_world.py, and traces new processes via sys_clone() again, but has a few more things to learn:
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.
......@@ -96,11 +96,11 @@ This is simalar to hello_world.py, and traces new processes via sys_clone() agai
1. ```b.attach_kprobe(event="sys_clone", fn_name="hello")```: Creates a kprobe for the sys_clone() kernel 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. Simalar to trace_print(), this is handy for hacking, but for real tooling we should switch to BPF_PERF_OUTPUT().
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 asynchrosous 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.
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:
......@@ -123,7 +123,7 @@ b = BPF(text="""
BPF_HASH(last);
void do_trace(struct pt_regs *ctx) {
int do_trace(struct pt_regs *ctx) {
u64 ts, *tsp, delta, key = 0;
// attempt to read stored timestamp
......@@ -140,6 +140,7 @@ void do_trace(struct pt_regs *ctx) {
// update stored timestamp
ts = bpf_ktime_get_ns();
last.update(&key, &ts);
return 0;
}
""")
......@@ -163,11 +164,11 @@ Things to learn:
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)```: Set the key to equal the value in the 2nd argument. This records the timestamp.
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 lession) to store the count of all sys_sync() 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.
Modify the sync_timing.py program (prior lesson) to store the count of all sys_sync() 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
......@@ -183,7 +184,7 @@ TIME(s) T BYTES LAT(ms)
[...]
```
And a code snippit:
And a code snippet:
```Python
[...]
......@@ -326,7 +327,7 @@ This may be improved in future bcc versions. Eg, the Python data struct could be
Rewrite sync_timing.py, from a prior lesson, to use ```BPF_PERF_OUTPUT```.
### Lesson 9. bitesize.py
### Lesson 9. bitehist.py
The following tool records a histogram of disk I/O sizes. Sample output:
......@@ -345,7 +346,7 @@ Tracing... Hit Ctrl-C to end.
128 -> 255 : 800 |**************************************|
```
Code is [examples/tracing/bitesize.py](../examples/tracing/bitesize.py):
Code is [examples/tracing/bitehist.py](../examples/tracing/bitehist.py):
```Python
from bcc import BPF
......@@ -378,7 +379,7 @@ except KeyboardInterrupt:
b["dist"].print_log2_hist("kbytes")
```
A recap from earlier lessions:
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()```.
......@@ -393,7 +394,7 @@ New things to learn:
### 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 bitesize.py from a prior lesson.
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
......@@ -486,7 +487,7 @@ while 1:
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.
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:
```
......@@ -603,7 +604,7 @@ TIME(s) COMM PID ARGS
24653340.510164998 node 24728 path:/images/favicon.png
```
Relevent code from [examples/tracing/nodejs_http_server.py](../examples/tracing/nodejs_http_server.py):
Relevant code from [examples/tracing/nodejs_http_server.py](../examples/tracing/nodejs_http_server.py):
```Python
if len(sys.argv) < 2:
......@@ -629,7 +630,7 @@ u = USDT(pid=int(pid))
u.enable_probe(probe="http__server__request", fn_name="do_trace")
# initialize BPF
b = BPF(text=bpf_text, usdt=u)
b = BPF(text=bpf_text, usdt_contexts=[u])
```
Things to learn:
......@@ -638,7 +639,7 @@ Things to learn:
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=u)```: Need to pass in our USDT object, ```u```, to BPF object creation.
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
......@@ -669,7 +670,7 @@ struct key_t {
u32 curr_pid;
};
// map_type, key_type, leaf_type, table_name, num_entry
BPF_TABLE("hash", struct key_t, u64, stats, 1024);
BPF_HASH(stats, struct key_t, u64, 1024);
// attach to finish_task_switch in kernel/sched/core.c, which has the following
// prototype:
// struct rq *finish_task_switch(struct task_struct *prev)
......
set(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)
add_subdirectory(lua)
\ No newline at end of file
endif()
# 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)
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)
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)
endif(INSTALL_CPP_EXAMPLES)
/*
* 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 <unistd.h>
#include <cstdlib>
#include <iomanip>
#include <iostream>
#include <string>
#include "BPF.h"
const std::string BPF_PROGRAM = R"(
#include <linux/sched.h>
#include <uapi/linux/ptrace.h>
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<int, uint64_t>("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;
}
/*
* 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 <signal.h>
#include <iostream>
#include <vector>
#include "BPF.h"
const std::string BPF_PROGRAM = R"(
#include <linux/sched.h>
#include <uapi/linux/ptrace.h>
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<event_t*>(data);
std::cout << "PID " << event->pid << " (" << event->name << ") ";
std::cout << "folly::RequestContext switch from " << event->old_addr << " to "
<< event->new_addr << std::endl;
}
ebpf::BPF* bpf;
void signal_handler(int s) {
std::cerr << "Terminating..." << std::endl;
delete bpf;
exit(0);
}
int main(int argc, char** argv) {
if (argc != 2) {
std::cout << "USAGE: FollyRequestContextSwitch PATH_TO_BINARY" << std::endl;
exit(1);
}
std::string binary_path(argv[1]);
bpf = new ebpf::BPF();
std::vector<ebpf::USDT> u;
u.emplace_back(binary_path, "folly", "request_context_switch_before",
"on_context_switch");
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[0]);
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;
}
signal(SIGINT, signal_handler);
std::cout << "Started tracing, hit Ctrl-C to terminate." << std::endl;
while (true)
bpf->poll_perf_buffer("events");
return 0;
}
/*
* Copyright (c) Facebook, Inc.
* Licensed under the Apache License, Version 2.0 (the "License")
*/
#include <unistd.h>
#include <fstream>
#include <iostream>
#include <string>
#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;
auto attach_res = bpf.attach_kprobe("sys_clone", "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("sys_clone");
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;
}
/*
* 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 <linux/perf_event.h>
#include <unistd.h>
#include <iomanip>
#include <iostream>
#include <string>
#include "BPF.h"
const std::string BPF_PROGRAM = R"(
#include <linux/ptrace.h>
#include <uapi/linux/bpf_perf_event.h>
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_init(&key, &zero);
(*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_init(&key, &zero);
(*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<event_t, uint64_t>("ref_count");
auto misses = bpf.get_hash_table<event_t, uint64_t>("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;
}
/*
* 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 <signal.h>
#include <iostream>
#include "BPF.h"
const std::string BPF_PROGRAM = R"(
#include <linux/sched.h>
#include <uapi/linux/ptrace.h>
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);
int on_urandom_read(struct urandom_read_args* attr) {
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<event_t*>(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);
}
int main(int argc, char** argv) {
bpf = new ebpf::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_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;
}
signal(SIGINT, signal_handler);
std::cout << "Started tracing, hit Ctrl-C to terminate." << std::endl;
while (true)
bpf->poll_perf_buffer("events");
return 0;
}
/*
* 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 <unistd.h>
#include <algorithm>
#include <cstdlib>
#include <iostream>
#include <string>
#include "BPF.h"
const std::string BPF_PROGRAM = R"(
#include <linux/ptrace.h>
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<query_probe_t, int>("queries");
auto table = table_handle.get_table_offline();
std::sort(table.begin(), table.end(), [](std::pair<query_probe_t, int> a,
std::pair<query_probe_t, int> 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;
}
/*
* 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 <unistd.h>
#include <algorithm>
#include <iostream>
#include "BPF.h"
const std::string BPF_PROGRAM = R"(
#include <linux/sched.h>
#include <uapi/linux/ptrace.h>
struct stack_key_t {
int pid;
char name[16];
int user_stack;
int kernel_stack;
};
BPF_STACK_TRACE(stack_traces, 10240)
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, BPF_F_REUSE_STACKID);
key.user_stack = stack_traces.get_stackid(
ctx, BPF_F_REUSE_STACKID | BPF_F_USER_STACK
);
u64 zero = 0, *val;
val = counts.lookup_or_init(&key, &zero);
(*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 table =
bpf.get_hash_table<stack_key_t, uint64_t>("counts").get_table_offline();
std::sort(table.begin(), table.end(), [](std::pair<stack_key_t, uint64_t> a,
std::pair<stack_key_t, uint64_t> b) {
return a.second < b.second;
});
auto stacks = bpf.get_stack_table("stack_traces");
for (auto it : table) {
std::cout << "PID: " << it.first.pid << " (" << it.first.name << ") "
<< "made " << it.second
<< " TCP sends on following stack: " << std::endl;
std::cout << " Kernel Stack:" << std::endl;
if (it.first.kernel_stack >= 0) {
auto syms = stacks.get_stack_symbol(it.first.kernel_stack, -1);
for (auto sym : syms)
std::cout << " " << sym << std::endl;
} else
std::cout << " " << it.first.kernel_stack << std::endl;
std::cout << " User Stack:" << std::endl;
if (it.first.user_stack >= 0) {
auto syms = stacks.get_stack_symbol(it.first.user_stack, it.first.pid);
for (auto sym : syms)
std::cout << " " << sym << std::endl;
} else
std::cout << " " << it.first.user_stack << std::endl;
}
auto detach_res = bpf.detach_kprobe("tcp_sendmsg");
if (detach_res.code() != 0) {
std::cerr << detach_res.msg() << std::endl;
return 1;
}
return 0;
}
......@@ -24,7 +24,7 @@ return function(BPF)
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]; }")
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:kprobe_poll_loop()
......
#!/usr/bin/env bcc-lua
--[[
Copyright 2016 Marek Vavrusa <mvavrusa@cloudflare.com>
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)
\ No newline at end of file
#!/usr/bin/env bcc-lua
--[[
Copyright 2016 Marek Vavrusa <mvavrusa@cloudflare.com>
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)
#!/usr/bin/env bcc-lua
--[[
Copyright 2016 Marek Vavrusa <mvavrusa@cloudflare.com>
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)
end
\ No newline at end of file
#!/usr/bin/env bcc-lua
--[[
Copyright 2016 Marek Vavrusa <mvavrusa@cloudflare.com>
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)
end
\ No newline at end of file
#!/usr/bin/env bcc-lua
--[[
Copyright 2016 Marek Vavrusa <mvavrusa@cloudflare.com>
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)
end
\ No newline at end of file
#!/usr/bin/env bcc-lua
--[[
Copyright 2016 Marek Vavrusa <mvavrusa@cloudflare.com>
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] += <payload length>`
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)
end
\ No newline at end of file
......@@ -24,7 +24,7 @@ struct key_t {
u32 curr_pid;
};
// map_type, key_type, leaf_type, table_name, num_entry
BPF_TABLE("hash", struct key_t, u64, stats, 1024);
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;
......
#!/usr/bin/env bcc-lua
--[[
Copyright 2016 Marek Vavrusa <mvavrusa@cloudflare.com>
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
#!/usr/bin/env bcc-lua
--[[
Copyright 2016 Marek Vavrusa <mvavrusa@cloudflare.com>
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
#!/usr/bin/env bcc-lua
--[[
Copyright 2016 Marek Vavrusa <mvavrusa@cloudflare.com>
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)
#!/usr/bin/env bcc-lua
--[[
Copyright 2016 Marek Vavrusa <mvavrusa@cloudflare.com>
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: <get|set> <key>
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)
set(EXAMPLE_FILES simulation.py)
set(EXAMPLE_PROGRAMS simple_tc.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)
......
......@@ -2,7 +2,7 @@
// Licensed under the Apache License, Version 2.0 (the "License")
#include <bcc/proto.h>
BPF_TABLE("hash", u32, int, vni2if, 1024);
BPF_HASH(vni2if, u32, int, 1024);
struct vni_key {
u64 mac;
......@@ -15,12 +15,12 @@ struct host {
u64 rx_pkts;
u64 tx_pkts;
};
BPF_TABLE("hash", struct vni_key, struct host, mac2host, 10240);
BPF_HASH(mac2host, struct vni_key, struct host);
struct config {
int tunnel_ifindex;
};
BPF_TABLE("hash", int, struct config, conf, 1);
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) {
......@@ -29,7 +29,8 @@ int handle_ingress(struct __sk_buff *skb) {
struct ethernet_t *ethernet = cursor_advance(cursor, sizeof(*ethernet));
struct bpf_tunnel_key tkey = {};
bpf_skb_get_tunnel_key(skb, &tkey, sizeof(tkey), 0);
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) {
......@@ -63,7 +64,8 @@ int handle_egress(struct __sk_buff *skb) {
u32 zero = 0;
tkey.tunnel_id = dst_host->tunnel_id;
tkey.remote_ipv4 = dst_host->remote_ipv4;
bpf_skb_set_tunnel_key(skb, &tkey, sizeof(tkey), 0);
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 = {};
......@@ -73,7 +75,8 @@ int handle_egress(struct __sk_buff *skb) {
return 1;
tkey.tunnel_id = dst_host->tunnel_id;
tkey.remote_ipv4 = dst_host->remote_ipv4;
bpf_skb_set_tunnel_key(skb, &tkey, sizeof(tkey), 0);
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;
......
......@@ -5,21 +5,22 @@
struct config {
int tunnel_ifindex;
};
BPF_TABLE("hash", int, struct config, conf, 1);
BPF_HASH(conf, int, struct config, 1);
struct tunnel_key {
u32 tunnel_id;
u32 remote_ipv4;
};
BPF_TABLE("hash", struct tunnel_key, int, tunkey2if, 1024);
BPF_HASH(tunkey2if, struct tunnel_key, int, 1024);
BPF_TABLE("hash", int, struct tunnel_key, if2tunkey, 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, sizeof(tkey), 0);
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;
......@@ -57,7 +58,8 @@ int handle_egress(struct __sk_buff *skb) {
if (key_p) {
tkey.tunnel_id = key_p->tunnel_id;
tkey.remote_ipv4 = key_p->remote_ipv4;
bpf_skb_set_tunnel_key(skb, &tkey, sizeof(tkey), 0);
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;
......
......@@ -43,7 +43,7 @@ struct dns_char_t
} BPF_PACKET_HEADER;
struct Key {
unsigned char p[32];
unsigned char p[255];
};
struct Leaf {
......@@ -51,7 +51,7 @@ struct Leaf {
unsigned char p[4];
};
BPF_TABLE("hash", struct Key, struct Leaf, cache, 128);
BPF_HASH(cache, struct Key, struct Leaf, 128);
int dns_matching(struct __sk_buff *skb)
{
......@@ -70,10 +70,6 @@ int dns_matching(struct __sk_buff *skb)
struct udp_t *udp = cursor_advance(cursor, sizeof(*udp));
if(udp->dport == 53){
// Our Cursor + the length of our udp packet - size of the udp header
// - the two 16bit values for QTYPE and QCLASS.
u8 *sentinel = cursor + udp->length - sizeof(*udp) - 4;
struct dns_hdr_t *dns_hdr = cursor_advance(cursor, sizeof(*dns_hdr));
// Do nothing if packet is not a request.
......@@ -84,22 +80,24 @@ int dns_matching(struct __sk_buff *skb)
u16 i = 0;
struct dns_char_t *c;
// This unroll worked not in latest BCC version.
for(u8 j = 0; i<255;i++){
if (cursor == sentinel) goto end; c = cursor_advance(cursor, 1); key.p[i++] = c->c;
#pragma unroll
for(i = 0; i<255;i++){
c = cursor_advance(cursor, 1);
if (c->c == 0)
break;
key.p[i] = c->c;
}
end:
{}
struct Leaf * lookup_leaf = cache.lookup(&key);
// If DNS name is contained in our map, drop packet.
// If DNS name is contained in our map, keep the packet
if(lookup_leaf) {
return 0;
bpf_trace_printk("Matched1\n");
return -1;
}
}
}
}
return -1;
// Drop the packet
return 0;
}
......@@ -8,11 +8,13 @@ import sys
import socket
import os
import struct
import dnslib
import argparse
def encode_dns(name):
size = 32
if len(name) > 253:
size = 255
if len(name) > 255:
raise Exception("DNS Name too long.")
b = bytearray(size)
i = 0;
......@@ -27,8 +29,22 @@ def encode_dns(name):
return (c_ubyte * size).from_buffer(b)
def add_cache_entry(cache, name):
key = cache.Key()
key.p = encode_dns(name)
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, required=True, help="Interface name")
req_args.add_argument("-d", "--domains", type=str, required=True,
help='List of domain names separated by comma. 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"))
......@@ -39,19 +55,47 @@ bpf = BPF(src_file = "dns_matching.c", debug=0)
function_dns_matching = bpf.load_func("dns_matching", BPF.SOCKET_FILTER)
#create raw socket, bind it to eth0
#create raw socket, bind it to user provided interface
#attach bpf program to socket created
BPF.attach_raw_socket(function_dns_matching, "eth1")
BPF.attach_raw_socket(function_dns_matching, args.interface)
# Get the table.
cache = bpf.get_table("cache")
# Create first entry for foo.bar
key = cache.Key()
key.p = encode_dns("foo.bar")
leaf = cache.Leaf()
leaf.p = (c_ubyte * 4).from_buffer(bytearray(4))
cache[key] = leaf
bpf.trace_print()
# Add cache entries
entries = [i.strip() for i in args.domains.split(",")]
for e in entries:
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
sock = socket.fromfd(socket_fd, socket.PF_PACKET, socket.SOCK_RAW, socket.IPPROTO_IP)
sock.setblocking(True)
while 1:
#retrieve raw packet from socket
packet_str = os.read(socket_fd, 2048)
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")
......@@ -2,24 +2,24 @@
#include <net/sock.h>
#include <bcc/proto.h>
#define IP_TCP 6
#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
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)
//BPF_TABLE(map_type, key_type, leaf_type, table_name, num_entry)
//map <Key, Leaf>
//tracing sessions having same Key(dst_ip, src_ip, dst_port,src_port)
BPF_TABLE("hash", struct Key, struct Leaf, sessions, 1024);
BPF_HASH(sessions, struct Key, struct Leaf, 1024);
/*eBPF program.
Filter IP and TCP packets, having payload not empty
......@@ -40,7 +40,7 @@ int http_filter(struct __sk_buff *skb) {
struct ethernet_t *ethernet = cursor_advance(cursor, sizeof(*ethernet));
//filter IP packets (ethernet type = 0x0800)
if (!(ethernet->type == 0x0800)) {
goto DROP;
goto DROP;
}
struct ip_t *ip = cursor_advance(cursor, sizeof(*ip));
......@@ -69,16 +69,16 @@ int http_filter(struct __sk_buff *skb) {
//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
//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 patload offset and length
payload_offset = ETH_HLEN + ip_header_length + tcp_header_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
......@@ -87,12 +87,13 @@ int http_filter(struct __sk_buff *skb) {
goto DROP;
}
//load firt 7 byte of payload into p (payload_array)
//load first 7 byte of payload into p (payload_array)
//direct access to skb not allowed
unsigned long p[7];
int i = 0;
int j = 0;
for (i = payload_offset ; i < (payload_offset + 7) ; i++) {
const int last_index = payload_offset + 7;
for (i = payload_offset ; i < last_index ; i++) {
p[j] = load_byte(skb , i);
j++;
}
......
......@@ -3,7 +3,7 @@
#Bertrone Matteo - Polytechnic of Turin
#November 2015
#
#eBPF application that parses HTTP packets
#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.
......@@ -38,7 +38,7 @@ def toHex(s):
if len(hv) == 1:
hv = '0'+hv
lst.append(hv)
return reduce(lambda x,y:x+y, lst)
#print str until CR+LF
......@@ -50,7 +50,7 @@ def printUntilCRLF(str):
return
print ("%c" % (str[k]), end = "")
print("")
return
return
#cleanup function
def cleanup():
......@@ -71,7 +71,7 @@ def cleanup():
del bpf_sessions[key]
except:
print("cleanup exception.")
return
return
#args
def usage():
......@@ -155,9 +155,9 @@ while 1:
#convert packet into bytearray
packet_bytearray = bytearray(packet_str)
#ethernet header length
ETH_HLEN = 14
ETH_HLEN = 14
#IP HEADER
#https://tools.ietf.org/html/rfc791
......@@ -166,18 +166,18 @@ while 1:
# |Version| IHL |Type of Service| Total Length |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
#
#IHL : Internet Header Length is the length of the internet header
#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,
#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
......@@ -189,18 +189,18 @@ while 1:
ip_src = int(toHex(ip_src_str),16)
ip_dst = int(toHex(ip_dst_str),16)
#TCP HEADER
#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
# 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.
#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
......@@ -209,17 +209,17 @@ while 1:
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))]
......@@ -238,14 +238,14 @@ while 1:
#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
#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.
else:
#url NOT entirely contained in first packet
#not found \r\n in payload.
#save current part of the payload_string in dictionary <key(ips,ipd,ports,portd),payload_string>
local_dictionary[binascii.hexlify(current_Key)] = payload_string
else:
......@@ -253,17 +253,17 @@ while 1:
#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
#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.
#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
#print HTTP GET/POST url
printUntilCRLF(prev_payload_string)
#clean bpf_sessions & local_dictionary
try:
......@@ -284,7 +284,7 @@ while 1:
except:
print ("error deleting from map or dict")
#update dictionary
local_dictionary[binascii.hexlify(current_Key)] = prev_payload_string
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
......
......@@ -2,7 +2,7 @@
#include <net/sock.h>
#include <bcc/proto.h>
#define IP_TCP 6
#define IP_TCP 6
#define ETH_HLEN 14
/*eBPF program.
......@@ -20,7 +20,7 @@ int http_filter(struct __sk_buff *skb) {
struct ethernet_t *ethernet = cursor_advance(cursor, sizeof(*ethernet));
//filter IP packets (ethernet type = 0x0800)
if (!(ethernet->type == 0x0800)) {
goto DROP;
goto DROP;
}
struct ip_t *ip = cursor_advance(cursor, sizeof(*ip));
......@@ -40,16 +40,16 @@ int http_filter(struct __sk_buff *skb) {
//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
//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 patload offset and length
payload_offset = ETH_HLEN + ip_header_length + tcp_header_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
......@@ -58,12 +58,13 @@ int http_filter(struct __sk_buff *skb) {
goto DROP;
}
//load firt 7 byte of payload into p (payload_array)
//load first 7 byte of payload into p (payload_array)
//direct access to skb not allowed
unsigned long p[7];
int i = 0;
int j = 0;
for (i = payload_offset ; i < (payload_offset + 7) ; i++) {
const int last_index = payload_offset + 7;
for (i = payload_offset ; i < last_index ; i++) {
p[j] = load_byte(skb , i);
j++;
}
......@@ -105,4 +106,4 @@ int http_filter(struct __sk_buff *skb) {
DROP:
return 0;
}
\ No newline at end of file
}
......@@ -3,7 +3,7 @@
#Bertrone Matteo - Polytechnic of Turin
#November 2015
#
#eBPF application that parses HTTP packets
#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.
......@@ -90,9 +90,9 @@ while 1:
#convert packet into bytearray
packet_bytearray = bytearray(packet_str)
#ethernet header length
ETH_HLEN = 14
ETH_HLEN = 14
#IP HEADER
#https://tools.ietf.org/html/rfc791
......@@ -101,34 +101,34 @@ while 1:
# |Version| IHL |Type of Service| Total Length |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
#
#IHL : Internet Header Length is the length of the internet header
#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,
#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
#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
# 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.
#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
......@@ -137,10 +137,10 @@ while 1:
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)
......
......@@ -7,7 +7,7 @@ struct ipkey {
u32 client_ip;
};
BPF_TABLE("hash", struct ipkey, int, learned_ips, 1024);
BPF_HASH(learned_ips, struct ipkey, int, 1024);
// trivial action
int pass(struct __sk_buff *skb) {
......@@ -57,7 +57,7 @@ int classify_neighbor(struct __sk_buff *skb) {
u32 sip = ip->src;
struct ipkey key = {.client_ip=sip};
int val = 1;
learned_ips.update(&key, &val);
learned_ips.insert(&key, &val);
goto EOP;
}
EOP:
......
......@@ -66,6 +66,7 @@ class Simulation(object):
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
......
#!/usr/bin/env 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 <uapi/linux/if_ether.h>
#include <uapi/linux/in6.h>
#include <uapi/linux/ipv6.h>
#include <uapi/linux/pkt_cls.h>
#include <uapi/linux/bpf.h>
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(skb_event.raw[22:38])
dst_ip = bytes(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: "ping -6 ff02::1%me"\n')
print("%-3s %-32s %-12s %-10s" % ("CPU", "SRC IP", "DST IP", "Magic"))
while True:
b.kprobe_poll()
finally:
if "me" in locals(): ipr.link("del", index=me)
......@@ -16,7 +16,7 @@ struct counters {
u64 rx_bytes;
};
BPF_TABLE("hash", struct ipkey, struct counters, stats, 1024);
BPF_HASH(stats, struct ipkey, struct counters, 1024);
BPF_TABLE("prog", int, int, parser, 10);
enum cb_index {
......
......@@ -12,10 +12,10 @@ struct ifindex_leaf_t {
};
// redirect based on mac -> out_ifindex (auto-learning)
BPF_TABLE("hash", int, struct ifindex_leaf_t, egress, 4096);
BPF_HASH(egress, int, struct ifindex_leaf_t, 4096);
// redirect based on mac -> out_ifindex (config-driven)
BPF_TABLE("hash", u64, struct ifindex_leaf_t, ingress, 4096);
BPF_HASH(ingress, u64, struct ifindex_leaf_t, 4096);
int handle_phys2virt(struct __sk_buff *skb) {
// only handle vlan packets
......
......@@ -12,11 +12,28 @@ import pyroute2
import time
import sys
if len(sys.argv) != 2:
print("Usage: {0} <ifdev>\n\ne.g.: {0} eth0".format(sys.argv[0]))
flags = 0
def usage():
print("Usage: {0} [-S] <ifdev>".format(sys.argv[0]))
print(" -S: use skb mode\n")
print("e.g.: {0} eth0\n".format(sys.argv[0]))
exit(1)
device = sys.argv[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
......@@ -116,7 +133,7 @@ int xdp_prog1(struct CTXTYPE *ctx) {
fn = b.load_func("xdp_prog1", mode)
if mode == BPF.XDP:
b.attach_xdp(device, fn)
b.attach_xdp(device, fn, flags)
else:
ip = pyroute2.IPRoute()
ipdb = pyroute2.IPDB(nl=ip)
......@@ -143,7 +160,7 @@ while 1:
break;
if mode == BPF.XDP:
b.remove_xdp(device)
b.remove_xdp(device, flags)
else:
ip.tc("del", "clsact", idx)
ipdb.release()
......@@ -37,7 +37,7 @@ b.attach_kprobe(event="sys_clone", fn_name="hello")
# define output data structure in Python
TASK_COMM_LEN = 16 # linux/sched.h
class Data(ct.Structure):
_fields_ = [("pid", ct.c_ulonglong),
_fields_ = [("pid", ct.c_uint),
("ts", ct.c_ulonglong),
("comm", ct.c_char * TASK_COMM_LEN)]
......
#!/usr/bin/env 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 <suchakra@shiftleft.io>
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))
Demonstrations 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.
......@@ -11,7 +11,7 @@
# Licensed under the Apache License, Version 2.0 (the "License")
from __future__ import print_function
from bcc import BPF, ProcessSymbols
from bcc import BPF
from time import sleep
import sys
......@@ -43,8 +43,6 @@ int alloc_enter(struct pt_regs *ctx, size_t size) {
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)
decoder = ProcessSymbols(pid)
# sleep until Ctrl-C
try:
sleep(99999999)
......@@ -57,4 +55,4 @@ 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):
print("\t%s (%x)" % (decoder.decode_addr(addr), addr))
print("\t%s" % b.sym(addr, pid, show_offset=True))
......@@ -46,7 +46,7 @@ if debug:
print(bpf_text)
# initialize BPF
b = BPF(text=bpf_text, usdt=u)
b = BPF(text=bpf_text, usdt_contexts=[u])
# header
print("%-18s %-16s %-6s %s" % ("TIME(s)", "COMM", "PID", "QUERY"))
......
......@@ -23,7 +23,7 @@ bpf_text = """
#include <uapi/linux/ptrace.h>
int do_trace(struct pt_regs *ctx) {
uint64_t addr;
char path[128];
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);
......@@ -39,7 +39,7 @@ if debug:
print(bpf_text)
# initialize BPF
b = BPF(text=bpf_text, usdt=u)
b = BPF(text=bpf_text, usdt_contexts=[u])
# header
print("%-18s %-16s %-6s %s" % ("TIME(s)", "COMM", "PID", "ARGS"))
......
......@@ -5,13 +5,6 @@
#
# 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")
#
......@@ -114,14 +107,14 @@ def print_event(cpu, data, size):
ts = time.time() - start_ts
if verbose:
print("%-18.9f %-12.12s %-6d %-3d %s" % (ts, event.comm, event.pid, cpu,
function))
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.ksymaddr(addr) if offset else b.ksym(addr)
print("\t%016x %s" % (addr, sym))
sym = b.ksym(addr, show_offset=offset)
print("\t%s" % sym)
print()
......
......@@ -8,12 +8,12 @@ to see how they were invoked. For example, tracing the submit_bio() call:
# ./stacksnoop submit_bio
TIME(s) SYSCALL
3592.838736000 submit_bio
ffffffff813bd961 submit_bio
ffffffff81257c12 submit_bh
ffffffff81301948 jbd2_journal_commit_transaction
ffffffff8130653a kjournald2
ffffffff810a2df8 kthread
ffffffff8183a122 ret_from_fork
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.
......@@ -28,12 +28,12 @@ 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
ffffffff813bd961 submit_bio
ffffffff81257c12 submit_bh
ffffffff81301948 jbd2_journal_commit_transaction
ffffffff8130653a kjournald2
ffffffff810a2df8 kthread
ffffffff8183a122 ret_from_fork
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).
......@@ -45,30 +45,30 @@ process:
# ./stacksnoop -v second_overflow
TIME(s) COMM PID CPU SYSCALL
3837.526433000 <idle> 0 1 second_overflow
ffffffff810fac41 second_overflow
ffffffff81102320 tick_do_update_jiffies64
ffffffff81102bf0 tick_irq_enter
ffffffff810882ac irq_enter
ffffffff8183c7df smp_apic_timer_interrupt
ffffffff8183aae2 apic_timer_interrupt
ffffffff81038f9e default_idle
ffffffff8103979f arch_cpu_idle
ffffffff810c69da default_idle_call
ffffffff810c6cd7 cpu_startup_entry
ffffffff81051cbe start_secondary
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 <idle> 0 1 second_overflow
ffffffff810fac41 second_overflow
ffffffff81102320 tick_do_update_jiffies64
ffffffff81102bf0 tick_irq_enter
ffffffff810882ac irq_enter
ffffffff8183c7df smp_apic_timer_interrupt
ffffffff8183aae2 apic_timer_interrupt
ffffffff81038f9e default_idle
ffffffff8103979f arch_cpu_idle
ffffffff810c69da default_idle_call
ffffffff810c6cd7 cpu_startup_entry
ffffffff81051cbe start_secondary
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().
......
......@@ -10,7 +10,7 @@
#
# Copyright (c) PLUMgrid, Inc.
# Licensed under the Apache License, Version 2.0 (the "License")
#
#
# Example output:
# $ sudo ./strlen_hist.py
# 22:12:52
......
......@@ -18,7 +18,7 @@ b = BPF(text="""
BPF_HASH(last);
void do_trace(struct pt_regs *ctx) {
int do_trace(struct pt_regs *ctx) {
u64 ts, *tsp, delta, key = 0;
// attempt to read stored timestamp
......@@ -35,6 +35,7 @@ void do_trace(struct pt_regs *ctx) {
// update stored timestamp
ts = bpf_ktime_get_ns();
last.update(&key, &ts);
return 0;
}
""")
......
......@@ -14,7 +14,7 @@ struct key_t {
u32 curr_pid;
};
// map_type, key_type, leaf_type, table_name, num_entry
BPF_TABLE("hash", struct key_t, u64, stats, 1024);
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;
......
......@@ -88,17 +88,17 @@ def inet_ntoa(addr):
# 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(" ")
except ValueError:
# Ignore messages from other tracers
continue
# Ignore messages from other tracers
if _tag != "trace_tcp4connect":
continue
# Read messages from kernel pipe
try:
(task, pid, cpu, flags, ts, msg) = b.trace_fields()
(_tag, saddr_hs, daddr_hs, dport_s) = msg.split(" ")
except ValueError:
# Ignore messages from other tracers
continue
# Ignore messages from other tracers
if _tag != "trace_tcp4connect":
continue
print("%-6d %-12.12s %-16s %-16s %-4s" % (pid, task,
inet_ntoa(int(saddr_hs, 16)),
......
......@@ -24,7 +24,7 @@ def cb(cpu, data, size):
prog = """
BPF_PERF_OUTPUT(events);
BPF_TABLE("array", int, u64, counters, 10);
BPF_ARRAY(counters, u64, 10);
int kprobe__sys_clone(void *ctx) {
struct {
u64 ts;
......
......@@ -38,7 +38,7 @@ int printarg(struct urandom_read_args *args) {
# load BPF program
b = BPF(text=bpf_text)
b.attach_tracepoint("random:urandom_read", "printarg")
b.attach_tracepoint(tp="random:urandom_read", fn_name="printarg")
# header
print("%-18s %-16s %-6s %s" % ("TIME(s)", "COMM", "PID", "GOTBITS"))
......
cmake_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)
#include <linux/blkdev.h>
#include <uapi/linux/ptrace.h>
/**
* @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;
}
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_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))
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)")
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.kprobe_poll()
Tested 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
```
cmake_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
)
// std
#include <algorithm>
#include <cstdlib>
#include <iostream>
#include <mutex>
#include <queue>
#include <random>
#include <sstream>
#include <string>
#include <thread>
// gnu-c
#include <sys/types.h>
#include <unistd.h>
// 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]
<< " <InputPrefix> <InputMinimum (1-50)> <InputMaximum (1-50)> <CallsPerSec (1-50)> <MinimumLatencyMs (1-50)> <MaximumLatencyMs (1-50)>"
<< 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::uint32_t>(std::max(1, std::min(50, std::atoi(argv[2]))));
}
if (argc > 3) {
inputMaximum = static_cast<std::uint32_t>(std::max(1, std::min(50, std::atoi(argv[3]))));
}
if (argc > 4) {
callsPerSec = static_cast<std::uint32_t>(std::max(1, std::min(50, std::atoi(argv[4]))));
}
if (argc > 5) {
minLatMs = static_cast<std::uint32_t>(std::max(1, std::min(50, std::atoi(argv[5]))));
}
if (argc > 6) {
maxLatMs = static_cast<std::uint32_t>(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<std::shared_future<OperationResponse>> responseQueue;
auto dequeueFuture = std::async(std::launch::async, [&]() {
while (true) {
bool empty = false;
{
std::lock_guard<std::mutex> 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<std::mutex> 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<std::mutex> 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;
}
cmake_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
)
This diff is collapsed.
# 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);
};
images/bcc_tracing_tools_2016.png

265 KB | W: | H:

images/bcc_tracing_tools_2016.png

264 KB | W: | H:

images/bcc_tracing_tools_2016.png
images/bcc_tracing_tools_2016.png
images/bcc_tracing_tools_2016.png
images/bcc_tracing_tools_2016.png
  • 2-up
  • Swipe
  • Onion skin
This diff is collapsed.
......@@ -65,7 +65,7 @@ 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.
Direction: R == read, W == write. This is a simplification.
.TP
MAJ
Major device number.
......
This diff is collapsed.
This diff is collapsed.
uobjnew.8
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -2,7 +2,7 @@
.SH NAME
ext4dist \- Summarize ext4 operation latency. Uses Linux eBPF/bcc.
.SH SYNOPSIS
.B ext4dist [\-h] [\-T] [\-N] [\-d] [interval] [count]
.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
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment