1use libunwind_rs::{Accessors, AddressSpace, Byteorder, Cursor, PtraceState};
2use nix::{errno::Errno, sys::ptrace};
3use std::fs::read_to_string;
4
5use crate::{
6 diag::{Error, Result},
7 hwaccess::Registers,
8 param::{Join, Value},
9 print::Layout,
10 progress::{Execution, Mode, State},
11};
12
13pub type CommandFn = fn(&[Value], &mut State) -> Result<()>;
19
20fn exit_with(args: &[Value], state: &mut State, f: CommandFn) -> Result<()> {
21 f(args, state)?;
22 state.set_execution(Execution::Exit);
23 Ok(())
24}
25
26fn proceed_with(args: &[Value], state: &mut State, f: CommandFn) -> Result<()> {
27 f(args, state)?;
28 state.set_execution(Execution::Run);
29 Ok(())
30}
31
32fn stall_with(args: &[Value], state: &mut State, f: CommandFn) -> Result<()> {
33 f(args, state)?;
34 state.set_execution(Execution::Skip);
35 Ok(())
36}
37
38fn do_breakpoint_common(
39 args: &[Value],
40 state: &mut State,
41 temporary: bool,
42) -> Result<()> {
43 let addr = match args {
44 [Value::Address(addr)] => Ok(*addr),
45 [] => state.prev_rip().ok_or_else(|| Error::from(Errno::ENODATA)),
46 _ => Err(Error::from(Errno::EINVAL)),
47 }?;
48
49 if let Ok(id) = state
50 .breakpoint_mgr()
51 .set_breakpoint(addr, temporary, true, None)
52 {
53 let id = id.ok_or_else(|| Error::from(Errno::ENODATA))?;
54 let bp_type = if temporary { "Temporary" } else { "Permanent" };
55 println!("{bp_type} breakpoint #{id} set at address {addr:#x}");
56 } else {
57 eprintln!("Failed to set breakpoint at address {addr:#x}");
58 }
59
60 Ok(())
61}
62
63pub fn do_ambiguous(args: &[Value], state: &mut State) -> Result<()> {
72 stall_with(args, state, |args, _| {
73 eprintln!("{}: ambiguous command", args.join(" "));
74 Ok(())
75 })
76}
77
78pub fn do_backtrace(args: &[Value], state: &mut State) -> Result<()> {
89 stall_with(args, state, |_, state| {
90 let pstate = PtraceState::new(u32::try_from(state.pid().as_raw())?)?;
91 let mut aspace = AddressSpace::new(Accessors::ptrace(), Byteorder::Default)?;
92 let mut cursor = Cursor::ptrace(&mut aspace, &pstate)?;
93
94 let mut i = 0;
95 loop {
96 let ip = cursor.ip()?;
97 let name = cursor
98 .proc_name()
99 .unwrap_or_else(|_| "<unknown>".to_string());
100
101 let frame = format!("#{i} {ip:#018x} in {name} ()");
102 match state.addr2line(u64::try_from(ip)?)? {
103 Some(line) => println!("{frame} at {}:{}", line.path(), line.line()),
104 None => println!("{frame}"),
105 }
106
107 if !cursor.step()? {
108 break;
109 }
110
111 i += 1;
112 }
113
114 Ok(())
115 })
116}
117
118pub fn do_breakpoint(args: &[Value], state: &mut State) -> Result<()> {
125 stall_with(args, state, |args, state| {
126 do_breakpoint_common(args, state, false)
127 })
128}
129
130pub fn do_continue(args: &[Value], state: &mut State) -> Result<()> {
138 proceed_with(args, state, |_, state| {
139 state.set_mode(Mode::Continue);
140 Ok(())
141 })
142}
143
144pub fn do_delete(args: &[Value], state: &mut State) -> Result<()> {
150 stall_with(args, state, |args, state| match args {
151 [Value::Id(id)] => {
152 if state.breakpoint_mgr().delete_breakpoint(*id).is_err() {
153 eprintln!("No breakpoint number {id}");
154 }
155 Ok(())
156 }
157 _ => Err(Error::from(Errno::EINVAL)),
158 })
159}
160
161pub fn do_examine(args: &[Value], state: &mut State) -> Result<()> {
167 stall_with(args, state, |args, state| match args {
168 [Value::Format(format), Value::Size(size), Value::Address(addr)] => {
169 let word_size = std::mem::size_of::<usize>();
170 let mut buf = vec![0u8; usize::try_from(*size)?];
171 let mut offset = 0;
172
173 while offset < buf.len() {
174 let read_addr = *addr + offset as u64;
175 let word = if let Ok(val) =
176 ptrace::read(state.pid(), read_addr as ptrace::AddressType)
177 {
178 #[allow(
179 clippy::cast_possible_truncation,
180 clippy::cast_sign_loss
181 )]
182 let ret = val as usize;
183 ret
184 } else {
185 eprintln!("Failed to read memory at {read_addr:#x}");
186 break;
187 };
188
189 let bytes_to_write = word_size.min(buf.len() - offset);
190 (0..bytes_to_write).try_for_each(|i| -> Result<()> {
191 buf[offset + i] = u8::try_from((word >> (i * 8)) & 0xff)?;
192 Ok(())
193 })?;
194 offset += bytes_to_write;
195 }
196
197 format.bytes(&buf, *addr)
198 }
199 _ => Err(Error::from(Errno::EINVAL)),
200 })
201}
202
203pub fn do_help(args: &[Value], state: &mut State) -> Result<()> {
211 stall_with(args, state, |args, _| {
212 println!("{}", args.join("\n"));
213 Ok(())
214 })
215}
216
217pub fn do_info_breakpoints(args: &[Value], state: &mut State) -> Result<()> {
223 stall_with(args, state, |_, state| {
224 state.print_breakpoints();
225 Ok(())
226 })
227}
228
229pub fn do_info_memory(args: &[Value], state: &mut State) -> Result<()> {
235 stall_with(args, state, |_, state| {
236 print!("{}", read_to_string(format!("/proc/{}/maps", state.pid()))?);
237 Ok(())
238 })
239}
240
241pub fn do_info_registers(args: &[Value], state: &mut State) -> Result<()> {
247 stall_with(args, state, |_, state| {
248 let regs = Registers::read(state.pid())?;
249 println!("{regs}");
250 Ok(())
251 })
252}
253
254pub fn do_invalid_arguments(args: &[Value], state: &mut State) -> Result<()> {
262 stall_with(args, state, |args, _| {
263 eprintln!("{}: invalid arguments", args.join(" "));
264 Ok(())
265 })
266}
267
268pub fn do_layout_asm(args: &[Value], state: &mut State) -> Result<()> {
276 stall_with(args, state, |_, state| {
277 if *state.layout() == Layout::Assembly {
278 eprintln!("Already in assembly layout mode");
279 } else {
280 println!("Switching to assembly layout mode");
281 state.set_requested_layout(Layout::Assembly);
282 }
283 Ok(())
284 })
285}
286
287pub fn do_layout_src(args: &[Value], state: &mut State) -> Result<()> {
296 stall_with(args, state, |_, state| {
297 if state.initial_layout() == Layout::Assembly {
298 eprintln!("No source layout available (DWARF symbols missing?)");
299 } else if *state.layout() == Layout::Source {
300 eprintln!("Already in source layout mode");
301 } else {
302 println!("Switching to source layout mode");
303 state.set_requested_layout(Layout::Source);
304 }
305 Ok(())
306 })
307}
308
309pub fn do_list(args: &[Value], state: &mut State) -> Result<()> {
318 stall_with(args, state, |_, state| {
319 if let Some(text) = state.printed() {
320 println!("{text}");
321 }
322 Ok(())
323 })
324}
325
326pub fn do_next(args: &[Value], state: &mut State) -> Result<()> {
334 proceed_with(args, state, |_, state| {
335 state.set_mode(Mode::StepOver);
336 Ok(())
337 })
338}
339
340pub fn do_nothing(args: &[Value], state: &mut State) -> Result<()> {
346 stall_with(args, state, |_, _| Ok(()))
347}
348
349pub fn do_quit(args: &[Value], state: &mut State) -> Result<()> {
357 exit_with(args, state, |_, _| {
358 println!("Exiting...");
359 Ok(())
360 })
361}
362
363pub fn do_step(args: &[Value], state: &mut State) -> Result<()> {
369 proceed_with(args, state, |_, _| Ok(()))
370}
371
372pub fn do_tbreakpoint(args: &[Value], state: &mut State) -> Result<()> {
380 stall_with(args, state, |args, state| {
381 do_breakpoint_common(args, state, true)
382 })
383}
384
385pub fn do_unknown(args: &[Value], state: &mut State) -> Result<()> {
393 stall_with(args, state, |args, _| {
394 eprintln!("{}: unknown command", args.join(" "));
395 Ok(())
396 })
397}
398
399#[cfg(test)]
400mod tests {
401 use super::*;
402
403 use nix::unistd::Pid;
404
405 use crate::{
406 param::Value,
407 print::Layout,
408 progress::{Execution, State},
409 };
410
411 #[test]
412 fn test_do_help_sets_skip() {
413 let mut state = State::new(Pid::from_raw(1), None);
414 let cmds: Vec<Value> = vec![Value::String("one"), Value::String("two")];
415 let res = do_help(&cmds, &mut state);
416 assert!(res.is_ok());
417 assert!(matches!(state.execution(), Execution::Skip));
418 }
419
420 #[test]
421 fn test_do_invalid_arguments_sets_skip() {
422 let mut state = State::new(Pid::from_raw(1), None);
423 let args: Vec<Value> = vec![Value::String("bad")];
424 let res = do_invalid_arguments(&args, &mut state);
425 assert!(res.is_ok());
426 assert!(matches!(state.execution(), Execution::Skip));
427 }
428
429 #[test]
430 fn test_do_nothing_sets_skip() {
431 let mut state = State::new(Pid::from_raw(1), None);
432 let res = do_nothing(&[], &mut state);
433 assert!(res.is_ok());
434 assert!(matches!(state.execution(), Execution::Skip));
435 }
436
437 #[test]
438 fn test_do_layout_asm_switches_when_not_assembly() {
439 let mut state = State::new(Pid::from_raw(1), None);
440 state.set_layout(Layout::Source);
441 let res = do_layout_asm(&[], &mut state);
442 assert!(res.is_ok());
443 assert!(matches!(state.execution(), Execution::Skip));
444 let taken = state.take_requested_layout();
445 assert!(taken.is_some());
446 assert_eq!(taken.unwrap(), Layout::Assembly);
447 }
448
449 #[test]
450 fn test_do_layout_src_no_source_available() {
451 let mut state = State::new(Pid::from_raw(1), None);
452 let res = do_layout_src(&[], &mut state);
453 assert!(res.is_ok());
454 assert!(matches!(state.execution(), Execution::Skip));
455 assert!(state.take_requested_layout().is_none());
456 }
457
458 #[test]
459 fn test_do_continue_next_step_quit() {
460 let mut state = State::new(Pid::from_raw(1), None);
461
462 let res = do_continue(&[], &mut state);
463 assert!(res.is_ok());
464 assert!(matches!(state.execution(), Execution::Run));
465 assert!(matches!(state.mode(), crate::progress::Mode::Continue));
466
467 let mut state2 = State::new(Pid::from_raw(1), None);
468 let res = do_next(&[], &mut state2);
469 assert!(res.is_ok());
470 assert!(matches!(state2.execution(), Execution::Run));
471 assert!(matches!(state2.mode(), crate::progress::Mode::StepOver));
472
473 let mut state3 = State::new(Pid::from_raw(1), None);
474 let res = do_step(&[], &mut state3);
475 assert!(res.is_ok());
476 assert!(matches!(state3.execution(), Execution::Run));
477
478 let mut state4 = State::new(Pid::from_raw(1), None);
479 let res = do_quit(&[], &mut state4);
480 assert!(res.is_ok());
481 assert!(matches!(state4.execution(), Execution::Exit));
482 }
483
484 #[test]
485 fn test_do_list_printed() {
486 let mut state = State::new(Pid::from_raw(1), None);
487 state.set_printed(Some("hello".to_string()));
488 let res = do_list(&[], &mut state);
489 assert!(res.is_ok());
490 assert!(matches!(state.execution(), Execution::Skip));
491 }
492}