use minidump::{CpuContext, MinidumpRawContext, UnifiedMemory};
use minidump_unwind::{CallStack, CallingConvention, FunctionArg, FunctionArgs};
pub fn fill_arguments(call_stack: &mut CallStack, stack_memory: Option<UnifiedMemory>) {
let args = call_stack
.frames
.iter()
.enumerate()
.map(|(frame_idx, frame)| {
if let (Some(mem), Some(func_name), MinidumpRawContext::X86(ctx)) =
(stack_memory, &frame.function_name, &frame.context.raw)
{
const POINTER_WIDTH: u64 = 4;
if let Some((calling_convention, argument_list)) = parse_x86_arg_list(func_name) {
let stack_base = mem.base_address().saturating_add(mem.size());
let caller_stack_pointer = call_stack
.frames
.get(frame_idx + 1)
.map(|f| f.context.get_stack_pointer())
.unwrap_or(stack_base);
let caller_frame_pointer = call_stack
.frames
.get(frame_idx + 2)
.map(|f| f.context.get_stack_pointer())
.unwrap_or(stack_base);
let mut read_head = caller_stack_pointer;
let mut pop_value = || {
if read_head < caller_frame_pointer {
let val = mem.get_memory_at_address::<u32>(read_head);
read_head += POINTER_WIDTH;
val.map(|val| val as u64)
} else {
None
}
};
let mut args = Vec::new();
match calling_convention {
CallingConvention::WindowsThisCall => {
let value = ctx
.get_register("eax", &frame.context.valid)
.map(|x| x as u64);
args.push(FunctionArg {
name: String::from("this"),
value,
});
}
CallingConvention::OtherThisCall => {
let value = pop_value();
args.push(FunctionArg {
name: String::from("this"),
value,
});
}
CallingConvention::Cdecl => {
}
}
args.extend(argument_list.iter().map(|&arg_name| {
let value = pop_value();
FunctionArg {
name: String::from(arg_name),
value,
}
}));
return Some(FunctionArgs {
calling_convention,
args,
});
}
}
None
})
.collect::<Vec<_>>();
for (frame, args) in call_stack.frames.iter_mut().zip(args) {
frame.arguments = args;
}
}
fn parse_x86_arg_list(func_name: &str) -> Option<(CallingConvention, Vec<&str>)> {
if let Some((func_name, arg_list)) = func_name.split_once('(') {
if let Some((arg_list, _junk)) = arg_list.rsplit_once(')') {
let calling_convention = if func_name.contains("::") {
let windows = true; if windows {
CallingConvention::WindowsThisCall
} else {
CallingConvention::OtherThisCall
}
} else {
CallingConvention::Cdecl
};
let mut args = Vec::new();
let mut arg_start = 0;
let mut template_depth = 0;
let mut paren_depth = 0;
for (idx, c) in arg_list.bytes().enumerate() {
match c as char {
'<' => template_depth += 1,
'>' => {
if template_depth > 0 {
template_depth -= 1;
} else {
return None;
}
}
'(' => paren_depth += 1,
')' => {
if paren_depth > 0 {
paren_depth -= 1;
} else {
return None;
}
}
',' => {
if template_depth == 0 && paren_depth == 0 {
args.push(arg_list[arg_start..idx].trim());
arg_start = idx + 1;
}
}
_ => {}
}
}
args.push(arg_list[arg_start..].trim());
if template_depth == 0 && paren_depth == 0 {
return Some((calling_convention, args));
}
}
}
None
}