bugstalker/debugger/
breakpoint.rs

1use crate::debugger::Debugger;
2use crate::debugger::address::{Address, RelocatedAddress};
3use crate::debugger::debugee::Debugee;
4use crate::debugger::debugee::dwarf::DebugInformation;
5use crate::debugger::debugee::dwarf::unit::PlaceDescriptorOwned;
6use crate::debugger::error::Error;
7use crate::debugger::error::Error::{NoDebugInformation, NoSuitablePlace, PlaceNotFound};
8use nix::libc::c_void;
9use nix::sys;
10use nix::unistd::Pid;
11use std::borrow::Cow;
12use std::cell::Cell;
13use std::collections::HashMap;
14use std::fmt::{Debug, Formatter};
15use std::mem;
16use std::path::PathBuf;
17use std::rc::Rc;
18use std::sync::atomic::{AtomicU32, Ordering};
19
20enum BrkptsToAddRequest {
21    Init(Vec<Breakpoint>),
22    Uninit(Vec<UninitBreakpoint>),
23}
24
25/// Parameters for construct a transparent breakpoint.
26pub enum CreateTransparentBreakpointRequest {
27    Line(String, u64, Rc<dyn Fn(&mut Debugger)>),
28    Function(String, Rc<dyn Fn(&mut Debugger)>),
29}
30
31impl CreateTransparentBreakpointRequest {
32    /// Create request for transparent breakpoint at function in source code.
33    ///
34    /// # Arguments
35    ///
36    /// * `f`: function search template
37    /// * `cb`: callback that invoked when breakpoint is heat
38    pub fn function(f: impl ToString, cb: impl Fn(&mut Debugger) + 'static) -> Self {
39        Self::Function(f.to_string(), Rc::new(cb))
40    }
41
42    /// Create request for transparent breakpoint at file:line in source code.
43    ///
44    /// # Arguments
45    ///
46    /// * `file`: file search template
47    /// * `line`: source code line
48    /// * `cb`: callback that invoked when breakpoint is heat
49    pub fn line(file: impl ToString, line: u64, cb: impl Fn(&mut Debugger) + 'static) -> Self {
50        Self::Line(file.to_string(), line, Rc::new(cb))
51    }
52
53    /// Return underline callback.
54    fn callback(&self) -> Rc<dyn Fn(&mut Debugger)> {
55        match self {
56            CreateTransparentBreakpointRequest::Line(_, _, cb) => cb.clone(),
57            CreateTransparentBreakpointRequest::Function(_, cb) => cb.clone(),
58        }
59    }
60}
61
62impl Debugger {
63    /// Create and enable breakpoint at debugee address space
64    ///
65    /// # Arguments
66    ///
67    /// * `addr`: address where debugee must be stopped
68    ///
69    /// # Errors
70    ///
71    /// Return [`SetupError::PlaceNotFound`] if no place found for address,
72    /// return [`BreakpointError::DebugInformation`] if errors occur while fetching debug information.
73    pub fn set_breakpoint_at_addr(
74        &mut self,
75        addr: RelocatedAddress,
76    ) -> Result<BreakpointView<'_>, Error> {
77        if self.debugee.is_in_progress() {
78            let dwarf = self
79                .debugee
80                .debug_info(addr)
81                .map_err(|_| NoDebugInformation("current place"))?;
82            let global_addr = addr.into_global(&self.debugee)?;
83
84            let place = dwarf
85                .find_place_from_pc(global_addr)?
86                .map(|p| p.to_owned())
87                .ok_or(PlaceNotFound(global_addr))?;
88
89            return self.breakpoints.add_and_enable(Breakpoint::new(
90                dwarf.pathname(),
91                addr,
92                self.process.pid(),
93                Some(place),
94            ));
95        }
96
97        Ok(self.breakpoints.add_uninit(UninitBreakpoint::new(
98            None::<PathBuf>,
99            Address::Relocated(addr),
100            self.process.pid(),
101            None,
102        )))
103    }
104
105    /// Disable and remove a breakpoint by it address.
106    ///
107    /// # Arguments
108    ///
109    /// * `addr`: breakpoint address
110    pub fn remove_breakpoint(
111        &mut self,
112        addr: Address,
113    ) -> Result<Option<BreakpointView<'_>>, Error> {
114        self.breakpoints.remove_by_addr(addr)
115    }
116
117    /// Disable and remove a breakpoint by it number.
118    ///
119    /// # Arguments
120    ///
121    /// * `number`: breakpoint number
122    pub fn remove_breakpoint_by_number(
123        &mut self,
124        number: u32,
125    ) -> Result<Option<BreakpointView<'_>>, Error> {
126        self.breakpoints.remove_by_num(number)
127    }
128
129    fn create_breakpoint_at_places(
130        &self,
131        places: Vec<(&DebugInformation, Vec<PlaceDescriptorOwned>)>,
132    ) -> Result<BrkptsToAddRequest, Error> {
133        let brkpts_to_add = if self.debugee.is_in_progress() {
134            let mut to_add = Vec::new();
135            for (dwarf, places) in places {
136                for place in places {
137                    let addr = place.address.relocate_to_segment(&self.debugee, dwarf)?;
138                    to_add.push(Breakpoint::new(
139                        dwarf.pathname(),
140                        addr,
141                        self.process.pid(),
142                        Some(place),
143                    ));
144                }
145            }
146            BrkptsToAddRequest::Init(to_add)
147        } else {
148            let mut to_add = Vec::new();
149            for (dwarf, places) in places {
150                for place in places {
151                    to_add.push(UninitBreakpoint::new(
152                        Some(dwarf.pathname()),
153                        Address::Global(place.address),
154                        self.process.pid(),
155                        Some(place),
156                    ));
157                }
158            }
159            BrkptsToAddRequest::Uninit(to_add)
160        };
161        Ok(brkpts_to_add)
162    }
163
164    fn add_breakpoints(
165        &mut self,
166        brkpts_to_add: BrkptsToAddRequest,
167    ) -> Result<Vec<BreakpointView<'_>>, Error> {
168        let result: Vec<_> = match brkpts_to_add {
169            BrkptsToAddRequest::Init(init_brkpts) => {
170                let mut result_addrs = Vec::with_capacity(init_brkpts.len());
171                for brkpt in init_brkpts {
172                    let addr = brkpt.addr;
173                    self.breakpoints.add_and_enable(brkpt)?;
174                    result_addrs.push(addr);
175                }
176                result_addrs
177                    .iter()
178                    .map(|addr| {
179                        BreakpointView::from(
180                            self.breakpoints
181                                .get_enabled(*addr)
182                                .expect("breakpoint must exists"),
183                        )
184                    })
185                    .collect()
186            }
187            BrkptsToAddRequest::Uninit(uninit_brkpts) => {
188                let mut result_addrs = Vec::with_capacity(uninit_brkpts.len());
189                for brkpt in uninit_brkpts {
190                    let addr = self.breakpoints.add_uninit(brkpt).addr;
191                    result_addrs.push(addr);
192                }
193                result_addrs
194                    .iter()
195                    .map(|addr| {
196                        BreakpointView::from(
197                            self.breakpoints
198                                .get_disabled(*addr)
199                                .expect("breakpoint must exists"),
200                        )
201                    })
202                    .collect()
203            }
204        };
205
206        Ok(result)
207    }
208
209    fn addresses_for_breakpoints_at_places(
210        &self,
211        places: &[(&DebugInformation, Vec<PlaceDescriptorOwned>)],
212    ) -> Result<impl Iterator<Item = Address> + use<>, Error> {
213        let mut init_addresses_to_remove: Vec<Address> = vec![];
214        if self.debugee.is_in_progress() {
215            for (dwarf, places) in places.iter() {
216                for place in places {
217                    let addr = place.address.relocate_to_segment(&self.debugee, dwarf)?;
218                    init_addresses_to_remove.push(Address::Relocated(addr));
219                }
220            }
221        };
222
223        let uninit_addresses_to_remove: Vec<_> = places
224            .iter()
225            .flat_map(|(_, places)| places.iter().map(|place| Address::Global(place.address)))
226            .collect();
227
228        Ok(init_addresses_to_remove
229            .into_iter()
230            .chain(uninit_addresses_to_remove))
231    }
232
233    /// Remove breakpoint from instruction.
234    ///
235    /// # Arguments
236    ///
237    /// * `addresses`: address of instruction where breakpoint may be found.
238    pub fn remove_breakpoints_at_addresses(
239        &mut self,
240        addresses: impl Iterator<Item = Address>,
241    ) -> Result<Vec<BreakpointView<'_>>, Error> {
242        let mut result = vec![];
243        for to_rem in addresses {
244            if let Some(view) = self.breakpoints.remove_by_addr(to_rem)? {
245                result.push(view)
246            }
247        }
248        Ok(result)
249    }
250
251    fn search_functions(
252        &self,
253        tpl: &str,
254    ) -> Result<Vec<(&DebugInformation, Vec<PlaceDescriptorOwned>)>, Error> {
255        let dwarfs = self.debugee.debug_info_all();
256
257        dwarfs
258            .iter()
259            .filter(|dwarf| dwarf.has_debug_info() && dwarf.tpl_in_pub_names(tpl) != Some(false))
260            .map(|&dwarf| {
261                let places = dwarf.search_places_for_fn_tpl(tpl)?;
262                Ok((dwarf, places))
263            })
264            .collect()
265    }
266
267    /// Create and enable breakpoint at debugee address space on the following function start.
268    ///
269    /// # Arguments
270    ///
271    /// * `template`: template for searchin functions where debugee must be stopped
272    ///
273    /// # Errors
274    ///
275    /// Return [`SetupError::PlaceNotFound`] if function not found,
276    /// return [`BreakpointError::DebugInformation`] if errors occur while fetching debug information.
277    pub fn set_breakpoint_at_fn(
278        &mut self,
279        template: &str,
280    ) -> Result<Vec<BreakpointView<'_>>, Error> {
281        let places = self.search_functions(template)?;
282        if places.iter().all(|(_, places)| places.is_empty()) {
283            return Err(NoSuitablePlace);
284        }
285
286        let brkpts = self.create_breakpoint_at_places(places)?;
287        self.add_breakpoints(brkpts)
288    }
289
290    /// Disable and remove breakpoint from function start.
291    ///
292    /// # Arguments
293    ///
294    /// * `template`: template for searchin functions where breakpoints must be deleted
295    pub fn remove_breakpoint_at_fn(
296        &mut self,
297        template: &str,
298    ) -> Result<Vec<BreakpointView<'_>>, Error> {
299        let places = self.search_functions(template)?;
300        let addresses = self.addresses_for_breakpoints_at_places(&places)?;
301        self.remove_breakpoints_at_addresses(addresses)
302    }
303
304    fn search_lines_in_file(
305        &self,
306        debug_info: &DebugInformation,
307        fine_tpl: &str,
308        line: u64,
309    ) -> Result<Vec<PlaceDescriptorOwned>, Error> {
310        let places = debug_info.find_closest_place(fine_tpl, line)?;
311        Ok(places.into_iter().map(|p| p.to_owned()).collect())
312    }
313
314    fn search_lines(
315        &self,
316        fine_tpl: &str,
317        line: u64,
318    ) -> Result<Vec<(&DebugInformation, Vec<PlaceDescriptorOwned>)>, Error> {
319        let dwarfs = self.debugee.debug_info_all();
320
321        dwarfs
322            .iter()
323            .filter(|dwarf| dwarf.has_debug_info())
324            .map(|&dwarf| {
325                let places = self.search_lines_in_file(dwarf, fine_tpl, line)?;
326                Ok((dwarf, places))
327            })
328            .collect()
329    }
330
331    /// Create and enable breakpoint at the following file and line number.
332    ///
333    /// # Arguments
334    ///
335    /// * `fine_name`: file name (ex: "main.rs")
336    /// * `line`: line number
337    ///
338    /// # Errors
339    ///
340    /// Return [`SetupError::PlaceNotFound`] if line or file not exist,
341    /// return [`BreakpointError::DebugInformation`] if errors occur while fetching debug information.
342    pub fn set_breakpoint_at_line(
343        &mut self,
344        fine_path_tpl: &str,
345        line: u64,
346    ) -> Result<Vec<BreakpointView<'_>>, Error> {
347        let places = self.search_lines(fine_path_tpl, line)?;
348        if places.iter().all(|(_, places)| places.is_empty()) {
349            return Err(NoSuitablePlace);
350        }
351
352        let brkpts = self.create_breakpoint_at_places(places)?;
353        self.add_breakpoints(brkpts)
354    }
355
356    /// Disable and remove breakpoint at the following file and line number.
357    ///
358    /// # Arguments
359    ///
360    /// * `fine_name`: file name (ex: "main.rs")
361    /// * `line`: line number
362    pub fn remove_breakpoint_at_line(
363        &mut self,
364        fine_name_tpl: &str,
365        line: u64,
366    ) -> Result<Vec<BreakpointView<'_>>, Error> {
367        let places = self.search_lines(fine_name_tpl, line)?;
368        let addresses = self.addresses_for_breakpoints_at_places(&places)?;
369        self.remove_breakpoints_at_addresses(addresses)
370    }
371
372    /// Create and enable transparent breakpoint.
373    ///
374    /// # Arguments
375    ///
376    /// * `request`: transparent breakpoint parameters
377    pub fn set_transparent_breakpoint(
378        &mut self,
379        request: CreateTransparentBreakpointRequest,
380    ) -> Result<(), Error> {
381        // transparent breakpoint currently may be set only at main object file instructions
382        let debug_info = self.debugee.program_debug_info()?;
383
384        let places: Vec<_> = match &request {
385            CreateTransparentBreakpointRequest::Line(file, line, _) => {
386                self.search_lines_in_file(debug_info, file, *line)?
387            }
388            CreateTransparentBreakpointRequest::Function(tpl, _) => {
389                if debug_info.has_debug_info() && debug_info.tpl_in_pub_names(tpl) != Some(false) {
390                    debug_info.search_places_for_fn_tpl(tpl)?
391                } else {
392                    vec![]
393                }
394            }
395        };
396
397        if places.is_empty() {
398            return Err(NoSuitablePlace);
399        }
400
401        let callback = request.callback();
402        let breakpoints: Vec<_> = places
403            .into_iter()
404            .flat_map(|place| {
405                let addr = place
406                    .address
407                    .relocate_to_segment(&self.debugee, debug_info)
408                    .ok()?;
409                Some(Breakpoint::new_transparent(
410                    debug_info.pathname(),
411                    addr,
412                    self.process.pid(),
413                    callback.clone(),
414                ))
415            })
416            .collect();
417
418        for brkpt in breakpoints {
419            self.breakpoints.add_and_enable(brkpt)?;
420        }
421
422        Ok(())
423    }
424
425    /// Return list of breakpoints.
426    pub fn breakpoints_snapshot(&self) -> Vec<BreakpointView<'_>> {
427        self.breakpoints.snapshot()
428    }
429
430    /// Add new deferred breakpoint by address in debugee address space.
431    pub fn add_deferred_at_addr(&mut self, addr: RelocatedAddress) {
432        self.breakpoints
433            .deferred_breakpoints
434            .push(DeferredBreakpoint::at_address(addr));
435    }
436
437    /// Add new deferred breakpoint by function name.
438    pub fn add_deferred_at_function(&mut self, function: &str) {
439        self.breakpoints
440            .deferred_breakpoints
441            .push(DeferredBreakpoint::at_function(function));
442    }
443
444    /// Add new deferred breakpoint by file and line.
445    pub fn add_deferred_at_line(&mut self, file: &str, line: u64) {
446        self.breakpoints
447            .deferred_breakpoints
448            .push(DeferredBreakpoint::at_line(file, line));
449    }
450
451    /// Refresh deferred breakpoints. Trying to set breakpoint if success - remove
452    /// breakpoint from a deferred list.
453    pub fn refresh_deferred(&mut self) -> Vec<Error> {
454        let mut errors = vec![];
455
456        let mut deferred_brkpts = mem::take(&mut self.breakpoints.deferred_breakpoints);
457        deferred_brkpts.retain(|brkpt| {
458            let mb_error = match &brkpt {
459                DeferredBreakpoint::Address(addr) => self.set_breakpoint_at_addr(*addr).err(),
460                DeferredBreakpoint::Line(file, line) => {
461                    self.set_breakpoint_at_line(file, *line).err()
462                }
463                DeferredBreakpoint::Function(function) => self.set_breakpoint_at_fn(function).err(),
464            };
465
466            match mb_error {
467                None => false,
468                Some(NoSuitablePlace) => true,
469                Some(err) => {
470                    errors.push(err);
471                    true
472                }
473            }
474        });
475        self.breakpoints.deferred_breakpoints = deferred_brkpts;
476
477        errors
478    }
479}
480
481#[derive(Clone)]
482pub enum BrkptType {
483    /// Breakpoint to program entry point
484    EntryPoint,
485    /// User defined breakpoint
486    UserDefined,
487    /// This breakpoint created as a companion to the watchpoint.
488    /// Stops the program when the watchpoint expression leaves a scope where it is valid.
489    /// Contains linked watchpoint numbers.
490    WatchpointCompanion(Vec<u32>),
491    /// Auxiliary breakpoints, using, for example, in step-over implementation.
492    Temporary,
493    /// Same is temporary breakpoints, using in async steps implementation.
494    TemporaryAsync,
495    /// Breakpoint at linker internal function that will always be called when the linker
496    /// begins to map in a library or unmap it, and again when the mapping change is complete.
497    LinkerMapFn,
498    /// Transparent breakpoints are transparent for debugger user and using it by inner mechanisms
499    /// like oracles.
500    Transparent(Rc<dyn Fn(&mut Debugger)>),
501}
502
503impl Debug for BrkptType {
504    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
505        match self {
506            BrkptType::EntryPoint => f.write_str("entry-point"),
507            BrkptType::UserDefined => f.write_str("user-defined"),
508            BrkptType::Temporary => f.write_str("temporary"),
509            BrkptType::LinkerMapFn => f.write_str("linker-map"),
510            BrkptType::Transparent(_) => f.write_str("transparent"),
511            BrkptType::WatchpointCompanion(_) => f.write_str("watchpoint-companion"),
512            BrkptType::TemporaryAsync => f.write_str("temporary-async"),
513        }
514    }
515}
516
517impl PartialEq for BrkptType {
518    fn eq(&self, other: &Self) -> bool {
519        match self {
520            BrkptType::EntryPoint => {
521                matches!(other, BrkptType::EntryPoint)
522            }
523            BrkptType::UserDefined => {
524                matches!(other, BrkptType::UserDefined)
525            }
526            BrkptType::Temporary => {
527                matches!(other, BrkptType::Temporary)
528            }
529            BrkptType::TemporaryAsync => {
530                matches!(other, BrkptType::TemporaryAsync)
531            }
532            BrkptType::LinkerMapFn => {
533                matches!(other, BrkptType::LinkerMapFn)
534            }
535            BrkptType::Transparent(_) => {
536                matches!(other, BrkptType::Transparent(_))
537            }
538            BrkptType::WatchpointCompanion(nums) => {
539                matches!(other, BrkptType::WatchpointCompanion(other_nums) if nums == other_nums)
540            }
541        }
542    }
543}
544
545static GLOBAL_BP_COUNTER: AtomicU32 = AtomicU32::new(1);
546
547/// Breakpoint representation.
548#[derive(Debug, Clone)]
549pub struct Breakpoint {
550    pub addr: RelocatedAddress,
551    pub pid: Pid,
552    /// Breakpoint number, > 0 for user defined breakpoints have a number, 0 for others
553    number: u32,
554    /// Place information, None if brkpt is a temporary or entry point
555    place: Option<PlaceDescriptorOwned>,
556    pub saved_data: Cell<u8>,
557    enabled: Cell<bool>,
558    r#type: BrkptType,
559    pub debug_info_file: PathBuf,
560}
561
562impl Breakpoint {
563    pub(crate) fn is_enabled(&self) -> bool {
564        self.enabled.get()
565    }
566}
567
568impl Breakpoint {
569    const INT3: u64 = 0xCC_u64;
570
571    #[inline(always)]
572    fn new_inner(
573        addr: RelocatedAddress,
574        pid: Pid,
575        number: u32,
576        place: Option<PlaceDescriptorOwned>,
577        r#type: BrkptType,
578        debug_info_file: PathBuf,
579    ) -> Self {
580        Self {
581            addr,
582            number,
583            pid,
584            place,
585            enabled: Default::default(),
586            saved_data: Default::default(),
587            r#type,
588            debug_info_file,
589        }
590    }
591
592    #[inline(always)]
593    pub fn new(
594        debug_info_file: impl Into<PathBuf>,
595        addr: RelocatedAddress,
596        pid: Pid,
597        place: Option<PlaceDescriptorOwned>,
598    ) -> Self {
599        Self::new_inner(
600            addr,
601            pid,
602            GLOBAL_BP_COUNTER.fetch_add(1, Ordering::Relaxed),
603            place,
604            BrkptType::UserDefined,
605            debug_info_file.into(),
606        )
607    }
608
609    #[inline(always)]
610    pub fn new_entry_point(
611        debug_info_file: impl Into<PathBuf>,
612        addr: RelocatedAddress,
613        pid: Pid,
614    ) -> Self {
615        Self::new_inner(
616            addr,
617            pid,
618            0,
619            None,
620            BrkptType::EntryPoint,
621            debug_info_file.into(),
622        )
623    }
624
625    #[inline(always)]
626    pub fn new_temporary(
627        debug_info_file: impl Into<PathBuf>,
628        addr: RelocatedAddress,
629        pid: Pid,
630    ) -> Self {
631        Self::new_inner(
632            addr,
633            pid,
634            0,
635            None,
636            BrkptType::Temporary,
637            debug_info_file.into(),
638        )
639    }
640
641    #[inline(always)]
642    pub fn new_temporary_async(
643        debug_info_file: impl Into<PathBuf>,
644        addr: RelocatedAddress,
645        pid: Pid,
646    ) -> Self {
647        Self::new_inner(
648            addr,
649            pid,
650            0,
651            None,
652            BrkptType::TemporaryAsync,
653            debug_info_file.into(),
654        )
655    }
656
657    #[inline(always)]
658    pub fn new_linker_map(addr: RelocatedAddress, pid: Pid) -> Self {
659        Self::new_inner(
660            addr,
661            pid,
662            0,
663            None,
664            BrkptType::LinkerMapFn,
665            PathBuf::default(),
666        )
667    }
668
669    #[inline(always)]
670    pub fn new_transparent(
671        debug_info_file: impl Into<PathBuf>,
672        addr: RelocatedAddress,
673        pid: Pid,
674        callback: Rc<dyn Fn(&mut Debugger)>,
675    ) -> Self {
676        Self::new_inner(
677            addr,
678            pid,
679            0,
680            None,
681            BrkptType::Transparent(callback),
682            debug_info_file.into(),
683        )
684    }
685
686    #[inline(always)]
687    pub(super) fn new_watchpoint_companion(
688        registry: &BreakpointRegistry,
689        wp_num: u32,
690        addr: RelocatedAddress,
691        pid: Pid,
692    ) -> Self {
693        let (wp_nums, brkpt_num) = if let Some(Breakpoint {
694            r#type: BrkptType::WatchpointCompanion(nums),
695            number,
696            ..
697        }) = registry.get_enabled(addr)
698        {
699            // reuse if a companion already exists
700            let mut nums = nums.to_vec();
701            nums.push(wp_num);
702            (nums, *number)
703        } else {
704            (
705                vec![wp_num],
706                GLOBAL_BP_COUNTER.fetch_add(1, Ordering::Relaxed),
707            )
708        };
709
710        Self::new_inner(
711            addr,
712            pid,
713            brkpt_num,
714            None,
715            BrkptType::WatchpointCompanion(wp_nums),
716            PathBuf::default(),
717        )
718    }
719
720    #[inline(always)]
721    pub fn number(&self) -> u32 {
722        self.number
723    }
724
725    /// Return breakpoint place information.
726    ///
727    /// # Panics
728    ///
729    /// Panic if a breakpoint is not a user defined.
730    /// It is the caller's responsibility to check that the type is [`BrkptType::UserDefined`].
731    pub fn place(&self) -> Option<&PlaceDescriptorOwned> {
732        match self.r#type {
733            BrkptType::UserDefined => self.place.as_ref(),
734            BrkptType::EntryPoint
735            | BrkptType::Temporary
736            | BrkptType::TemporaryAsync
737            | BrkptType::LinkerMapFn
738            | BrkptType::WatchpointCompanion(_)
739            | BrkptType::Transparent(_) => {
740                panic!("only user defined breakpoint has a place attribute")
741            }
742        }
743    }
744
745    pub fn is_entry_point(&self) -> bool {
746        self.r#type == BrkptType::EntryPoint
747    }
748
749    pub fn is_wp_companion(&self) -> bool {
750        matches!(self.r#type, BrkptType::WatchpointCompanion(_))
751    }
752
753    #[inline(always)]
754    pub fn r#type(&self) -> &BrkptType {
755        &self.r#type
756    }
757
758    #[inline(always)]
759    pub fn is_temporary(&self) -> bool {
760        matches!(self.r#type, BrkptType::Temporary)
761    }
762
763    #[inline(always)]
764    pub fn is_temporary_async(&self) -> bool {
765        matches!(self.r#type, BrkptType::TemporaryAsync)
766    }
767
768    pub fn enable(&self) -> Result<(), Error> {
769        let addr = self.addr.as_usize() as *mut c_void;
770        let data = sys::ptrace::read(self.pid, addr).map_err(Error::Ptrace)?;
771        self.saved_data.set((data & 0xff) as u8);
772        let data_with_pb = (data & !0xff) as u64 | Self::INT3;
773        unsafe {
774            sys::ptrace::write(self.pid, addr, data_with_pb as *mut c_void)
775                .map_err(Error::Ptrace)?;
776        }
777        self.enabled.set(true);
778
779        Ok(())
780    }
781
782    pub fn disable(&self) -> Result<(), Error> {
783        let addr = self.addr.as_usize() as *mut c_void;
784        let data = sys::ptrace::read(self.pid, addr).map_err(Error::Ptrace)? as u64;
785        let restored: u64 = (data & !0xff) | self.saved_data.get() as u64;
786        unsafe {
787            sys::ptrace::write(self.pid, addr, restored as *mut c_void).map_err(Error::Ptrace)?;
788        }
789        self.enabled.set(false);
790
791        Ok(())
792    }
793}
794
795/// User defined breakpoint template,
796/// may create if debugee program not running and
797/// there is no, and there is no way to determine the relocated address.
798#[derive(Debug, Clone)]
799pub struct UninitBreakpoint {
800    addr: Address,
801    pid: Pid,
802    number: u32,
803    place: Option<PlaceDescriptorOwned>,
804    r#type: BrkptType,
805    debug_info_file: Option<PathBuf>,
806}
807
808impl UninitBreakpoint {
809    fn new_inner(
810        addr: Address,
811        pid: Pid,
812        number: u32,
813        place: Option<PlaceDescriptorOwned>,
814        r#type: BrkptType,
815        debug_info_file: Option<PathBuf>,
816    ) -> Self {
817        Self {
818            addr,
819            pid,
820            number,
821            place,
822            r#type,
823            debug_info_file,
824        }
825    }
826
827    pub fn new(
828        debug_info_file: Option<impl Into<PathBuf>>,
829        addr: Address,
830        pid: Pid,
831        place: Option<PlaceDescriptorOwned>,
832    ) -> Self {
833        Self::new_inner(
834            addr,
835            pid,
836            GLOBAL_BP_COUNTER.fetch_add(1, Ordering::Relaxed),
837            place,
838            BrkptType::UserDefined,
839            debug_info_file.map(|path| path.into()),
840        )
841    }
842
843    pub fn new_inherited(addr: Address, brkpt: Breakpoint) -> Self {
844        Self::new_inner(
845            addr,
846            brkpt.pid,
847            brkpt.number,
848            brkpt.place,
849            BrkptType::UserDefined,
850            Some(brkpt.debug_info_file),
851        )
852    }
853
854    pub fn new_entry_point(
855        debug_info_file: Option<impl Into<PathBuf>>,
856        addr: Address,
857        pid: Pid,
858    ) -> Self {
859        Self::new_inner(
860            addr,
861            pid,
862            0,
863            None,
864            BrkptType::EntryPoint,
865            debug_info_file.map(|path| path.into()),
866        )
867    }
868
869    /// Return a breakpoint created from template.
870    ///
871    /// # Panics
872    ///
873    /// Method will panic if calling with unloaded debugee.
874    pub fn try_into_brkpt(self, debugee: &Debugee) -> Result<Breakpoint, Error> {
875        debug_assert!(
876            self.r#type == BrkptType::EntryPoint || self.r#type == BrkptType::UserDefined
877        );
878
879        let (global_addr, rel_addr) = match self.addr {
880            Address::Relocated(addr) => (addr.into_global(debugee)?, Some(addr)),
881            Address::Global(addr) => (addr, None),
882        };
883
884        let dwarf = match self.debug_info_file {
885            None if self.r#type == BrkptType::EntryPoint => debugee.program_debug_info().ok(),
886            None => rel_addr.and_then(|addr| debugee.debug_info(addr).ok()),
887            Some(path) => debugee.debug_info_from_file(&path).ok(),
888        }
889        .ok_or(NoDebugInformation("breakpoint"))?;
890
891        let place = if self.r#type == BrkptType::UserDefined {
892            if self.place.is_some() {
893                self.place
894            } else {
895                Some(
896                    dwarf
897                        .find_place_from_pc(global_addr)?
898                        .ok_or(PlaceNotFound(global_addr))?
899                        .to_owned(),
900                )
901            }
902        } else {
903            None
904        };
905
906        Ok(Breakpoint::new_inner(
907            global_addr.relocate_to_segment(debugee, dwarf)?,
908            self.pid,
909            self.number,
910            place,
911            self.r#type,
912            dwarf.pathname().into(),
913        ))
914    }
915}
916
917pub struct BreakpointView<'a> {
918    pub addr: Address,
919    pub number: u32,
920    pub place: Option<Cow<'a, PlaceDescriptorOwned>>,
921}
922
923impl From<Breakpoint> for BreakpointView<'_> {
924    fn from(brkpt: Breakpoint) -> Self {
925        Self {
926            addr: Address::Relocated(brkpt.addr),
927            number: brkpt.number,
928            place: brkpt.place.map(Cow::Owned),
929        }
930    }
931}
932
933impl<'a> From<&'a Breakpoint> for BreakpointView<'a> {
934    fn from(brkpt: &'a Breakpoint) -> Self {
935        Self {
936            addr: Address::Relocated(brkpt.addr),
937            number: brkpt.number,
938            place: brkpt.place.as_ref().map(Cow::Borrowed),
939        }
940    }
941}
942
943impl From<UninitBreakpoint> for BreakpointView<'_> {
944    fn from(brkpt: UninitBreakpoint) -> Self {
945        Self {
946            addr: brkpt.addr,
947            number: brkpt.number,
948            place: brkpt.place.map(Cow::Owned),
949        }
950    }
951}
952
953impl<'a> From<&'a UninitBreakpoint> for BreakpointView<'a> {
954    fn from(brkpt: &'a UninitBreakpoint) -> Self {
955        Self {
956            addr: brkpt.addr,
957            number: brkpt.number,
958            place: brkpt.place.as_ref().map(Cow::Borrowed),
959        }
960    }
961}
962
963#[derive(Clone, Debug, PartialEq)]
964pub struct BreakpointViewOwned {
965    pub addr: Address,
966    pub number: u32,
967    pub place: Option<PlaceDescriptorOwned>,
968}
969
970impl BreakpointView<'_> {
971    pub fn to_owned(&self) -> BreakpointViewOwned {
972        BreakpointViewOwned {
973            addr: self.addr,
974            number: self.number,
975            place: self.place.clone().map(|p| p.into_owned()),
976        }
977    }
978}
979
980/// User breakpoint deferred until a shared library with target place will be loaded.
981pub enum DeferredBreakpoint {
982    Address(RelocatedAddress),
983    Line(String, u64),
984    Function(String),
985}
986
987impl DeferredBreakpoint {
988    pub fn at_address(addr: RelocatedAddress) -> DeferredBreakpoint {
989        DeferredBreakpoint::Address(addr)
990    }
991
992    pub fn at_line(file: &str, line: u64) -> DeferredBreakpoint {
993        DeferredBreakpoint::Line(file.to_string(), line)
994    }
995
996    pub fn at_function(function: &str) -> DeferredBreakpoint {
997        DeferredBreakpoint::Function(function.to_string())
998    }
999}
1000
1001/// Container for application breakpoints.
1002/// Supports active breakpoints and uninit breakpoints.
1003#[derive(Default)]
1004pub struct BreakpointRegistry {
1005    /// Active breakpoint list.
1006    breakpoints: HashMap<RelocatedAddress, Breakpoint>,
1007    /// Non-active breakpoint list.
1008    disabled_breakpoints: HashMap<Address, UninitBreakpoint>,
1009    /// List of deferred breakpoints, refresh all time when shared library loading.
1010    deferred_breakpoints: Vec<DeferredBreakpoint>,
1011}
1012
1013impl BreakpointRegistry {
1014    /// Add a new breakpoint to registry and enable it.
1015    pub fn add_and_enable(&mut self, brkpt: Breakpoint) -> Result<BreakpointView<'_>, Error> {
1016        if let Some(existed) = self.breakpoints.get(&brkpt.addr) {
1017            existed.disable()?;
1018        }
1019        brkpt.enable()?;
1020
1021        let addr = brkpt.addr;
1022        self.breakpoints.insert(addr, brkpt);
1023        Ok((&self.breakpoints[&addr]).into())
1024    }
1025
1026    pub fn get_enabled(&self, addr: RelocatedAddress) -> Option<&Breakpoint> {
1027        self.breakpoints.get(&addr)
1028    }
1029
1030    pub fn get_disabled(&self, addr: Address) -> Option<&UninitBreakpoint> {
1031        self.disabled_breakpoints.get(&addr)
1032    }
1033
1034    /// Add uninit breakpoint, this means that breakpoint will be created later.
1035    pub fn add_uninit(&mut self, brkpt: UninitBreakpoint) -> BreakpointView<'_> {
1036        let addr = brkpt.addr;
1037        self.disabled_breakpoints.insert(addr, brkpt);
1038        (&self.disabled_breakpoints[&addr]).into()
1039    }
1040
1041    /// Decrease watchpoint reference counter for companion breakpoint.
1042    /// Remove breakpoint if there are no more references to watchpoints.
1043    ///
1044    /// # Arguments
1045    ///
1046    /// * `num`: companion breakpoint number
1047    /// * `target_wp_num`: watchpoint number
1048    ///
1049    /// # Panics
1050    ///
1051    /// Panic if breakpoint is not a companion type.
1052    pub fn decrease_companion_rc(&mut self, num: u32, target_wp_num: u32) -> Result<(), Error> {
1053        let Some(companion) = self
1054            .breakpoints
1055            .values_mut()
1056            .find(|brkpt| brkpt.number == num)
1057        else {
1058            return Ok(());
1059        };
1060
1061        let BrkptType::WatchpointCompanion(wps) = companion.r#type() else {
1062            panic!("not a watchpoint companion");
1063        };
1064        debug_assert!(wps.contains(&target_wp_num));
1065
1066        if wps.len() == 1 && wps[0] == target_wp_num {
1067            self.remove_by_num(num)?;
1068        } else {
1069            let new_wps: Vec<_> = wps
1070                .iter()
1071                .filter(|&&wp_num| wp_num != target_wp_num)
1072                .copied()
1073                .collect();
1074            companion.r#type = BrkptType::WatchpointCompanion(new_wps);
1075        };
1076
1077        Ok(())
1078    }
1079
1080    /// Remove breakpoint or uninit breakpoint from registry.
1081    pub fn remove_by_addr(
1082        &mut self,
1083        addr: Address,
1084    ) -> Result<Option<BreakpointView<'static>>, Error> {
1085        if let Some(brkpt) = self.disabled_breakpoints.remove(&addr) {
1086            return Ok(Some(brkpt.into()));
1087        }
1088        if let Address::Relocated(addr) = addr
1089            && let Some(brkpt) = self.breakpoints.remove(&addr)
1090        {
1091            if brkpt.is_enabled() {
1092                brkpt.disable()?;
1093            }
1094            return Ok(Some(brkpt.into()));
1095        }
1096        Ok(None)
1097    }
1098
1099    /// Remove enabled breakpoint from registry by it number.
1100    pub fn remove_by_num(&mut self, number: u32) -> Result<Option<BreakpointView<'static>>, Error> {
1101        if let Some(addr) = self.disabled_breakpoints.iter().find_map(|(addr, brkpt)| {
1102            if brkpt.number == number {
1103                return Some(addr);
1104            }
1105            None
1106        }) {
1107            return self.remove_by_addr(*addr);
1108        }
1109
1110        if let Some(addr) = self.breakpoints.iter().find_map(|(addr, brkpt)| {
1111            if brkpt.number == number {
1112                return Some(addr);
1113            }
1114            None
1115        }) {
1116            return self.remove_by_addr(Address::Relocated(*addr));
1117        }
1118
1119        Ok(None)
1120    }
1121
1122    /// Enable currently disabled breakpoints.
1123    pub fn enable_all_breakpoints(&mut self, debugee: &Debugee) -> Vec<Error> {
1124        let mut errors = vec![];
1125        let mut disabled_breakpoints = mem::take(&mut self.disabled_breakpoints);
1126        for (_, uninit_brkpt) in disabled_breakpoints.drain() {
1127            let brkpt = match uninit_brkpt.try_into_brkpt(debugee) {
1128                Ok(b) => b,
1129                Err(e) => {
1130                    errors.push(e);
1131                    continue;
1132                }
1133            };
1134
1135            if let Err(e) = self.add_and_enable(brkpt) {
1136                errors.push(e);
1137            }
1138        }
1139        errors
1140    }
1141
1142    /// Enable entry point breakpoint if it disabled.
1143    pub fn enable_entry_breakpoint(&mut self, debugee: &Debugee) -> Result<(), Error> {
1144        let Some((&key, _)) = self
1145            .disabled_breakpoints
1146            .iter()
1147            .find(|(_, brkpt)| brkpt.r#type == BrkptType::EntryPoint)
1148        else {
1149            return Ok(());
1150        };
1151
1152        let uninit_entry_point_brkpt = self.disabled_breakpoints.remove(&key).unwrap();
1153
1154        let brkpt = uninit_entry_point_brkpt.try_into_brkpt(debugee)?;
1155        self.add_and_enable(brkpt)?;
1156
1157        Ok(())
1158    }
1159
1160    /// Disable currently enabled breakpoints.
1161    pub fn disable_all_breakpoints(&mut self, debugee: &Debugee) -> Result<Vec<Error>, Error> {
1162        let mut errors = vec![];
1163        let mut breakpoints = std::mem::take(&mut self.breakpoints);
1164        for (_, brkpt) in breakpoints.drain() {
1165            if let Err(e) = brkpt.disable() {
1166                errors.push(e);
1167            }
1168
1169            let addr = Address::Global(brkpt.addr.into_global(debugee)?);
1170            match brkpt.r#type {
1171                BrkptType::EntryPoint => {
1172                    self.add_uninit(UninitBreakpoint::new_entry_point(
1173                        Some(brkpt.debug_info_file),
1174                        addr,
1175                        brkpt.pid,
1176                    ));
1177                }
1178                BrkptType::UserDefined => {
1179                    self.add_uninit(UninitBreakpoint::new_inherited(addr, brkpt));
1180                }
1181                BrkptType::Temporary
1182                | BrkptType::TemporaryAsync
1183                | BrkptType::LinkerMapFn
1184                | BrkptType::Transparent(_)
1185                | BrkptType::WatchpointCompanion(_) => {}
1186            }
1187        }
1188        Ok(errors)
1189    }
1190
1191    /// Update pid of all breakpoints.
1192    pub fn update_pid(&mut self, new_pid: Pid) {
1193        self.breakpoints
1194            .iter_mut()
1195            .for_each(|(_, brkpt)| brkpt.pid = new_pid);
1196        self.disabled_breakpoints
1197            .iter_mut()
1198            .for_each(|(_, brkpt)| brkpt.pid = new_pid);
1199    }
1200
1201    /// Return vector of currently enabled breakpoints.
1202    pub fn active_breakpoints(&self) -> Vec<&Breakpoint> {
1203        self.breakpoints.values().collect()
1204    }
1205
1206    /// Return view for all user-defined breakpoints.
1207    pub fn snapshot(&self) -> Vec<BreakpointView<'_>> {
1208        let active_bps = self
1209            .breakpoints
1210            .values()
1211            .filter(|&bp| bp.r#type() == &BrkptType::UserDefined)
1212            .map(BreakpointView::from);
1213        let disabled_brkpts = self
1214            .disabled_breakpoints
1215            .values()
1216            .filter(|&bp| bp.r#type == BrkptType::UserDefined)
1217            .map(BreakpointView::from);
1218
1219        let mut snap = active_bps.chain(disabled_brkpts).collect::<Vec<_>>();
1220        snap.sort_by(|a, b| a.number.cmp(&b.number));
1221
1222        snap
1223    }
1224}