-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Kemal Akkoyun <kakkoyun@gmail.com>
- Loading branch information
Showing
41 changed files
with
149,117 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
--- | ||
BasedOnStyle: Google | ||
AllowShortIfStatementsOnASingleLine: false | ||
AllowShortLoopsOnASingleLine: false | ||
ColumnLimit: 120 | ||
IndentWidth: 4 | ||
SortIncludes: false |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
target/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
src/bpf/vmlinux.h linguist-generated |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
name: CI | ||
|
||
on: | ||
push: | ||
branches: [main] | ||
pull_request: | ||
branches: [main] | ||
|
||
jobs: | ||
build: | ||
name: Build | ||
runs-on: ubuntu-22.04 | ||
strategy: | ||
fail-fast: false | ||
matrix: | ||
rust: [stable] | ||
steps: | ||
- uses: actions/checkout@v3 | ||
- uses: dtolnay/rust-toolchain@1.71.0 | ||
with: | ||
toolchain: ${{matrix.rust}} | ||
components: rust-src, rustfmt | ||
- name: Install build system dependencies | ||
run: | | ||
export DEBIAN_FRONTEND=noninteractive | ||
sudo apt-get -y install --no-install-recommends \ | ||
curl \ | ||
ca-certificates \ | ||
clang \ | ||
make \ | ||
pkg-config \ | ||
libelf-dev \ | ||
zlib1g-dev | ||
- name: Build | ||
run: | | ||
export RUSTFLAGS='-L /usr/lib/x86_64-linux-gnu' | ||
cargo build | ||
- name: Static build | ||
run: | | ||
export RUSTFLAGS='-L /usr/lib/x86_64-linux-gnu -C target-feature=+crt-static' | ||
cargo build --target x86_64-unknown-linux-gnu | ||
lint: | ||
name: Lint | ||
runs-on: ubuntu-22.04 | ||
strategy: | ||
fail-fast: false | ||
matrix: | ||
rust: [stable] | ||
steps: | ||
- uses: actions/checkout@v3 | ||
- uses: dtolnay/rust-toolchain@1.71.0 | ||
with: | ||
toolchain: ${{matrix.rust}} | ||
components: rust-src, rustfmt | ||
- name: Run cargo fmt | ||
run: | | ||
# These files are generated at build time, so some rustfmt versions | ||
# fail with Error writing files: failed to resolve mod `bpf` if it | ||
# does not exist | ||
touch src/bpf/py-perf.rs | ||
touch src/bpf/features.rs | ||
cargo fmt | ||
git diff --exit-code | ||
clippy: | ||
name: Clippy | ||
runs-on: ubuntu-22.04 | ||
steps: | ||
- uses: actions/checkout@v3 | ||
- uses: dtolnay/rust-toolchain@1.71.0 | ||
with: | ||
components: rust-src, clippy, rustfmt | ||
- name: Install build system dependencies | ||
run: | | ||
export DEBIAN_FRONTEND=noninteractive | ||
sudo apt-get -y install --no-install-recommends \ | ||
curl \ | ||
ca-certificates \ | ||
clang \ | ||
make \ | ||
pkg-config \ | ||
libelf-dev \ | ||
zlib1g-dev | ||
- name: Run clippy | ||
run: | | ||
export RUSTFLAGS='-L /usr/lib/x86_64-linux-gnu' | ||
cargo clippy -- -Dclippy::all | ||
test: | ||
name: Test | ||
runs-on: ubuntu-22.04 | ||
strategy: | ||
fail-fast: false | ||
matrix: | ||
rust: [stable] | ||
steps: | ||
- uses: actions/checkout@v3 | ||
- uses: dtolnay/rust-toolchain@1.71.0 | ||
with: | ||
toolchain: ${{matrix.rust}} | ||
components: rust-src, rustfmt | ||
- name: Install build system dependencies | ||
run: | | ||
export DEBIAN_FRONTEND=noninteractive | ||
sudo apt-get -y install --no-install-recommends \ | ||
curl \ | ||
ca-certificates \ | ||
clang \ | ||
make \ | ||
pkg-config \ | ||
libelf-dev \ | ||
zlib1g-dev | ||
- name: Run unittests | ||
run: | | ||
export RUSTFLAGS='-L /usr/lib/x86_64-linux-gnu' | ||
export RUST_BACKTRACE=1 | ||
cargo test -- --skip py-perf::tests | ||
- name: Install podman | ||
run: sudo apt-get -y install --no-install-recommends podman | ||
- name: Pull Ruby containers | ||
run: tools/pull_ruby_images | ||
- name: Run integration tests | ||
run: | | ||
export RUSTFLAGS='-L /usr/lib/x86_64-linux-gnu' | ||
export RUST_BACKTRACE=1 | ||
# Running only 3.1.2 for a bit, will enable the rest once we make sure | ||
# that things are looking good | ||
cargo test -- py-perf::tests::py-perf_test_3_1_2 --nocapture |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
[package] | ||
name = "py-perf" | ||
description = "A Proof-of-concept low-overhead sampling CPU profiler written in Rust for Python implemented using eBPF." | ||
version = "0.1.0" | ||
edition = "2021" | ||
repository = "https://github.com/kakkoyun/py-perf" | ||
authors = ["Kemal Akkoyun <kakkoyun@gmail.com>"] | ||
keywords = ["bpf", "ebpf", "python", "CPython", "profiler"] | ||
license = "Apache-2.0" | ||
categories = ["development-tools", "profiling", "performance"] | ||
readme = "README.md" | ||
|
||
[profile.release] | ||
lto = true | ||
|
||
[dependencies] | ||
anyhow = { version = "1.0", features = ["backtrace"] } | ||
chrono = "0.4" | ||
clap = { version = "4.3", features = ["derive"] } | ||
crossbeam = "0.8.2" | ||
ctrlc = "3.4" | ||
env_logger = "0.10" | ||
errno = "0.3" | ||
goblin = "0.7" | ||
humantime = "2" | ||
inferno = "0.11" | ||
libbpf-rs = { version = "0.21", features = ["static"] } | ||
libc = "0.2" | ||
log = "0.4" | ||
nix = "0.26" | ||
num_cpus = "1.16" | ||
perf-event-open-sys = "4.0" | ||
plain = "0.2.3" | ||
# pprof = { path = "../../Sandbox/Profiling/pprof-rs", features = [ | ||
pprof = { git = "ssh://git@github.com/kakkoyun/py-spy.git", features = [ | ||
"flamegraph", | ||
"inferno", | ||
"protobuf", | ||
"protobuf-codec", | ||
] } | ||
proc-maps = "0.3" | ||
# py-spy = { path = "../../Sandbox/Profilers/py-spy" } | ||
# TODO(kakkoyun): Send a patch to upstream. | ||
py-spy = { git = "ssh://git@github.com/kakkoyun/py-spy.git" } | ||
remoteprocess = { version = "0.4.12", features = ["unwind"] } | ||
serde = { version = "1.0", features = ["derive"] } | ||
serde_json = "1.0" | ||
serde_yaml = "0.9" | ||
thiserror = "1.0" | ||
time = { version = "0.3.24", features = [ | ||
"formatting", | ||
"local-offset", | ||
"macros", | ||
] } | ||
|
||
[build-dependencies] | ||
bindgen = "0.66" | ||
libbpf-cargo = "0.21" | ||
|
||
[workspace] | ||
members = [".", "xtask"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
# TODO(kakkoyun): DRY this file. | ||
|
||
target/static-libs: | ||
@echo "Building static-libs" | ||
./scripts/download_and_build_static_libs.sh | ||
|
||
target/debug/deps/libelf.a: target/static-libs | ||
mkdir -p target/debug/deps | ||
cp target/static-libs/libelf.a target/debug/deps/libelf.a | ||
|
||
target/release/deps/libelf.a: target/static-libs | ||
mkdir -p target/release/deps | ||
cp target/static-libs/libelf.a target/release/deps/libelf.a | ||
|
||
target/debug/deps/libz.a: target/static-libs | ||
mkdir -p target/debug/deps | ||
cp target/static-libs/libz.a target/debug/deps/libz.a | ||
|
||
target/release/deps/libz.a: target/static-libs | ||
mkdir -p target/release/deps | ||
cp target/static-libs/libz.a target/release/deps/libz.a | ||
|
||
deps: target/debug/deps/libelf.a target/debug/deps/libz.a target/release/deps/libelf.a target/release/deps/libz.a | ||
mkdir -p out/ruby_versions | ||
mkdir -p out/python_versions | ||
|
||
.PHONY: build | ||
build: target/debug/deps/libelf.a target/debug/deps/libz.a | ||
cargo build | ||
|
||
.PHONY: release-build | ||
release-build: target/release/deps/libelf.a target/release/deps/libz.a | ||
cargo build --release | ||
|
||
.PHONY: clean | ||
clean: | ||
rm -rf target |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,113 @@ | ||
[![wakatime](https://wakatime.com/badge/user/c03c2c3a-0328-4e74-ba79-1ce0eb43a4f8/project/6de0edd3-d3d9-48b1-8f9e-e019fc7b42f1.svg)](https://wakatime.com/badge/user/c03c2c3a-0328-4e74-ba79-1ce0eb43a4f8/project/6de0edd3-d3d9-48b1-8f9e-e019fc7b42f1) | ||
|
||
# py-perf | ||
[Proof-of-concept] Low-overhead sampling profiler for Python implemented using eBPF | ||
|
||
A Proof-of-concept low-overhead sampling CPU profiler written in Rust for Python implemented using eBPF. | ||
It is heavily "influenced" by [rbperf](https://github.com/javierhonduco/rbperf) and [py-spy](https://github.com/benfred/py-spy). | ||
|
||
> IT IS NOT READY FOR PRODUCTION USE AND IT IS NOT INTENDED TO BE A REPLACEMENT FOR EXISTING TOOLS. | ||
> If you are looking for a production-ready tool, please check out [parca-agent](https://github.com/parca-dev/parca-agent) instead. | ||
## Features | ||
|
||
The main goals for `py-perf` are: | ||
|
||
- On-CPU profiling support | ||
- Low overhead | ||
- Profiled processes don't have to be restarted or modified in any way | ||
|
||
## Installation | ||
|
||
The latest release is available [here](https://github.com/kakkoyun/py-perf/releases/latest). | ||
|
||
## Usage | ||
|
||
### CPU sampling | ||
|
||
```shell | ||
sudo py-perf record --pid `pidof python` cpu | ||
``` | ||
|
||
Some debug information will be printed, and a flame graph called `py-perf_flame_$date` will be written to disk 🎉 | ||
|
||
## Supported Python versions | ||
|
||
The currently supported Python (CPython) versions: | ||
|
||
- **2.7**: 2.7.x | ||
- **3.x**: 3.3.x, 3.5.x, 3.6.x, 3.7.x, 3.8.x, 3.9.x, 3.10.x, 3.11.x | ||
|
||
## Supported kernels | ||
|
||
Linux kernel 4.18 is the minimum required version but 5.x and greater is recommended. | ||
|
||
## Building | ||
|
||
To build `py-perf` you would need a modern Linux machine with: | ||
|
||
- The Rust toolchain | ||
- `clang` to compile the BPF code | ||
- `elfutils` and `zlib` installed | ||
- `make` and `pkg-config` to build libbpf | ||
|
||
Once the dependencies are installed: | ||
|
||
```shell | ||
# As we are statically linking elfutils and zlib, we have to tell Rustc | ||
# where are they located. On my Ubuntu system they are under | ||
$ export RUSTFLAGS='-L /usr/lib/x86_64-linux-gnu' | ||
$ cargo build [--release] | ||
``` | ||
|
||
The built binary can be found under `target/(debug|release)/py-perf`. | ||
|
||
## Developing and troubleshooting | ||
|
||
Debug logs can be enabled with `RUST_LOG=debug`. The info subcommand, `py-perf info` shows the supported BPF features as well as other supported details. | ||
|
||
## Stability | ||
|
||
`py-perf` is in active development and the CLI and APIs might change any time. | ||
|
||
## Bugs | ||
|
||
If you encounter any bugs, feel free to open an issue on py-perf's [repo](https://github.com/kakkoyun/py-perf). | ||
|
||
## Acknowledgments | ||
|
||
`py-perf` wouldn't be possible without all the open-source projects that we benefit from, such as [Rust](https://github.com/rust-lang), [rbperf](https://github.com/javierhonduco/rbperf), [py-spy](https://github.com/benfred/py-spy) and all the superb crates we use in this project, Python, the BPF ecosystem, and many others! | ||
|
||
## License | ||
|
||
User-space code: Apache 2 | ||
|
||
Kernel-space code (eBPF profiler): GNU General Public License, version 2 | ||
|
||
#### TODO | ||
|
||
- TODO(kakkoyun): Add sections from parca-agent! | ||
- TODO(kakkoyun): Add reference to bcc, bcc/granulate and linux/tool examples from facebook. | ||
|
||
## Features: | ||
|
||
- Supports profiling Python processes running in Docker containers. Tested using official Python | ||
Docker images (`python:X.Y`). | ||
- Supports glibc- and musl-based environments. | ||
- Supports Python compiled in both PIE and non-PIE configurations. | ||
- Supports Python running standalone and as a library (linked with `libpythonX.Y`). | ||
|
||
## Limitations: | ||
|
||
- Architecture: x86_64. | ||
- Linux kernel version: oldest version tested is 4.14. Versions 4.11-4.14 may work. Required for | ||
`bpf_probe_read_str`. | ||
- BCC version: using BCC nightly is recommended. v0.17 is known to work. | ||
- Clang/LLVM: at least version 9. | ||
|
||
## Overview | ||
|
||
PyPerf uses Linux's perf events subsystem to gather stack samples of running Python interpreters at | ||
a constant interval. Instead of capturing native execution stacks, PyPerf reads the information | ||
stored by the Python interpreter regarding the current state of execution. Unlike many existing | ||
tools however, the memory of the process is read from a kernel context. The advantages of this | ||
approach are mainly reduced system overhead and no intervention with the program being profiled. |
Oops, something went wrong.