Procházet zdrojové kódy

darkirc: Log memory usage

x před 1 měsícem
rodič
revize
c2b87d26de

+ 3 - 0
bin/darkirc/src/crypto/rln.rs

@@ -21,6 +21,7 @@ use darkfi::{
         rln::{epoch_of, hash_event, Blob, RegistrationAttestation, RLN2_SIGNAL_ZKBIN},
         Event, EventGraphPtr,
     },
+    util::memory::log_memory,
     zk::{
         halo2::{Field, Value},
         Proof, Witness, ZkCircuit,
@@ -221,6 +222,7 @@ impl RlnIdentity {
             internal_nullifier,
         ];
         let circuit = ZkCircuit::new(witnesses, &zkbin);
+        log_memory("before local signal proving");
         let pk = eg.zk_keys.load_signal_pk()?;
 
         info!(
@@ -229,6 +231,7 @@ impl RlnIdentity {
             event.id(),
         );
         let proof = Proof::create(&pk, &[circuit], &pi, &mut OsRng)?;
+        log_memory("after local signal proving");
 
         Ok(Blob {
             proof,

+ 3 - 0
bin/darkirc/src/irc/services/nickserv.rs

@@ -63,6 +63,7 @@ use darkfi::{
         rln::{create_slash_proof, RLNNode, SlashBlob, GENESIS_USER_MSG_LIMIT},
         Event,
     },
+    util::memory::log_memory,
     Result,
 };
 use darkfi_sdk::{crypto::pasta_prelude::PrimeField, pasta::pallas};
@@ -800,11 +801,13 @@ impl NickServ {
         // the actual proof generation does not need the lock, but
         // the API takes &mut so we hold it for the whole call.
         let identity_secret_hash = identity.identity_secret_hash();
+        log_memory("before slash proving");
         let slash_pk = evgr.zk_keys.load_slash_pk()?;
         let (proof, root) = {
             let mut id_state = evgr.identity_state.write().await;
             create_slash_proof(identity_secret_hash, &mut id_state, &slash_pk)?
         };
+        log_memory("after slash proving");
 
         let slash_blob = SlashBlob { proof, identity_secret_hash, merkle_root: root };
         let blob_bytes = serialize_async(&slash_blob).await;

+ 8 - 2
bin/darkirc/src/main.rs

@@ -37,7 +37,10 @@ use darkfi::{
         util::JsonValue,
     },
     system::{sleep, StoppableTask, Subscription},
-    util::path::{expand_path, get_config_path},
+    util::{
+        memory::log_memory,
+        path::{expand_path, get_config_path},
+    },
     Error, Result,
 };
 use darkfi_sdk::crypto::pasta_prelude::PrimeField;
@@ -429,6 +432,7 @@ pub const DARKIRC_GENESIS_COMMITMENTS_REPR: &[[u8; 32]] = &[
             return Err(e.into());
         }
     };
+    log_memory("after sled open");
     let p2p_settings: darkfi::net::Settings =
         (env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"), args.net).try_into()?;
     let p2p = match P2p::new(p2p_settings, ex.clone()).await {
@@ -462,6 +466,7 @@ pub const DARKIRC_GENESIS_COMMITMENTS_REPR: &[[u8; 32]] = &[
             return Err(e);
         }
     };
+    log_memory("after EventGraph construction");
 
     // The prune task is only spawned when `hours_rotation > 0`. We
     // require rotation here, so the unwrap is safe.
@@ -748,7 +753,8 @@ async fn sync_task(
             info!("Syncing static DAG");
             match event_graph.static_sync().await {
                 Ok(()) => {
-                    info!("Static synced successfully")
+                    info!("Static synced successfully");
+                    log_memory("after static sync");
                 }
                 Err(e) => {
                     error!("Failed syncing static graph: {e}");

+ 3 - 0
src/event_graph/mod.rs

@@ -44,6 +44,7 @@ use url::Url;
 use crate::{
     net::{channel::Channel, P2pPtr},
     system::{msleep, Publisher, PublisherPtr, StoppableTask, StoppableTaskPtr, Subscription},
+    util::memory::log_memory,
     Error, Result,
 };
 
@@ -763,6 +764,7 @@ impl EventGraph {
     ) -> Result<EventGraphPtr> {
         config.validate()?;
         let zk_keys = Arc::new(ZkKeys::build_and_load(&sled_db)?);
+        log_memory("after RLN key initialization");
         Self::with_zk_keys(p2p, sled_db, datastore, replay_mode, config, zk_keys, ex).await
     }
 
@@ -859,6 +861,7 @@ impl EventGraph {
         // static DAG authoritative for the current identity tree.
         if config.hours_rotation > 0 {
             self_.bootstrap_genesis_identities().await?;
+            log_memory("after genesis identity bootstrap");
         }
 
         self_.audit_static_blobs().await?;

+ 154 - 0
src/util/memory.rs

@@ -0,0 +1,154 @@
+/* This file is part of DarkFi (https://dark.fi)
+ *
+ * Copyright (C) 2020-2026 Dyne.org foundation
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+//! Process memory telemetry helpers.
+
+use std::fmt;
+
+use super::logger::verbose;
+
+/// Best-effort process memory snapshot for the current platform.
+#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
+pub struct MemorySnapshot {
+    /// Current resident set size in bytes.
+    pub rss_bytes: Option<u64>,
+    /// Peak resident set size in bytes.
+    pub peak_rss_bytes: Option<u64>,
+    /// Apple physical footprint in bytes, when available.
+    pub physical_footprint_bytes: Option<u64>,
+}
+
+/// Return current process memory counters without allocating large state.
+pub fn memory_snapshot() -> MemorySnapshot {
+    platform_memory_snapshot()
+}
+
+/// Log current process memory counters with a stable tag and label.
+pub fn log_memory(label: &str) {
+    let snapshot = memory_snapshot();
+    verbose!(
+        target: "darkfi::memory",
+        "[MEMORY] {label}: rss={} peak_rss={} physical_footprint={}",
+        Bytes(snapshot.rss_bytes),
+        Bytes(snapshot.peak_rss_bytes),
+        Bytes(snapshot.physical_footprint_bytes),
+    );
+}
+
+struct Bytes(Option<u64>);
+
+impl fmt::Display for Bytes {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        let Some(bytes) = self.0 else { return f.write_str("unknown") };
+
+        let mib = bytes as f64 / 1_048_576.0;
+        write!(f, "{mib:.1} MiB ({bytes} bytes)")
+    }
+}
+
+#[cfg(any(target_os = "linux", target_os = "android"))]
+fn platform_memory_snapshot() -> MemorySnapshot {
+    let (rss_bytes, peak_rss_bytes) = proc_status_memory();
+    MemorySnapshot {
+        rss_bytes,
+        peak_rss_bytes: peak_rss_bytes.or_else(rusage_peak_rss_bytes),
+        physical_footprint_bytes: None,
+    }
+}
+
+#[cfg(any(target_os = "ios", target_os = "macos"))]
+fn platform_memory_snapshot() -> MemorySnapshot {
+    let (rss_bytes, physical_footprint_bytes) = match apple_rusage_info() {
+        Some(usage) => (Some(usage.ri_resident_size), Some(usage.ri_phys_footprint)),
+        None => (None, None),
+    };
+
+    MemorySnapshot { rss_bytes, peak_rss_bytes: rusage_peak_rss_bytes(), physical_footprint_bytes }
+}
+
+#[cfg(all(
+    unix,
+    not(any(target_os = "linux", target_os = "android", target_os = "ios", target_os = "macos"))
+))]
+fn platform_memory_snapshot() -> MemorySnapshot {
+    MemorySnapshot {
+        rss_bytes: None,
+        peak_rss_bytes: rusage_peak_rss_bytes(),
+        physical_footprint_bytes: None,
+    }
+}
+
+#[cfg(not(unix))]
+fn platform_memory_snapshot() -> MemorySnapshot {
+    MemorySnapshot::default()
+}
+
+#[cfg(any(target_os = "linux", target_os = "android"))]
+fn proc_status_memory() -> (Option<u64>, Option<u64>) {
+    let Ok(status) = std::fs::read_to_string("/proc/self/status") else { return (None, None) };
+
+    (parse_status_kib(&status, "VmRSS:"), parse_status_kib(&status, "VmHWM:"))
+}
+
+#[cfg(any(target_os = "linux", target_os = "android", test))]
+fn parse_status_kib(status: &str, key: &str) -> Option<u64> {
+    status.lines().find_map(|line| {
+        let value = line.strip_prefix(key)?.split_whitespace().next()?;
+        value.parse::<u64>().ok().map(|kib| kib.saturating_mul(1024))
+    })
+}
+
+#[cfg(any(target_os = "ios", target_os = "macos"))]
+fn apple_rusage_info() -> Option<libc::rusage_info_v4> {
+    let mut usage = std::mem::MaybeUninit::<libc::rusage_info_v4>::uninit();
+    let ret = unsafe {
+        libc::proc_pid_rusage(
+            libc::getpid(),
+            libc::RUSAGE_INFO_V4,
+            usage.as_mut_ptr() as *mut libc::rusage_info_t,
+        )
+    };
+
+    if ret == 0 {
+        Some(unsafe { usage.assume_init() })
+    } else {
+        None
+    }
+}
+
+#[cfg(unix)]
+fn rusage_peak_rss_bytes() -> Option<u64> {
+    let mut usage = std::mem::MaybeUninit::<libc::rusage>::uninit();
+    let ret = unsafe { libc::getrusage(libc::RUSAGE_SELF, usage.as_mut_ptr()) };
+    if ret != 0 {
+        return None
+    }
+
+    let usage = unsafe { usage.assume_init() };
+    u64::try_from(usage.ru_maxrss).ok().map(|rss| rss.saturating_mul(rusage_maxrss_multiplier()))
+}
+
+#[cfg(any(target_os = "ios", target_os = "macos"))]
+fn rusage_maxrss_multiplier() -> u64 {
+    1
+}
+
+#[cfg(all(unix, not(any(target_os = "ios", target_os = "macos"))))]
+fn rusage_maxrss_multiplier() -> u64 {
+    1024
+}

+ 3 - 0
src/util/mod.rs

@@ -40,6 +40,9 @@ pub mod ringbuffer;
 /// Logging utilities
 pub mod logger;
 
+/// Process memory telemetry helpers
+pub mod memory;
+
 /// Permuted Congruential Generator (PCG)
 /// This is an insecure PRNG used for simulations and tests.
 #[cfg(feature = "rand")]