use std::collections::BTreeMap;
use std::ptr::NonNull;
use std::sync::Arc;
use std::sync::{Mutex, RwLock};
use crate::dbg_backend::DebugBackend;
use crate::gdb::BreakpointManager;
use crate::symbols::{SymbolIndex, SymbolStore};
use crate::target::object::DriverObjectInfo;
use crate::target::{Target, ThreadInfo};
use crate::types::{Dtb, VirtAddr};
const PROCESS_COMPLETION_LIMIT: usize = 4096;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum CompletionStrategy {
None,
Symbol,
Expression,
Type,
Process,
Thread,
Vcpu,
Breakpoint,
Driver,
Alias,
}
impl CompletionStrategy {
pub fn from_kebab(s: &str) -> Option<Self> {
Some(match s {
"none" | "" => Self::None,
"symbol" => Self::Symbol,
"expression" => Self::Expression,
"type" => Self::Type,
"process" => Self::Process,
"thread" => Self::Thread,
"vcpu" => Self::Vcpu,
"breakpoint" => Self::Breakpoint,
"driver" => Self::Driver,
"alias" => Self::Alias,
_ => return None,
})
}
}
pub type ProcessCache = Vec<(String, u64)>;
pub type VcpuCache = Vec<String>;
pub type ThreadCache = Vec<ThreadInfo>;
pub type BreakpointCache = Vec<(u32, bool, VirtAddr, Option<String>)>;
pub type DriverObjectCache = Vec<DriverObjectInfo>;
pub type ExpressionVariableCache = Vec<(String, &'static str)>;
pub type UserCommandCache = Vec<(String, String, Vec<CompletionStrategy>)>;
pub type AliasCache = Vec<(String, String)>;
#[derive(Clone)]
pub struct ReplCaches {
pub symbols: Arc<RwLock<SymbolIndex>>,
pub types: Arc<RwLock<SymbolIndex>>,
pub symbol_store: Arc<SymbolStore>,
pub dtb: Arc<RwLock<Dtb>>,
pub processes: Arc<RwLock<ProcessCache>>,
pub threads: Arc<RwLock<ThreadCache>>,
pub vcpus: Arc<RwLock<VcpuCache>>,
pub breakpoints: Arc<RwLock<BreakpointCache>>,
pub drivers: Arc<RwLock<DriverObjectCache>>,
pub registers: Arc<Vec<String>>,
pub expression_variables: Arc<RwLock<ExpressionVariableCache>>,
pub user_commands: Arc<RwLock<UserCommandCache>>,
pub aliases: Arc<RwLock<AliasCache>>,
}
impl ReplCaches {
pub fn processes_for_completion(&self, target: Option<&Target>) -> ProcessCache {
if let Some(processes) = target.and_then(|target| target.matching_processes(None).ok()) {
let processes: ProcessCache = processes
.into_iter()
.take(PROCESS_COMPLETION_LIMIT)
.map(|p| (p.name, p.pid))
.collect();
*self.processes.write().unwrap() = processes.clone();
return processes;
}
self.processes.read().unwrap().clone()
}
pub fn drivers_for_completion(&self, target: Option<&Target>) -> DriverObjectCache {
if let Some(drivers) = target.and_then(|target| target.enumerate_driver_objects().ok()) {
*self.drivers.write().unwrap() = drivers.clone();
return drivers;
}
self.drivers.read().unwrap().clone()
}
pub fn refresh_vcpus(&self, client: &mut dyn DebugBackend) {
if let Ok(vcpus) = client.thread_list() {
*self.vcpus.write().unwrap() = vcpus;
}
}
pub fn clear_threads(&self) {
self.threads.write().unwrap().clear();
}
pub fn refresh_breakpoints(&self, breakpoints: &BreakpointManager) {
*self.breakpoints.write().unwrap() = breakpoints
.list()
.iter()
.map(|bp| (bp.id, bp.enabled, bp.address, bp.symbol.clone()))
.collect();
}
pub fn refresh_symbol_context(&self, debugger: &Target) {
let new_dtb = debugger.current_dtb();
if *self.dtb.read().unwrap() == new_dtb {
return;
}
*self.symbols.write().unwrap() = debugger.current_symbol_index();
*self.types.write().unwrap() = debugger.current_types_index();
*self.dtb.write().unwrap() = new_dtb;
}
pub fn refresh_expression_context(&self, debugger: &Target) {
let mut variables = BTreeMap::new();
for name in debugger.user_vars.keys() {
if !self.registers.contains(name) {
variables.insert(name.clone(), "Variable");
}
}
for index in 0..debugger.results.len() {
variables.insert(index.to_string(), "Result");
}
for var in debugger.builtin_variables() {
variables.entry(var.name.to_string()).or_insert("Builtin");
}
*self.expression_variables.write().unwrap() = variables.into_iter().collect();
}
}
#[derive(Clone, Default)]
pub struct TargetLoan(Arc<Mutex<Option<LentTarget>>>);
#[derive(Clone, Copy)]
struct LentTarget(NonNull<Target>);
unsafe impl Send for LentTarget {}
const _: () = {
fn assert_sync<T: Sync>() {}
let _ = assert_sync::<Target>;
};
impl TargetLoan {
fn slot(&self) -> std::sync::MutexGuard<'_, Option<LentTarget>> {
self.0
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
}
pub fn lend<R>(&self, target: &Target, f: impl FnOnce() -> R) -> R {
struct Reset<'a>(&'a TargetLoan);
impl Drop for Reset<'_> {
fn drop(&mut self) {
*self.0.slot() = None;
}
}
let _reset = Reset(self);
*self.slot() = Some(LentTarget(NonNull::from(target)));
f()
}
pub fn with<R>(&self, f: impl FnOnce(Option<&Target>) -> R) -> R {
let slot = self.slot();
match *slot {
Some(LentTarget(ptr)) => f(Some(unsafe { ptr.as_ref() })),
None => f(None),
}
}
}