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
25pub enum CreateTransparentBreakpointRequest {
27 Line(String, u64, Rc<dyn Fn(&mut Debugger)>),
28 Function(String, Rc<dyn Fn(&mut Debugger)>),
29}
30
31impl CreateTransparentBreakpointRequest {
32 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 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 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 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 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 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 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 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 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 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 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 pub fn set_transparent_breakpoint(
378 &mut self,
379 request: CreateTransparentBreakpointRequest,
380 ) -> Result<(), Error> {
381 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 pub fn breakpoints_snapshot(&self) -> Vec<BreakpointView<'_>> {
427 self.breakpoints.snapshot()
428 }
429
430 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 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 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 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 EntryPoint,
485 UserDefined,
487 WatchpointCompanion(Vec<u32>),
491 Temporary,
493 TemporaryAsync,
495 LinkerMapFn,
498 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#[derive(Debug, Clone)]
549pub struct Breakpoint {
550 pub addr: RelocatedAddress,
551 pub pid: Pid,
552 number: u32,
554 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 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 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#[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 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
980pub 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#[derive(Default)]
1004pub struct BreakpointRegistry {
1005 breakpoints: HashMap<RelocatedAddress, Breakpoint>,
1007 disabled_breakpoints: HashMap<Address, UninitBreakpoint>,
1009 deferred_breakpoints: Vec<DeferredBreakpoint>,
1011}
1012
1013impl BreakpointRegistry {
1014 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 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 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 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 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 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 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 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 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 pub fn active_breakpoints(&self) -> Vec<&Breakpoint> {
1203 self.breakpoints.values().collect()
1204 }
1205
1206 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}