Debugger: Add conditional and hit conditional breakpoint functionality (#27760)

This PR adds conditional and hit condition breakpoint functionality 

cc @osiewicz 

Co-authored-by: Remco Smits: <djsmits12@gmail.com>

Release Notes:

- N/A *or* Added/Fixed/Improved ...

---------

Co-authored-by: Remco Smits <djsmits12@gmail.com>
This commit is contained in:
Anthony Eid 2025-03-31 15:12:23 -04:00 committed by GitHub
parent dc64ec9cc8
commit d517a212dc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 368 additions and 50 deletions

View file

@ -294,6 +294,60 @@ impl BreakpointStore {
})
}
}
BreakpointEditAction::EditHitCondition(hit_condition) => {
if !hit_condition.is_empty() {
let found_bp =
breakpoint_set
.breakpoints
.iter_mut()
.find_map(|(other_pos, other_bp)| {
if breakpoint.0 == *other_pos {
Some(other_bp)
} else {
None
}
});
if let Some(found_bp) = found_bp {
found_bp.hit_condition = Some(hit_condition.clone());
} else {
breakpoint.1.hit_condition = Some(hit_condition.clone());
// We did not remove any breakpoint, hence let's toggle one.
breakpoint_set.breakpoints.push(breakpoint.clone());
}
} else if breakpoint.1.hit_condition.is_some() {
breakpoint_set.breakpoints.retain(|(other_pos, other)| {
&breakpoint.0 != other_pos && other.hit_condition.is_none()
})
}
}
BreakpointEditAction::EditCondition(condition) => {
if !condition.is_empty() {
let found_bp =
breakpoint_set
.breakpoints
.iter_mut()
.find_map(|(other_pos, other_bp)| {
if breakpoint.0 == *other_pos {
Some(other_bp)
} else {
None
}
});
if let Some(found_bp) = found_bp {
found_bp.condition = Some(condition.clone());
} else {
breakpoint.1.condition = Some(condition.clone());
// We did not remove any breakpoint, hence let's toggle one.
breakpoint_set.breakpoints.push(breakpoint.clone());
}
} else if breakpoint.1.condition.is_some() {
breakpoint_set.breakpoints.retain(|(other_pos, other)| {
&breakpoint.0 != other_pos && other.condition.is_none()
})
}
}
}
if breakpoint_set.breakpoints.is_empty() {
@ -424,6 +478,8 @@ impl BreakpointStore {
path: path.clone(),
state: breakpoint.state,
message: breakpoint.message.clone(),
condition: breakpoint.condition.clone(),
hit_condition: breakpoint.hit_condition.clone(),
}
})
.collect()
@ -447,6 +503,8 @@ impl BreakpointStore {
path: path.clone(),
message: breakpoint.message.clone(),
state: breakpoint.state,
hit_condition: breakpoint.hit_condition.clone(),
condition: breakpoint.condition.clone(),
}
})
.collect(),
@ -500,6 +558,8 @@ impl BreakpointStore {
Breakpoint {
message: bp.message,
state: bp.state,
condition: bp.condition,
hit_condition: bp.hit_condition,
},
))
}
@ -537,13 +597,15 @@ pub enum BreakpointStoreEvent {
impl EventEmitter<BreakpointStoreEvent> for BreakpointStore {}
type LogMessage = Arc<str>;
type BreakpointMessage = Arc<str>;
#[derive(Clone, Debug)]
pub enum BreakpointEditAction {
Toggle,
InvertState,
EditLogMessage(LogMessage),
EditLogMessage(BreakpointMessage),
EditCondition(BreakpointMessage),
EditHitCondition(BreakpointMessage),
}
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
@ -574,7 +636,10 @@ impl BreakpointState {
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct Breakpoint {
pub message: Option<Arc<str>>,
pub message: Option<BreakpointMessage>,
/// How many times do we hit the breakpoint until we actually stop at it e.g. (2 = 2 times of the breakpoint action)
pub hit_condition: Option<BreakpointMessage>,
pub condition: Option<BreakpointMessage>,
pub state: BreakpointState,
}
@ -582,6 +647,17 @@ impl Breakpoint {
pub fn new_standard() -> Self {
Self {
state: BreakpointState::Enabled,
hit_condition: None,
condition: None,
message: None,
}
}
pub fn new_condition(hit_condition: &str) -> Self {
Self {
state: BreakpointState::Enabled,
condition: None,
hit_condition: Some(hit_condition.into()),
message: None,
}
}
@ -589,6 +665,8 @@ impl Breakpoint {
pub fn new_log(log_message: &str) -> Self {
Self {
state: BreakpointState::Enabled,
hit_condition: None,
condition: None,
message: Some(log_message.into()),
}
}
@ -601,6 +679,11 @@ impl Breakpoint {
BreakpointState::Disabled => proto::BreakpointState::Disabled.into(),
},
message: self.message.as_ref().map(|s| String::from(s.as_ref())),
condition: self.condition.as_ref().map(|s| String::from(s.as_ref())),
hit_condition: self
.hit_condition
.as_ref()
.map(|s| String::from(s.as_ref())),
})
}
@ -610,7 +693,9 @@ impl Breakpoint {
Some(proto::BreakpointState::Disabled) => BreakpointState::Disabled,
None | Some(proto::BreakpointState::Enabled) => BreakpointState::Enabled,
},
message: breakpoint.message.map(|message| message.into()),
message: breakpoint.message.map(Into::into),
condition: breakpoint.condition.map(Into::into),
hit_condition: breakpoint.hit_condition.map(Into::into),
})
}
@ -631,6 +716,8 @@ pub struct SourceBreakpoint {
pub row: u32,
pub path: Arc<Path>,
pub message: Option<Arc<str>>,
pub condition: Option<Arc<str>>,
pub hit_condition: Option<Arc<str>>,
pub state: BreakpointState,
}
@ -639,8 +726,12 @@ impl From<SourceBreakpoint> for dap::SourceBreakpoint {
Self {
line: bp.row as u64 + 1,
column: None,
condition: None,
hit_condition: None,
condition: bp
.condition
.map(|condition| String::from(condition.as_ref())),
hit_condition: bp
.hit_condition
.map(|hit_condition| String::from(hit_condition.as_ref())),
log_message: bp.message.map(|message| String::from(message.as_ref())),
mode: None,
}