Skip to content

Commit

Permalink
[feature] #4083: Add pprof-rs support (#4250)
Browse files Browse the repository at this point in the history
Signed-off-by: Shanin Roman <[email protected]>
  • Loading branch information
Erigara authored Feb 6, 2024
1 parent e5ad44a commit eca8569
Show file tree
Hide file tree
Showing 11 changed files with 244 additions and 8 deletions.
10 changes: 9 additions & 1 deletion .github/workflows/iroha2-profiling-image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,13 @@ on:
default: profiling
IROHA2_RUSTFLAGS:
required: false
default: -C force-frame-pointers=on --cfg wasm_profiling
default: -C force-frame-pointers=on
IROHA2_FEATURES:
required: false
default: profiling
IROHA2_CARGOFLAGS:
required: false
default: -Z build-std

jobs:
registry:
Expand Down Expand Up @@ -51,6 +57,8 @@ jobs:
build-args: |
"PROFILE=${{ github.event.inputs.IROHA2_PROFILE }}"
"RUSTFLAGS=${{ github.event.inputs.IROHA2_RUSTFLAGS }}"
"FEATURES=${{ github.event.inputs.IROHA2_FEATURES }}"
"CARGOFLAGS=${{ github.event.inputs.IROHA2_CARGOFLAGS }}"
file: ${{ github.event.inputs.IROHA2_DOCKERFILE }}
# This context specification is required
context: .
15 changes: 12 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -272,18 +272,18 @@ tokio-console http://127.0.0.1:5555

To optimize performance it's useful to profile iroha.

To do that you should compile iroha with `profiling` profile and with `wasm_profiling` compilation flag:
To do that you should compile iroha with `profiling` profile and with `profiling` feature:

```bash
RUSTFLAGS="-C force-frame-pointers=on --cfg wasm_profiling" cargo build --profile profiling
RUSTFLAGS="-C force-frame-pointers=on" cargo +nightly -Z build-std build --target your-desired-target --profile profiling --features profiling
```

Then start iroha and attach profiler of your choice to the iroha pid.

Alternatively it's possible to build iroha inside docker with profiler support and profile iroha this way.

```bash
docker build -f Dockerfile.glibc --build-arg="PROFILE=profiling" --build-arg='RUSTFLAGS=-C force-frame-pointers=on --cfg wasm_profiling' -t iroha2:profiling .
docker build -f Dockerfile.glibc --build-arg="PROFILE=profiling" --build-arg='RUSTFLAGS=-C force-frame-pointers=on' --build-arg='FEATURES=profiling' --build-arg='CARGOFLAGS=-Z build-std' -t iroha2:profiling .
```

E.g. using perf (available only on linux):
Expand All @@ -303,6 +303,15 @@ It can be done by running:
cargo run --bin iroha_wasm_builder_cli -- build ./path/to/executor --outfile executor.wasm
```

With profiling feature enabled iroha exposes endpoint to scrap pprof profiles:

```bash
# profile iroha for 30 seconds and get protobuf profile
curl host:port/debug/pprof/profile?seconds=30 -o profile.pb
# analyze profile in browser (required installed go)
go tool pprof -web profile.pb
```

</details>

## Style Guides
Expand Down
104 changes: 103 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -256,4 +256,4 @@ lto = true

[profile.profiling]
inherits = "release"
debug = "line-tables-only"
debug = "limited"
4 changes: 3 additions & 1 deletion Dockerfile.glibc
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ WORKDIR /iroha
COPY . .
ARG PROFILE="deploy"
ARG RUSTFLAGS=""
RUN RUSTFLAGS="${RUSTFLAGS}" mold --run cargo build --target x86_64-unknown-linux-gnu --profile "${PROFILE}"
ARG FEATURES=""
ARG CARGOFLAGS=""
RUN RUSTFLAGS="${RUSTFLAGS}" mold --run cargo ${CARGOFLAGS} build --target x86_64-unknown-linux-gnu --profile "${PROFILE}" --features "${FEATURES}"

# final image
FROM debian:bookworm-slim
Expand Down
2 changes: 2 additions & 0 deletions config/src/torii.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ pub mod uri {
pub const SCHEMA: &str = "schema";
/// URI for getting the API version currently used
pub const API_VERSION: &str = "api_version";
/// URI for getting cpu profile
pub const PROFILE: &str = "debug/pprof/profile";
}

#[cfg(test)]
Expand Down
2 changes: 2 additions & 0 deletions core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ cli = []
dev-telemetry = ["telemetry", "iroha_telemetry/dev-telemetry"]
# Support Prometheus metrics. See https://prometheus.io/.
expensive-telemetry = ["iroha_telemetry/metric-instrumentation"]
# Profiler integration for wasmtime
profiling = []

[badges]
is-it-maintained-issue-resolution = { repository = "https://github.com/hyperledger/iroha" }
Expand Down
2 changes: 1 addition & 1 deletion core/src/smartcontracts/wasm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ fn create_config() -> Result<Config> {
.consume_fuel(true)
.cache_config_load_default()
.map_err(Error::Initialization)?;
#[cfg(wasm_profiling)]
#[cfg(feature = "profiling")]
{
config.profiler(wasmtime::ProfilingStrategy::PerfMap);
}
Expand Down
4 changes: 4 additions & 0 deletions torii/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ workspace = true
[features]
# Enables Telemetry (i.e. Status, Metrics, and API Version) endpoints
telemetry = ["iroha_telemetry", "iroha_core/telemetry", "serde_json"]
# Enables profiling endpoint
profiling = ["pprof"]
# Enables Data Model Schema endpoint
schema = ["iroha_schema_gen"]

Expand All @@ -46,3 +48,5 @@ serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true, optional = true }
async-trait = { workspace = true }
parity-scale-codec = { workspace = true, features = ["derive"] }
# TODO: switch to original crate once fix is merged (https://github.com/tikv/pprof-rs/pull/241)
pprof = { git = " https://github.com/Erigara/pprof-rs", branch = "fix_pointer_align", optional = true, default-features = false, features = ["protobuf-codec", "frame-pointer", "cpp"] }
29 changes: 29 additions & 0 deletions torii/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,30 @@ impl Torii {
let get_router = get_router.or(warp::path(uri::SCHEMA)
.and_then(|| async { Ok::<_, Infallible>(routing::handle_schema().await) }));

#[cfg(feature = "profiling")]
let get_router = {
// `warp` panics if there is `/` in the string given to the `warp::path` filter
// Path filter has to be boxed to have a single uniform type during iteration
let profile_router_path = uri::PROFILE
.split('/')
.skip_while(|p| p.is_empty())
.fold(warp::any().boxed(), |path_filter, path| {
path_filter.and(warp::path(path)).boxed()
});

let profiling_lock = std::sync::Arc::new(tokio::sync::Mutex::new(()));
get_router.or(profile_router_path
.and(warp::query::<routing::profiling::ProfileParams>())
.and_then(move |params| {
let profiling_lock = Arc::clone(&profiling_lock);
async move {
Ok::<_, Infallible>(
routing::profiling::handle_profile(params, profiling_lock).await,
)
}
}))
};

let post_router = warp::post()
.and(
endpoint4(
Expand Down Expand Up @@ -279,6 +303,9 @@ pub enum Error {
#[cfg(feature = "telemetry")]
/// Failed to get Prometheus metrics
Prometheus(#[source] eyre::Report),
#[cfg(feature = "profiling")]
/// Failed to get pprof profile
Pprof(#[source] eyre::Report),
#[cfg(feature = "telemetry")]
/// Failed to get status
StatusFailure(#[source] eyre::Report),
Expand Down Expand Up @@ -315,6 +342,8 @@ impl Error {
},
#[cfg(feature = "telemetry")]
Prometheus(_) | StatusFailure(_) => StatusCode::INTERNAL_SERVER_ERROR,
#[cfg(feature = "profiling")]
Pprof(_) => StatusCode::INTERNAL_SERVER_ERROR,
ConfigurationFailure(_) => StatusCode::INTERNAL_SERVER_ERROR,
}
}
Expand Down
Loading

0 comments on commit eca8569

Please sign in to comment.