use std::{
ops::Deref,
sync::Arc,
time::{Duration, Instant},
};
use once_cell::sync::Lazy;
use parking_lot::Mutex;
use rusqlite::{backup, params, Connection};
use serde::{Deserialize, Serialize};
use smol::Task;
use structopt::StructOpt;
use crate::{
config::{CommonOpt, CONFIG},
ALLOCATOR,
};
#[derive(Debug, StructOpt, Deserialize, Serialize, Clone)]
pub struct DebugPackOpt {
#[structopt(flatten)]
pub common: CommonOpt,
#[structopt(long)]
pub export_to: String, }
pub struct DebugPack {
conn: Arc<Mutex<Connection>>,
}
pub static DEBUGPACK: Lazy<Arc<DebugPack>> = Lazy::new(|| {
let dp = match CONFIG.deref() {
crate::config::Opt::Connect(connect_opt) => {
DebugPack::new(&connect_opt.common.debugpack_path).unwrap()
}
crate::config::Opt::BridgeTest(bt_pot) => {
DebugPack::new(&bt_pot.common.debugpack_path).unwrap()
}
crate::config::Opt::Sync(sync_opt) => {
DebugPack::new(&sync_opt.common.debugpack_path).unwrap()
}
crate::config::Opt::BinderProxy(bp_opt) => {
DebugPack::new(&bp_opt.common.debugpack_path).unwrap()
}
crate::config::Opt::Debugpack(dp_opt) => {
DebugPack::new(&dp_opt.common.debugpack_path).unwrap()
}
};
Arc::new(dp)
});
pub static START_TIME: Lazy<Instant> = Lazy::new(Instant::now);
pub static TIMESERIES_LOOP: Lazy<Task<()>> = Lazy::new(|| {
smolscale::spawn(async {
loop {
let _ = DEBUGPACK.add_timeseries("memory", ALLOCATOR.allocated() as f64);
let _ = DEBUGPACK.add_timeseries("uptime", START_TIME.elapsed().as_secs_f64());
smol::Timer::after(Duration::from_secs(1)).await;
}
})
});
impl DebugPack {
pub fn new(db_path: &str) -> anyhow::Result<Self> {
let conn = Connection::open(db_path)?;
conn.execute(
"create table if not exists timeseries (
timestamp timestamp,
key text,
value real)",
[],
)?;
conn.execute(
"create table if not exists loglines (
timestamp timestamp,
line text)",
[],
)?;
Ok(Self {
conn: Arc::new(Mutex::new(conn)),
})
}
pub fn add_logline(&self, logline: &str) -> anyhow::Result<usize> {
match self.conn.lock().execute(
"insert into loglines (timestamp, line) values (datetime(), ?1)",
params![logline],
) {
Ok(n) => Ok(n),
Err(e) => anyhow::bail!(e),
}
}
pub fn loglines_count(&self) -> anyhow::Result<i32> {
let size: i32 = self
.conn
.lock()
.query_row("select count(*) from loglines", [], |row| row.get(0))?;
Ok(size)
}
pub fn add_timeseries(&self, key: &str, value: f64) -> anyhow::Result<()> {
self.conn.lock().execute(
"insert into timeseries (timestamp, key, value) values (datetime(), ?1, ?2)",
params![key, value],
)?;
Ok(())
}
pub fn backup(&self, dest: &str) -> anyhow::Result<()> {
let mut dst = Connection::open(dest)?;
let src = self.conn.lock();
let backup = backup::Backup::new(&src, &mut dst)?;
backup.run_to_completion(5, Duration::from_millis(100), None)?;
Ok(())
}
}
pub(crate) fn export_debugpak(dest: &str) -> anyhow::Result<()> {
DEBUGPACK.backup(dest)
}