summaryrefslogtreecommitdiffstats
path: root/cpukit/libdebugger
diff options
context:
space:
mode:
authorKinsey Moore <kinsey.moore@oarcorp.com>2022-02-22 16:01:33 -0600
committerJoel Sherrill <joel@rtems.org>2022-02-23 08:35:45 -0600
commit16d40ce7ff59c1b75b922e81a9b829ae19c37ff3 (patch)
treeb658571e075a9f7e5cd69224c57349ba8d047082 /cpukit/libdebugger
parentmicroblaze: Decouple exceptions from interrupts (diff)
downloadrtems-16d40ce7ff59c1b75b922e81a9b829ae19c37ff3.tar.bz2
cpukit/libdebugger: Avoid cascade for interrupts
This updates behavior of libdebugger to handle debug exceptions in interrupt context by temporarily removing a software breakpoint, stepping, and then resuming afterward.
Diffstat (limited to 'cpukit/libdebugger')
-rw-r--r--cpukit/libdebugger/rtems-debugger-target.c105
-rw-r--r--cpukit/libdebugger/rtems-debugger-target.h5
2 files changed, 104 insertions, 6 deletions
diff --git a/cpukit/libdebugger/rtems-debugger-target.c b/cpukit/libdebugger/rtems-debugger-target.c
index c298a62357..2b55c93513 100644
--- a/cpukit/libdebugger/rtems-debugger-target.c
+++ b/cpukit/libdebugger/rtems-debugger-target.c
@@ -167,6 +167,32 @@ rtems_debugger_target_reg_table_size(void)
return 0;
}
+bool
+rtems_debugger_target_swbreak_is_configured( uintptr_t addr )
+{
+ size_t i;
+ rtems_debugger_target_swbreak *swbreaks;
+ rtems_debugger_target *target = rtems_debugger->target;
+
+ if ( target == NULL ) {
+ return false;
+ }
+
+ swbreaks = target->swbreaks.block;
+
+ if ( swbreaks == NULL ) {
+ return false;
+ }
+
+ for ( i = 0; i < target->swbreaks.level; ++i ) {
+ if ( (uintptr_t) swbreaks[ i ].address == addr ) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
int
rtems_debugger_target_swbreak_control(bool insert, uintptr_t addr, DB_UINT kind)
{
@@ -323,13 +349,81 @@ rtems_debugger_target_swbreak_remove(void)
return r;
}
+uintptr_t saved_break_address = 0;
+rtems_id saved_tid = 0;
+
+static rtems_debugger_target_exc_action
+soft_step_and_continue(CPU_Exception_frame* frame)
+{
+ uintptr_t break_address;
+ rtems_debugger_target *target = rtems_debugger->target;
+ Thread_Control *thread = _Thread_Get_executing();
+ const rtems_id tid = thread->Object.id;
+ rtems_debugger_thread fake_debugger_thread;
+
+ /*
+ * If this was a hwbreak, cascade. If this is a swbreak replace the contents
+ * of the instruction, step then return the swbreak's contents.
+ */
+ if ((target->capabilities & RTEMS_DEBUGGER_TARGET_CAP_SWBREAK) == 0) {
+ target_printk("rtems-db: exception in an interrupt, cascading\n");
+ rtems_debugger_unlock();
+ return rtems_debugger_target_exc_cascade;
+ }
+
+ break_address = rtems_debugger_target_frame_pc( frame );
+ if ( rtems_debugger_target_swbreak_is_configured( break_address ) == false ) {
+ target_printk("rtems-db: exception in an interrupt, cascading\n");
+ rtems_debugger_unlock();
+ return rtems_debugger_target_exc_cascade;
+ }
+
+ /* Remove the current breakpoint */
+ rtems_debugger_target_swbreak_control(
+ false,
+ break_address,
+ target->breakpoint_size
+ );
+
+ /* Save off thread ID and break address for later usage */
+ saved_tid = tid;
+ saved_break_address = break_address;
+
+ /* Populate the fake rtems_debugger_thread */
+ fake_debugger_thread.flags |= RTEMS_DEBUGGER_THREAD_FLAG_STEP;
+ fake_debugger_thread.frame = frame;
+ target_printk("rtems-db: stepping to the next instruction\n");
+ rtems_debugger_target_thread_stepping(&fake_debugger_thread);
+
+ /* rtems_debugger_unlock() not called until the step is resolved */
+ return rtems_debugger_target_exc_step;
+}
+
rtems_debugger_target_exc_action
rtems_debugger_target_exception(CPU_Exception_frame* frame)
{
+ Thread_Control* thread = _Thread_Get_executing();
+ const rtems_id tid = thread->Object.id;
+
+ /* Resolve outstanding step+continue */
+ if ( saved_break_address != 0 && tid == saved_tid ) {
+ rtems_debugger_target_swbreak_control(
+ true,
+ saved_break_address,
+ rtems_debugger->target->breakpoint_size
+ );
+ saved_break_address = saved_tid = 0;
+
+ /* Release the debugger lock now that the step+continue is complete */
+ target_printk("rtems-db: resuming after step\n");
+ rtems_debugger_unlock();
+ return rtems_debugger_target_exc_consumed;
+ }
+
+ rtems_debugger_lock();
+
if (!rtems_interrupt_is_in_progress()) {
rtems_debugger_threads* threads = rtems_debugger->threads;
- Thread_Control* thread = _Thread_Get_executing();
- const rtems_id tid = thread->Object.id;
rtems_id* excludes;
uintptr_t pc;
const rtems_debugger_thread_stepper* stepper;
@@ -340,8 +434,6 @@ rtems_debugger_target_exception(CPU_Exception_frame* frame)
" frame:%08" PRIxPTR "\n",
tid, (intptr_t) thread, (intptr_t) frame);
- rtems_debugger_lock();
-
/*
* If the thread is in the debugger recover. If the access is from gdb
* continue else shutdown and let the user know.
@@ -430,9 +522,10 @@ rtems_debugger_target_exception(CPU_Exception_frame* frame)
return rtems_debugger_target_exc_consumed;
}
- rtems_debugger_printf("rtems-db: exception in an interrupt, cascading\n");
+ target_printk("[} tid:%08" PRIx32 ": exception in interrupt context\n", tid);
- return rtems_debugger_target_exc_cascade;
+ /* soft_step_and_continue releases the debugger lock */
+ return soft_step_and_continue( frame );
}
void
diff --git a/cpukit/libdebugger/rtems-debugger-target.h b/cpukit/libdebugger/rtems-debugger-target.h
index 7836f93bd6..3f6ceac80b 100644
--- a/cpukit/libdebugger/rtems-debugger-target.h
+++ b/cpukit/libdebugger/rtems-debugger-target.h
@@ -222,6 +222,11 @@ extern int rtems_debugger_target_swbreak_insert(void);
extern int rtems_debugger_target_swbreak_remove(void);
/**
+ * Determine whether a software breakpoint is configured for the given address.
+ */
+extern bool rtems_debugger_target_swbreak_is_configured( uintptr_t addr );
+
+/**
* Insert hardware breakpoints into the hardware.
*/
extern int rtems_debugger_target_hwbreak_insert(void);