bugstalker/debugger/
mod.rs

1pub mod address;
2pub mod r#async;
3mod breakpoint;
4pub mod call;
5mod code;
6mod debugee;
7mod error;
8pub mod process;
9pub mod register;
10pub mod rust;
11mod step;
12mod utils;
13pub mod variable;
14mod watchpoint;
15
16pub use breakpoint::BreakpointView;
17pub use breakpoint::BreakpointViewOwned;
18pub use breakpoint::CreateTransparentBreakpointRequest;
19use call::CallCache;
20pub use debugee::FrameInfo;
21pub use debugee::FunctionAssembly;
22pub use debugee::FunctionRange;
23pub use debugee::RegionInfo;
24pub use debugee::ThreadSnapshot;
25pub use debugee::dwarf::Symbol;
26pub use debugee::dwarf::r#type::TypeDeclaration;
27pub use debugee::dwarf::unit::FunctionDie;
28pub use debugee::dwarf::unit::PlaceDescriptor;
29pub use debugee::dwarf::unit::PlaceDescriptorOwned;
30pub use debugee::dwarf::unwind;
31pub use debugee::tracee::Tracee;
32pub use error::Error;
33pub use watchpoint::WatchpointView;
34pub use watchpoint::WatchpointViewOwned;
35
36use crate::debugger::Error::Syscall;
37use crate::debugger::address::{Address, GlobalAddress, RelocatedAddress};
38use crate::debugger::breakpoint::{Breakpoint, BreakpointRegistry, BrkptType, UninitBreakpoint};
39use crate::debugger::debugee::dwarf::DwarfUnwinder;
40use crate::debugger::debugee::dwarf::r#type::TypeCache;
41use crate::debugger::debugee::dwarf::unwind::Backtrace;
42use crate::debugger::debugee::tracer::{StopReason, TraceContext};
43use crate::debugger::debugee::{Debugee, ExecutionStatus, Location};
44use crate::debugger::error::Error::{
45    FrameNotFound, Hook, ProcessNotStarted, Ptrace, RegisterNameNotFound, UnwindNoContext,
46};
47use crate::debugger::process::{Child, Installed};
48use crate::debugger::register::debug::BreakCondition;
49use crate::debugger::register::{DwarfRegisterMap, Register, RegisterMap};
50use crate::debugger::step::StepResult;
51use crate::debugger::variable::dqe::{Dqe, Selector};
52use crate::debugger::variable::execute::QueryResult;
53use crate::debugger::variable::value::Value;
54use crate::debugger::watchpoint::WatchpointRegistry;
55use crate::oracle::Oracle;
56use crate::{print_warns, weak_error};
57use indexmap::IndexMap;
58use log::debug;
59use nix::libc::{c_void, uintptr_t};
60use nix::sys;
61use nix::sys::signal;
62use nix::sys::signal::{SIGKILL, Signal};
63use nix::sys::wait::{WaitStatus, waitpid};
64use nix::unistd::Pid;
65use object::Object;
66use regex::Regex;
67use std::cell::RefCell;
68use std::cell::RefMut;
69use std::ffi::c_long;
70use std::path::{Path, PathBuf};
71use std::str::FromStr;
72use std::sync::Arc;
73use std::{fs, mem};
74
75/// Trait for the reverse interaction between the debugger and the user interface.
76pub trait EventHook {
77    /// Called when user defined breakpoint is reached.
78    ///
79    /// # Arguments
80    ///
81    /// * `pc`: address of instruction where breakpoint is reached
82    /// * `num`: breakpoint number
83    /// * `place`: stop place information
84    /// * `function`: function debug information entry
85    fn on_breakpoint(
86        &self,
87        pc: RelocatedAddress,
88        num: u32,
89        place: Option<PlaceDescriptor>,
90        function: Option<&FunctionDie>,
91    ) -> anyhow::Result<()>;
92
93    /// Called when watchpoint is activated.
94    ///
95    /// # Arguments
96    ///
97    /// * `pc`: address of instruction where breakpoint is reached
98    /// * `num`: breakpoint number
99    /// * `place`: breakpoint number
100    /// * `condition`: reason of a watchpoint activation
101    /// * `dqe_string`: stringified data query expression (if exist)
102    /// * `old_value`: previous expression or mem location value
103    /// * `new_value`: current expression or mem location value
104    /// * `end_of_scope`: true if watchpoint activated cause end of scope is reached
105    #[allow(clippy::too_many_arguments)]
106    fn on_watchpoint(
107        &self,
108        pc: RelocatedAddress,
109        num: u32,
110        place: Option<PlaceDescriptor>,
111        condition: BreakCondition,
112        dqe_string: Option<&str>,
113        old_value: Option<&Value>,
114        new_value: Option<&Value>,
115        end_of_scope: bool,
116    ) -> anyhow::Result<()>;
117
118    /// Called when one of step commands is done.
119    ///
120    /// # Arguments
121    ///
122    /// * `pc`: address of instruction where breakpoint is reached
123    /// * `place`: stop place information
124    /// * `function`: function debug information entry
125    fn on_step(
126        &self,
127        pc: RelocatedAddress,
128        place: Option<PlaceDescriptor>,
129        function: Option<&FunctionDie>,
130    ) -> anyhow::Result<()>;
131
132    /// Called when one of async step commands is done.
133    ///
134    /// # Arguments
135    ///
136    /// * `pc`: address of instruction where breakpoint is reached
137    /// * `place`: stop place information
138    /// * `function`: function debug information entry
139    /// * `task_id`: asynchronous task id
140    /// * `task_completed`: true if task is already completed
141    fn on_async_step(
142        &self,
143        pc: RelocatedAddress,
144        place: Option<PlaceDescriptor>,
145        function: Option<&FunctionDie>,
146        task_id: u64,
147        task_completed: bool,
148    ) -> anyhow::Result<()>;
149
150    /// Called when debugee receive an OS signal. Debugee is in signal-stop at this moment.
151    ///
152    /// # Arguments
153    ///
154    /// * `signal`: received OS signal
155    fn on_signal(&self, signal: Signal);
156
157    /// Called right after debugee exit.
158    ///
159    /// # Arguments
160    ///
161    /// * `code`: exit code
162    fn on_exit(&self, code: i32);
163
164    /// Called single time for each debugee process (on start or after reinstall).
165    ///
166    /// # Arguments
167    ///
168    /// * `pid`: debugee process pid
169    fn on_process_install(&self, pid: Pid, object: Option<&object::File>);
170}
171
172pub struct NopHook {}
173
174impl EventHook for NopHook {
175    fn on_breakpoint(
176        &self,
177        _: RelocatedAddress,
178        _: u32,
179        _: Option<PlaceDescriptor>,
180        _: Option<&FunctionDie>,
181    ) -> anyhow::Result<()> {
182        Ok(())
183    }
184
185    fn on_watchpoint(
186        &self,
187        _: RelocatedAddress,
188        _: u32,
189        _: Option<PlaceDescriptor>,
190        _: BreakCondition,
191        _: Option<&str>,
192        _: Option<&Value>,
193        _: Option<&Value>,
194        _: bool,
195    ) -> anyhow::Result<()> {
196        Ok(())
197    }
198
199    fn on_step(
200        &self,
201        _: RelocatedAddress,
202        _: Option<PlaceDescriptor>,
203        _: Option<&FunctionDie>,
204    ) -> anyhow::Result<()> {
205        Ok(())
206    }
207
208    fn on_async_step(
209        &self,
210        _: RelocatedAddress,
211        _: Option<PlaceDescriptor>,
212        _: Option<&FunctionDie>,
213        _: u64,
214        _: bool,
215    ) -> anyhow::Result<()> {
216        Ok(())
217    }
218
219    fn on_signal(&self, _: Signal) {}
220
221    fn on_exit(&self, _: i32) {}
222
223    fn on_process_install(&self, _: Pid, _: Option<&object::File>) {}
224}
225
226#[macro_export]
227macro_rules! disable_when_not_stared {
228    ($this: expr) => {
229        if !$this.debugee.is_in_progress() {
230            return Err($crate::debugger::error::Error::ProcessNotStarted);
231        }
232    };
233}
234
235/// Exploration context. Contains current explored thread and program counter.
236/// May be changed by user (by `thread` or `frame` command)
237/// or by debugger (at breakpoints, after steps, etc.).
238#[derive(Clone, Debug)]
239pub struct ExplorationContext {
240    focus_location: Location,
241    focus_frame: u32,
242}
243
244impl ExplorationContext {
245    /// Create a new context with known thread but without known program counter-value.
246    /// It is useful when debugee is not started yet or restarted.
247    ///
248    /// # Arguments
249    ///
250    /// * `pid`: thread id
251    pub fn new_non_running(pid: Pid) -> ExplorationContext {
252        Self {
253            focus_location: Location {
254                pc: 0_u64.into(),
255                global_pc: 0_u64.into(),
256                pid,
257            },
258            focus_frame: 0,
259        }
260    }
261
262    /// Create new context.
263    pub fn new(location: Location, frame_num: u32) -> Self {
264        Self {
265            focus_location: location,
266            focus_frame: frame_num,
267        }
268    }
269
270    #[inline(always)]
271    pub fn location(&self) -> Location {
272        self.focus_location
273    }
274
275    #[inline(always)]
276    pub fn frame_num(&self) -> u32 {
277        self.focus_frame
278    }
279
280    #[inline(always)]
281    pub fn pid_on_focus(&self) -> Pid {
282        self.location().pid
283    }
284}
285
286/// Debugger structure builder.
287#[derive(Default)]
288pub struct DebuggerBuilder<H: EventHook + 'static = NopHook> {
289    oracles: Vec<Arc<dyn Oracle>>,
290    hooks: Option<H>,
291}
292
293impl<H: EventHook + 'static> DebuggerBuilder<H> {
294    /// Create a new builder.
295    pub fn new() -> Self {
296        Self {
297            oracles: vec![],
298            hooks: None,
299        }
300    }
301
302    /// Add oracles.
303    ///
304    /// # Arguments
305    ///
306    /// * `oracles`: list of oracles
307    pub fn with_oracles(self, oracles: Vec<Arc<dyn Oracle>>) -> Self {
308        Self { oracles, ..self }
309    }
310
311    /// Add event hooks implementation
312    ///
313    /// # Arguments
314    ///
315    /// * `hooks`: hooks implementation
316    pub fn with_hooks(self, hooks: H) -> Self {
317        Self {
318            hooks: Some(hooks),
319            ..self
320        }
321    }
322
323    /// Return all oracles.
324    pub fn oracles(&self) -> impl Iterator<Item = &dyn Oracle> {
325        self.oracles.iter().map(|oracle| oracle.as_ref())
326    }
327
328    /// Create a debugger.
329    ///
330    /// # Arguments
331    ///
332    /// * `process`: debugee process
333    pub fn build(self, process: Child<Installed>) -> Result<Debugger, Error> {
334        if let Some(hooks) = self.hooks {
335            Debugger::new(process, hooks, self.oracles)
336        } else {
337            Debugger::new(process, NopHook {}, self.oracles)
338        }
339    }
340}
341
342/// Main structure of bug-stalker, control debugee state and provides application functionality.
343pub struct Debugger {
344    /// Child process where debugee is running.
345    process: Child<Installed>,
346    /// Debugee static/runtime state and control flow.
347    debugee: Debugee,
348    /// Active and non-active breakpoints lists.
349    breakpoints: BreakpointRegistry,
350    /// Watchpoints lists.
351    watchpoints: WatchpointRegistry,
352    /// Type declaration cache.
353    type_cache: RefCell<TypeCache>,
354    /// Debugger interrupt with UI by EventHook trait.
355    hooks: Box<dyn EventHook>,
356    /// Current exploration context.
357    expl_context: ExplorationContext,
358    /// Map of name -> (oracle, installed flag) pairs.
359    oracles: IndexMap<&'static str, (Arc<dyn Oracle>, bool)>,
360    /// Cache for called functions.
361    call_cache: RefCell<CallCache>,
362}
363
364impl Debugger {
365    fn new(
366        process: Child<Installed>,
367        hooks: impl EventHook + 'static,
368        oracles: impl IntoIterator<Item = Arc<dyn Oracle>>,
369    ) -> Result<Self, Error> {
370        let program_path = Path::new(process.program());
371
372        let file = fs::File::open(program_path)?;
373        let mmap = unsafe { memmap2::Mmap::map(&file)? };
374        let object = object::File::parse(&*mmap)?;
375
376        let entry_point = GlobalAddress::from(object.entry());
377        let mut breakpoints = BreakpointRegistry::default();
378        breakpoints.add_uninit(UninitBreakpoint::new_entry_point(
379            None::<PathBuf>,
380            Address::Global(entry_point),
381            process.pid(),
382        ));
383
384        let process_id = process.pid();
385        hooks.on_process_install(process_id, Some(&object));
386
387        let debugee = if process.is_external() {
388            Debugee::new_from_external_process(program_path, &process, &object)?
389        } else {
390            Debugee::new_non_running(program_path, &process, &object)?
391        };
392
393        Ok(Self {
394            debugee,
395            process,
396            breakpoints,
397            watchpoints: WatchpointRegistry::default(),
398            hooks: Box::new(hooks),
399            type_cache: RefCell::default(),
400            call_cache: RefCell::default(),
401            expl_context: ExplorationContext::new_non_running(process_id),
402            oracles: oracles
403                .into_iter()
404                .map(|oracle| (oracle.name(), (oracle, false)))
405                .collect(),
406        })
407    }
408
409    /// Return installed oracle, or `None` if oracle not found or not installed.
410    ///
411    /// # Arguments
412    ///
413    /// * `name`: oracle name
414    pub fn get_oracle(&self, name: &str) -> Option<&dyn Oracle> {
415        self.oracles
416            .get(name)
417            .and_then(|(oracle, install)| install.then_some(oracle.as_ref()))
418    }
419
420    /// Same as `get_oracle` but return an `Arc<dyn Oracle>`
421    pub fn get_oracle_arc(&self, name: &str) -> Option<Arc<dyn Oracle>> {
422        self.oracles
423            .get(name)
424            .and_then(|(oracle, install)| install.then_some(oracle.clone()))
425    }
426
427    /// Return all oracles.
428    pub fn all_oracles(&self) -> impl Iterator<Item = &dyn Oracle> {
429        self.oracles.values().map(|(oracle, _)| oracle.as_ref())
430    }
431
432    /// Same as `all_oracles` but return iterator over `Arc<dyn Oracle>`
433    pub fn all_oracles_arc(&self) -> impl Iterator<Item = Arc<dyn Oracle>> + '_ {
434        self.oracles.values().map(|(oracle, _)| oracle.clone())
435    }
436
437    pub fn process(&self) -> &Child<Installed> {
438        &self.process
439    }
440
441    pub fn set_hook(&mut self, hooks: impl EventHook + 'static) {
442        self.hooks = Box::new(hooks);
443    }
444
445    /// Return last set exploration context.
446    #[inline(always)]
447    pub fn exploration_ctx(&self) -> &ExplorationContext {
448        &self.expl_context
449    }
450
451    /// Update current program counters for current in focus thread.
452    fn expl_ctx_update_location(&mut self) -> Result<&ExplorationContext, Error> {
453        let old_ctx = self.exploration_ctx();
454        self.expl_context = ExplorationContext::new(
455            self.debugee
456                .get_tracee_ensure(old_ctx.pid_on_focus())
457                .location(&self.debugee)?,
458            0,
459        );
460        Ok(&self.expl_context)
461    }
462
463    fn expl_ctx_swap(&mut self, new: ExplorationContext) {
464        self.expl_context = new;
465    }
466
467    /// Restore frame from user defined to real.
468    fn expl_ctx_restore_frame(&mut self) -> Result<&ExplorationContext, Error> {
469        self.expl_ctx_update_location()
470    }
471
472    /// Change in focus thread and update program counters.
473    ///
474    /// # Arguments
475    ///
476    /// * `pid`: new in focus thread id
477    fn expl_ctx_switch_thread(&mut self, pid: Pid) -> Result<&ExplorationContext, Error> {
478        self.expl_context = ExplorationContext::new(
479            self.debugee
480                .get_tracee_ensure(pid)
481                .location(&self.debugee)?,
482            0,
483        );
484        Ok(&self.expl_context)
485    }
486
487    /// Continue debugee execution. Step over breakpoint if called at it.
488    /// Return if breakpoint is reached or signal occurred or debugee exit.
489    ///
490    /// **! change exploration context**
491    fn continue_execution(&mut self) -> Result<StopReason, Error> {
492        if let Some(sign_or_wp) = self.step_over_breakpoint()? {
493            match sign_or_wp {
494                StopReason::Watchpoint(pid, current_pc, ty) => {
495                    self.execute_on_watchpoint_hook(pid, current_pc, &ty)?;
496                    return Ok(StopReason::Watchpoint(pid, current_pc, ty));
497                }
498                StopReason::SignalStop(pid, sign) => {
499                    self.hooks.on_signal(sign);
500                    return Ok(StopReason::SignalStop(pid, sign));
501                }
502                _ => {
503                    unreachable!("unexpected reason")
504                }
505            }
506        }
507
508        let stop_reason = loop {
509            let event = self.debugee.trace_until_stop(TraceContext::new(
510                &self.breakpoints.active_breakpoints(),
511                &self.watchpoints,
512            ))?;
513            match event {
514                StopReason::DebugeeExit(code) => {
515                    // ignore all possible errors on watchpoints disabling
516                    _ = self.watchpoints.clear_local_disable_global(
517                        self.debugee.tracee_ctl(),
518                        &mut self.breakpoints,
519                    );
520                    // ignore all possible errors on breakpoints disabling
521                    _ = self.breakpoints.disable_all_breakpoints(&self.debugee);
522                    self.hooks.on_exit(code);
523                    break event;
524                }
525                StopReason::DebugeeStart => {
526                    self.breakpoints.enable_entry_breakpoint(&self.debugee)?;
527                    // no need to update expl context cause next stop been soon, on entry point
528                }
529                StopReason::NoSuchProcess(_) => {
530                    return Err(ProcessNotStarted);
531                }
532                StopReason::Breakpoint(pid, current_pc) => {
533                    self.expl_ctx_switch_thread(pid)?;
534
535                    if let Some(bp) = self.breakpoints.get_enabled(current_pc) {
536                        match bp.r#type() {
537                            BrkptType::EntryPoint => {
538                                print_warns!(
539                                    self.breakpoints.enable_all_breakpoints(&self.debugee)
540                                );
541                                print_warns!(self.watchpoints.refresh(&self.debugee));
542
543                                // rendezvous already available at this point
544                                let brk = self.debugee.rendezvous().r_brk();
545                                self.breakpoints.add_and_enable(Breakpoint::new_linker_map(
546                                    brk,
547                                    self.process.pid(),
548                                ))?;
549
550                                // check oracles is ready
551                                let oracles = self.oracles.clone();
552                                self.oracles = oracles.into_iter().map(|(key, (oracle, _))| {
553                                    let ready = oracle.ready_for_install(self);
554                                    if !ready {
555                                        debug!(target: "oracle", "oracle `{}` is disabled", oracle.name());
556                                    }
557
558                                    (key, (oracle, ready))
559                                }).collect();
560
561                                let oracles = self.oracles.clone();
562                                let ready_oracles = oracles.into_values().filter(|(_, a)| *a);
563                                for (oracle, _) in ready_oracles {
564                                    let spy_points = oracle.spy_points();
565                                    for request in spy_points {
566                                        weak_error!(self.set_transparent_breakpoint(request));
567                                    }
568                                }
569
570                                // ignore possible signals and watchpoints
571                                while self.step_over_breakpoint()?.is_some() {}
572                                continue;
573                            }
574                            BrkptType::LinkerMapFn => {
575                                // ignore possible signals and watchpoints
576                                while self.step_over_breakpoint()?.is_some() {}
577                                print_warns!(self.refresh_deferred());
578                                continue;
579                            }
580                            BrkptType::UserDefined => {
581                                let pc = current_pc.into_global(&self.debugee)?;
582                                let dwarf = self
583                                    .debugee
584                                    .debug_info(self.exploration_ctx().location().pc)?;
585                                let place = weak_error!(dwarf.find_place_from_pc(pc)).flatten();
586                                let func = weak_error!(dwarf.find_function_by_pc(pc))
587                                    .flatten()
588                                    .map(|f| f.die);
589                                self.hooks
590                                    .on_breakpoint(current_pc, bp.number(), place, func)
591                                    .map_err(Hook)?;
592                                break event;
593                            }
594                            BrkptType::WatchpointCompanion(_) => {
595                                unreachable!("should not coming from tracer directly");
596                            }
597                            BrkptType::Temporary | BrkptType::TemporaryAsync => {
598                                break event;
599                            }
600                            BrkptType::Transparent(callback) => {
601                                callback.clone()(self);
602
603                                match self.step_over_breakpoint()? {
604                                    Some(StopReason::SignalStop(pid, sign)) => {
605                                        self.hooks.on_signal(sign);
606                                        return Ok(StopReason::SignalStop(pid, sign));
607                                    }
608                                    Some(StopReason::Watchpoint(pid, addr, ty)) => {
609                                        self.execute_on_watchpoint_hook(pid, addr, &ty)?;
610                                        return Ok(StopReason::Watchpoint(pid, current_pc, ty));
611                                    }
612                                    _ => continue,
613                                }
614                            }
615                        }
616                    }
617                }
618                StopReason::SignalStop(pid, sign) => {
619                    if !self.debugee.is_in_progress() {
620                        continue;
621                    }
622
623                    self.expl_ctx_switch_thread(pid)?;
624                    self.hooks.on_signal(sign);
625                    break event;
626                }
627                StopReason::Watchpoint(pid, current_pc, ref ty) => {
628                    self.expl_ctx_switch_thread(pid)?;
629                    self.execute_on_watchpoint_hook(pid, current_pc, ty)?;
630                    break event;
631                }
632            }
633        };
634
635        Ok(stop_reason)
636    }
637
638    /// Restart debugee by recreating debugee process, save all user-defined breakpoints.
639    /// Return when new debugee stopped or ends.
640    ///
641    /// **! change exploration context**
642    pub fn restart_debugee(&mut self) -> Result<Pid, Error> {
643        match self.debugee.execution_status() {
644            ExecutionStatus::Unload => {
645                // all breakpoints and watchpoints already disabled by default
646            }
647            ExecutionStatus::InProgress => {
648                print_warns!(
649                    self.watchpoints.clear_local_disable_global(
650                        self.debugee.tracee_ctl(),
651                        &mut self.breakpoints
652                    )
653                );
654                print_warns!(self.breakpoints.disable_all_breakpoints(&self.debugee)?);
655            }
656            ExecutionStatus::Exited => {
657                // all breakpoints and watchpoints
658                // already disabled by [`StopReason::DebugeeExit`] handler
659            }
660        }
661
662        if !self.debugee.is_exited() {
663            let proc_pid = self.process.pid();
664            signal::kill(proc_pid, SIGKILL).map_err(|e| Syscall("kill", e))?;
665            _ = self
666                .debugee
667                .tracer_mut()
668                .resume(TraceContext::new(&[], &self.watchpoints));
669        }
670
671        self.process = self.process.install()?;
672
673        let new_debugee = self.debugee.extend(self.process.pid());
674        _ = mem::replace(&mut self.debugee, new_debugee);
675
676        // breakpoints will be enabled later, when StopReason::DebugeeStart state is reached
677        self.breakpoints.update_pid(self.process.pid());
678
679        self.hooks.on_process_install(self.process.pid(), None);
680        self.expl_context = ExplorationContext::new_non_running(self.process.pid());
681        self.continue_execution()?;
682        Ok(self.process.pid())
683    }
684
685    fn start_debugee_inner(&mut self, force: bool, dry_start: bool) -> Result<(), Error> {
686        if dry_start {
687            if (self.debugee.is_in_progress() || self.debugee.is_exited()) && !force {
688                return Err(Error::AlreadyRun);
689            }
690            return Ok(());
691        }
692
693        match self.debugee.execution_status() {
694            ExecutionStatus::Unload => {
695                self.continue_execution()?;
696            }
697            ExecutionStatus::InProgress | ExecutionStatus::Exited if force => {
698                self.restart_debugee()?;
699            }
700            ExecutionStatus::InProgress | ExecutionStatus::Exited => return Err(Error::AlreadyRun),
701        };
702
703        Ok(())
704    }
705
706    /// Start and execute debugee.
707    /// Return when debugee stopped or ends.
708    ///
709    /// # Errors
710    ///
711    /// Return error if debugee already run or execution fails.
712    pub fn start_debugee(&mut self) -> Result<(), Error> {
713        self.start_debugee_inner(false, false)
714    }
715
716    /// Start and execute debugee. Restart if debugee already started.
717    /// Return when debugee stopped or ends.
718    pub fn start_debugee_force(&mut self) -> Result<(), Error> {
719        self.start_debugee_inner(true, false)
720    }
721
722    /// Dry start debugee. Return immediately.
723    ///
724    /// # Errors
725    ///
726    /// Return error if debugee already runs.
727    pub fn dry_start_debugee(&mut self) -> Result<(), Error> {
728        self.start_debugee_inner(false, true)
729    }
730
731    /// Continue debugee execution.
732    pub fn continue_debugee(&mut self) -> Result<(), Error> {
733        disable_when_not_stared!(self);
734        self.continue_execution()?;
735        Ok(())
736    }
737
738    /// Return list of symbols matching regular expression.
739    ///
740    /// # Arguments
741    ///
742    /// * `regex`: regular expression
743    pub fn get_symbols(&self, regex: &str) -> Result<Vec<&Symbol>, Error> {
744        let regex = Regex::new(regex)?;
745
746        Ok(self
747            .debugee
748            .debug_info_all()
749            .iter()
750            .flat_map(|dwarf| dwarf.find_symbols(&regex))
751            .collect())
752    }
753
754    /// Return in focus frame information.
755    pub fn frame_info(&self) -> Result<FrameInfo, Error> {
756        disable_when_not_stared!(self);
757        self.debugee.frame_info(self.exploration_ctx())
758    }
759
760    /// Set new frame into focus.
761    ///
762    /// # Arguments
763    ///
764    /// * `num`: frame number in backtrace
765    pub fn set_frame_into_focus(&mut self, num: u32) -> Result<u32, Error> {
766        disable_when_not_stared!(self);
767        let ctx = self.exploration_ctx();
768        let backtrace = self.debugee.unwind(ctx.pid_on_focus())?;
769        let frame = backtrace.get(num as usize).ok_or(FrameNotFound(num))?;
770        self.expl_context = ExplorationContext::new(
771            Location {
772                pc: frame.ip,
773                global_pc: frame.ip.into_global(&self.debugee)?,
774                pid: ctx.pid_on_focus(),
775            },
776            num,
777        );
778        Ok(num)
779    }
780
781    /// Execute `on_step` callback with current exploration context
782    fn execute_on_step_hook(&self) -> Result<(), Error> {
783        let ctx = self.exploration_ctx();
784        let pc = ctx.location().pc;
785        let global_pc = ctx.location().global_pc;
786        let dwarf = self.debugee.debug_info(pc)?;
787        let place = weak_error!(dwarf.find_place_from_pc(global_pc)).flatten();
788        let func = weak_error!(dwarf.find_function_by_pc(global_pc))
789            .flatten()
790            .map(|f| f.die);
791        self.hooks.on_step(pc, place, func).map_err(Hook)
792    }
793
794    /// Execute `on_async_step` callback with current exploration context
795    fn execute_on_async_step_hook(&self, task_id: u64, task_completed: bool) -> Result<(), Error> {
796        let ctx = self.exploration_ctx();
797        let pc = ctx.location().pc;
798        let global_pc = ctx.location().global_pc;
799        let dwarf = self.debugee.debug_info(pc)?;
800        let place = weak_error!(dwarf.find_place_from_pc(global_pc)).flatten();
801        let func = weak_error!(dwarf.find_function_by_pc(global_pc))
802            .flatten()
803            .map(|f| f.die);
804        self.hooks
805            .on_async_step(pc, place, func, task_id, task_completed)
806            .map_err(Hook)
807    }
808
809    /// Do a single step (until debugee reaches a different source line).
810    ///
811    /// **! change exploration context**
812    pub fn step_into(&mut self) -> Result<(), Error> {
813        disable_when_not_stared!(self);
814        self.expl_ctx_restore_frame()?;
815
816        match self.step_in()? {
817            StepResult::Done => self.execute_on_step_hook(),
818            StepResult::SignalInterrupt { signal, quiet } if !quiet => {
819                self.hooks.on_signal(signal);
820                Ok(())
821            }
822            StepResult::WatchpointInterrupt {
823                pid,
824                addr,
825                ref ty,
826                quiet,
827            } if !quiet => self.execute_on_watchpoint_hook(pid, addr, ty),
828            _ => Ok(()),
829        }
830    }
831
832    /// Move in focus thread to the next instruction.
833    ///
834    /// **! change exploration context**
835    pub fn stepi(&mut self) -> Result<(), Error> {
836        disable_when_not_stared!(self);
837        self.expl_ctx_restore_frame()?;
838
839        match self.single_step_instruction()? {
840            Some(StopReason::SignalStop(_, sign)) => {
841                self.hooks.on_signal(sign);
842                Ok(())
843            }
844            Some(StopReason::Watchpoint(pid, addr, ref ty)) => {
845                self.execute_on_watchpoint_hook(pid, addr, ty)
846            }
847            _ => self.execute_on_step_hook(),
848        }
849    }
850
851    /// Return list of currently running debugee threads.
852    pub fn thread_state(&self) -> Result<Vec<ThreadSnapshot>, Error> {
853        disable_when_not_stared!(self);
854        self.debugee.thread_state(self.exploration_ctx())
855    }
856
857    /// Sets the thread into focus.
858    ///
859    /// # Arguments
860    ///
861    /// * `num`: thread number
862    pub fn set_thread_into_focus(&mut self, num: u32) -> Result<Tracee, Error> {
863        disable_when_not_stared!(self);
864        let tracee = self.debugee.get_tracee_by_num(num)?;
865        self.expl_ctx_switch_thread(tracee.pid)?;
866        Ok(tracee)
867    }
868
869    /// Return stack trace.
870    ///
871    /// # Arguments
872    ///
873    /// * `pid`: thread id
874    pub fn backtrace(&self, pid: Pid) -> Result<Backtrace, Error> {
875        disable_when_not_stared!(self);
876        self.debugee.unwind(pid)
877    }
878
879    /// Read N bytes from a debugee process.
880    ///
881    /// # Arguments
882    ///
883    /// * `addr`: address in debugee address space where reads
884    /// * `read_n`: read byte count
885    pub fn read_memory(&self, addr: usize, read_n: usize) -> Result<Vec<u8>, Error> {
886        disable_when_not_stared!(self);
887        read_memory_by_pid(self.debugee.tracee_ctl().proc_pid(), addr, read_n).map_err(Ptrace)
888    }
889
890    /// Write sizeof(uintptr_t) bytes in debugee address space.
891    /// Note that little endian byte order will be used when writing.
892    ///
893    /// # Arguments
894    ///
895    /// * `addr`: address to write
896    /// * `value`: value to write
897    pub fn write_memory(&self, addr: uintptr_t, value: uintptr_t) -> Result<(), Error> {
898        disable_when_not_stared!(self);
899        unsafe {
900            sys::ptrace::write(
901                self.debugee.tracee_ctl().proc_pid(),
902                addr as *mut c_void,
903                value as *mut c_void,
904            )
905            .map_err(Ptrace)
906        }
907    }
908
909    /// Move to higher stack frame.
910    pub fn step_out(&mut self) -> Result<(), Error> {
911        disable_when_not_stared!(self);
912        self.expl_ctx_restore_frame()?;
913        self.step_out_frame()?;
914        self.execute_on_step_hook()
915    }
916
917    /// Do debugee step (over subroutine calls to).
918    pub fn step_over(&mut self) -> Result<(), Error> {
919        disable_when_not_stared!(self);
920        self.expl_ctx_restore_frame()?;
921        match self.step_over_any()? {
922            StepResult::Done => self.execute_on_step_hook(),
923            StepResult::SignalInterrupt { signal, quiet } if !quiet => {
924                self.hooks.on_signal(signal);
925                Ok(())
926            }
927            StepResult::WatchpointInterrupt {
928                pid,
929                addr,
930                ref ty,
931                quiet,
932            } if !quiet => self.execute_on_watchpoint_hook(pid, addr, ty),
933            _ => Ok(()),
934        }
935    }
936
937    /// Reads all local variables from current function in current thread.
938    pub fn read_local_variables(&self) -> Result<Vec<QueryResult<'_>>, Error> {
939        disable_when_not_stared!(self);
940
941        let executor = variable::execute::DqeExecutor::new(self);
942        let eval_result = executor.query(&Dqe::Variable(Selector::Any))?;
943        Ok(eval_result)
944    }
945
946    /// Reads any variable from the current thread, uses a select expression to filter variables
947    /// and fetch their properties (such as structure fields or array elements).
948    ///
949    /// # Arguments
950    ///
951    /// * `select_expr`: data query expression
952    pub fn read_variable(&self, select_expr: Dqe) -> Result<Vec<QueryResult<'_>>, Error> {
953        disable_when_not_stared!(self);
954        let executor = variable::execute::DqeExecutor::new(self);
955        let eval_result = executor.query(&select_expr)?;
956        Ok(eval_result)
957    }
958
959    ///  Reads any variable from the current thread, uses a select expression to filter variables
960    /// and return their names.
961    ///
962    /// # Arguments
963    ///
964    /// * `select_expr`: data query expression
965    pub fn read_variable_names(&self, select_expr: Dqe) -> Result<Vec<String>, Error> {
966        disable_when_not_stared!(self);
967        let executor = variable::execute::DqeExecutor::new(self);
968        executor.query_names(&select_expr)
969    }
970
971    /// Reads any argument from the current function, uses a select expression to filter variables
972    /// and fetch their properties (such as structure fields or array elements).
973    ///
974    /// # Arguments
975    ///
976    /// * `select_expr`: data query expression
977    pub fn read_argument(&self, select_expr: Dqe) -> Result<Vec<QueryResult<'_>>, Error> {
978        disable_when_not_stared!(self);
979        let executor = variable::execute::DqeExecutor::new(self);
980        let eval_result = executor.query_arguments(&select_expr)?;
981        Ok(eval_result)
982    }
983
984    /// Reads any argument from the current function, uses a select expression to filter arguments
985    /// and return their names.
986    ///
987    /// # Arguments
988    ///
989    /// * `select_expr`: data query expression
990    pub fn read_argument_names(&self, select_expr: Dqe) -> Result<Vec<String>, Error> {
991        disable_when_not_stared!(self);
992        let executor = variable::execute::DqeExecutor::new(self);
993        executor.query_arguments_names(&select_expr)
994    }
995
996    /// Return following register value.
997    ///
998    /// # Arguments
999    ///
1000    /// * `register_name`: x86-64 register name (ex: `rip`)
1001    pub fn get_register_value(&self, register_name: &str) -> Result<u64, Error> {
1002        disable_when_not_stared!(self);
1003
1004        let r = Register::from_str(register_name)
1005            .map_err(|_| RegisterNameNotFound(register_name.into()))?;
1006        Ok(RegisterMap::current(self.exploration_ctx().pid_on_focus())?.value(r))
1007    }
1008
1009    /// Return registers dump for on focus thread at instruction defined by pc.
1010    ///
1011    /// # Arguments
1012    ///
1013    /// * `pc`: program counter value
1014    pub fn current_thread_registers_at_pc(
1015        &self,
1016        pc: RelocatedAddress,
1017    ) -> Result<DwarfRegisterMap, Error> {
1018        disable_when_not_stared!(self);
1019        let unwinder = DwarfUnwinder::new(&self.debugee);
1020        let location = Location {
1021            pc,
1022            global_pc: pc.into_global(&self.debugee)?,
1023            pid: self.exploration_ctx().pid_on_focus(),
1024        };
1025        Ok(unwinder
1026            // there is no chance to determine frame number,
1027            // cause pc may have owned by code outside backtrace,
1028            // so set frame num to 0 is ok
1029            .context_for(&ExplorationContext::new(location, 0))?
1030            .ok_or(UnwindNoContext)?
1031            .registers())
1032    }
1033
1034    /// Set new register value.
1035    ///
1036    /// # Arguments
1037    ///
1038    /// * `register_name`: x86-64 register name (ex: `rip`)
1039    /// * `val`: 8 bite value
1040    pub fn set_register_value(&self, register_name: &str, val: u64) -> Result<(), Error> {
1041        disable_when_not_stared!(self);
1042
1043        let in_focus_pid = self.exploration_ctx().pid_on_focus();
1044        let mut map = RegisterMap::current(in_focus_pid)?;
1045        map.update(
1046            Register::try_from(register_name)
1047                .map_err(|_| RegisterNameNotFound(register_name.into()))?,
1048            val,
1049        );
1050        map.persist(in_focus_pid)
1051    }
1052
1053    /// Return list of known files income from dwarf parser.
1054    pub fn known_files(&self) -> impl Iterator<Item = &PathBuf> {
1055        self.debugee
1056            .debug_info_all()
1057            .into_iter()
1058            .filter_map(|dwarf| dwarf.known_files().ok())
1059            .flatten()
1060    }
1061
1062    /// Return a list of shared libraries.
1063    pub fn shared_libs(&self) -> Vec<RegionInfo> {
1064        self.debugee.dump_mapped_regions()
1065    }
1066
1067    /// Return a list of disassembled instruction for a function in focus.
1068    pub fn disasm(&self) -> Result<FunctionAssembly, Error> {
1069        disable_when_not_stared!(self);
1070        self.debugee.disasm(
1071            self.exploration_ctx(),
1072            &self.breakpoints.active_breakpoints(),
1073        )
1074    }
1075
1076    /// Return two place descriptors, at the start and at the end of the current function.
1077    pub fn current_function_range(&self) -> Result<FunctionRange<'_>, Error> {
1078        disable_when_not_stared!(self);
1079        self.debugee.function_range(self.exploration_ctx())
1080    }
1081
1082    /// Return cache for called functions.
1083    fn call_cache(&self) -> RefMut<'_, CallCache> {
1084        self.call_cache.borrow_mut()
1085    }
1086}
1087
1088impl Drop for Debugger {
1089    fn drop(&mut self) {
1090        if self.process.is_external() {
1091            _ = self.breakpoints.disable_all_breakpoints(&self.debugee);
1092            // drain all watchpoints before terminating the process
1093            self.watchpoints
1094                .clear_all(self.debugee.tracee_ctl(), &mut self.breakpoints);
1095
1096            let current_tids: Vec<Pid> = self
1097                .debugee
1098                .tracee_ctl()
1099                .tracee_iter()
1100                .map(|t| t.pid)
1101                .collect();
1102
1103            if !current_tids.is_empty() {
1104                current_tids.iter().for_each(|tid| {
1105                    sys::ptrace::detach(*tid, None).expect("detach debugee");
1106                });
1107
1108                signal::kill(self.debugee.tracee_ctl().proc_pid(), Signal::SIGCONT)
1109                    .expect("kill debugee");
1110            }
1111
1112            return;
1113        }
1114
1115        match self.debugee.execution_status() {
1116            ExecutionStatus::Unload => {
1117                signal::kill(self.debugee.tracee_ctl().proc_pid(), Signal::SIGKILL)
1118                    .expect("kill debugee");
1119                waitpid(self.debugee.tracee_ctl().proc_pid(), None).expect("waiting child");
1120            }
1121            ExecutionStatus::InProgress => {
1122                // ignore all possible errors on breakpoints disabling
1123                _ = self.breakpoints.disable_all_breakpoints(&self.debugee);
1124                // drain all watchpoints before terminating the process
1125                self.watchpoints
1126                    .clear_all(self.debugee.tracee_ctl(), &mut self.breakpoints);
1127
1128                let current_tids: Vec<Pid> = self
1129                    .debugee
1130                    .tracee_ctl()
1131                    .tracee_iter()
1132                    .map(|t| t.pid)
1133                    .collect();
1134
1135                // todo currently ok only if all threads in group stop
1136                // continue all threads with SIGSTOP
1137                let prepare_stopped: Vec<_> = current_tids
1138                    .into_iter()
1139                    .filter(|&tid| sys::ptrace::cont(tid, Signal::SIGSTOP).is_ok())
1140                    .collect();
1141                let stopped: Vec<_> = prepare_stopped
1142                    .into_iter()
1143                    .filter(|&tid| waitpid(tid, None).is_ok())
1144                    .collect();
1145                // detach ptrace
1146                stopped.into_iter().for_each(|tid| {
1147                    sys::ptrace::detach(tid, None).expect("detach tracee");
1148                });
1149                // kill debugee process
1150                signal::kill(self.debugee.tracee_ctl().proc_pid(), Signal::SIGKILL)
1151                    .expect("kill debugee");
1152                let wait_result = loop {
1153                    let wait_result = waitpid(Pid::from_raw(-1), None).expect("waiting debugee");
1154                    if wait_result.pid() == Some(self.debugee.tracee_ctl().proc_pid()) {
1155                        break wait_result;
1156                    }
1157                };
1158
1159                debug_assert!(matches!(
1160                    wait_result,
1161                    WaitStatus::Signaled(_, Signal::SIGKILL, _)
1162                ));
1163            }
1164            ExecutionStatus::Exited => {}
1165        }
1166    }
1167}
1168
1169/// Read N bytes from `PID` process.
1170pub fn read_memory_by_pid(pid: Pid, addr: usize, read_n: usize) -> Result<Vec<u8>, nix::Error> {
1171    let mut read_reminder = read_n as isize;
1172    let mut result = Vec::with_capacity(read_n);
1173
1174    let single_read_size = mem::size_of::<c_long>();
1175
1176    let mut addr = addr as *mut c_long;
1177    while read_reminder > 0 {
1178        let value = sys::ptrace::read(pid, addr as *mut c_void)?;
1179        result.extend(value.to_ne_bytes().into_iter().take(read_reminder as usize));
1180
1181        read_reminder -= single_read_size as isize;
1182        addr = unsafe { addr.offset(1) };
1183    }
1184
1185    debug_assert!(result.len() == read_n);
1186
1187    Ok(result)
1188}