Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions Cargo.lock

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

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ key-wallet-manager = { git = "https://github.com/dashpay/rust-dashcore", rev = "
dash-network = { git = "https://github.com/dashpay/rust-dashcore", rev = "981e97f1015960ae5d277afdabcba1cbbc0b3a63" }
dashcore-rpc = { git = "https://github.com/dashpay/rust-dashcore", rev = "981e97f1015960ae5d277afdabcba1cbbc0b3a63" }

tokio-metrics = "0.5"

# Size-tuned profile for the iOS `rs-unified-sdk-ffi` staticlib, which
# otherwise ships huge. Inherits `release` and is ONLY used by the iOS
# build (`build_ios.sh --profile release`)
Expand Down
2 changes: 2 additions & 0 deletions packages/rs-platform-wallet-ffi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ once_cell = "1.19"
parking_lot = { version = "0.12", features = ["send_guard"] }
lazy_static = "1.4"
tokio = { version = "1", features = ["rt-multi-thread"] }
tokio-metrics = { workspace = true, optional = true }

# Core dependencies (for Network type)
dashcore = { workspace = true }
Expand Down Expand Up @@ -65,6 +66,7 @@ cbindgen = "0.27"
[features]
default = []
mocks = []
tokio-metrics = ["dep:tokio-metrics", "tokio/time"]
# Opt-in Orchard / shielded support. Pulls in the heavy
# `grovedb-commitment-tree` + `zip32` dependencies via
# `platform-wallet/shielded`. Builds that don't enable this feature
Expand Down
43 changes: 35 additions & 8 deletions packages/rs-platform-wallet-ffi/src/logging.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,15 @@ fn enable_file_logging(log_level: &str, path: &Path) -> bool {
let Some(f_sdk) = open_file(path.join("dash_sdk").join("run.log")) else {
return false;
};
let Some(f_sdk_metrics) = open_file(path.join("dash_sdk").join("metrics.log")) else {
return false;
};
let Some(f_pw) = open_file(path.join("platform_wallet").join("run.log")) else {
return false;
};
let Some(f_pw_metrics) = open_file(path.join("platform_wallet").join("metrics.log")) else {
return false;
};
let Some(f_spv) = open_file(path.join("dash_spv").join("run.log")) else {
return false;
};
Expand All @@ -57,14 +63,29 @@ fn enable_file_logging(log_level: &str, path: &Path) -> bool {
.with_writer(Mutex::new(f_sdk))
.with_ansi(false)
.with_filter(tracing_subscriber::EnvFilter::new(format!(
"dash_sdk={log_level},rs_sdk_ffi={log_level}"
"dash_sdk={log_level},rs_sdk_ffi={log_level},rs_sdk_ffi::metrics=off"
)));

let l_sdk_metrics = tracing_subscriber::fmt::layer()
.with_writer(Mutex::new(f_sdk_metrics))
.with_ansi(false)
.with_filter(tracing_subscriber::EnvFilter::new(format!(
"rs_sdk_ffi::metrics={log_level}"
)));

let l_pw = tracing_subscriber::fmt::layer()
.with_writer(Mutex::new(f_pw))
.with_ansi(false)
.with_filter(tracing_subscriber::EnvFilter::new(format!(
"platform_wallet={log_level},platform_wallet_ffi={log_level}"
"platform_wallet={log_level},platform_wallet_ffi={log_level},\
platform_wallet_ffi::metrics=off"
)));

let l_pw_metrics = tracing_subscriber::fmt::layer()
.with_writer(Mutex::new(f_pw_metrics))
.with_ansi(false)
.with_filter(tracing_subscriber::EnvFilter::new(format!(
"platform_wallet_ffi::metrics={log_level}"
)));

let l_spv = tracing_subscriber::fmt::layer()
Expand Down Expand Up @@ -95,13 +116,19 @@ fn enable_file_logging(log_level: &str, path: &Path) -> bool {

let stdout_layer = tracing_subscriber::fmt::layer().with_filter(broad_env_filter(log_level));

let layers: Vec<Box<dyn Layer<tracing_subscriber::Registry> + Send + Sync>> = vec![
stdout_layer.boxed(),
l_sdk.boxed(),
l_sdk_metrics.boxed(),
l_pw.boxed(),
l_pw_metrics.boxed(),
l_spv.boxed(),
l_kw.boxed(),
l_grpc.boxed(),
];

if tracing_subscriber::registry()
.with(stdout_layer)
.with(l_sdk)
.with(l_pw)
.with(l_spv)
.with(l_kw)
.with(l_grpc)
.with(layers)
.try_init()
.is_err()
{
Expand Down
39 changes: 37 additions & 2 deletions packages/rs-platform-wallet-ffi/src/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,16 @@ const WORKER_STACK_BYTES: usize = 8 * 1024 * 1024;
/// here, rather than the (small) calling thread.
pub(crate) fn runtime() -> &'static tokio::runtime::Runtime {
static RT: once_cell::sync::Lazy<tokio::runtime::Runtime> = once_cell::sync::Lazy::new(|| {
tokio::runtime::Builder::new_multi_thread()
let rt = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.thread_stack_size(WORKER_STACK_BYTES)
.build()
.expect("Failed to create tokio runtime for platform-wallet-ffi")
.expect("Failed to create tokio runtime for platform-wallet-ffi");

#[cfg(feature = "tokio-metrics")]
metrics::spawn_sampler(&rt);

rt
});
&RT
}
Expand All @@ -54,3 +59,33 @@ where
let rt = runtime();
rt.block_on(async move { rt.spawn(future).await.expect("tokio worker panicked") })
}

#[cfg(feature = "tokio-metrics")]
mod metrics {
use std::time::Duration;

pub(super) fn spawn_sampler(rt: &tokio::runtime::Runtime) {
let runtime_monitor = tokio_metrics::RuntimeMonitor::new(rt.handle());
let mut rt_intervals = runtime_monitor.intervals();

rt.spawn(async move {
loop {
tokio::time::sleep(Duration::from_secs(1)).await;
let Some(r) = rt_intervals.next() else { break };

tracing::info!(
target: "platform_wallet_ffi::metrics",
workers = r.workers_count,
live_tasks = r.live_tasks_count,
busy_ratio = r.busy_ratio(),
mean_poll_us = r.mean_poll_duration.as_micros() as u64,
mean_polls_per_park = r.mean_polls_per_park(),
steals = r.total_steal_count,
global_queue_depth = r.global_queue_depth,
local_queue_depth = r.total_local_queue_depth,
overflow = r.total_overflow_count,
);
}
});
Comment thread
ZocoLini marked this conversation as resolved.
}
}
2 changes: 2 additions & 0 deletions packages/rs-sdk-ffi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ bincode = { version = "=2.0.1", features = ["serde"] }

# Async runtime
tokio = { version = "1.41", features = ["rt-multi-thread", "macros", "sync", "time"] }
tokio-metrics = { workspace = true, optional = true }

# Error handling
thiserror = "2.0"
Expand Down Expand Up @@ -92,6 +93,7 @@ log = "0.4"

[features]
default = []
tokio-metrics = ["dep:tokio-metrics"]

# Optional mocks for development/testing; maps to dash-sdk's mocks
mocks = ["dash-sdk/mocks"]
Expand Down
97 changes: 97 additions & 0 deletions packages/rs-sdk-ffi/src/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@

use std::future::Future;
use std::ops::Deref;
#[cfg(feature = "tokio-metrics")]
use std::sync::atomic::{AtomicUsize, Ordering};
Comment thread
ZocoLini marked this conversation as resolved.
use tokio::runtime::{Builder, Runtime};

/// Stack size for the OS thread that drives blocking FFI calls.
Expand Down Expand Up @@ -61,6 +63,20 @@ impl BigStackRuntime {
.thread_stack_size(WORKER_STACK_SIZE)
.enable_all()
.build()?;

#[cfg(feature = "tokio-metrics")]
static COUNTER: AtomicUsize = AtomicUsize::new(0);

#[cfg(feature = "tokio-metrics")]
metrics::spawn_sampler(
&runtime,
format!(
"dash-sdk-ffi-shared-runtime-{}",
COUNTER.fetch_add(1, Ordering::SeqCst)
)
.as_str(),
);

Ok(BigStackRuntime(runtime))
}

Expand All @@ -71,6 +87,20 @@ impl BigStackRuntime {
.thread_stack_size(WORKER_STACK_SIZE)
.enable_all()
.build()?;

#[cfg(feature = "tokio-metrics")]
static COUNTER: AtomicUsize = AtomicUsize::new(0);

#[cfg(feature = "tokio-metrics")]
metrics::spawn_sampler(
&runtime,
format!(
"dash-sdk-ffi-isolated-runtime-{}",
COUNTER.fetch_add(1, Ordering::SeqCst)
)
.as_str(),
);

Ok(BigStackRuntime(runtime))
}

Expand All @@ -86,6 +116,9 @@ impl BigStackRuntime {
where
F: Future,
{
#[cfg(feature = "tokio-metrics")]
let _block_on_guard = metrics::BlockOnGuard::new();

// `std::thread::Builder::spawn_scoped` is the only API that both (a) lets
// the worker borrow non-`'static` data — the FFI call's references — and
// (b) lets us set the stack size. It requires the moved-in closure (and
Expand Down Expand Up @@ -144,6 +177,70 @@ impl Deref for BigStackRuntime {
}
}

#[cfg(feature = "tokio-metrics")]
mod metrics {
use std::sync::atomic::{AtomicUsize, Ordering};
use std::time::{Duration, Instant};

static BLOCK_ON_IN_FLIGHT: AtomicUsize = AtomicUsize::new(0);

pub(super) struct BlockOnGuard {
start: Instant,
in_flight: usize,
}

impl BlockOnGuard {
pub(super) fn new() -> Self {
let in_flight = BLOCK_ON_IN_FLIGHT.fetch_add(1, Ordering::Relaxed) + 1;
Self {
start: Instant::now(),
in_flight,
}
}
}

impl Drop for BlockOnGuard {
fn drop(&mut self) {
let elapsed_us = self.start.elapsed().as_micros() as u64;
BLOCK_ON_IN_FLIGHT.fetch_sub(1, Ordering::Relaxed);
tracing::info!(
target: "rs_sdk_ffi::metrics",
kind = "block_on",
elapsed_us,
in_flight = self.in_flight,
);
}
}
Comment thread
ZocoLini marked this conversation as resolved.

pub(super) fn spawn_sampler(rt: &tokio::runtime::Runtime, runtime_name: &str) {
let runtime_monitor = tokio_metrics::RuntimeMonitor::new(rt.handle());
let mut rt_intervals = runtime_monitor.intervals();

let runtime_name = runtime_name.to_string();

rt.spawn(async move {
loop {
tokio::time::sleep(Duration::from_secs(1)).await;
let Some(r) = rt_intervals.next() else { break };

tracing::info!(
target: "rs_sdk_ffi::metrics",
runtime = %runtime_name,
workers = r.workers_count,
live_tasks = r.live_tasks_count,
busy_ratio = r.busy_ratio(),
mean_poll_us = r.mean_poll_duration.as_micros() as u64,
mean_polls_per_park = r.mean_polls_per_park(),
steals = r.total_steal_count,
global_queue_depth = r.global_queue_depth,
local_queue_depth = r.total_local_queue_depth,
overflow = r.total_overflow_count,
);
}
});
Comment thread
ZocoLini marked this conversation as resolved.
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
6 changes: 5 additions & 1 deletion packages/rs-unified-sdk-ffi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,8 @@ default = []
# Forwards to `platform-wallet-ffi/shielded`. Pulls Orchard / ZK
# shielded sync support into the unified iOS framework. Off by
# default so the framework's transparent surface stays slim.
shielded = ["platform-wallet-ffi/shielded"]
shielded = ["platform-wallet-ffi/shielded"]
tokio-metrics = [
"platform-wallet-ffi/tokio-metrics",
"rs-sdk-ffi/tokio-metrics",
]
5 changes: 5 additions & 0 deletions packages/swift-sdk/build_ios.sh
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,11 @@ EOF
# the bundled SDK exposes the platform-wallet shielded FFI.
CARGO_FEATURES="shielded"

if [ "$PROFILE" = "dev-ios" ]; then
CARGO_FEATURES="$CARGO_FEATURES tokio-metrics"
log_info " → tokio-metrics enabled (dev profile)"
fi

# iOS device
if $BUILD_IOS; then
IOS_TARGET="aarch64-apple-ios"
Expand Down
Loading