use crate::debugger::context::gcx;
use crate::debugger::debugee::dwarf::DebugInformation;
use crate::debugger::debugee::dwarf::eval::{EvaluationContext, ExpressionEvaluator};
use crate::debugger::debugee::dwarf::r#type::ComplexType;
use crate::debugger::debugee::dwarf::unit::BsUnit;
use crate::debugger::debugee::dwarf::unit::die_ref::{Argument, FatDieRef, Typed, Variable};
use crate::debugger::error::Error;
use crate::debugger::error::Error::FunctionNotFound;
use crate::debugger::variable::dqe::{DataCast, Dqe, PointerCast, Selector};
use crate::debugger::variable::value::Value;
use crate::debugger::variable::value::parser::{ParseContext, ValueModifiers, ValueParser};
use crate::debugger::variable::r#virtual::VirtualVariableDie;
use crate::debugger::variable::{Identity, ObjectBinaryRepr};
use crate::debugger::{Debugger, read_memory_by_pid};
use crate::{ref_resolve_unit_call, resolve_unit_call, weak_error};
use bytes::Bytes;
use gimli::Range;
use std::fmt::Debug;
use std::rc::Rc;
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum QueryResultKind {
Root,
Expression,
}
#[derive(Clone)]
pub struct QueryResult<'a> {
pub value: Option<Value>,
scope: Option<Box<[Range]>>,
kind: QueryResultKind,
base_type: Rc<ComplexType>,
identity: Identity,
evcx_builder: EvaluationContextBuilder<'a>,
}
impl QueryResult<'_> {
pub fn unit(&self) -> &BsUnit {
self.evcx_builder.unit()
}
#[inline(always)]
pub fn value(&self) -> &Value {
self.value.as_ref().expect("should be `Some`")
}
#[inline(always)]
pub fn into_value(mut self) -> Value {
self.value.take().expect("should be `Some`")
}
#[inline(always)]
pub fn into_identified_value(mut self) -> (Identity, Value) {
(self.identity, self.value.take().expect("should be `Some`"))
}
#[inline(always)]
pub fn kind(&self) -> QueryResultKind {
self.kind
}
#[inline(always)]
pub fn type_graph(&self) -> &ComplexType {
self.base_type.as_ref()
}
#[inline(always)]
pub fn identity(&self) -> &Identity {
&self.identity
}
#[inline(always)]
pub fn scope(&self) -> &Option<Box<[Range]>> {
&self.scope
}
pub fn with_evcx<T, F: FnOnce(&EvaluationContext) -> T>(&self, cb: F) -> T {
self.evcx_builder.with_evcx(cb)
}
pub fn modify_value<F: FnOnce(&ParseContext, Value) -> Option<Value>>(
mut self,
cb: F,
) -> Option<Self> {
let value = self.value.take().expect("should be `Some`");
let type_graph = self.type_graph();
let eval_cb = |evcx: &EvaluationContext| {
let pcx = &ParseContext { evcx, type_graph };
cb(pcx, value)
};
let new_value = self.evcx_builder.with_evcx(eval_cb)?;
self.value = Some(new_value);
Some(self)
}
}
impl PartialEq for QueryResult<'_> {
fn eq(&self, other: &Self) -> bool {
self.value == other.value && self.identity == other.identity
}
}
#[derive(Clone)]
enum EvaluationContextBuilder<'a> {
Ready(&'a Debugger, ExpressionEvaluator<'a>),
Virtual {
debugger: &'a Debugger,
debug_info: &'a DebugInformation,
unit: &'a BsUnit,
},
}
impl EvaluationContextBuilder<'_> {
pub fn unit(&self) -> &BsUnit {
match self {
EvaluationContextBuilder::Ready(_, evaluator) => evaluator.unit(),
EvaluationContextBuilder::Virtual { unit, .. } => unit,
}
}
fn with_evcx<T, F: FnOnce(&EvaluationContext) -> T>(&self, cb: F) -> T {
let evaluator;
let evcx = match self {
EvaluationContextBuilder::Ready(debugger, evaluator) => EvaluationContext {
evaluator,
ecx: debugger.ecx(),
},
EvaluationContextBuilder::Virtual {
debugger,
debug_info,
unit,
..
} => {
let dwarf = debug_info.dwarf();
evaluator = resolve_unit_call!(
dwarf,
unit,
evaluator,
&debugger.debugee,
debug_info.dwarf()
);
EvaluationContext {
evaluator: &evaluator,
ecx: debugger.ecx(),
}
}
};
cb(&evcx)
}
}
#[macro_export]
macro_rules! type_from_cache {
($variable: expr, $cache: expr) => {
$variable
.deref_ensure()
.type_ref()
.and_then(
|type_ref| match $cache.entry(($variable.unit().id, type_ref)) {
std::collections::hash_map::Entry::Occupied(o) => {
Some(std::rc::Rc::clone(o.get()))
}
std::collections::hash_map::Entry::Vacant(v) => $variable.r#type().map(|t| {
let t = std::rc::Rc::new(t);
v.insert(t.clone());
t
}),
},
)
.ok_or_else(|| {
$crate::debugger::variable::value::ParsingError::Assume(
$crate::debugger::variable::value::AssumeError::NoType("variable"),
)
})
};
}
pub struct DqeExecutor<'a> {
debugger: &'a Debugger,
}
impl<'dbg> DqeExecutor<'dbg> {
pub fn new(debugger: &'dbg Debugger) -> Self {
Self { debugger }
}
fn variable_die_by_selector(
&self,
selector: &Selector,
) -> Result<Vec<FatDieRef<'dbg, Variable>>, Error> {
let ecx = self.debugger.ecx();
let debugee = &self.debugger.debugee;
let (current_func, _) = debugee
.debug_info(ecx.location().pc)?
.find_function_by_pc(ecx.location().global_pc)?
.ok_or(FunctionNotFound(ecx.location().global_pc))?;
let vars = match selector {
Selector::Name {
var_name,
local_only: local,
} => {
let local_variants = current_func
.local_variable(ecx.location().global_pc, var_name)
.map(|v| vec![v])
.unwrap_or_default();
let local = *local;
if !local && local_variants.is_empty() {
debugee
.debug_info(ecx.location().pc)?
.find_variables(ecx.location(), var_name)?
} else {
local_variants
}
}
Selector::Any => current_func.local_variables(ecx.location().global_pc),
};
Ok(vars)
}
fn param_die_by_selector(
&self,
selector: &Selector,
) -> Result<Vec<FatDieRef<'dbg, Argument>>, Error> {
let ecx_loc = self.debugger.ecx().location();
let debugee = &self.debugger.debugee;
let (current_function, _) = debugee
.debug_info(ecx_loc.pc)?
.find_function_by_pc(ecx_loc.global_pc)?
.ok_or(FunctionNotFound(ecx_loc.global_pc))?;
let params = current_function.parameters();
let params = match selector {
Selector::Name { var_name, .. } => params
.into_iter()
.filter(|r| r.deref_ensure().name().as_ref() == Some(var_name))
.collect::<Vec<_>>(),
Selector::Any => params,
};
Ok(params)
}
fn apply_select_die(
&self,
selector: &Selector,
on_args: bool,
) -> Result<Vec<QueryResult<'dbg>>, Error> {
fn root_from_die<'dbg, H: Typed>(
debugger: &'dbg Debugger,
die_ref: &FatDieRef<'dbg, H>,
ranges: Option<Box<[Range]>>,
) -> Option<QueryResult<'dbg>> {
let r#type = gcx().with_type_cache(|tc| weak_error!(type_from_cache!(die_ref, tc)))?;
let evaluator = ref_resolve_unit_call!(
die_ref,
evaluator,
&debugger.debugee,
die_ref.debug_info.dwarf()
);
let context_builder = EvaluationContextBuilder::Ready(debugger, evaluator);
let value = context_builder.with_evcx(|evcx| {
let data = die_ref.read_value(debugger.ecx(), &debugger.debugee, &r#type);
let parser = ValueParser::new();
let pcx = &ParseContext {
evcx,
type_graph: &r#type,
};
let modifiers = &ValueModifiers::from_identity(pcx, Identity::from_die(die_ref));
parser.parse(pcx, data, modifiers)
})?;
Some(QueryResult {
value: Some(value),
scope: ranges,
kind: QueryResultKind::Root,
base_type: r#type,
identity: Identity::from_die(die_ref),
evcx_builder: context_builder,
})
}
match on_args {
true => {
let params = self.param_die_by_selector(selector)?;
Ok(params
.iter()
.filter_map(|arg_die| {
root_from_die(
self.debugger,
arg_die,
arg_die.max_range().map(|r| {
let scope: Box<[Range]> = Box::new([r]);
scope
}),
)
})
.collect())
}
false => {
let vars = self.variable_die_by_selector(selector)?;
Ok(vars
.iter()
.filter_map(|var_die| root_from_die(self.debugger, var_die, var_die.ranges()))
.collect())
}
}
}
fn apply_ptr_cast_op(&self, ptr_cast: &PointerCast) -> Result<QueryResult<'dbg>, Error> {
let mut var_die = VirtualVariableDie::workpiece();
let var_die_ref = var_die.init_with_type(&self.debugger.debugee, &ptr_cast.ty)?;
let r#type = gcx().with_type_cache(|tc| type_from_cache!(var_die_ref, tc))?;
let context_builder = EvaluationContextBuilder::Virtual {
debugger: self.debugger,
debug_info: var_die_ref.debug_info,
unit: var_die_ref.unit(),
};
let value = context_builder.with_evcx(|evcx| {
let data = ObjectBinaryRepr {
raw_data: Bytes::copy_from_slice(&ptr_cast.ptr.to_le_bytes()),
address: None,
size: std::mem::size_of::<usize>(),
};
let parser = ValueParser::new();
let pcx = &ParseContext {
evcx,
type_graph: &r#type,
};
parser.parse(pcx, Some(data), &ValueModifiers::default())
});
Ok(QueryResult {
value,
scope: None,
kind: QueryResultKind::Expression,
base_type: r#type,
identity: Identity::default(),
evcx_builder: context_builder,
})
}
fn apply_data_cast(&self, data_cast: &DataCast) -> Result<QueryResult<'dbg>, Error> {
let mut var_die = VirtualVariableDie::workpiece();
let debug_info = self
.debugger
.debugee
.debug_info_from_file(&data_cast.ty_debug_info)?;
let var_die_ref = var_die.init_with_known_type(
debug_info,
data_cast.ty_unit_off,
data_cast.ty_die_off,
)?;
let r#type = gcx().with_type_cache(|tc| type_from_cache!(var_die_ref, tc))?;
let context_builder = EvaluationContextBuilder::Virtual {
debugger: self.debugger,
debug_info: var_die_ref.debug_info,
unit: var_die_ref.unit(),
};
let value = context_builder.with_evcx(|evcx| {
let size = r#type.type_size_in_bytes(evcx, r#type.root())? as usize;
let raw_data = weak_error!(read_memory_by_pid(
evcx.ecx.pid_on_focus(),
data_cast.ptr,
size
))?;
let data = ObjectBinaryRepr {
raw_data: Bytes::copy_from_slice(&raw_data),
address: Some(data_cast.ptr),
size,
};
let parser = ValueParser::new();
let pcx = &ParseContext {
evcx,
type_graph: &r#type,
};
parser.parse(pcx, Some(data), &ValueModifiers::default())
});
Ok(QueryResult {
value,
scope: None,
kind: QueryResultKind::Expression,
base_type: r#type,
identity: Identity::default(),
evcx_builder: context_builder,
})
}
fn apply_dqe(&self, dqe: &Dqe, on_args: bool) -> Result<Vec<QueryResult<'dbg>>, Error> {
match dqe {
Dqe::Variable(selector) => self.apply_select_die(selector, on_args),
Dqe::PtrCast(ptr_cast) => self.apply_ptr_cast_op(ptr_cast).map(|q| vec![q]),
Dqe::DataCast(data_cast) => self.apply_data_cast(data_cast).map(|q| vec![q]),
Dqe::Field(next, field) => {
let results = self.apply_dqe(next, on_args)?;
Ok(results
.into_iter()
.filter_map(|q| q.modify_value(|_, val| val.field(field)))
.collect())
}
Dqe::Index(next, idx) => {
let results = self.apply_dqe(next, on_args)?;
Ok(results
.into_iter()
.filter_map(|q| q.modify_value(|_, val| val.index(idx)))
.collect())
}
Dqe::Slice(next, left, right) => {
let results = self.apply_dqe(next, on_args)?;
Ok(results
.into_iter()
.filter_map(|q| q.modify_value(|pcx, val| val.slice(pcx, *left, *right)))
.collect())
}
Dqe::Deref(next) => {
let results = self.apply_dqe(next, on_args)?;
Ok(results
.into_iter()
.filter_map(|q| q.modify_value(|pcx, val| val.deref(pcx)))
.collect())
}
Dqe::Address(next) => {
let results = self.apply_dqe(next, on_args)?;
Ok(results
.into_iter()
.filter_map(|q| q.modify_value(|pcx, val| val.address(pcx)))
.collect())
}
Dqe::Canonic(next) => {
let results = self.apply_dqe(next, on_args)?;
Ok(results
.into_iter()
.filter_map(|q| q.modify_value(|_, val| Some(val.canonic())))
.collect())
}
}
}
pub fn query(&self, dqe: &Dqe) -> Result<Vec<QueryResult<'dbg>>, Error> {
self.apply_dqe(dqe, false)
}
pub fn query_names(&self, dqe: &Dqe) -> Result<Vec<String>, Error> {
match dqe {
Dqe::Variable(selector) => {
let vars = self.variable_die_by_selector(selector)?;
Ok(vars
.into_iter()
.filter_map(|die_ref| die_ref.deref_ensure().name())
.collect())
}
_ => unreachable!("unexpected expression variant"),
}
}
pub fn query_arguments(&self, dqe: &Dqe) -> Result<Vec<QueryResult<'dbg>>, Error> {
self.apply_dqe(dqe, true)
}
pub fn query_arguments_names(&self, dqe: &Dqe) -> Result<Vec<String>, Error> {
match dqe {
Dqe::Variable(selector) => {
let params = self.param_die_by_selector(selector)?;
Ok(params
.into_iter()
.filter_map(|r| r.deref_ensure().name())
.collect())
}
_ => unreachable!("unexpected expression variant"),
}
}
}