add debugging routines; fix zero-length key creation case

- add a "dangerous debug" command that only exists when the feature
"pddbtest" is enabled. The feature can't work on real hardware, but
still, it enables things like dumping of the database and storing
the keys for inspection, so it's isolated in a feature that prevents
the code from being compiled in the first place when it's not selected.

- fix a bug where keys that have zero length aren't being created
correctly. this is a common use case for reserving some space for
data that isn't yet available.
This commit is contained in:
bunnie 2022-03-29 01:47:28 +08:00
parent 23041ab1f1
commit 55b05cfd80
4 changed files with 76 additions and 14 deletions

View file

@ -68,7 +68,7 @@ pub(crate) const FSCB_FILL_COEFFICIENT: f32 = 0.5;
pub(crate) const FSCB_FILL_UNCERTAINTY: f32 = 0.1;
#[allow(dead_code)]
pub(crate) const PDDB_DEFAULT_SYSTEM_BASIS: &'static str = ".System";
pub const PDDB_DEFAULT_SYSTEM_BASIS: &'static str = ".System";
// this isn't an "official" basis, but it is used for the AAD for encrypting the FastSpace structure
#[allow(dead_code)]
pub(crate) const PDDB_FAST_SPACE_SYSTEM_BASIS: &'static str = ".FastSpace";
@ -120,7 +120,7 @@ pub(crate) enum Opcode {
Quit,
/// Write debug dump (only available in hosted mode)
#[cfg(not(any(target_os = "none", target_os = "xous")))]
DbgDump,
DangerousDebug,
}
#[derive(num_derive::FromPrimitive, num_derive::ToPrimitive, Debug)]
pub(crate) enum PollOp {
@ -320,6 +320,19 @@ impl PddbKeyAttrIpc {
}
}
/// Debugging commands, available only in hosted mode
#[cfg(not(any(target_os = "none", target_os = "xous")))]
#[derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
pub struct PddbDangerousDebug {
pub request: DebugRequest,
pub dump_name: xous_ipc::String::<128>,
}
#[cfg(not(any(target_os = "none", target_os = "xous")))]
#[derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
pub enum DebugRequest {
Dump,
Remount,
}
#[cfg(test)]
mod tests {

View file

@ -419,7 +419,6 @@ impl DictCacheEntry {
if let KeyCacheData::Small(cache_data) = kcache.data.as_mut().expect("small pool should all have their data 'hot' if the index entry is also in cache") {
cache_data.clean = false;
// grow the data cache to accommodate the necessary length; this should be efficient because we reserved space when the vector was allocated
assert!(cache_data.data.len() != 0, "(temporary assert - expected data in the cache for a particular test. Remove this assert if you're hitting this on an actual zero-lenth data set.");
while cache_data.data.len() < data.len() + offset {
cache_data.data.push(0);
}

View file

@ -583,11 +583,30 @@ impl Pddb {
}
Ok(dict_list)
}
/// Triggers a dump of the PDDB to host disk
#[cfg(not(any(target_os = "none", target_os = "xous")))]
pub fn dbg_dump(&self, name: &str) -> Result<()> {
let ipc_str = xous_ipc::String::<128>::from_str(name);
ipc_str.send(self.conn).expect("couldn't initiate debug dump");
Ok(())
let ipc = PddbDangerousDebug {
request: DebugRequest::Dump,
dump_name: xous_ipc::String::from_str(name),
};
let buf = Buffer::into_buf(ipc)
.or(Err(Error::new(ErrorKind::Other, "Xous internal error")))?;
buf.lend(self.conn, Opcode::DangerousDebug.to_u32().unwrap())
.or(Err(Error::new(ErrorKind::Other, "Xous internal error"))).map(|_| ())
}
/// Triggers an umount/remount, forcing a read of the PDDB from disk back into the cache structures.
/// It's a cheesy way to test a power cycle, without having to power cycle.
#[cfg(not(any(target_os = "none", target_os = "xous")))]
pub fn dbg_remount(&self) -> Result<()> {
let ipc = PddbDangerousDebug {
request: DebugRequest::Remount,
dump_name: xous_ipc::String::new(),
};
let buf = Buffer::into_buf(ipc)
.or(Err(Error::new(ErrorKind::Other, "Xous internal error")))?;
buf.lend(self.conn, Opcode::DangerousDebug.to_u32().unwrap())
.or(Err(Error::new(ErrorKind::Other, "Xous internal error"))).map(|_| ())
}
}
@ -595,7 +614,7 @@ impl Drop for Pddb {
fn drop(&mut self) {
let cid = xous::connect(self.cb_sid).unwrap();
send_message(cid, Message::new_blocking_scalar(CbOp::Quit.to_usize().unwrap(), 0, 0, 0, 0)).unwrap();
unsafe{xous::disconnect(cid).unwrap();}
unsafe{xous::disconnect(cid).ok();}
if let Some(handle) = self.cb_handle.take() {
handle.join().expect("couldn't terminate callback helper thread");
}

View file

@ -730,13 +730,29 @@ fn xmain() -> ! {
buffer.replace(req).unwrap(); continue
}
}
let alloc_hint = if let Some(hint) = req.alloc_hint {Some(hint as usize)} else {None};
if basis_cache.key_attributes(&mut pddb_os, dict, key, bname).is_err() {
if !req.create_key {
req.result = PddbRequestCode::NotFound;
buffer.replace(req).unwrap(); continue
} else {
// create an empty key placeholder
let empty: [u8; 0] = [];
match basis_cache.key_update(&mut pddb_os,
dict, key, &empty, None, alloc_hint, bname, true
) {
Ok(_) => {},
Err(e) => {
log::error!("Couldn't allocate key: {:?}", e);
match e.kind() {
std::io::ErrorKind::NotFound => req.result = PddbRequestCode::NotMounted,
std::io::ErrorKind::OutOfMemory => req.result = PddbRequestCode::NoFreeSpace,
_ => req.result = PddbRequestCode::InternalError,
}
buffer.replace(req).unwrap(); continue
}
}
}
// by default keys are created when they are written on the first try.
// ...do need to remember to create an "empty" key if we try to read a key that doesn't already exist, tho...
}
// at this point, we have established a basis/dict/key tuple.
let token: ApiToken = [pddb_os.trng_u32(), pddb_os.trng_u32(), pddb_os.trng_u32()];
@ -746,7 +762,7 @@ fn xmain() -> ! {
key: String::from(key),
basis: if let Some(name) = bname {Some(String::from(name))} else {None},
conn: cid,
alloc_hint: if let Some(hint) = req.alloc_hint {Some(hint as usize)} else {None},
alloc_hint,
};
token_dict.insert(token, token_record);
req.token = Some(token);
@ -1089,10 +1105,25 @@ fn xmain() -> ! {
modals.show_notification(&note).expect("couldn't show basis list");
},
#[cfg(not(any(target_os = "none", target_os = "xous")))]
Some(Opcode::DbgDump) => {
let dumpname = xous_ipc::String::<128>::from_message(msg.body.memory_message().unwrap()).unwrap();
log::info!("dumping {}", dumpname);
pddb_os.dbg_dump(Some(dumpname.as_str().unwrap().to_string()), None);
Some(Opcode::DangerousDebug) => {
let buffer = unsafe { Buffer::from_memory_message(msg.body.memory_message().unwrap()) };
let dbg = buffer.to_original::<PddbDangerousDebug, _>().unwrap();
match dbg.request {
DebugRequest::Dump => {
log::info!("dumping pddb to {}", dbg.dump_name.as_str().unwrap());
pddb_os.dbg_dump(Some(dbg.dump_name.as_str().unwrap().to_string()), None);
},
DebugRequest::Remount => {
log::info!("attempting remount");
basis_cache = BasisCache::new(); // this effectively erases the PDDB from memory
if let Some(sys_basis) = pddb_os.pddb_mount() {
log::info!("remount successful");
basis_cache.basis_add(sys_basis);
} else {
log::info!("remount failed");
}
}
}
}
Some(Opcode::Quit) => {
log::warn!("quitting the PDDB server");