use super::{
core_status::DapStatus,
dap_types,
repl_commands_helpers::{build_expanded_commands, command_completions},
request_helpers::{
DisassemblyAmount, disassemble_target_memory, get_dap_source, get_svd_variable_reference,
get_variable_reference, set_instruction_breakpoint,
},
};
use crate::cmd::dap_server::{
DebuggerError,
debug_adapter::{
dap::repl_commands::{EvalResponse, EvalResult},
protocol::{ProtocolAdapter, ProtocolHelper},
},
server::{
configuration::ConsoleLog,
core_data::CoreHandle,
session_data::{BreakpointType, SourceLocationScope},
},
};
use crate::util::rtt;
use anyhow::{Context, Result, anyhow};
use base64::{Engine as _, engine::general_purpose as base64_engine};
use dap_types::*;
use parse_int::parse;
use probe_rs::{
CoreInformation, CoreRegister, CoreStatus, HaltReason, RegisterDataType, RegisterRole,
RegisterValue, UnwindRule,
};
use probe_rs_debug::{
ColumnType, ObjectRef, SteppingMode, VariableName, VerifiedBreakpoint,
exception_handler_for_core,
registers::{DebugRegister, DebugRegisters},
stack_frame::StackFrameInfo,
};
use serde::{Serialize, de::DeserializeOwned};
use serde_json::Value;
use typed_path::NativePathBuf;
use std::{fmt::Display, str, time::Duration};
type ProgressId = i64;
pub struct DebugAdapter<P: ProtocolAdapter + ?Sized> {
pub(crate) halt_after_reset: bool,
configuration_done: bool,
pub(crate) all_cores_halted: bool,
progress_id: ProgressId,
pub(crate) supports_progress_reporting: bool,
pub(crate) supports_ansi_styling: bool,
pub(crate) lines_start_at_1: bool,
pub(crate) columns_start_at_1: bool,
pub(crate) vscode_quirks: bool,
adapter: P,
}
impl<P: ProtocolAdapter> DebugAdapter<P> {
pub fn new(adapter: P) -> DebugAdapter<P> {
DebugAdapter {
vscode_quirks: false,
halt_after_reset: false,
configuration_done: false,
all_cores_halted: true,
progress_id: 0,
supports_progress_reporting: false,
supports_ansi_styling: false,
lines_start_at_1: true,
columns_start_at_1: true,
adapter,
}
}
pub(crate) fn configuration_is_done(&self) -> bool {
self.configuration_done
}
pub(crate) fn pause(
&mut self,
target_core: &mut CoreHandle<'_>,
request: &Request,
) -> Result<()> {
let response = match self.pause_impl(target_core) {
Ok(cpu_info) => Ok(Some(format!(
"Core stopped at address {:#010x}",
cpu_info.pc
))),
Err(error) => Err(&DebuggerError::Other(anyhow!("{error}"))),
};
self.send_response(request, response)
}
pub(crate) fn disconnect(
&mut self,
target_core: &mut CoreHandle<'_>,
request: &Request,
) -> Result<()> {
let arguments: DisconnectArguments = get_arguments(self, request)?;
let must_halt_debuggee = arguments.terminate_debuggee.unwrap_or(false)
|| arguments.suspend_debuggee.unwrap_or(false);
if must_halt_debuggee {
let _ = target_core.core.halt(Duration::from_millis(100));
}
self.send_response::<DisconnectResponse>(request, Ok(None))
}
pub(crate) fn read_memory(
&mut self,
target_core: &mut CoreHandle<'_>,
request: &Request,
) -> Result<()> {
let arguments: ReadMemoryArguments = get_arguments(self, request)?;
let memory_offset = arguments.offset.unwrap_or(0);
let address: u64 = match parse::<u64>(arguments.memory_reference.as_ref()) {
Ok(address) => address.wrapping_add(memory_offset as u64), Err(err) => {
return self.send_response::<()>(
request,
Err(&DebuggerError::Other(anyhow!(
"Failed to parse memory reference {:?}: {err}",
arguments.memory_reference
))),
);
}
};
let result_buffer = target_core
.read_memory_lossy(address, arguments.count as usize)
.unwrap_or_default();
let num_bytes_unread = arguments.count as usize - result_buffer.len();
if !result_buffer.is_empty() || (self.vscode_quirks && arguments.count == 0) {
let response = base64_engine::STANDARD.encode(&result_buffer);
self.send_response(
request,
Ok(Some(ReadMemoryResponseBody {
address: format!("{address:#010x}"),
data: Some(response),
unreadable_bytes: if num_bytes_unread == 0 {
None
} else {
Some(num_bytes_unread as i64)
},
})),
)
} else {
self.send_response::<()>(
request,
Err(&DebuggerError::Other(anyhow!(
"Could not read any data at address {address:#010x}"
))),
)
}
}
pub(crate) fn write_memory(
&mut self,
target_core: &mut CoreHandle<'_>,
request: &Request,
) -> Result<()> {
let arguments: WriteMemoryArguments = get_arguments(self, request)?;
let memory_offset = arguments.offset.unwrap_or(0);
let address: u64 = match parse::<u64>(arguments.memory_reference.as_ref()) {
Ok(address) => address.wrapping_add(memory_offset as u64), Err(err) => {
return self.send_response::<()>(
request,
Err(&DebuggerError::Other(anyhow!(
"Failed to parse memory reference {:?}: {err}",
arguments.memory_reference
))),
);
}
};
let data_bytes = match base64_engine::STANDARD.decode(&arguments.data) {
Ok(decoded_bytes) => decoded_bytes,
Err(error) => {
return self.send_response::<()>(
request,
Err(&DebuggerError::Other(anyhow!(
"Could not decode base64 data:{:?} : {:?}",
arguments.data,
error
))),
);
}
};
if let Err(error) = target_core.write_memory(address, &data_bytes) {
return self.send_response::<()>(request, Err(&DebuggerError::ProbeRs(error)));
}
self.send_response(
request,
Ok(Some(WriteMemoryResponseBody {
bytes_written: Some(data_bytes.len() as i64),
offset: None,
})),
)?;
self.send_event(
"memory",
Some(MemoryEventBody {
count: data_bytes.len() as i64,
memory_reference: format!("{address:#010x}"),
offset: 0,
}),
)
}
pub(crate) fn evaluate(
&mut self,
target_core: &mut CoreHandle<'_>,
request: &Request,
) -> Result<()> {
let arguments: EvaluateArguments = get_arguments(self, request)?;
let mut response_body = EvaluateResponseBody {
indexed_variables: None,
memory_reference: None,
named_variables: None,
presentation_hint: None,
result: format!("<invalid expression {:?}>", arguments.expression),
type_: None,
variables_reference: 0,
value_location_reference: None,
};
if let Some(context) = &arguments.context {
if context == "clipboard" {
response_body.result = arguments.expression;
} else if context == "repl" {
match self.handle_repl(target_core, &arguments) {
Ok(EvalResponse::Body(body)) => response_body = body,
Ok(EvalResponse::Message(message)) => response_body.result = message,
Err(DebuggerError::UserMessage(message)) => response_body.result = message,
Err(error) => response_body.result = format!("{error:?}"),
}
} else {
let expression = arguments.expression.clone();
let mut expression_resolved = false;
let frame_index = match arguments.frame_id.map(ObjectRef::try_from).transpose() {
Ok(Some(frame_id)) => target_core
.core_data
.stack_frames
.iter()
.position(|stack_frame| stack_frame.id == frame_id),
Ok(None) => {
if target_core.core_data.stack_frames.is_empty() {
None
} else {
Some(0)
}
}
Err(e) => {
tracing::warn!("Invalid frame_id: {e}");
if target_core.core_data.stack_frames.is_empty() {
None
} else {
Some(0)
}
}
};
if let Some(frame_index) = frame_index
&& let Some(stack_frame) =
target_core.core_data.stack_frames.get_mut(frame_index)
{
if let Some(register_value) = stack_frame
.registers
.get_register_by_name(expression.as_str())
.and_then(|reg| reg.value)
{
response_body.type_ = Some(format!("{}", VariableName::RegistersRoot));
response_body.result = format!("{register_value}");
expression_resolved = true;
} else {
let mut variable: Option<probe_rs_debug::Variable> = None;
let mut variable_cache: Option<&mut probe_rs_debug::VariableCache> = None;
if let Some(search_cache) = stack_frame.local_variables.as_mut() {
if search_cache.len() == 1 {
let mut root_variable = search_cache.root_variable().clone();
#[allow(
clippy::expect_used,
reason = "Expect should be unreachable"
)]
target_core
.core_data
.debug_info
.as_ref()
.expect(
"This code should not be reached without debug information",
)
.cache_deferred_variables(
search_cache,
&mut target_core.core,
&mut root_variable,
StackFrameInfo {
registers: &stack_frame.registers,
frame_base: stack_frame.frame_base,
canonical_frame_address: stack_frame
.canonical_frame_address,
},
)?;
}
if let Ok(expression_as_key) = expression.parse::<ObjectRef>() {
variable = search_cache.get_variable_by_key(expression_as_key);
} else {
variable = search_cache
.get_variable_by_name(&VariableName::Named(expression.clone()));
}
if variable.is_some() {
variable_cache = Some(search_cache);
}
}
if let (Some(variable), Some(variable_cache)) = (variable, variable_cache) {
let (
variables_reference,
named_child_variables_cnt,
indexed_child_variables_cnt,
) = get_variable_reference(&variable, variable_cache);
response_body.indexed_variables = Some(indexed_child_variables_cnt);
response_body.memory_reference =
Some(variable.memory_location.to_string());
response_body.named_variables = Some(named_child_variables_cnt);
response_body.result = variable.to_string(variable_cache);
response_body.type_ = Some(variable.type_name());
response_body.variables_reference = variables_reference.into();
expression_resolved = true;
}
}
}
if !expression_resolved
&& let Some(static_cache) = &mut target_core.core_data.static_variables
{
if static_cache.len() == 1 {
let mut root_variable = static_cache.root_variable().clone();
if root_variable.variable_node_type.is_deferred()
&& !static_cache.has_children(&root_variable)
{
if let Some(top_frame) = target_core.core_data.stack_frames.first() {
let registers = top_frame.registers.clone();
let frame_info = StackFrameInfo {
registers: ®isters,
frame_base: top_frame.frame_base,
canonical_frame_address: top_frame.canonical_frame_address,
};
#[allow(
clippy::expect_used,
reason = "Expect should be unreachable"
)]
target_core
.core_data
.debug_info
.as_ref()
.expect(
"This code should not be reached without debug information",
)
.cache_deferred_variables(
static_cache,
&mut target_core.core,
&mut root_variable,
frame_info,
)?;
} else {
tracing::error!(
"Could not cache deferred static variables. No register data available."
);
}
}
}
let mut static_variable = if let Ok(expression_as_key) =
expression.parse::<ObjectRef>()
{
static_cache.get_variable_by_key(expression_as_key)
} else {
static_cache.get_variable_by_name(&VariableName::Named(expression.clone()))
};
if let Some(static_variable) = static_variable.as_mut() {
if static_variable.variable_node_type.is_deferred()
&& !static_cache.has_children(static_variable)
{
if let Some(top_frame) = target_core.core_data.stack_frames.first() {
let registers = top_frame.registers.clone();
let frame_info = StackFrameInfo {
registers: ®isters,
frame_base: top_frame.frame_base,
canonical_frame_address: top_frame.canonical_frame_address,
};
#[allow(
clippy::expect_used,
reason = "Expect should be unreachable"
)]
target_core
.core_data
.debug_info
.as_ref()
.expect(
"This code should not be reached without debug information",
)
.cache_deferred_variables(
static_cache,
&mut target_core.core,
static_variable,
frame_info,
)?;
} else {
tracing::error!(
"Could not cache deferred static variable: {}. No register data available.",
static_variable.name
);
}
}
static_variable.extract_value(&mut target_core.core, static_cache);
static_cache.update_variable(static_variable)?;
let (
variables_reference,
named_child_variables_cnt,
indexed_child_variables_cnt,
) = get_variable_reference(static_variable, static_cache);
response_body.indexed_variables = Some(indexed_child_variables_cnt);
response_body.memory_reference =
Some(static_variable.memory_location.to_string());
response_body.named_variables = Some(named_child_variables_cnt);
response_body.result = static_variable.to_string(static_cache);
response_body.type_ = Some(static_variable.type_name());
response_body.variables_reference = variables_reference.into();
expression_resolved = true;
}
}
if !expression_resolved
&& let Some(core_peripherals) = &target_core.core_data.core_peripherals
{
let svd_cache = &core_peripherals.svd_variable_cache;
let svd_variable =
if let Ok(expression_as_key) = expression.parse::<ObjectRef>() {
svd_cache.get_variable_by_key(expression_as_key)
} else {
svd_cache.get_variable_by_name(&expression)
};
if let Some(svd_variable) = svd_variable {
let (variables_reference, named_child_variables_cnt) =
get_svd_variable_reference(svd_variable, svd_cache);
response_body.indexed_variables = None;
response_body.memory_reference = svd_variable.memory_reference();
response_body.named_variables = Some(named_child_variables_cnt);
response_body.result = svd_variable.get_value(&mut target_core.core);
response_body.type_ = svd_variable.type_name();
response_body.variables_reference = variables_reference.into();
}
}
}
}
self.send_response(request, Ok(Some(response_body)))
}
fn handle_repl(
&mut self,
target_core: &mut CoreHandle<'_>,
arguments: &EvaluateArguments,
) -> EvalResult {
let (command_root, last_piece, repl_commands) = build_expanded_commands(
&target_core.core_data.repl_commands,
arguments.expression.trim(),
);
let Some(repl_command) = repl_commands.first() else {
return Err(DebuggerError::UserMessage(format!(
"Unknown REPL command: {}.",
command_root.trim()
)));
};
if repl_command.requires_target_halted && !target_core.core.core_halted()? {
return Err(DebuggerError::UserMessage(
"The target is running. Only the 'break', 'help' or 'quit' commands are allowed."
.to_string(),
));
}
let argument_string = arguments
.expression
.trim_start_matches(&command_root)
.trim_start()
.trim_start_matches(last_piece)
.trim_start();
(repl_command.handler)(target_core, argument_string, arguments, self)
}
pub(crate) fn completions(
&mut self,
target_core: &mut CoreHandle<'_>,
request: &Request,
) -> Result<()> {
let arguments: CompletionsArguments = get_arguments(self, request)?;
let response_body = CompletionsResponseBody {
targets: command_completions(&target_core.core_data.repl_commands, arguments),
};
self.send_response(request, Ok(Some(response_body)))
}
pub(crate) fn set_variable(
&mut self,
target_core: &mut CoreHandle<'_>,
request: &Request,
) -> Result<()> {
let arguments: SetVariableArguments = get_arguments(self, request)?;
let mut response_body = SetVariableResponseBody {
indexed_variables: None,
named_variables: None,
type_: None,
value: String::new(),
variables_reference: None,
memory_reference: None,
value_location_reference: None,
};
let parent_key: ObjectRef = arguments.variables_reference.into();
let new_value = &arguments.value;
match target_core
.core_data
.stack_frames
.iter()
.position(|stack_frame| stack_frame.id == parent_key)
{
Some(stack_frame_index) => {
let is_top_stack_frame = target_core
.core_data
.stack_frames
.first()
.is_some_and(|stack_frame| stack_frame.id == parent_key);
if !is_top_stack_frame {
return self.send_response::<SetVariableResponseBody>(
request,
Err(&DebuggerError::Other(anyhow!(
"Register writes are only supported for the top stack frame."
))),
);
}
let Some(register) = find_register_by_dap_name(
&target_core.core_data.stack_frames[stack_frame_index].registers,
arguments.name.as_str(),
) else {
return self.send_response::<SetVariableResponseBody>(
request,
Err(&DebuggerError::Other(anyhow!(
"Register '{}' was not found in this stack frame.",
arguments.name
))),
);
};
let register_name = register.get_register_name();
let register_id = register.core_register.id;
let register_value = match parse_register_value(new_value, register.core_register) {
Ok(register_value) => register_value,
Err(error) => {
return self.send_response::<SetVariableResponseBody>(
request,
Err(&DebuggerError::Other(anyhow!(
"Failed to parse value for register {register_name}: {error}"
))),
);
}
};
match target_core.core.core_halted() {
Ok(true) => {}
Ok(false) => {
return self.send_response::<SetVariableResponseBody>(
request,
Err(&DebuggerError::Other(anyhow!(
"Register writes require the target core to be halted."
))),
);
}
Err(error) => {
return self.send_response::<SetVariableResponseBody>(
request,
Err(&DebuggerError::Other(anyhow!(
"Failed to read core status before writing register {register_name}: {error}"
))),
);
}
}
if let Err(error) = target_core.core.write_core_reg(register_id, register_value) {
return self.send_response::<SetVariableResponseBody>(
request,
Err(&DebuggerError::Other(anyhow!(
"Failed to write register {register_name}: {error}"
))),
);
}
let written_register_value = match target_core.core.read_core_reg(register_id) {
Ok(written_register_value) => written_register_value,
Err(error) => {
return self.send_response::<SetVariableResponseBody>(
request,
Err(&DebuggerError::Other(anyhow!(
"Failed to read register {register_name} after writing it: {error}"
))),
);
}
};
if register_requires_exact_readback(register.core_register)
&& written_register_value != register_value
{
return self.send_response::<SetVariableResponseBody>(
request,
Err(&DebuggerError::Other(anyhow!(
"Register {register_name} read back as {written_register_value} after writing {register_value}."
))),
);
}
if register_write_requires_stack_frame_refresh(register.core_register) {
if let Err(error) = refresh_stack_frames(target_core, parent_key) {
let message = format!(
"Register {register_name} was written, but stack frames could not be refreshed: {error}"
);
tracing::warn!("{message}");
target_core.core_data.stack_frames.clear();
self.show_message(MessageSeverity::Warning, message);
}
} else {
let stack_frame = &mut target_core.core_data.stack_frames[stack_frame_index];
if let Some(cached_register) =
stack_frame.registers.get_register_mut(register_id)
{
cached_register.value = Some(written_register_value);
}
}
response_body.indexed_variables = Some(0);
response_body.named_variables = Some(0);
response_body.type_ = Some(format!("{:?}", register.core_register.data_type()));
response_body.value = written_register_value.to_string();
response_body.variables_reference = Some(0);
}
None => {
let variable_name = VariableName::Named(arguments.name.clone());
let mut cache_variable: Option<probe_rs_debug::Variable> = None;
let mut variable_cache: Option<&mut probe_rs_debug::VariableCache> = None;
for search_frame in target_core.core_data.stack_frames.iter_mut() {
if let Some(search_cache) = &mut search_frame.local_variables
&& let Some(search_variable) =
search_cache.get_variable_by_name_and_parent(&variable_name, parent_key)
{
cache_variable = Some(search_variable);
variable_cache = Some(search_cache);
break;
}
}
if let (Some(cache_variable), Some(variable_cache)) =
(cache_variable, variable_cache)
{
match cache_variable.update_value(
&mut target_core.core,
variable_cache,
new_value.clone(),
) {
Ok(()) => {
let (
variables_reference,
named_child_variables_cnt,
indexed_child_variables_cnt,
) = get_variable_reference(&cache_variable, variable_cache);
response_body.variables_reference = Some(variables_reference.into());
response_body.named_variables = Some(named_child_variables_cnt);
response_body.indexed_variables = Some(indexed_child_variables_cnt);
response_body.type_ = Some(format!("{:?}", cache_variable.type_name));
response_body.value.clone_from(new_value);
}
Err(error) => {
return self.send_response::<SetVariableResponseBody>(
request,
Err(&DebuggerError::Other(anyhow!(
"Failed to update variable: {}, with new value {new_value:?}: {error:?}",
cache_variable.name,
))),
);
}
}
}
}
}
let response = if response_body.value.is_empty() {
Err(&DebuggerError::Other(anyhow!(
"Failed to update variable: {}, with new value {:?} : Please report this as a bug.",
arguments.name,
arguments.value
)))
} else {
Ok(Some(response_body))
};
self.send_response(request, response)
}
pub(crate) fn restart(
&mut self,
target_core: &mut CoreHandle<'_>,
request: Option<&Request>,
) -> Result<()> {
let core_info = match self.reset_and_halt_core(target_core) {
Ok(core_info) => core_info,
Err(error) => {
return self.show_error_message(&DebuggerError::Other(anyhow!("{error}")));
}
};
if let Some(request) = request {
if !self.halt_after_reset {
if let Err(error) = self.r#continue(target_core, request) {
return self.send_response::<()>(
request,
Err(&DebuggerError::Other(anyhow!("{error}"))),
);
}
self.send_response::<()>(request, Ok(None))?;
let event_body = Some(ContinuedEventBody {
all_threads_continued: Some(false), thread_id: target_core.id() as i64,
});
self.send_event("continued", event_body)?;
} else {
self.send_response::<()>(request, Ok(None))?;
let event_body = Some(StoppedEventBody {
reason: "restart".to_owned(),
description: Some(
CoreStatus::Halted(HaltReason::External)
.short_long_status(None)
.1,
),
thread_id: Some(target_core.id() as i64),
preserve_focus_hint: None,
text: None,
all_threads_stopped: Some(self.all_cores_halted),
hit_breakpoint_ids: None,
});
self.send_event("stopped", event_body)?;
}
} else if self.configuration_is_done() {
let event_body = Some(StoppedEventBody {
reason: "restart".to_owned(),
description: Some(
CoreStatus::Halted(HaltReason::External)
.short_long_status(Some(core_info.pc))
.1,
),
thread_id: Some(target_core.id() as i64),
preserve_focus_hint: None,
text: None,
all_threads_stopped: Some(self.all_cores_halted),
hit_breakpoint_ids: None,
});
self.send_event("stopped", event_body)?;
}
Ok(())
}
#[tracing::instrument(level = "debug", skip_all, name = "Handle configuration done")]
pub(crate) fn configuration_done(
&mut self,
target_core: &mut CoreHandle<'_>,
request: &Request,
) -> Result<()> {
let current_core_status = target_core.core.status()?;
if current_core_status.is_halted() {
if self.halt_after_reset
|| matches!(
current_core_status,
CoreStatus::Halted(HaltReason::Breakpoint(_))
)
{
let program_counter = target_core
.core
.read_core_reg(target_core.core.program_counter())
.ok();
let event_body = Some(StoppedEventBody {
reason: current_core_status
.short_long_status(program_counter)
.0
.to_owned(),
description: Some(current_core_status.short_long_status(program_counter).1),
thread_id: Some(target_core.id() as i64),
preserve_focus_hint: None,
text: None,
all_threads_stopped: Some(self.all_cores_halted),
hit_breakpoint_ids: None,
});
self.send_event("stopped", event_body)?;
} else {
tracing::debug!(
"Core is halted, but not due to a breakpoint and halt_after_reset is not set. Continuing."
);
self.r#continue(target_core, request)?;
}
}
self.configuration_done = true;
self.send_response::<()>(request, Ok(None))
}
pub(crate) fn set_breakpoints(
&mut self,
target_core: &mut CoreHandle<'_>,
request: &Request,
) -> Result<()> {
let args: SetBreakpointsArguments = get_arguments(self, request)?;
let Some(source_path) = args.source.path.as_ref() else {
return self.send_response::<()>(
request,
Err(&DebuggerError::Other(anyhow!(
"Could not get a valid source path from arguments: {args:?}"
))),
);
};
if let Err(error) = target_core.clear_breakpoints(BreakpointType::SourceBreakpoint {
source: Box::new(args.source.clone()),
location: SourceLocationScope::All,
}) {
return self.send_response::<()>(
request,
Err(&DebuggerError::Other(anyhow!(
"Failed to clear existing breakpoints before setting new ones: {error}"
))),
);
}
let mut created_breakpoints: Vec<Breakpoint> = Vec::new();
let source_path = NativePathBuf::from(source_path).to_typed_path_buf();
for bp in args.breakpoints.as_deref().unwrap_or_default() {
let requested_breakpoint_line = if self.lines_start_at_1 {
bp.line as u64
} else {
bp.line as u64 + 1
};
let requested_breakpoint_column = if self.columns_start_at_1 {
Some(bp.column.unwrap_or(1) as u64)
} else {
Some(bp.column.unwrap_or(0) as u64 + 1)
};
let bp = match target_core.verify_and_set_breakpoint(
source_path.to_path(),
requested_breakpoint_line,
requested_breakpoint_column,
&args.source,
) {
Ok(VerifiedBreakpoint {
address,
source_location,
}) => Breakpoint {
column: source_location.column.map(|col| match col {
ColumnType::LeftEdge => 0_i64,
ColumnType::Column(c) => c as i64,
}),
end_column: None,
end_line: None,
id: Some(address as i64),
line: source_location.line.map(|line| line as i64),
message: Some(format!(
"Source breakpoint at memory address: {address:#010X}"
)),
source: Some(args.source.clone()),
instruction_reference: Some(format!("{address:#010X}")),
offset: None,
verified: true,
reason: None,
},
Err(error) => Breakpoint {
column: None,
end_column: None,
end_line: None,
id: None,
line: Some(bp.line),
message: Some(error.to_string()),
source: None,
instruction_reference: None,
offset: None,
verified: false,
reason: Some("failed".to_string()),
},
};
created_breakpoints.push(bp);
}
let breakpoint_body = SetBreakpointsResponseBody {
breakpoints: created_breakpoints,
};
self.send_response(request, Ok(Some(breakpoint_body)))
}
pub(crate) fn set_instruction_breakpoints(
&mut self,
target_core: &mut CoreHandle<'_>,
request: &Request,
) -> Result<()> {
let arguments: SetInstructionBreakpointsArguments = get_arguments(self, request)?;
if let Err(error) = target_core.clear_breakpoints(BreakpointType::InstructionBreakpoint) {
tracing::warn!("Failed to clear instruction breakpoints. {}", error)
}
let instruction_breakpoint_body = SetInstructionBreakpointsResponseBody {
breakpoints: arguments
.breakpoints
.into_iter()
.map(|requested_breakpoint| {
set_instruction_breakpoint(requested_breakpoint, target_core)
})
.collect(),
};
for breakpoint_response in &instruction_breakpoint_body.breakpoints {
if !breakpoint_response.verified
&& let Some(message) = &breakpoint_response.message
{
self.log_to_console(format!("Warning: {message}"));
self.show_message(MessageSeverity::Warning, message.clone());
}
}
self.send_response(request, Ok(Some(instruction_breakpoint_body)))
}
pub(crate) fn threads(
&mut self,
target_core: &mut CoreHandle<'_>,
request: &Request,
) -> Result<()> {
if !self.configuration_is_done() {
let current_core_status = target_core.core.status()?;
return
self.send_response::<()>(
request,
Err(&DebuggerError::Other(anyhow!(
"Received request for `threads`, while last known core status was {current_core_status:?}",
))),
);
}
let threads = vec![Thread {
id: target_core.id() as i64,
name: target_core.core_data.target_name.clone(),
}];
self.send_response(request, Ok(Some(ThreadsResponseBody { threads })))
}
pub(crate) fn stack_trace(
&mut self,
target_core: &mut CoreHandle<'_>,
request: &Request,
) -> Result<()> {
match target_core.core.status() {
Ok(status) => {
if !status.is_halted() {
return self.send_response::<()>(
request,
Err(&DebuggerError::Other(anyhow!(
"Core must be halted before requesting a stack trace"
))),
);
}
}
Err(error) => {
return self.send_response::<()>(request, Err(&DebuggerError::ProbeRs(error)));
}
};
let arguments: StackTraceArguments = get_arguments(self, request)?;
let total_frames = target_core.core_data.stack_frames.len() as i64;
let mut levels = arguments.levels.unwrap_or(0);
let start_frame = arguments.start_frame.unwrap_or(0);
tracing::debug!("Start frame: {} Levels: {}", start_frame, levels);
if levels == 0 {
levels = total_frames;
}
const PAGE_SIZE: i64 = 50;
let first_frame = start_frame as usize;
let last_frame = if total_frames < PAGE_SIZE {
total_frames
} else {
start_frame + levels
} as usize;
let Some(frames) = target_core
.core_data
.stack_frames
.get(first_frame..last_frame)
else {
return self.send_response::<()>(
request,
Err(&DebuggerError::Other(anyhow!(
"Request for stack trace failed with invalid arguments: {arguments:?}"
))),
);
};
let frame_list: Vec<StackFrame> = frames
.iter()
.map(|frame| {
let column = frame
.source_location
.as_ref()
.and_then(|sl| sl.column)
.map(|col| match col {
ColumnType::LeftEdge => 0,
ColumnType::Column(c) => c,
})
.unwrap_or(0);
let line = frame
.source_location
.as_ref()
.and_then(|sl| sl.line)
.unwrap_or(0) as i64;
let function_display_name = if frame.is_inlined {
format!("{} #[inline]", frame.function_name)
} else {
frame.function_name.clone()
};
let source = if let Some(source_location) = &frame.source_location {
get_dap_source(source_location)
} else {
tracing::debug!("No source location present for frame!");
None
};
StackFrame {
id: frame.id.into(),
name: function_display_name,
source,
line,
column: column as i64,
end_column: None,
end_line: None,
module_id: None,
presentation_hint: Some("normal".to_owned()),
can_restart: Some(false),
instruction_pointer_reference: Some(format!("{}", frame.pc)),
}
})
.collect();
let body = StackTraceResponseBody {
stack_frames: frame_list,
total_frames: Some(total_frames),
};
self.send_response(request, Ok(Some(body)))
}
pub(crate) fn scopes(
&mut self,
target_core: &mut CoreHandle<'_>,
request: &Request,
) -> Result<()> {
let arguments: ScopesArguments = get_arguments(self, request)?;
let mut dap_scopes: Vec<Scope> = vec![];
if let Some(core_peripherals) = &target_core.core_data.core_peripherals {
let peripherals_root_variable = core_peripherals.svd_variable_cache.root_variable_key();
dap_scopes.push(Scope {
line: None,
column: None,
end_column: None,
end_line: None,
expensive: true, indexed_variables: None,
name: "Peripherals".to_string(),
presentation_hint: Some("registers".to_string()),
named_variables: None,
source: None,
variables_reference: peripherals_root_variable.into(),
});
};
if let Some(static_root_variable) = target_core
.core_data
.static_variables
.as_ref()
.map(|stack_frame| stack_frame.root_variable())
{
dap_scopes.push(Scope {
line: None,
column: None,
end_column: None,
end_line: None,
expensive: true, indexed_variables: None,
name: "Static".to_string(),
presentation_hint: Some("statics".to_string()),
named_variables: None,
source: None,
variables_reference: static_root_variable.variable_key().into(),
});
};
let frame_id: ObjectRef = arguments.frame_id.into();
tracing::trace!("Getting scopes for frame {:?}", frame_id);
if let Some(stack_frame) = target_core.get_stackframe(frame_id) {
dap_scopes.push(Scope {
line: None,
column: None,
end_column: None,
end_line: None,
expensive: true, indexed_variables: None,
name: "Registers".to_string(),
presentation_hint: Some("registers".to_string()),
named_variables: None,
source: None,
variables_reference: stack_frame.id.into(),
});
if let Some(locals_root_variable) = stack_frame
.local_variables
.as_ref()
.map(|stack_frame| stack_frame.root_variable())
{
dap_scopes.push(Scope {
line: stack_frame
.source_location
.as_ref()
.and_then(|location| location.line.map(|line| line as i64)),
column: stack_frame.source_location.as_ref().and_then(|l| {
l.column.map(|c| match c {
ColumnType::LeftEdge => 0,
ColumnType::Column(c) => c as i64,
})
}),
end_column: None,
end_line: None,
expensive: false, indexed_variables: None,
name: "Variables".to_string(),
presentation_hint: Some("locals".to_string()),
named_variables: None,
source: None,
variables_reference: locals_root_variable.variable_key().into(),
});
}
}
self.send_response(request, Ok(Some(ScopesResponseBody { scopes: dap_scopes })))
}
pub(crate) fn get_disassembled_source(
&mut self,
target_core: &mut CoreHandle<'_>,
memory_reference: i64,
byte_offset: i64,
instruction_offset: i64,
instruction_count: i64,
) -> Result<Vec<dap_types::DisassembledInstruction>, DebuggerError> {
let assembly_lines = disassemble_target_memory(
target_core,
instruction_offset,
byte_offset,
memory_reference as u64,
DisassemblyAmount::Instructions(instruction_count),
)?;
if assembly_lines.is_empty() {
Err(DebuggerError::Other(anyhow::anyhow!(
"No valid instructions found at memory reference {memory_reference:#010x?}"
)))
} else {
Ok(assembly_lines)
}
}
pub(crate) fn disassemble(
&mut self,
target_core: &mut CoreHandle<'_>,
request: &Request,
) -> Result<()> {
let arguments: DisassembleArguments = get_arguments(self, request)?;
if let Ok(memory_reference) = parse_int::parse::<u64>(&arguments.memory_reference) {
match self.get_disassembled_source(
target_core,
memory_reference as i64,
arguments.offset.unwrap_or(0_i64),
arguments.instruction_offset.unwrap_or(0_i64),
arguments.instruction_count,
) {
Ok(disassembled_instructions) => self.send_response(
request,
Ok(Some(DisassembleResponseBody {
instructions: disassembled_instructions,
})),
),
Err(error) => {
self.send_response::<()>(request, Err(&DebuggerError::Other(anyhow!(error))))
}
}
} else {
self.send_response::<()>(
request,
Err(&DebuggerError::Other(anyhow!(
"Invalid memory reference {:?}",
arguments.memory_reference
))),
)
}
}
pub(crate) fn variables(
&mut self,
target_core: &mut CoreHandle<'_>,
request: &Request,
) -> Result<()> {
let Some(ref debug_info) = target_core.core_data.debug_info else {
return self.send_response::<()>(
request,
Err(&DebuggerError::Other(anyhow!(
"Cannot resolve variables without debug information"
))),
);
};
let arguments: VariablesArguments = get_arguments(self, request)?;
let variable_ref: ObjectRef = arguments.variables_reference.into();
if let Some(svd_cache) = target_core
.core_data
.core_peripherals
.as_ref()
.map(|cp| &cp.svd_variable_cache)
&& svd_cache.get_variable_by_key(variable_ref).is_some()
{
let dap_variables: Vec<Variable> = svd_cache
.get_children(variable_ref)
.iter()
.map(|variable| {
let (variables_reference, named_child_variables_cnt) =
get_svd_variable_reference(variable, svd_cache);
let name = if let Some(last_part) =
variable.name().split_terminator('.').next_back()
{
last_part.to_string()
} else {
variable.name().to_string()
};
Variable {
name,
evaluate_name: Some(variable.name().to_string()),
memory_reference: variable.memory_reference(),
indexed_variables: None,
named_variables: Some(named_child_variables_cnt),
presentation_hint: None,
type_: variable.type_name(),
value: {
variable.get_value(&mut target_core.core)
},
variables_reference: variables_reference.into(),
declaration_location_reference: None,
value_location_reference: None,
}
})
.collect();
return self.send_response(
request,
Ok(Some(VariablesResponseBody {
variables: dap_variables,
})),
);
}
let mut parent_variable: Option<probe_rs_debug::Variable> = None;
let mut variable_cache: Option<&mut probe_rs_debug::VariableCache> = None;
let mut frame_info: Option<StackFrameInfo<'_>> = None;
let registers;
if let Some(search_cache) = &mut target_core.core_data.static_variables
&& let Some(search_variable) = search_cache.get_variable_by_key(variable_ref)
{
parent_variable = Some(search_variable);
variable_cache = Some(search_cache);
if let Some(top_level_frame) = target_core.core_data.stack_frames.first() {
registers = top_level_frame.registers.clone();
frame_info = Some(StackFrameInfo {
registers: ®isters,
frame_base: top_level_frame.frame_base,
canonical_frame_address: top_level_frame.canonical_frame_address,
});
}
}
if parent_variable.is_none() {
for stack_frame in target_core.core_data.stack_frames.iter_mut() {
if let Some(search_cache) = &mut stack_frame.local_variables
&& let Some(search_variable) = search_cache.get_variable_by_key(variable_ref)
{
parent_variable = Some(search_variable);
variable_cache = Some(search_cache);
frame_info = Some(StackFrameInfo {
registers: &stack_frame.registers,
frame_base: stack_frame.frame_base,
canonical_frame_address: stack_frame.canonical_frame_address,
});
break;
}
if stack_frame.id == variable_ref {
let dap_variables: Vec<Variable> = stack_frame
.registers
.0
.iter()
.map(|register| Variable {
name: register.get_register_name(),
evaluate_name: Some(register.get_register_name()),
memory_reference: None,
indexed_variables: None,
named_variables: None,
presentation_hint: None, type_: Some(format!("{}", VariableName::RegistersRoot)),
value: register.value.unwrap_or_default().to_string(),
variables_reference: 0,
declaration_location_reference: None,
value_location_reference: None,
})
.collect();
return self.send_response(
request,
Ok(Some(VariablesResponseBody {
variables: dap_variables,
})),
);
}
}
}
if let Some(variable_cache) = variable_cache {
if let Some(parent_variable) = parent_variable.as_mut()
&& parent_variable.variable_node_type.is_deferred()
&& !variable_cache.has_children(parent_variable)
{
if let Some(frame_info) = frame_info {
debug_info.cache_deferred_variables(
variable_cache,
&mut target_core.core,
parent_variable,
frame_info,
)?;
} else {
tracing::error!(
"Could not cache deferred child variables for variable: {}. No register data available.",
parent_variable.name
);
}
}
let dap_variables: Vec<Variable> = variable_cache
.get_children(variable_ref)
.filter(|variable| match &arguments.filter {
Some(filter) => match filter.as_str() {
"indexed" => variable.is_indexed(),
"named" => !variable.is_indexed(),
other => {
tracing::error!("Received invalid variable filter: {}", other);
false
}
},
None => true,
})
.map(|variable| {
let (
variables_reference,
named_child_variables_cnt,
indexed_child_variables_cnt,
) = get_variable_reference(variable, variable_cache);
Variable {
name: variable.name.to_string(),
evaluate_name: None,
memory_reference: Some(variable.memory_location.to_string()),
indexed_variables: Some(indexed_child_variables_cnt),
named_variables: Some(named_child_variables_cnt),
presentation_hint: None,
type_: Some(variable.type_name()),
value: variable.to_string(variable_cache),
variables_reference: variables_reference.into(),
declaration_location_reference: None,
value_location_reference: None,
}
})
.collect();
self.send_response(
request,
Ok(Some(VariablesResponseBody {
variables: dap_variables,
})),
)
} else {
let err = DebuggerError::Other(anyhow!(
"No variable information found for {}!",
arguments.variables_reference
));
let res: Result<Option<u32>, _> = Err(&err);
self.send_response(request, res)
}
}
pub(crate) fn r#continue(
&mut self,
target_core: &mut CoreHandle<'_>,
request: &Request,
) -> Result<()> {
match self.continue_impl(target_core) {
Ok(all_continued) if request.command == "continue" => {
self.send_response(
request,
Ok(Some(ContinueResponseBody {
all_threads_continued: Some(all_continued),
})),
)
}
Ok(_) => Ok(()),
Err(error) => {
self.send_response::<()>(request, Err(&DebuggerError::Other(anyhow!("{error}"))))?;
Err(error)
}
}
}
pub(crate) fn next(
&mut self,
target_core: &mut CoreHandle<'_>,
request: &Request,
) -> Result<()> {
let arguments: NextArguments = get_arguments(self, request)?;
let stepping_granularity = match arguments.granularity {
Some(SteppingGranularity::Instruction) => SteppingMode::StepInstruction,
_ => SteppingMode::OverStatement,
};
self.debug_step(stepping_granularity, target_core, request)
}
pub(crate) fn step_in(
&mut self,
target_core: &mut CoreHandle<'_>,
request: &Request,
) -> Result<()> {
let arguments: StepInArguments = get_arguments(self, request)?;
let stepping_granularity = match arguments.granularity {
Some(SteppingGranularity::Instruction) => SteppingMode::StepInstruction,
_ => SteppingMode::IntoStatement,
};
self.debug_step(stepping_granularity, target_core, request)
}
pub(crate) fn step_out(
&mut self,
target_core: &mut CoreHandle<'_>,
request: &Request,
) -> Result<()> {
let arguments: StepOutArguments = get_arguments(self, request)?;
let stepping_granularity = match arguments.granularity {
Some(SteppingGranularity::Instruction) => SteppingMode::StepInstruction,
_ => SteppingMode::OutOfStatement,
};
self.debug_step(stepping_granularity, target_core, request)
}
fn debug_step(
&mut self,
stepping_mode: SteppingMode,
target_core: &mut CoreHandle<'_>,
request: &Request,
) -> Result<(), anyhow::Error> {
self.step_impl(stepping_mode, target_core)?;
self.send_response::<()>(request, Ok(None))?;
Ok(())
}
pub fn listen_for_request(&mut self) -> anyhow::Result<Option<Request>> {
self.adapter.listen_for_request()
}
pub fn send_response<S: Serialize + std::fmt::Debug>(
&mut self,
request: &Request,
response: Result<Option<S>, &DebuggerError>,
) -> Result<()> {
self.adapter.send_response(request, response)
}
pub fn show_error_message(&mut self, response: &DebuggerError) -> Result<()> {
let expanded_error = {
let mut response_message = response.to_string();
let mut offset_iterations = 0;
let mut child_error: Option<&dyn std::error::Error> =
std::error::Error::source(&response);
while let Some(source_error) = child_error {
offset_iterations += 1;
response_message = format!("{response_message}\n",);
for _offset_counter in 0..offset_iterations {
response_message = format!("{response_message}\t");
}
response_message = format!(
"{}{}",
response_message,
<dyn std::error::Error>::to_string(source_error)
);
child_error = std::error::Error::source(source_error);
}
response_message
};
if self
.adapter
.show_message(MessageSeverity::Error, expanded_error)
{
Ok(())
} else {
Err(anyhow!("Failed to send error response"))
}
}
#[tracing::instrument(level = "trace", skip_all)]
pub fn send_event<S: Serialize>(
&mut self,
event_type: &str,
event_body: Option<S>,
) -> Result<()> {
tracing::debug!("Sending event: {}", event_type);
self.adapter.send_event(event_type, event_body)
}
pub fn log_to_console(&mut self, message: impl AsRef<str>) -> bool {
self.adapter.log_to_console(message)
}
pub fn show_message(&mut self, severity: MessageSeverity, message: impl AsRef<str>) -> bool {
self.adapter.show_message(severity, message)
}
pub fn rtt_window(
&mut self,
channel_number: u32,
channel_name: String,
data_format: rtt::DataFormat,
) -> bool {
let Ok(event_body) = serde_json::to_value(RttChannelEventBody {
channel_number,
channel_name,
data_format,
}) else {
return false;
};
self.send_event("probe-rs-rtt-channel-config", Some(event_body))
.is_ok()
}
pub fn open_prompt(&mut self, kind: PromptKind, name: &str, handle: u32) -> bool {
let Ok(event_body) = serde_json::to_value(CreatePromptEventBody {
prompt_kind: kind,
prompt_name: name.to_string(),
prompt_handle: handle,
}) else {
return false;
};
self.send_event("probe-rs-create-prompt", Some(event_body))
.is_ok()
}
pub fn rtt_output(&mut self, channel_number: u32, rtt_data: String) -> bool {
let Ok(event_body) = serde_json::to_value(RttDataEventBody {
channel_number,
data: rtt_data,
}) else {
return false;
};
self.send_event("probe-rs-rtt-data", Some(event_body))
.is_ok()
}
fn new_progress_id(&mut self) -> ProgressId {
let id = self.progress_id;
self.progress_id += 1;
id
}
pub fn start_progress(
&mut self,
title: &str,
request_id: Option<ProgressId>,
) -> Result<ProgressId> {
anyhow::ensure!(
self.supports_progress_reporting,
"Progress reporting is not supported by client."
);
let progress_id = self.new_progress_id();
self.send_event(
"progressStart",
Some(ProgressStartEventBody {
cancellable: Some(false),
message: None,
percentage: None,
progress_id: progress_id.to_string(),
request_id,
title: title.to_owned(),
}),
)?;
Ok(progress_id)
}
pub fn end_progress(&mut self, progress_id: ProgressId) -> Result<()> {
anyhow::ensure!(
self.supports_progress_reporting,
"Progress reporting is not supported by client."
);
self.send_event(
"progressEnd",
Some(ProgressEndEventBody {
message: None,
progress_id: progress_id.to_string(),
}),
)
}
pub fn update_progress(
&mut self,
progress: Option<f64>,
message: Option<impl Display>,
progress_id: i64,
) -> Result<ProgressId> {
anyhow::ensure!(
self.supports_progress_reporting,
"Progress reporting is not supported by client."
);
let percentage = progress.map(|progress| progress * 100.0);
self.send_event(
"progressUpdate",
Some(ProgressUpdateEventBody {
message: message.map(|msg| match percentage {
None => msg.to_string(),
Some(100.0) => msg.to_string(),
Some(percentage) => format!("{msg} ({percentage:02.0}%)"),
}),
percentage,
progress_id: progress_id.to_string(),
}),
)?;
Ok(progress_id)
}
pub(crate) fn set_console_log_level(&mut self, error: ConsoleLog) {
self.adapter.set_console_log_level(error)
}
}
impl<P: ProtocolAdapter + ?Sized> DebugAdapter<P> {
pub(crate) fn pause_impl(
&mut self,
target_core: &mut CoreHandle<'_>,
) -> Result<CoreInformation> {
let cpu_info = target_core.core.halt(Duration::from_millis(500))?;
let new_status = target_core.core.status()?;
let event_body = Some(StoppedEventBody {
reason: "pause".to_owned(),
description: Some(new_status.short_long_status(Some(cpu_info.pc)).1),
thread_id: Some(target_core.id() as i64),
preserve_focus_hint: Some(false),
text: None,
all_threads_stopped: Some(self.all_cores_halted),
hit_breakpoint_ids: None,
});
target_core.core_data.last_known_status = CoreStatus::Halted(HaltReason::Request);
self.dyn_send_event(
"stopped",
event_body.map(|event_body| serde_json::to_value(event_body).unwrap_or_default()),
)?;
Ok(cpu_info)
}
pub(crate) fn continue_impl(&mut self, target_core: &mut CoreHandle<'_>) -> Result<bool> {
target_core.core.run()?;
target_core.reset_core_status(self);
Ok(true) }
pub(crate) fn reset_and_halt_core(
&mut self,
target_core: &mut CoreHandle<'_>,
) -> Result<CoreInformation> {
let core_info = target_core
.core
.reset_and_halt(Duration::from_millis(500))?;
target_core.reapply_breakpoints();
target_core.reset_core_status(self);
Ok(core_info)
}
pub(crate) fn step_impl(
&mut self,
stepping_mode: SteppingMode,
target_core: &mut CoreHandle<'_>,
) -> Result<u64> {
target_core.reset_core_status(self);
let (new_status, program_counter) = match stepping_mode.step(
&mut target_core.core,
target_core.core_data.debug_info.as_ref(),
) {
Ok((new_status, program_counter)) => (new_status, program_counter),
Err(probe_rs_debug::DebugError::WarnAndContinue { message }) => {
let pc_at_error = target_core
.core
.read_core_reg(target_core.core.program_counter())?;
self.dyn_show_message(
MessageSeverity::Information,
format!("Step error @{pc_at_error:#010X}: {message}"),
);
(target_core.core.status()?, pc_at_error)
}
Err(other_error) => {
target_core.core.halt(Duration::from_millis(100)).ok();
return Err(other_error).context("Unexpected error during stepping");
}
};
target_core.core_data.last_known_status = CoreStatus::Halted(HaltReason::Step);
if matches!(new_status, CoreStatus::Halted(_)) {
let event_body = StoppedEventBody {
reason: target_core
.core_data
.last_known_status
.short_long_status(None)
.0
.to_string(),
description: Some(
CoreStatus::Halted(HaltReason::Step)
.short_long_status(Some(program_counter))
.1,
),
thread_id: Some(target_core.id() as i64),
preserve_focus_hint: None,
text: None,
all_threads_stopped: Some(self.all_cores_halted),
hit_breakpoint_ids: None,
};
self.dyn_send_event("stopped", serde_json::to_value(event_body).ok())?;
}
Ok(program_counter)
}
#[tracing::instrument(level = "trace", skip_all)]
pub fn dyn_send_event(&mut self, event_type: &str, event_body: Option<Value>) -> Result<()> {
tracing::debug!("Sending event: {}", event_type);
self.adapter.dyn_send_event(event_type, event_body)
}
pub fn dyn_show_message(&mut self, severity: MessageSeverity, message: String) -> bool {
self.adapter.dyn_show_message(severity, message)
}
}
fn find_register_by_dap_name(
registers: &DebugRegisters,
register_name: &str,
) -> Option<DebugRegister> {
registers
.0
.iter()
.find(|register| register.get_register_name() == register_name)
.cloned()
.or_else(|| registers.get_register_by_name(register_name))
}
fn parse_register_value(value: &str, register: &CoreRegister) -> Result<RegisterValue> {
let RegisterDataType::UnsignedInteger(size_in_bits) = register.data_type() else {
return Err(anyhow!(
"Writing non-integer register {} is not supported.",
register
));
};
anyhow::ensure!(
(1..=128).contains(&size_in_bits),
"Register {} has unsupported width {size_in_bits} bits.",
register
);
let parsed_value = parse_unsigned_register_value(value)?;
let max_value = if size_in_bits == 128 {
u128::MAX
} else {
(1_u128 << size_in_bits) - 1
};
anyhow::ensure!(
parsed_value <= max_value,
"Value {value:?} does not fit in {}-bit register {}.",
size_in_bits,
register
);
Ok(match size_in_bits {
1..=32 => RegisterValue::U32(parsed_value as u32),
33..=64 => RegisterValue::U64(parsed_value as u64),
65..=128 => RegisterValue::U128(parsed_value),
_ => unreachable!("register width was checked above"),
})
}
fn parse_unsigned_register_value(value: &str) -> Result<u128> {
let trimmed_value = value.trim();
anyhow::ensure!(!trimmed_value.is_empty(), "Register value cannot be empty.");
anyhow::ensure!(
!trimmed_value.starts_with('-'),
"Register value must be unsigned."
);
let trimmed_value = trimmed_value.strip_prefix('+').unwrap_or(trimmed_value);
let normalized_value = trimmed_value.replace('_', "");
anyhow::ensure!(
!normalized_value.is_empty(),
"Register value cannot be empty."
);
let (digits, radix) = normalized_value
.strip_prefix("0x")
.or_else(|| normalized_value.strip_prefix("0X"))
.map(|digits| (digits, 16))
.unwrap_or((normalized_value.as_str(), 10));
anyhow::ensure!(!digits.is_empty(), "Register value cannot be empty.");
u128::from_str_radix(digits, radix).with_context(|| format!("Invalid register value {value:?}"))
}
fn register_requires_exact_readback(register: &CoreRegister) -> bool {
!register.register_has_role(RegisterRole::ProgramCounter)
&& !register.register_has_role(RegisterRole::ProcessorStatus)
&& !register.register_has_role(RegisterRole::FloatingPointStatus)
}
fn register_write_requires_stack_frame_refresh(register: &CoreRegister) -> bool {
register.unwind_rule != UnwindRule::Clear
|| register.register_has_role(RegisterRole::ProgramCounter)
|| register.register_has_role(RegisterRole::FramePointer)
|| register.register_has_role(RegisterRole::StackPointer)
|| register.register_has_role(RegisterRole::MainStackPointer)
|| register.register_has_role(RegisterRole::ProcessStackPointer)
|| register.register_has_role(RegisterRole::ReturnAddress)
}
fn refresh_stack_frames(
target_core: &mut CoreHandle<'_>,
preserved_top_frame_id: ObjectRef,
) -> Result<()> {
let Some(debug_info) = target_core.core_data.debug_info.as_ref() else {
target_core.core_data.stack_frames.clear();
return Ok(());
};
let initial_registers = DebugRegisters::from_core(&mut target_core.core);
let exception_interface = exception_handler_for_core(target_core.core.core_type());
let instruction_set = target_core.core.instruction_set().ok();
let mut stack_frames = debug_info.unwind(
&mut target_core.core,
initial_registers,
exception_interface.as_ref(),
instruction_set,
500,
)?;
if let Some(top_frame) = stack_frames.first_mut() {
top_frame.id = preserved_top_frame_id;
}
target_core.core_data.stack_frames = stack_frames;
Ok(())
}
pub fn get_arguments<T: DeserializeOwned, P: ProtocolAdapter>(
debug_adapter: &mut DebugAdapter<P>,
req: &Request,
) -> Result<T, DebuggerError> {
let Some(raw_arguments) = &req.arguments else {
debug_adapter.send_response::<()>(req, Err(&DebuggerError::InvalidRequest))?;
return Err(DebuggerError::Other(anyhow!(
"Failed to get {} arguments",
req.command
)));
};
match serde_json::from_value(raw_arguments.to_owned()) {
Ok(value) => Ok(value),
Err(e) => {
let err = anyhow!(
"Failed to deserialize {} arguments: {}, error: {}",
req.command,
raw_arguments,
e
);
debug_adapter.send_response::<()>(req, Err(&e.into()))?;
Err(DebuggerError::Other(err))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use probe_rs::{RegisterId, UnwindRule};
static U32_ROLES: [RegisterRole; 1] = [RegisterRole::Core("r0")];
static U32_REGISTER: CoreRegister = CoreRegister {
id: RegisterId(0),
roles: &U32_ROLES,
data_type: RegisterDataType::UnsignedInteger(32),
unwind_rule: UnwindRule::Clear,
};
static U64_ROLES: [RegisterRole; 1] = [RegisterRole::Core("x0")];
static U64_REGISTER: CoreRegister = CoreRegister {
id: RegisterId(1),
roles: &U64_ROLES,
data_type: RegisterDataType::UnsignedInteger(64),
unwind_rule: UnwindRule::Clear,
};
static U128_ROLES: [RegisterRole; 1] = [RegisterRole::Core("v0")];
static U128_REGISTER: CoreRegister = CoreRegister {
id: RegisterId(2),
roles: &U128_ROLES,
data_type: RegisterDataType::UnsignedInteger(128),
unwind_rule: UnwindRule::Clear,
};
static FP_ROLES: [RegisterRole; 1] = [RegisterRole::Core("f0")];
static FP_REGISTER: CoreRegister = CoreRegister {
id: RegisterId(3),
roles: &FP_ROLES,
data_type: RegisterDataType::FloatingPoint(32),
unwind_rule: UnwindRule::Clear,
};
static PC_ROLES: [RegisterRole; 2] = [RegisterRole::Core("pc"), RegisterRole::ProgramCounter];
static PC_REGISTER: CoreRegister = CoreRegister {
id: RegisterId(4),
roles: &PC_ROLES,
data_type: RegisterDataType::UnsignedInteger(32),
unwind_rule: UnwindRule::Clear,
};
static A0_ROLES: [RegisterRole; 2] = [RegisterRole::Core("a0"), RegisterRole::Other("x10")];
static A0_REGISTER: CoreRegister = CoreRegister {
id: RegisterId(10),
roles: &A0_ROLES,
data_type: RegisterDataType::UnsignedInteger(32),
unwind_rule: UnwindRule::Clear,
};
static STATUS_ROLES: [RegisterRole; 2] =
[RegisterRole::Core("xpsr"), RegisterRole::ProcessorStatus];
static STATUS_REGISTER: CoreRegister = CoreRegister {
id: RegisterId(16),
roles: &STATUS_ROLES,
data_type: RegisterDataType::UnsignedInteger(32),
unwind_rule: UnwindRule::Clear,
};
static SP_ROLES: [RegisterRole; 2] = [RegisterRole::Core("sp"), RegisterRole::StackPointer];
static SP_REGISTER: CoreRegister = CoreRegister {
id: RegisterId(17),
roles: &SP_ROLES,
data_type: RegisterDataType::UnsignedInteger(32),
unwind_rule: UnwindRule::Clear,
};
static LR_ROLES: [RegisterRole; 2] = [RegisterRole::Core("lr"), RegisterRole::ReturnAddress];
static LR_REGISTER: CoreRegister = CoreRegister {
id: RegisterId(18),
roles: &LR_ROLES,
data_type: RegisterDataType::UnsignedInteger(32),
unwind_rule: UnwindRule::Clear,
};
static PRESERVED_ROLES: [RegisterRole; 1] = [RegisterRole::Core("r4")];
static PRESERVED_REGISTER: CoreRegister = CoreRegister {
id: RegisterId(19),
roles: &PRESERVED_ROLES,
data_type: RegisterDataType::UnsignedInteger(32),
unwind_rule: UnwindRule::Preserve,
};
#[test]
fn parse_register_value_supports_decimal_hex_and_separators() -> Result<()> {
assert_eq!(
parse_register_value("4660", &U32_REGISTER)?,
RegisterValue::U32(0x1234)
);
assert_eq!(
parse_register_value("0x1234", &U32_REGISTER)?,
RegisterValue::U32(0x1234)
);
assert_eq!(
parse_register_value("0X1234", &U32_REGISTER)?,
RegisterValue::U32(0x1234)
);
assert_eq!(
parse_register_value("0x1_0000", &U32_REGISTER)?,
RegisterValue::U32(0x1_0000)
);
Ok(())
}
#[test]
fn parse_register_value_selects_storage_width() -> Result<()> {
assert_eq!(
parse_register_value("0xffff_ffff", &U32_REGISTER)?,
RegisterValue::U32(u32::MAX)
);
assert_eq!(
parse_register_value("0xffff_ffff_ffff_ffff", &U64_REGISTER)?,
RegisterValue::U64(u64::MAX)
);
assert_eq!(
parse_register_value("0xffff_ffff_ffff_ffff_ffff_ffff_ffff_ffff", &U128_REGISTER)?,
RegisterValue::U128(u128::MAX)
);
Ok(())
}
#[test]
fn parse_register_value_rejects_invalid_input() {
assert!(parse_register_value("", &U32_REGISTER).is_err());
assert!(parse_register_value("-1", &U32_REGISTER).is_err());
assert!(parse_register_value("0xnot_hex", &U32_REGISTER).is_err());
assert!(parse_register_value("0x1_0000_0000", &U32_REGISTER).is_err());
assert!(parse_register_value("1", &FP_REGISTER).is_err());
}
#[test]
fn find_register_by_dap_name_matches_display_name_first() -> Result<()> {
let registers = DebugRegisters(vec![
DebugRegister {
core_register: &PC_REGISTER,
dwarf_id: Some(0),
value: Some(RegisterValue::U32(0x1000)),
},
DebugRegister {
core_register: &A0_REGISTER,
dwarf_id: Some(10),
value: Some(RegisterValue::U32(1)),
},
]);
let pc_register = find_register_by_dap_name(®isters, "pc/PC")
.ok_or_else(|| anyhow::anyhow!("pc/PC register not found"))?;
assert_eq!(pc_register.core_register.id, PC_REGISTER.id);
let a0_register = find_register_by_dap_name(®isters, "a0/x10")
.ok_or_else(|| anyhow::anyhow!("a0/x10 register not found"))?;
assert_eq!(a0_register.core_register.id, A0_REGISTER.id);
Ok(())
}
#[test]
fn find_register_by_dap_name_falls_back_to_role_names() -> Result<()> {
let registers = DebugRegisters(vec![DebugRegister {
core_register: &A0_REGISTER,
dwarf_id: Some(10),
value: Some(RegisterValue::U32(1)),
}]);
let a0_register = find_register_by_dap_name(®isters, "x10")
.ok_or_else(|| anyhow::anyhow!("x10 register not found"))?;
assert_eq!(a0_register.core_register.id, A0_REGISTER.id);
assert!(find_register_by_dap_name(®isters, "x11").is_none());
Ok(())
}
#[test]
fn exact_readback_is_not_required_for_hardware_normalized_registers() {
assert!(register_requires_exact_readback(&U32_REGISTER));
assert!(register_requires_exact_readback(&A0_REGISTER));
assert!(!register_requires_exact_readback(&PC_REGISTER));
assert!(!register_requires_exact_readback(&STATUS_REGISTER));
}
#[test]
fn stack_frame_refresh_is_required_for_unwind_sensitive_registers() {
assert!(!register_write_requires_stack_frame_refresh(&U32_REGISTER));
assert!(register_write_requires_stack_frame_refresh(&PC_REGISTER));
assert!(register_write_requires_stack_frame_refresh(&SP_REGISTER));
assert!(register_write_requires_stack_frame_refresh(&LR_REGISTER));
assert!(register_write_requires_stack_frame_refresh(
&PRESERVED_REGISTER
));
}
}