Skip to content

Commit 05f53c2

Browse files
author
Max Vilimpoc
committed
Fix nuket#7: Support mbed OS 5.5 and higher CMSIS-RTOS 2 API
1 parent 31822ad commit 05f53c2

File tree

2 files changed

+143
-13
lines changed

2 files changed

+143
-13
lines changed

README.md

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,38 @@
11
# mbed-memory-status
22

3-
## Note
4-
5-
As of mbed OS 5.5, this code no longer works. The switch to CMSIS-RTOS2 as the base OS has removed all of the low-level APIs that were relied upon.
6-
7-
The last version of mbed OS that will work is [https://github.com/ARMmbed/mbed-os/releases/tag/mbed-os-5.4.7](mbed-os-5.4.7).
8-
93
## Purpose
104

115
Print thread stack, ISR stack, and global heap locations, sizes, and utilization at runtime when using mbed OS. Useful for tracking down total runtime memory usage and stack overflows.
126

137
Does *not* use printf(). It *will* automatically initialize the default serial port to 115200 8N1 using the low-level mbed `serial_api.h` if no other instantiation has occurred.
148

9+
The code has now been fixed to detect whether it is running on the CMSIS-RTOS 1 or CMSIS-RTOS 2 API and will adjust its low-level API calls accordingly.
10+
1511
## Example
16-
```
12+
13+
```c
14+
#include "mbed_memory_status.h"
15+
1716
int main()
1817
{
1918
print_all_thread_info();
2019
print_heap_and_isr_stack_info();
2120
}
2221
```
2322

23+
## Building
24+
25+
When building the code, make sure to pass the `MBED_STACK_STATS_ENABLED` compiler macro as follows, otherwise mbed will eliminate the stack canary code:
26+
27+
```
28+
mbed compile -D MBED_STACK_STATS_ENABLED=1
29+
```
30+
31+
This value can also be added permanently to the `mbed_app.json` macros.
32+
2433
## Output
2534

26-
Using ARM RTX RTOS on mbed, this will print something like:
35+
Using ARM RTX RTOS on up to mbed 5.4.7, this will print something like:
2736

2837
```
2938
stack ( start: 20005100 end: 20005420 size: 00000320 used: 00000070 ) thread ( id: 2000542C entry: 00020D91 )
@@ -33,6 +42,16 @@ Using ARM RTX RTOS on mbed, this will print something like:
3342
isr_stack ( start: 20007800 end: 20008000 size: 00000800 used: 000002B0 )
3443
```
3544

45+
Using CMSIS-RTOS 2 from mbed 5.5 and higher, we gain access to thread names, so this will print something like:
46+
47+
```
48+
stack ( start: 20004628 end: 20005628 size: 00001000 used: 000000F8 ) thread ( id: 200045D8 entry: 0001CEBD name: main_thread )
49+
stack ( start: 200040A0 end: 200042A0 size: 00000200 used: 00000100 ) thread ( id: 20003D58 entry: 0001D019 name: unknown )
50+
stack ( start: 20003DA0 end: 200040A0 size: 00000300 used: 00000068 ) thread ( id: 20003D10 entry: 0001F1BD name: unknown )
51+
heap ( start: 20005738 end: 2000FC00 size: 0000A4C8 used: 00000000 ) alloc ( ok: 00000000 fail: 00000000 )
52+
isr_stack ( start: 2000FC00 end: 20010000 size: 00000400 )
53+
```
54+
3655
## Use
3756

3857
Add to your program:
@@ -56,8 +75,18 @@ Then define this in `mbed_memory_status.c`, or via the `mbed_app.json` macros, o
5675
#define DEBUG_ISR_STACK_USAGE 1
5776
```
5877
59-
## Why
78+
## Gotchas
79+
80+
On mbed 5.5 and up, there may be a Heisenbug when calling print_thread_info() inside of osKernelLock()!
81+
82+
This error sometimes appears on the serial console shortly after chip startup, but not always:
83+
`mbed assertation failed: os_timer->get_tick() == svcRtxKernelGetTickCount(), file: .\mbed-os\rtos\TARGET_CORTEX\mbed_rtx_idle.c`
84+
85+
The RTOS seems to be asserting an idle constraint violation due to the slowness of sending data through the serial port, but it does not happen consistently.
86+
87+
## Why This Exists
6088
6189
This code exists because of a stupid amount of bug-hunting:
90+
6291
> https://vilimpoc.org/blog/2017/02/01/stack-heap-and-thread-crash-hunting-in-mbed-os/
6392
> https://vilimpoc.org/blog/2017/02/04/isr-stack-usage-on-mbed/

mbed_memory_status.c

Lines changed: 105 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@
3232
#include "platform/mbed_critical.h"
3333
#include "platform/mbed_stats.h"
3434

35+
#if !MBED_STACK_STATS_ENABLED
36+
#warning MBED_STACK_STATS_ENABLED != 1, so there will be no stack usage measurements.
37+
#endif
38+
3539
#ifndef DEBUG_ISR_STACK_USAGE
3640
#define DEBUG_ISR_STACK_USAGE 0
3741
#endif
@@ -192,10 +196,24 @@ static void debug_print_pointer(const void * pointer)
192196
#if (defined (MBED_CONF_RTOS_PRESENT) && (MBED_CONF_RTOS_PRESENT != 0))
193197
#include "cmsis_os.h"
194198

195-
// Temporarily #undef NULL or the compiler complains about previous def.
196-
#undef NULL
197-
#include "rt_TypeDef.h"
198-
199+
// cmsis_os.h provides some useful defines:
200+
//
201+
// For mbed OS 5.4 and lower, osCMSIS == 0x10002U (see: rtos/rtx/TARGET_CORTEX_M)
202+
// For mbed OS 5.5 and higher, osCMSIS == 0x20001U (see: rtos/TARGET_CORTEX/rtx{4|5})
203+
//
204+
// Starting in mbed OS 5.5, a new RTOS layer was introduced with a different API.
205+
206+
#if (osCMSIS < 0x20000U)
207+
// Temporarily #undef NULL or the compiler complains about previous def.
208+
#undef NULL
209+
#include "rt_TypeDef.h"
210+
#else
211+
#include "rtx_lib.h"
212+
// #include <stdlib.h> // Include if you need malloc() / free() below. (probably better for non-C99 compilers)
213+
#endif
214+
215+
#if (osCMSIS < 0x20000U)
216+
199217
// No public forward declaration for this.
200218
extern P_TCB rt_tid2ptcb (osThreadId thread_id);
201219

@@ -248,6 +266,89 @@ void print_all_thread_info(void)
248266
_osThreadEnumFree(enumId);
249267
}
250268

269+
#else
270+
271+
static void print_thread_info(osThreadId threadId)
272+
{
273+
// Refs: rtx_lib.h - #define os_thread_t osRtxThread_t
274+
// rtx_os.h - typedef struct osRtxThread_s { } osRtxThread_t
275+
276+
if (!threadId) return;
277+
278+
os_thread_t * tcb = (os_thread_t *) threadId;
279+
280+
uint32_t stackSize = osThreadGetStackSize(threadId);
281+
uint32_t stackUsed = osThreadGetStackSpace(threadId);
282+
283+
DPL(" stack ( start: ");
284+
debug_print_pointer(tcb->stack_mem);
285+
286+
DPL(" end: ");
287+
debug_print_pointer((uint8_t *) tcb->stack_mem + stackSize);
288+
289+
DPL(" size: ");
290+
debug_print_u32(stackSize);
291+
292+
DPL(" used: ");
293+
debug_print_u32(stackSize - stackUsed);
294+
295+
DPL(" ) ");
296+
297+
DPL("thread ( id: ");
298+
debug_print_pointer(threadId);
299+
300+
DPL(" entry: ");
301+
debug_print_u32(tcb->thread_addr);
302+
303+
DPL(" name: ");
304+
DPL(osThreadGetName(threadId) ? osThreadGetName(threadId) : "unknown");
305+
306+
DPL(" )\r\n");
307+
}
308+
309+
void print_all_thread_info(void)
310+
{
311+
// Refs: mbed_stats.c - mbed_stats_stack_get_each()
312+
313+
uint32_t threadCount = osThreadGetCount();
314+
osThreadId_t threads[threadCount];
315+
316+
// osThreadId_t * threads = malloc(sizeof(osThreadId_t) * threadCount);
317+
// MBED_ASSERT(NULL != threads);
318+
319+
memset(threads, 0, threadCount * sizeof(osThreadId_t));
320+
321+
// This will probably only work if the number of threads remains constant
322+
// (i.e. the number of thread control blocks remains constant)
323+
//
324+
// This is probably the case on a deterministic realtime embedded system
325+
// with limited SRAM.
326+
327+
osKernelLock();
328+
329+
threadCount = osThreadEnumerate(threads, threadCount);
330+
331+
for (uint32_t i = 0; i < threadCount; i++)
332+
{
333+
// There seems to be a Heisenbug when calling print_thread_info()
334+
// inside of osKernelLock()!
335+
336+
// This error may appear on the serial console:
337+
// mbed assertation failed: os_timer->get_tick() == svcRtxKernelGetTickCount(), file: .\mbed-os\rtos\TARGET_CORTEX\mbed_rtx_idle.c
338+
339+
// The RTOS seems to be asserting an idle constraint violation due
340+
// to the slowness of sending data through the serial port, but it
341+
// does not happen consistently.
342+
print_thread_info(threads[i]);
343+
}
344+
345+
osKernelUnlock();
346+
347+
// free(threads);
348+
}
349+
350+
#endif
351+
251352
void print_current_thread_id(void)
252353
{
253354
DPL("Current thread: ");

0 commit comments

Comments
 (0)