Skip to content

Commit 4db4ae2

Browse files
authored
FreeRTOS-MPU: Read running task stack pointer from $sp register (#73)
* FreeRTOS-MPU: Read running task stack pointer from $sp register * FreeRTOS-MPU: try Cortex-A/R $r13_usr before $sp During ISRs and other exception handlers, $sp uses one of the banked r13 registers (r13_irq, r13_fiq, etc.). Use $r13_usr if available instead. * FreeRTOS-MPU: try Cortex-M $psp before $sp During ISRs and other exception handlers, $sp is linked to $msp. Use the task stack pointer $psp if available instead. * getStackPointerRegVal: try $sp_usr for OpenOCD compatibility
1 parent 2ba3978 commit 4db4ae2

File tree

2 files changed

+47
-5
lines changed

2 files changed

+47
-5
lines changed

src/rtos/rtos-common.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,31 @@ export abstract class RTOSBase {
194194
}
195195
}
196196

197+
protected async getStackPointerRegVal(frameId: number): Promise<number | undefined> {
198+
const spRegs = [
199+
// Cortex-M ports use PSP for task stacks, MSP for exceptions
200+
'psp',
201+
// Cortex-A/R uses r13_usr for task stacks, r13_irq, r13_fiq, r13_svc, etc. for exceptions
202+
'r13_usr',
203+
// ...but OpenOCD calls it sp_usr, not r13_usr
204+
'sp_usr',
205+
// Both GDB and LLDB provide a generic stack pointer alias named $sp
206+
// on platforms that don't already define a register named "sp". For
207+
// other debuggers, this method may fail to find the stack pointer
208+
// register.
209+
'sp',
210+
];
211+
for (const spReg of spRegs) {
212+
try {
213+
const val = parseInt((await this.evalForVarValue(frameId, `$${spReg}`)) || '');
214+
if (val) {
215+
return val;
216+
}
217+
} catch (e) {}
218+
}
219+
return undefined;
220+
}
221+
197222
protected async detectArchitectureIfEmpty(
198223
architecture: Architecture | undefined,
199224
frameId: number

src/rtos/rtos-freertos.ts

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -645,7 +645,6 @@ export class RTOSFreeRTOS extends RTOSCommon.RTOSBase {
645645
(await this.getExprVal('(char *)' + thInfo['pcTaskName']?.exp, frameId)) || '';
646646
const match = tmpThName.match(/"([^*]*)"$/);
647647
const thName = match ? match[1] : tmpThName;
648-
const stackInfo = await this.getStackInfo(thInfo, 0xA5, frameId);
649648
// This is the order we want stuff in
650649
const display: { [key: string]: RTOSCommon.DisplayRowItem } = {};
651650
const mySetter = (x: DisplayFields, text: string, value?: any) => {
@@ -692,6 +691,7 @@ export class RTOSFreeRTOS extends RTOSCommon.RTOSBase {
692691
}
693692
}
694693
}
694+
const stackInfo = await this.getStackInfo(threadRunning, thInfo, 0xA5, frameId);
695695
mySetter(DisplayFields.StackStart, RTOSCommon.hexFormat(stackInfo.stackStart));
696696
mySetter(
697697
DisplayFields.StackTop,
@@ -750,15 +750,16 @@ export class RTOSFreeRTOS extends RTOSCommon.RTOSBase {
750750
});
751751
}
752752

753-
protected async getStackInfo(thInfo: RTOSCommon.RTOSStrToValueMap, waterMark: number, frameId: number) {
753+
protected async getStackInfo(threadRunning: boolean, thInfo: RTOSCommon.RTOSStrToValueMap, waterMark: number, frameId: number) {
754754
const pxStack = thInfo['pxStack']?.val;
755755
const pxTopOfStack = thInfo['pxTopOfStack']?.val;
756756
const pxEndOfStack = thInfo['pxEndOfStack']?.val;
757757
const stackInfo: RTOSCommon.RTOSStackInfo = {
758758
stackStart: parseInt(pxStack),
759759
stackTop: parseInt(pxTopOfStack)
760760
};
761-
const mpuStackTop = await this.mpuGetStackTop(thInfo, frameId);
761+
762+
const mpuStackTop = await this.mpuGetStackTop(threadRunning, thInfo, frameId);
762763
if (mpuStackTop === null) {
763764
stackInfo.stackTop = undefined;
764765
} else if (mpuStackTop !== undefined) {
@@ -817,6 +818,7 @@ export class RTOSFreeRTOS extends RTOSCommon.RTOSBase {
817818
// * undefined: no MPU context was found (meaning pxTopOfStack can be used for deltas)
818819
// * null: MPU context was found, but could not be interpreted (unsupported architecture, unexpected size, etc.)
819820
private async mpuGetStackTop(
821+
threadRunning: boolean,
820822
thInfo: RTOSCommon.RTOSStrToValueMap,
821823
frameId: number
822824
): Promise<number | null | undefined> {
@@ -825,8 +827,11 @@ export class RTOSFreeRTOS extends RTOSCommon.RTOSBase {
825827
}
826828
const xMPUSettings = (await this.getVarChildrenObj(thInfo['xMPUSettings'].ref, 'xMPUSettings')) || {};
827829

828-
// non-privileged tasks use a separate system call stack, and back up
829-
// their task SP. Use that backup if it's present and non-NULL.
830+
// When a non-privileged task voluntarily relinquishes the CPU,
831+
// uxContext contains a pointer into the system call stack, rather than
832+
// the task stack. While that task is paused, pulTaskStack
833+
// (pulTaskStackPointer on some ports) will contain a backup of the
834+
// task's stack pointer. (continued...)
830835
if ('xSystemCallStackInfo' in xMPUSettings) {
831836
const xSystemCallStackInfo =
832837
(await this.getVarChildrenObj(xMPUSettings['xSystemCallStackInfo'].ref, 'xSystemCallStackInfo')) || {};
@@ -840,6 +845,18 @@ export class RTOSFreeRTOS extends RTOSCommon.RTOSBase {
840845
}
841846
}
842847

848+
// (...) However, when that task returns to a RUNNING state,
849+
// pulTaskStack gets zeroed and the only way to get a stack top that
850+
// bears any resemblance to reality is to read directly from the stack
851+
// pointer register. Note that this can still have surprising results if
852+
// the program is halted in an ISR or system call.
853+
if (threadRunning) {
854+
const taskSP = await this.getStackPointerRegVal(frameId);
855+
if (taskSP !== undefined) {
856+
return taskSP;
857+
}
858+
}
859+
843860
if (!('ulContext' in xMPUSettings)) {
844861
return undefined;
845862
}

0 commit comments

Comments
 (0)