1use crate::debugger::Error::Hook;
2use crate::debugger::address::{GlobalAddress, RelocatedAddress};
3use crate::debugger::breakpoint::{Breakpoint, BreakpointRegistry};
4use crate::debugger::debugee::dwarf::r#type::TypeIdentity;
5use crate::debugger::debugee::tracee::TraceeCtl;
6use crate::debugger::debugee::tracer::WatchpointHitType;
7use crate::debugger::debugee::{Debugee, Location};
8use crate::debugger::register::debug::{
9 BreakCondition, BreakSize, DebugRegisterNumber, HardwareDebugState,
10};
11use crate::debugger::unwind::FrameID;
12use crate::debugger::variable::dqe::Dqe;
13use crate::debugger::variable::execute::{DqeExecutor, QueryResult};
14use crate::debugger::variable::value::{ScalarValue, SupportedScalar, Value};
15use crate::debugger::{Debugger, Error, ExplorationContext, Tracee};
16use crate::{debugger, disable_when_not_stared, weak_error};
17use log::error;
18use nix::unistd::Pid;
19use std::borrow::Cow;
20use std::mem;
21use std::sync::atomic::{AtomicU32, Ordering};
22
23#[derive(Debug)]
24struct ExpressionTarget {
25 source_string: String,
27 dqe: Dqe,
29 last_value: Option<Value>,
31 frame_id: Option<FrameID>,
34 tid: Pid,
36 companion: Option<u32>,
39}
40
41impl ExpressionTarget {
42 fn underlying_dqe(&self) -> &Dqe {
43 let Dqe::Address(ref underlying_dqe) = self.dqe else {
44 unreachable!("infallible: watchpoint always contains an address DQE");
45 };
46 underlying_dqe
47 }
48}
49
50#[derive(Debug)]
51struct AddressTarget {
52 last_value: Option<Value>,
55}
56
57impl AddressTarget {
58 fn refresh_last_value(&mut self, pid: Pid, hw: &HardwareBreakpoint) -> Option<Value> {
59 let read_size = match hw.size {
60 BreakSize::Bytes1 => 1,
61 BreakSize::Bytes2 => 2,
62 BreakSize::Bytes8 => 8,
63 BreakSize::Bytes4 => 4,
64 };
65
66 let maybe_data = weak_error!(debugger::read_memory_by_pid(
67 pid,
68 hw.address.as_usize(),
69 read_size
70 ));
71 let new_val = maybe_data.map(|data| {
72 let (t, u) = match hw.size {
73 BreakSize::Bytes1 => (
74 "u8",
75 SupportedScalar::U8(u8::from_ne_bytes(
76 data.try_into().expect("unexpected size"),
77 )),
78 ),
79 BreakSize::Bytes2 => (
80 "u16",
81 SupportedScalar::U16(u16::from_ne_bytes(
82 data.try_into().expect("unexpected size"),
83 )),
84 ),
85 BreakSize::Bytes8 => (
86 "u64",
87 SupportedScalar::U64(u64::from_ne_bytes(
88 data.try_into().expect("unexpected size"),
89 )),
90 ),
91 BreakSize::Bytes4 => (
92 "u32",
93 SupportedScalar::U32(u32::from_ne_bytes(
94 data.try_into().expect("unexpected size"),
95 )),
96 ),
97 };
98 Value::Scalar(ScalarValue {
99 value: Some(u),
100 type_ident: TypeIdentity::no_namespace(t),
101 type_id: None,
102 raw_address: None,
103 })
104 });
105
106 mem::replace(&mut self.last_value, new_val)
107 }
108}
109
110#[derive(Debug)]
111enum Subject {
112 Expression(ExpressionTarget),
114 Address(AddressTarget),
116}
117
118#[derive(Debug)]
119struct HardwareBreakpoint {
120 address: RelocatedAddress,
122 size: BreakSize,
124 register: Option<DebugRegisterNumber>,
126 condition: BreakCondition,
128}
129
130impl HardwareBreakpoint {
131 fn new(address: RelocatedAddress, size: BreakSize, condition: BreakCondition) -> Self {
132 Self {
133 address,
134 size,
135 register: None,
136 condition,
137 }
138 }
139
140 fn enable(&mut self, tracee_ctl: &TraceeCtl) -> Result<HardwareDebugState, Error> {
141 let mut state = HardwareDebugState::current(tracee_ctl.proc_pid())?;
142
143 let free_register = [
145 DebugRegisterNumber::DR0,
146 DebugRegisterNumber::DR1,
147 DebugRegisterNumber::DR2,
148 DebugRegisterNumber::DR3,
149 ]
150 .into_iter()
151 .find(|&dr_num| !state.dr7.dr_enabled(dr_num, false))
152 .ok_or(Error::WatchpointLimitReached)?;
153
154 state.address_regs[free_register as usize] = self.address.as_usize();
156 state
157 .dr7
158 .configure_bp(free_register, self.condition, self.size);
159 state.dr7.set_dr(free_register, false, true);
160 tracee_ctl.tracee_iter().for_each(|t| {
161 if let Err(e) = state.sync(t.pid) {
162 error!("set hardware breakpoint for thread {}: {e}", t.pid)
163 }
164 });
165 self.register = Some(free_register);
166
167 Ok(state)
168 }
169
170 fn disable(&mut self, tracee_ctl: &TraceeCtl) -> Result<HardwareDebugState, Error> {
171 let mut state = HardwareDebugState::current(tracee_ctl.proc_pid())?;
172 let register = self.register.expect("should exist");
173 state.dr7.set_dr(register, false, false);
174 tracee_ctl.tracee_iter().for_each(|t| {
175 if let Err(e) = state.sync(t.pid) {
176 error!("remove hardware breakpoint for thread {}: {e}", t.pid)
177 }
178 });
179 self.register = None;
180 Ok(state)
181 }
182
183 fn address_already_observed(
184 tracee_ctl: &TraceeCtl,
185 address: RelocatedAddress,
186 ) -> Result<bool, Error> {
187 let state = HardwareDebugState::current(tracee_ctl.proc_pid())?;
188 Ok(state
189 .address_regs
190 .iter()
191 .enumerate()
192 .any(|(dr, in_use_addr)| {
193 let enabled = state.dr7.dr_enabled(
194 DebugRegisterNumber::from_repr(dr).expect("infallible"),
195 false,
196 );
197 enabled && *in_use_addr == address.as_usize()
198 }))
199 }
200}
201
202static GLOBAL_WP_COUNTER: AtomicU32 = AtomicU32::new(1);
203
204#[derive(Debug)]
206pub struct Watchpoint {
207 number: u32,
209 hw: HardwareBreakpoint,
211 subject: Subject,
213 temporary: bool,
215}
216
217fn call_with_context<F, T>(debugger: &mut Debugger, ctx: ExplorationContext, f: F) -> T
218where
219 F: FnOnce(&Debugger) -> T,
220{
221 let old_ctx = mem::replace(&mut debugger.expl_context, ctx);
222 let result = f(debugger);
223 debugger.expl_context = old_ctx;
224 result
225}
226
227impl Watchpoint {
228 pub fn is_temporary(&self) -> bool {
229 self.temporary
230 }
231
232 fn execute_dqe(debugger: &Debugger, dqe: Dqe) -> Result<QueryResult<'_>, Error> {
233 let executor = DqeExecutor::new(debugger);
234
235 let mut evaluation_on_vars_results = executor.query(&dqe)?;
238 let mut evaluation_on_args_results;
239 let expr_result = match evaluation_on_vars_results.len() {
240 0 => {
241 evaluation_on_args_results = executor.query_arguments(&dqe)?;
242 match evaluation_on_args_results.len() {
243 0 => return Err(Error::WatchSubjectNotFound),
244 1 => evaluation_on_args_results.pop().expect("infallible"),
245 _ => return Err(Error::WatchpointCollision),
246 }
247 }
248 1 => evaluation_on_vars_results.pop().expect("infallible"),
249 _ => return Err(Error::WatchpointCollision),
250 };
251 Ok(expr_result)
252 }
253
254 pub fn from_dqe(
263 debugger: &mut Debugger,
264 expr_source: &str,
265 dqe: Dqe,
266 condition: BreakCondition,
267 ) -> Result<(HardwareDebugState, Self), Error> {
268 let dqe = Dqe::Address(dqe.boxed());
270
271 let address_dqe_result = Self::execute_dqe(debugger, dqe.clone())?;
272 let Value::Pointer(ptr) = address_dqe_result.value() else {
273 unreachable!("infallible: address DQE always return a pointer")
274 };
275
276 let address = RelocatedAddress::from(ptr.value.ok_or(Error::WatchpointNoAddress)? as usize);
277 if HardwareBreakpoint::address_already_observed(debugger.debugee.tracee_ctl(), address)? {
278 return Err(Error::AddressAlreadyObserved);
279 }
280
281 let size = ptr.target_type_size.ok_or(Error::WatchpointUndefinedSize)?;
282 if size > u8::MAX as u64 {
283 return Err(Error::WatchpointWrongSize);
284 };
285 let size = BreakSize::try_from(size as u8)?;
286
287 let mut end_of_scope_brkpt = None;
288 let mut frame_id = None;
289 if let Some(scope) = address_dqe_result.scope() {
290 let expl_ctx = debugger.exploration_ctx();
292 let frame_num = expl_ctx.frame_num();
293 let backtrace = debugger.backtrace(expl_ctx.pid_on_focus())?;
294 frame_id = backtrace
295 .get(frame_num as usize)
296 .ok_or(Error::FrameNotFound(frame_num))?
297 .id();
298
299 let pc = expl_ctx.location().pc;
300 let dwarf = debugger.debugee.debug_info(pc)?;
301
302 let mut best_place = None;
305
306 for range in scope.iter() {
307 let maybe_place = dwarf.find_exact_place_from_pc(GlobalAddress::from(range.end))?;
308 if let Some(range_end_place) = maybe_place {
309 let mut place = range_end_place.clone();
310
311 let mut suitable_place = loop {
321 if place.is_stmt {
322 break Some(place);
323 }
324 if place.epilog_begin || place.end_sequence {
325 break None;
326 }
327 match place.next() {
328 None => break None,
329 Some(p) => place = p,
330 }
331 };
332
333 if suitable_place.is_none()
334 && let Some(mut place) = range_end_place.prev()
335 {
336 suitable_place = loop {
337 if place.address <= GlobalAddress::from(range.begin) {
338 break None;
339 }
340 if place.is_stmt {
341 break Some(place);
342 }
343 if place.prolog_end || place.end_sequence {
344 break None;
345 }
346 match place.prev() {
347 None => break None,
348 Some(p) => place = p,
349 }
350 };
351 }
352
353 if let Some(suitable_place) = suitable_place {
354 match best_place {
355 None => best_place = Some(suitable_place),
356 Some(max) if max.line_number <= suitable_place.line_number => {
357 best_place = Some(suitable_place)
358 }
359 _ => {}
360 }
361 }
362 }
363 }
364
365 let best_place = best_place.ok_or(Error::UnknownScope)?;
366
367 let end_of_scope = best_place
368 .address
369 .relocate_to_segment(&debugger.debugee, dwarf)?;
370 let next_wp_num = GLOBAL_WP_COUNTER.load(Ordering::Relaxed);
371 let brkpt = Breakpoint::new_watchpoint_companion(
372 &debugger.breakpoints,
373 next_wp_num,
374 end_of_scope,
375 expl_ctx.pid_on_focus(),
376 );
377 let brkpt_view = debugger.breakpoints.add_and_enable(brkpt)?;
378 end_of_scope_brkpt = Some(brkpt_view.number);
379 }
380
381 let mut target = ExpressionTarget {
382 source_string: expr_source.to_string(),
383 dqe,
384 last_value: None,
385 frame_id,
386 tid: debugger.exploration_ctx().pid_on_focus(),
387 companion: end_of_scope_brkpt,
388 };
389 let underlying_dqe = target.underlying_dqe().clone();
390 let var = Self::execute_dqe(debugger, underlying_dqe)
391 .map(|ev| ev.into_value())
392 .ok();
393 target.last_value = var;
394
395 let mut hw_brkpt = HardwareBreakpoint::new(address, size, condition);
396 let state = hw_brkpt.enable(debugger.debugee.tracee_ctl())?;
397
398 let this = Self {
399 number: GLOBAL_WP_COUNTER.fetch_add(1, Ordering::Relaxed),
400 hw: hw_brkpt,
401 subject: Subject::Expression(target),
402 temporary: false,
403 };
404 Ok((state, this))
405 }
406
407 fn from_raw_addr(
417 tracee_ctl: &TraceeCtl,
418 addr: RelocatedAddress,
419 size: BreakSize,
420 condition: BreakCondition,
421 temporary: bool,
422 ) -> Result<(HardwareDebugState, Self), Error> {
423 debug_assert!(
424 condition == BreakCondition::DataWrites || condition == BreakCondition::DataReadsWrites
425 );
426 if HardwareBreakpoint::address_already_observed(tracee_ctl, addr)? {
427 return Err(Error::AddressAlreadyObserved);
428 }
429 let mut hw_brkpt = HardwareBreakpoint::new(addr, size, condition);
430 let state = hw_brkpt.enable(tracee_ctl)?;
431
432 let mut target = AddressTarget { last_value: None };
433 target.refresh_last_value(tracee_ctl.proc_pid(), &hw_brkpt);
434
435 let this = Self {
436 number: GLOBAL_WP_COUNTER.fetch_add(1, Ordering::Relaxed),
437 hw: hw_brkpt,
438 subject: Subject::Address(target),
439 temporary,
440 };
441
442 Ok((state, this))
443 }
444
445 pub fn register(&self) -> Option<DebugRegisterNumber> {
447 self.hw.register
448 }
449
450 pub fn number(&self) -> u32 {
452 self.number
453 }
454
455 fn last_value(&self) -> Option<&Value> {
456 match &self.subject {
457 Subject::Expression(e) => e.last_value.as_ref(),
458 Subject::Address(_) => None,
459 }
460 }
461
462 fn scoped(&self) -> bool {
463 matches!(
464 self.subject,
465 Subject::Expression(ExpressionTarget {
466 companion: Some(_),
467 ..
468 })
469 )
470 }
471
472 pub fn disable(
479 &mut self,
480 tracee_ctl: &TraceeCtl,
481 breakpoints: &mut BreakpointRegistry,
482 ) -> Result<HardwareDebugState, Error> {
483 let state = self.hw.disable(tracee_ctl)?;
485
486 if let Subject::Expression(ref e) = self.subject {
487 if let Some(brkpt) = e.companion {
489 breakpoints.decrease_companion_rc(brkpt, self.number)?;
490 }
491 }
492 Ok(state)
493 }
494
495 fn refresh(&mut self, tracee_ctl: &TraceeCtl) -> Result<HardwareDebugState, Error> {
497 if let Subject::Expression(ref mut expr_t) = self.subject {
498 expr_t.last_value = None;
499 }
500 let state = self.hw.enable(tracee_ctl)?;
501 Ok(state)
502 }
503}
504
505pub struct WatchpointView<'a> {
507 pub number: u32,
508 pub address: RelocatedAddress,
509 pub condition: BreakCondition,
510 pub source_dqe: Option<Cow<'a, str>>,
511 pub size: BreakSize,
512}
513
514impl<'a> From<&'a Watchpoint> for WatchpointView<'a> {
515 fn from(wp: &'a Watchpoint) -> Self {
516 Self {
517 number: wp.number,
518 address: wp.hw.address,
519 condition: wp.hw.condition,
520 source_dqe: if let Subject::Expression(ref t) = wp.subject {
521 Some(Cow::Borrowed(&t.source_string))
522 } else {
523 None
524 },
525 size: wp.hw.size,
526 }
527 }
528}
529
530impl From<Watchpoint> for WatchpointView<'_> {
531 fn from(mut wp: Watchpoint) -> Self {
532 Self {
533 number: wp.number,
534 address: wp.hw.address,
535 condition: wp.hw.condition,
536 source_dqe: if let Subject::Expression(ref mut t) = wp.subject {
537 let s = mem::take(&mut t.source_string);
538 Some(Cow::Owned(s))
539 } else {
540 None
541 },
542 size: wp.hw.size,
543 }
544 }
545}
546
547impl WatchpointView<'_> {
548 pub fn to_owned(&self) -> WatchpointViewOwned {
549 WatchpointViewOwned {
550 number: self.number,
551 address: self.address,
552 condition: self.condition,
553 source_dqe: self.source_dqe.as_ref().map(|dqe| dqe.to_string()),
554 size: self.size,
555 }
556 }
557}
558
559#[derive(Clone, Debug, PartialEq)]
560pub struct WatchpointViewOwned {
561 pub number: u32,
562 pub address: RelocatedAddress,
563 pub condition: BreakCondition,
564 pub source_dqe: Option<String>,
565 pub size: BreakSize,
566}
567
568#[derive(Default)]
570pub struct WatchpointRegistry {
571 watchpoints: Vec<Watchpoint>,
573 last_seen_state: Option<HardwareDebugState>,
576}
577
578impl WatchpointRegistry {
579 fn add(&mut self, state: HardwareDebugState, wp: Watchpoint) -> WatchpointView<'_> {
580 self.last_seen_state = Some(state);
581 self.watchpoints.push(wp);
582
583 (&self.watchpoints[self.watchpoints.len() - 1]).into()
584 }
585
586 #[inline(always)]
588 pub fn get(&self, number: u32) -> Option<&Watchpoint> {
589 self.watchpoints.iter().find(|wp| wp.number() == number)
590 }
591
592 #[inline(always)]
594 pub fn all(&self) -> &[Watchpoint] {
595 self.watchpoints.as_slice()
596 }
597
598 #[inline(always)]
600 pub fn all_mut(&mut self) -> &mut [Watchpoint] {
601 self.watchpoints.as_mut()
602 }
603
604 fn remove(
605 &mut self,
606 tracee_ctl: &TraceeCtl,
607 brkpts: &mut BreakpointRegistry,
608 idx: usize,
609 ) -> Result<Option<WatchpointView<'_>>, Error> {
610 let mut wp = self.watchpoints.remove(idx);
611 let state = wp.disable(tracee_ctl, brkpts)?;
612 self.last_seen_state = Some(state);
613 Ok(Some(wp.into()))
614 }
615
616 fn remove_by_num(
618 &mut self,
619 tracee_ctl: &TraceeCtl,
620 breakpoints: &mut BreakpointRegistry,
621 num: u32,
622 ) -> Result<Option<WatchpointView<'_>>, Error> {
623 let Some(to_remove) = self.watchpoints.iter().position(|wp| wp.number == num) else {
624 return Ok(None);
625 };
626 self.remove(tracee_ctl, breakpoints, to_remove)
627 }
628
629 fn remove_by_addr(
631 &mut self,
632 tracee_ctl: &TraceeCtl,
633 breakpoints: &mut BreakpointRegistry,
634 addr: RelocatedAddress,
635 ) -> Result<Option<WatchpointView<'_>>, Error> {
636 let Some(to_remove) = self.watchpoints.iter().position(|wp| wp.hw.address == addr) else {
637 return Ok(None);
638 };
639 self.remove(tracee_ctl, breakpoints, to_remove)
640 }
641
642 fn remove_by_dqe(
644 &mut self,
645 tracee_ctl: &TraceeCtl,
646 breakpoints: &mut BreakpointRegistry,
647 dqe: Dqe,
648 ) -> Result<Option<WatchpointView<'_>>, Error> {
649 let needle = Dqe::Address(dqe.boxed());
650 let Some(to_remove) = self.watchpoints.iter().position(|wp| {
651 if let Subject::Expression(ExpressionTarget { dqe: wp_dqe, .. }) = &wp.subject {
652 &needle == wp_dqe
653 } else {
654 false
655 }
656 }) else {
657 return Ok(None);
658 };
659 self.remove(tracee_ctl, breakpoints, to_remove)
660 }
661
662 pub fn clear_all(&mut self, tracee_ctl: &TraceeCtl, breakpoints: &mut BreakpointRegistry) {
664 let wp_count = self.watchpoints.len();
665 for _ in 0..wp_count {
666 weak_error!(self.remove(tracee_ctl, breakpoints, 0));
667 }
668 self.last_seen_state = None;
669 }
670
671 pub fn clear_local_disable_global(
674 &mut self,
675 tracee_ctl: &TraceeCtl,
676 breakpoints: &mut BreakpointRegistry,
677 ) -> Vec<Error> {
678 let wp_count = self.watchpoints.len();
679 let mut result = vec![];
680
681 let mut j = 0;
682 for _ in 0..wp_count {
683 if self.watchpoints[j].scoped() {
684 if let Err(e) = self.remove(tracee_ctl, breakpoints, j) {
685 result.push(e);
686 }
687 } else {
688 if let Err(e) = self.watchpoints[j].disable(tracee_ctl, breakpoints) {
689 result.push(e);
690 }
691 j += 1;
692 }
693 }
694 self.last_seen_state = None;
695 result
696 }
697
698 pub fn distribute_to_tracee(&self, tracee: &Tracee) -> Result<(), Error> {
700 if let Some(ref state) = self.last_seen_state
701 && let Err(e) = state.sync(tracee.pid)
702 {
703 error!("set hardware breakpoint for thread {}: {e}", tracee.pid)
704 }
705 Ok(())
706 }
707
708 pub fn refresh(&mut self, debugee: &Debugee) -> Vec<Error> {
710 self.watchpoints
711 .iter_mut()
712 .filter_map(|wp| {
713 debug_assert!(!wp.scoped());
715 match wp.refresh(debugee.tracee_ctl()) {
716 Ok(state) => {
717 self.last_seen_state = Some(state);
718 None
719 }
720 Err(e) => Some(e),
721 }
722 })
723 .collect()
724 }
725}
726
727impl Debugger {
728 pub fn set_watchpoint_on_expr(
736 &mut self,
737 expr_source: &str,
738 dqe: Dqe,
739 condition: BreakCondition,
740 ) -> Result<WatchpointView<'_>, Error> {
741 disable_when_not_stared!(self);
742 let (hw_state, wp) = Watchpoint::from_dqe(self, expr_source, dqe, condition)?;
743 Ok(self.watchpoints.add(hw_state, wp))
744 }
745
746 pub fn set_watchpoint_on_memory(
754 &mut self,
755 addr: RelocatedAddress,
756 size: BreakSize,
757 condition: BreakCondition,
758 temporary: bool,
759 ) -> Result<WatchpointView<'_>, Error> {
760 disable_when_not_stared!(self);
761 let (hw_state, wp) =
762 Watchpoint::from_raw_addr(self.debugee.tracee_ctl(), addr, size, condition, temporary)?;
763 Ok(self.watchpoints.add(hw_state, wp))
764 }
765
766 pub fn remove_watchpoint_by_number(
772 &mut self,
773 num: u32,
774 ) -> Result<Option<WatchpointView<'_>>, Error> {
775 let breakpoints = &mut self.breakpoints;
776 self.watchpoints
777 .remove_by_num(self.debugee.tracee_ctl(), breakpoints, num)
778 }
779
780 pub fn remove_watchpoint_by_addr(
786 &mut self,
787 addr: RelocatedAddress,
788 ) -> Result<Option<WatchpointView<'_>>, Error> {
789 let breakpoints = &mut self.breakpoints;
790 self.watchpoints
791 .remove_by_addr(self.debugee.tracee_ctl(), breakpoints, addr)
792 }
793
794 pub fn remove_watchpoint_by_expr(
800 &mut self,
801 dqe: Dqe,
802 ) -> Result<Option<WatchpointView<'_>>, Error> {
803 let breakpoints = &mut self.breakpoints;
804 self.watchpoints
805 .remove_by_dqe(self.debugee.tracee_ctl(), breakpoints, dqe)
806 }
807
808 pub fn watchpoint_list(&self) -> Vec<WatchpointView<'_>> {
810 self.watchpoints.all().iter().map(|wp| wp.into()).collect()
811 }
812
813 pub(super) fn execute_on_watchpoint_hook(
814 &mut self,
815 tid: Pid,
816 pc: RelocatedAddress,
817 ty: &WatchpointHitType,
818 ) -> Result<(), Error> {
819 match ty {
820 WatchpointHitType::DebugRegister(reg) => {
821 let maybe_wp = self
822 .watchpoints
823 .all()
824 .iter()
825 .find(|wp| wp.register() == Some(*reg) && !wp.temporary);
826
827 if let Some(wp) = maybe_wp {
828 let number = wp.number();
829
830 match &wp.subject {
831 Subject::Expression(target) => {
832 let dqe = target.underlying_dqe().clone();
833 let current_tid = self.exploration_ctx().pid_on_focus();
834
835 let new_value = match target.frame_id {
836 None => {
837 Watchpoint::execute_dqe(self, dqe).map(|qr| qr.into_value())
838 }
839 Some(_) if target.tid != current_tid => {
842 Watchpoint::execute_dqe(self, dqe).map(|qr| qr.into_value())
843 }
844 Some(frame_id) => {
845 let bt = self.backtrace(current_tid)?;
846 let (num, frame) = bt
847 .iter()
848 .enumerate()
849 .find(|(_, frame)| frame.id() == Some(frame_id))
850 .ok_or(Error::VarFrameNotFound)?;
851
852 let loc = Location::new(
853 frame.ip,
854 frame.ip.into_global(&self.debugee).unwrap(),
855 current_tid,
856 );
857 let ctx = ExplorationContext::new(loc, num as u32);
858 call_with_context(self, ctx, |debugger| {
859 Watchpoint::execute_dqe(debugger, dqe)
860 .map(|qr| qr.into_value())
861 })
862 }
863 };
864
865 let new_value = new_value.ok();
866
867 let wp_mut = self
868 .watchpoints
869 .all_mut()
870 .iter_mut()
871 .find(|wp| wp.register() == Some(*reg))
872 .expect("infallible");
873 let Subject::Expression(t) = &mut wp_mut.subject else {
874 unreachable!()
875 };
876 let old = mem::replace(&mut t.last_value, new_value);
877
878 let dwarf = self.debugee.debug_info(pc)?;
879 let place = weak_error!(
880 dwarf.find_place_from_pc(pc.into_global(&self.debugee)?)
881 )
882 .flatten();
883
884 self.hooks
885 .on_watchpoint(
886 pc,
887 number,
888 place,
889 wp_mut.hw.condition,
890 Some(&t.source_string),
891 old.as_ref(),
892 t.last_value.as_ref(),
893 false,
894 )
895 .map_err(Hook)?;
896 }
897 Subject::Address(_) => {
898 let wp_mut = self
899 .watchpoints
900 .all_mut()
901 .iter_mut()
902 .find(|wp| wp.register() == Some(*reg))
903 .expect("infallible");
904 let Subject::Address(t) = &mut wp_mut.subject else {
905 unreachable!()
906 };
907 let old = t.refresh_last_value(tid, &wp_mut.hw);
908
909 let dwarf = self.debugee.debug_info(pc)?;
910 let place = weak_error!(
911 dwarf.find_place_from_pc(pc.into_global(&self.debugee)?)
912 )
913 .flatten();
914 self.hooks
915 .on_watchpoint(
916 pc,
917 number,
918 place,
919 wp_mut.hw.condition,
920 None,
921 old.as_ref(),
922 t.last_value.as_ref(),
923 false,
924 )
925 .map_err(Hook)?;
926 }
927 }
928 }
929 }
930 WatchpointHitType::EndOfScope(wps) => {
931 let watchpoints = wps
932 .iter()
933 .filter_map(|&num| self.watchpoints.get(num))
934 .collect::<Vec<_>>();
935 debug_assert_eq!(watchpoints.len(), wps.len());
936
937 let dwarf = self.debugee.debug_info(pc)?;
938 let place =
939 weak_error!(dwarf.find_place_from_pc(pc.into_global(&self.debugee)?)).flatten();
940
941 for wp in watchpoints {
942 let dqe_string =
943 if let Subject::Expression(ExpressionTarget { source_string, .. }) =
944 &wp.subject
945 {
946 Some(source_string.as_str())
947 } else {
948 None
949 };
950
951 self.hooks
952 .on_watchpoint(
953 pc,
954 wp.number(),
955 place.clone(),
956 wp.hw.condition,
957 dqe_string,
958 wp.last_value(),
959 None,
960 true,
961 )
962 .map_err(Hook)?;
963 }
964 for number in wps {
965 self.remove_watchpoint_by_number(*number)?;
966 }
967 }
968 }
969
970 Ok(())
971 }
972}