Skip to content

Commit 0944c1e

Browse files
adamgreenfacchinm
authored andcommitted
Improve break/watchpoints and add single step
Additions include: * Add assembly language routines for DebugMon and fault handlers. * The fault handler checks to see if the fault was caused by handler or thread mode code. * If thread mode then a crash in thread code was encountered so signal the main debugger thread to report the crash to GDB. * A debug event in handler code will escalate to a hard fault since the DebugMon handler runs at such a low priority. When ThreadMRI detects such an escalated debug event, it will disable DWT watchpoints and FPB hardware breakpoints and continue executing the handler code. It will also pend a DebugMon interrupt to fire before returning to thread mode code so the DWT and FPB modules can be re-enabled. * An actual crash in handler mode will be forwarded to the existing mbed-os fault logging code. * The DebugMon handler handles 3 scenarios: * A debug event generated by thread mode code will signal the main debugger thread to report the event to GDB. * Re-enable DWT watchpoints and FPB breakpoints for thread mode code after they were disabled by a debug event generated in handler mode. * Enable or disable single stepping based on the ID of the thread to be executed when DebugMon completes. This allow ThreadMRI to only enable single stepping when switching back to a specific thread. * There are also assembly language stubs for SVCall, SysTick, and PendSV faults/interrupts. These stubs pend the DebugMon interrupt when single stepping is enabled. This is done because it is these 3 faults/interrupts which can execute the RTX thread context switching code. * Now clears the various fault registers (HFSR, DFSR, etc) after storing them in the mriCortexMState object so that they don't accumulate.
1 parent 97c1a67 commit 0944c1e

File tree

5 files changed

+319
-110
lines changed

5 files changed

+319
-110
lines changed

libraries/ThreadMRI/src/ThreadMRI.cpp

Lines changed: 144 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
// the USB serial connection.
1717
#include <Arduino.h>
1818
#include "ThreadMRI.h"
19+
#include <cmsis_os2.h>
20+
#include <rtx_os.h>
1921
extern "C"
2022
{
2123
#include "core/mri.h"
@@ -25,12 +27,6 @@ extern "C"
2527
#include "architectures/armv7-m/armv7-m.h"
2628
#include "architectures/armv7-m/debug_cm3.h"
2729
}
28-
// UNDONE: Might not need this.
29-
#include <signal.h>
30-
#include <string.h>
31-
32-
#include <cmsis_os2.h>
33-
#include <rtx_os.h>
3430

3531
// UNDONE: Switch back to the USB/CDC based serial port.
3632
#undef Serial
@@ -63,23 +59,30 @@ static const char g_memoryMapXml[] = "<?xml version=\"1.0\"?>"
6359
// Bit in LR set to 0 when automatic stacking of floating point registers occurs during exception handling.
6460
#define LR_FLOAT_STACK (1 << 4)
6561

66-
// Thread flag used to indicate that a debug event has occured.
62+
// Thread syncronization flag used to indicate that a debug event has occured.
6763
#define MRI_THREAD_DEBUG_EVENT_FLAG (1 << 0)
6864

6965
osThreadId_t g_mriThreadId;
7066

7167
volatile osThreadId_t g_haltingThreadId;
7268

69+
// UNDONE: Get rid of.
70+
volatile osThreadId_t g_test;
71+
7372
osThreadId_t g_threads[64];
7473
uint32_t g_threadCount;
7574

75+
volatile osThreadId_t mriThreadSingleStepThreadId;
76+
77+
volatile uint32_t mriThreadEnableDWTandFPB;
78+
7679

7780
ThreadMRI::ThreadMRI()
7881
{
7982
mriInit("");
8083
}
8184

82-
// UNDONE: This is just for initial bringup.
85+
// Main entry point into MRI debugger core.
8386
extern "C" void mriDebugException(void);
8487

8588
// UNDONE: Could push into debugException() API.
@@ -109,10 +112,12 @@ static void suspendAllApplicationThreads() {
109112
g_threadCount = osThreadEnumerate(g_threads, sizeof(g_threads)/sizeof(g_threads[0]));
110113
for (uint32_t i = 0 ; i < g_threadCount ; i++) {
111114
osThreadId_t thread = g_threads[i];
115+
const char* pThreadName = osThreadGetName(thread);
112116

113-
if (thread != g_mriThreadId) {
117+
if (thread != g_mriThreadId && strcmp(pThreadName, "rtx_idle") != 0) {
114118
osThreadSuspend(thread);
115-
} else {
119+
}
120+
else {
116121
g_threads[i] = 0;
117122
}
118123
}
@@ -176,20 +181,32 @@ static void readThreadContext(osThreadId_t thread) {
176181

177182
static __NO_RETURN void mriMain(void *pv)
178183
{
184+
// Run the code which suspends, resumes, etc the other threads at highest priority so that it doesn't context
185+
// switch to one of the other threads. Switch to normal priority when running mriDebugException() though.
186+
osThreadSetPriority(osThreadGetId(), osPriorityRealtime7);
187+
179188
while (1) {
180-
// Wait for next debug event to occur. Set priority to highest level before blocking so that it can safely
181-
// put all of the other application threads to sleep upon wakeup.
182-
osThreadSetPriority(osThreadGetId(), osPriorityRealtime7);
183-
osThreadFlagsWait(MRI_THREAD_DEBUG_EVENT_FLAG, osFlagsWaitAny, osWaitForever);
184-
suspendAllApplicationThreads();
185-
osThreadSetPriority(osThreadGetId(), osPriorityNormal);
189+
int waitResult = osThreadFlagsWait(MRI_THREAD_DEBUG_EVENT_FLAG, osFlagsWaitAny, osWaitForever);
190+
while (waitResult < 0) {
191+
// Something bad has happened if we hang here.
192+
}
193+
suspendAllApplicationThreads();
186194

187-
if (g_haltingThreadId == 0) {
188-
continue;
195+
while (g_haltingThreadId == 0) {
196+
// Something bad has happened if we hang here.
189197
}
190198
readThreadContext(g_haltingThreadId);
199+
200+
osThreadSetPriority(osThreadGetId(), osPriorityNormal);
191201
mriDebugException();
192-
resumeApplicationThreads();
202+
osThreadSetPriority(osThreadGetId(), osPriorityRealtime7);
203+
204+
if (Platform_IsSingleStepping()) {
205+
mriThreadSingleStepThreadId = g_haltingThreadId;
206+
osThreadResume(mriThreadSingleStepThreadId);
207+
} else {
208+
resumeApplicationThreads();
209+
}
193210
g_haltingThreadId = 0;
194211
}
195212
}
@@ -200,51 +217,143 @@ static __NO_RETURN void mriMain(void *pv)
200217
// Global Platform_* functions needed by MRI to initialize and communicate with MRI.
201218
// These functions will perform most of their work through the DebugSerial singleton.
202219
// ---------------------------------------------------------------------------------------------------------------------
220+
// Assembly Language fault handling stubs. They do some preprocessing and then call the C handler below if appropriate.
221+
extern "C" void mriDebugMonitorHandlerStub(void);
222+
extern "C" void mriFaultHandlerStub(void);
223+
// Assembly Language stubs for RTX context switching routines. They check to see if DebugMon should be pended before
224+
// calling the actual RTX handlers.
225+
extern "C" void mriSVCHandlerStub(void);
226+
extern "C" void mriPendSVHandlerStub(void);
227+
extern "C" void mriSysTickHandlerStub(void);
228+
229+
230+
203231
// Forward Function Declarations
232+
static bool hasEncounteredDebugEvent();
233+
static void recordAndClearFaultStatusBits();
234+
static void wakeMriMainToDebugCurrentThread();
235+
static void stopSingleStepping();
204236
static void switchFaultHandlersToDebugger();
237+
static void switchRtxHandlersToDebugStubs();
238+
205239

206-
static void mriDebugMonitorHandler(void)
240+
extern "C" void mriDebugMonitorHandler(uint32_t excReturn)
241+
{
242+
excReturn &= 0xF;
243+
bool isThreadMode = (excReturn == 0xD || excReturn == 0x9);
244+
while (!isThreadMode) {
245+
// DebugMon is running at such low priority that we should be getting ready to return to thread mode.
246+
}
247+
248+
if (!hasEncounteredDebugEvent()) {
249+
if (mriThreadEnableDWTandFPB) {
250+
enableDWTandITM();
251+
enableFPB();
252+
mriThreadEnableDWTandFPB = 0;
253+
}
254+
if (mriThreadSingleStepThreadId) {
255+
// Code is written to handle case where single stepping gets enabled because current thread was the one
256+
// to be single stepped but then a higher priority interrupt comes in and makes another thread the
257+
// current thread so single stepping should be disabled again.
258+
if (mriThreadSingleStepThreadId == osThreadGetId()) {
259+
enableSingleStep();
260+
} else {
261+
disableSingleStep();
262+
}
263+
}
264+
265+
return;
266+
}
267+
268+
// Get here when a debug event of interest has occurred in a thread.
269+
recordAndClearFaultStatusBits();
270+
stopSingleStepping();
271+
wakeMriMainToDebugCurrentThread();
272+
}
273+
274+
// This function is called if a fault (hard, mem, bus, usage) has occurred while running code in thread mode.
275+
// It is also called when a debug event has forced a hard fault while running code in handler mode.
276+
extern "C" void mriFaultHandler(uint32_t excReturn)
277+
{
278+
excReturn &= 0xF;
279+
bool isThreadMode = (excReturn == 0xD || excReturn == 0x9);
280+
if (isThreadMode) {
281+
recordAndClearFaultStatusBits();
282+
stopSingleStepping();
283+
wakeMriMainToDebugCurrentThread();
284+
return;
285+
}
286+
287+
// The asm stub calling this function has already verified that a debug event during handler mode caused
288+
// this fault.
289+
mriThreadEnableDWTandFPB = 1;
290+
SCB->DFSR = SCB->DFSR;
291+
SCB->HFSR = SCB_HFSR_DEBUGEVT_Msk;
292+
disableDWTandITM();
293+
disableFPB();
294+
setMonitorPending();
295+
}
296+
297+
static bool hasEncounteredDebugEvent()
298+
{
299+
return SCB->DFSR != 0;
300+
301+
}
302+
303+
static void recordAndClearFaultStatusBits()
207304
{
208-
// Record information about cause of exception/debug event.
209305
mriCortexMState.exceptionNumber = getCurrentlyExecutingExceptionNumber();
210306
mriCortexMState.dfsr = SCB->DFSR;
211307
mriCortexMState.hfsr = SCB->HFSR;
212308
mriCortexMState.cfsr = SCB->CFSR;
213309
mriCortexMState.mmfar = SCB->MMFAR;
214310
mriCortexMState.bfar = SCB->BFAR;
215311

216-
// Clear the debug event bits as they are already recorded.
217-
if (mriCortexMState.exceptionNumber == 12) {
218-
SCB->DFSR = mriCortexMState.dfsr;
219-
}
312+
// Clear fault status bits by writing 1s to bits that are already set.
313+
SCB->DFSR = mriCortexMState.dfsr;
314+
SCB->HFSR = mriCortexMState.hfsr;
315+
SCB->CFSR = mriCortexMState.cfsr;
316+
}
220317

221-
g_haltingThreadId = osThreadGetId();
318+
static void wakeMriMainToDebugCurrentThread()
319+
{
320+
disableSingleStep();
321+
g_test = osThreadGetId();
322+
g_haltingThreadId = g_test; // UNDONE: osThreadGetId();
222323
osThreadFlagsSet(g_mriThreadId, MRI_THREAD_DEBUG_EVENT_FLAG);
223324
}
224325

225-
static void mriFaultHandler(void)
326+
static void stopSingleStepping()
226327
{
227-
// UNDONE: Differentiate.
228-
mriDebugMonitorHandler();
328+
disableSingleStep();
329+
mriThreadSingleStepThreadId = NULL;
229330
}
230331

231-
232332
void Platform_Init(Token* pParameterTokens)
233333
{
234334
__try
235335
mriCortexMInit((Token*)pParameterTokens);
236336
__catch
237337
__rethrow;
238338

339+
mriThreadEnableDWTandFPB = 0;
340+
mriThreadSingleStepThreadId = NULL;
239341
switchFaultHandlersToDebugger();
240-
NVIC_SetVector(DebugMonitor_IRQn, (uint32_t)mriDebugMonitorHandler);
342+
switchRtxHandlersToDebugStubs();
241343
}
242344

243345
static void switchFaultHandlersToDebugger(void) {
244-
NVIC_SetVector(HardFault_IRQn, (uint32_t)mriFaultHandler);
245-
NVIC_SetVector(MemoryManagement_IRQn, (uint32_t)mriFaultHandler);
246-
NVIC_SetVector(BusFault_IRQn, (uint32_t)mriFaultHandler);
247-
NVIC_SetVector(UsageFault_IRQn, (uint32_t)mriFaultHandler);
346+
NVIC_SetVector(HardFault_IRQn, (uint32_t)mriFaultHandlerStub);
347+
NVIC_SetVector(MemoryManagement_IRQn, (uint32_t)mriFaultHandlerStub);
348+
NVIC_SetVector(BusFault_IRQn, (uint32_t)mriFaultHandlerStub);
349+
NVIC_SetVector(UsageFault_IRQn, (uint32_t)mriFaultHandlerStub);
350+
NVIC_SetVector(DebugMonitor_IRQn, (uint32_t)mriDebugMonitorHandlerStub);
351+
}
352+
353+
static void switchRtxHandlersToDebugStubs(void) {
354+
NVIC_SetVector(SVCall_IRQn, (uint32_t)mriSVCHandlerStub);
355+
NVIC_SetVector(PendSV_IRQn, (uint32_t)mriPendSVHandlerStub);
356+
NVIC_SetVector(SysTick_IRQn, (uint32_t)mriSysTickHandlerStub);
248357
}
249358

250359

0 commit comments

Comments
 (0)