use std::alloc::{GlobalAlloc, Layout, System};
use std::sync::Arc;
use std::time::Duration;
use log::{error, info};
use wacore::stats::AllocMeter;
use whatsapp_rust::prelude::*;
struct AttributingAllocator;
unsafe impl GlobalAlloc for AttributingAllocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
let ptr = unsafe { System.alloc(layout) };
if !ptr.is_null() {
AllocMeter::on_alloc(layout.size());
}
ptr
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
AllocMeter::on_dealloc(layout.size());
unsafe { System.dealloc(ptr, layout) }
}
}
#[global_allocator]
static GLOBAL: AttributingAllocator = AttributingAllocator;
fn main() {
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init();
let rt = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.expect("Failed to build tokio runtime");
rt.block_on(async {
let store = match SqliteStore::new("session_a.db").await {
Ok(store) => store,
Err(e) => {
error!("failed to create SQLite backend: {e}");
return;
}
};
let meter = Arc::new(AllocMeter::new());
let bot = Bot::builder()
.with_backend(store)
.with_alloc_meter(meter.clone())
.on_qr_code(|code, timeout| async move {
info!("QR code (valid {}s):\n{code}", timeout.as_secs());
})
.build()
.await;
let bot = match bot {
Ok(bot) => bot,
Err(e) => {
error!("failed to build bot: {e}");
return;
}
};
let client = bot.client();
let run = tokio::spawn(bot.run());
let mut ticker = tokio::time::interval(Duration::from_secs(10));
loop {
tokio::select! {
_ = ticker.tick() => {
let snap = meter.snapshot();
info!(
"session A alloc churn: {}B allocated / {}B freed across {} allocs (net {}B)",
snap.allocated_bytes, snap.freed_bytes, snap.allocations, snap.net_bytes(),
);
info!("\n{}", client.resource_report().await);
}
_ = tokio::signal::ctrl_c() => {
info!("Shutting down...");
client.disconnect().await;
break;
}
}
}
let _ = run.await;
});
}