From ad24b7d9826a12d4171dd0ac197812bb1f3e663f Mon Sep 17 00:00:00 2001 From: Russ Butler Date: Wed, 7 Sep 2016 00:37:56 -0500 Subject: [PATCH 1/7] Add RTX API to enumerate running threads Add the functions osThreadsEnumStart, osThreadEnumNext and osThreadEnumFree to allow enumeration of running threads. Protect thread creation, thread exit and thread termination with a mutex so threads are not created or destroyed while an enumeration is ongoing. --- rtos/rtx/TARGET_CORTEX_A/cmsis_os.h | 23 +++++++++++ rtos/rtx/TARGET_CORTEX_A/rt_CMSIS.c | 55 ++++++++++++++++++++++++++- rtos/rtx/TARGET_CORTEX_M/cmsis_os.h | 23 +++++++++++ rtos/rtx/TARGET_CORTEX_M/rt_CMSIS.c | 59 +++++++++++++++++++++++++++-- 4 files changed, 154 insertions(+), 6 deletions(-) diff --git a/rtos/rtx/TARGET_CORTEX_A/cmsis_os.h b/rtos/rtx/TARGET_CORTEX_A/cmsis_os.h index 5ead34c9932..d1449cc77c3 100644 --- a/rtos/rtx/TARGET_CORTEX_A/cmsis_os.h +++ b/rtos/rtx/TARGET_CORTEX_A/cmsis_os.h @@ -160,6 +160,7 @@ used throughout the whole project. #define osFeature_Semaphore 65535 ///< maximum count for \ref osSemaphoreCreate function #define osFeature_Wait 0 ///< osWait function: 1=available, 0=not available #define osFeature_SysTick 1 ///< osKernelSysTick functions: 1=available, 0=not available +#define osFeature_ThreadEnum 1 ///< Thread enumeration available #if defined (__CC_ARM) #define os_InRegs __value_in_regs // Compiler specific: force struct in registers @@ -261,6 +262,8 @@ typedef struct os_messageQ_cb *osMessageQId; /// \note CAN BE CHANGED: \b os_mailQ_cb is implementation specific in every CMSIS-RTOS. typedef struct os_mailQ_cb *osMailQId; +/// Thread enumeration ID identifies the enumeration (pointer to a thread enumeration control block). +typedef uint32_t *osThreadEnumId; /// Thread Definition structure contains startup information of a thread. /// \note CAN BE CHANGED: \b os_thread_def is implementation specific in every CMSIS-RTOS. @@ -823,6 +826,26 @@ osStatus osMailFree (osMailQId queue_id, void *mail); #endif // Mail Queues available +// ==== Thread Enumeration Functions ==== + +#if (defined (osFeature_ThreadEnum) && (osFeature_ThreadEnum != 0)) // Thread enumeration available + +/// Start a thread enumeration. +/// \return an enumeration ID or NULL on error. +osThreadEnumId osThreadsEnumStart(void); + +/// Get the next task ID in the enumeration. +/// \return a thread ID or NULL on if the end of the enumeration has been reached. +osThreadId osThreadEnumNext(osThreadEnumId enum_id); + +/// Free the enumeration structure. +/// \param[in] enum_id pointer to the enumeration ID that was obtained with \ref osThreadsEnumStart. +/// \return status code that indicates the execution status of the function. +osStatus osThreadEnumFree(osThreadEnumId enum_id); + +#endif // Thread Enumeration available + + // ==== RTX Extensions ==== /// os_suspend: http://www.keil.com/support/man/docs/rlarm/rlarm_os_suspend.htm diff --git a/rtos/rtx/TARGET_CORTEX_A/rt_CMSIS.c b/rtos/rtx/TARGET_CORTEX_A/rt_CMSIS.c index 97218d0fecb..2a23d1958e8 100644 --- a/rtos/rtx/TARGET_CORTEX_A/rt_CMSIS.c +++ b/rtos/rtx/TARGET_CORTEX_A/rt_CMSIS.c @@ -479,6 +479,9 @@ extern osMessageQId osMessageQId_osTimerMessageQ; extern U32 IRQNestLevel; /* Indicates whether inside an ISR, and the depth of nesting. 0 = not in ISR. */ +// Thread creation and destruction mutex +osMutexDef(osThreadMutex); +osMutexId osMutexId_osThreadMutex; // ==== Helper Functions ==== @@ -596,6 +599,8 @@ osStatus svcKernelInitialize (void) { // Create OS Timers resources (Message Queue & Thread) osMessageQId_osTimerMessageQ = svcMessageCreate (&os_messageQ_def_osTimerMessageQ, NULL); osThreadId_osTimerThread = svcThreadCreate(&os_thread_def_osTimerThread, NULL); + // Initialize thread mutex + osMutexId_osThreadMutex = osMutexCreate(osMutex(osThreadMutex)); } sysThreadError(osOK); @@ -856,7 +861,12 @@ osThreadId osThreadCreate (const osThreadDef_t *thread_def, void *argument) { // Privileged and not running return svcThreadCreate(thread_def, argument); } else { - return __svcThreadCreate(thread_def, argument); + osThreadId id; + osMutexWait(osMutexId_osThreadMutex, osWaitForever); + // Thread mutex must be held when a thread is created or terminated + id = __svcThreadCreate(thread_def, argument); + osMutexRelease(osMutexId_osThreadMutex); + return id; } } @@ -868,8 +878,13 @@ osThreadId osThreadGetId (void) { /// Terminate execution of a thread and remove it from ActiveThreads osStatus osThreadTerminate (osThreadId thread_id) { + osStatus status; if (__exceptional_mode()) return osErrorISR; // Not allowed in ISR - return __svcThreadTerminate(thread_id); + osMutexWait(osMutexId_osThreadMutex, osWaitForever); + // Thread mutex must be held when a thread is created or terminated + status = __svcThreadTerminate(thread_id); + osMutexRelease(osMutexId_osThreadMutex); + return status; } /// Pass control to next thread that is in state READY @@ -893,6 +908,10 @@ osPriority osThreadGetPriority (osThreadId thread_id) { /// INTERNAL - Not Public /// Auto Terminate Thread on exit (used implicitly when thread exists) __NO_RETURN void osThreadExit (void) { + // Thread mutex must be held when a thread is created or terminated + // Note - the mutex will be released automatically by the os when + // the thread is terminated + osMutexWait(osMutexId_osThreadMutex, osWaitForever); __svcThreadTerminate(__svcThreadGetId()); for (;;); // Should never come here } @@ -911,6 +930,38 @@ uint8_t osThreadGetState (osThreadId thread_id) { } #endif +osThreadEnumId osThreadsEnumStart() { + static uint32_t thread_enum_index; + osMutexWait(osMutexId_osThreadMutex, osWaitForever); + thread_enum_index = 0; + return &thread_enum_index; +} + +osThreadId osThreadEnumNext(osThreadEnumId enum_id) { + uint32_t i; + osThreadId id = NULL; + uint32_t *index = (uint32_t*)enum_id; + for (i = *index; i < os_maxtaskrun; i++) { + if (os_active_TCB[i] != NULL) { + id = (osThreadId)os_active_TCB[i]; + break; + } + } + if (i == os_maxtaskrun) { + // Include the idle task at the end of the enumeration + id = &os_idle_TCB; + } + *index = i + 1; + return id; +} + +osStatus osThreadEnumFree(osThreadEnumId enum_id) { + uint32_t *index = (uint32_t*)enum_id; + *index = 0; + osMutexRelease(osMutexId_osThreadMutex); + return osOK; +} + // ==== Generic Wait Functions ==== // Generic Wait Service Calls declarations diff --git a/rtos/rtx/TARGET_CORTEX_M/cmsis_os.h b/rtos/rtx/TARGET_CORTEX_M/cmsis_os.h index ac052ff82b8..c59ff189949 100644 --- a/rtos/rtx/TARGET_CORTEX_M/cmsis_os.h +++ b/rtos/rtx/TARGET_CORTEX_M/cmsis_os.h @@ -99,6 +99,7 @@ #define osFeature_Semaphore 65535 ///< Maximum count for \ref osSemaphoreCreate function #define osFeature_Wait 0 ///< osWait not available #define osFeature_SysTick 1 ///< osKernelSysTick functions available +#define osFeature_ThreadEnum 1 ///< Thread enumeration available #if defined (__CC_ARM) #define os_InRegs __value_in_regs // Compiler specific: force struct in registers @@ -188,6 +189,8 @@ typedef struct os_messageQ_cb *osMessageQId; /// Mail ID identifies the mail queue (pointer to a mail queue control block). typedef struct os_mailQ_cb *osMailQId; +/// Thread enumeration ID identifies the enumeration (pointer to a thread enumeration control block). +typedef uint32_t *osThreadEnumId; /// Thread Definition structure contains startup information of a thread. typedef struct os_thread_def { @@ -680,6 +683,26 @@ osStatus osMailFree (osMailQId queue_id, void *mail); #endif // Mail Queues available +// ==== Thread Enumeration Functions ==== + +#if (defined (osFeature_ThreadEnum) && (osFeature_ThreadEnum != 0)) // Thread enumeration available + +/// Start a thread enumeration. +/// \return an enumeration ID or NULL on error. +osThreadEnumId osThreadsEnumStart(void); + +/// Get the next task ID in the enumeration. +/// \return a thread ID or NULL on if the end of the enumeration has been reached. +osThreadId osThreadEnumNext(osThreadEnumId enum_id); + +/// Free the enumeration structure. +/// \param[in] enum_id pointer to the enumeration ID that was obtained with \ref osThreadsEnumStart. +/// \return status code that indicates the execution status of the function. +osStatus osThreadEnumFree(osThreadEnumId enum_id); + +#endif // Thread Enumeration available + + // ==== RTX Extensions ==== /// Suspend the RTX task scheduler. diff --git a/rtos/rtx/TARGET_CORTEX_M/rt_CMSIS.c b/rtos/rtx/TARGET_CORTEX_M/rt_CMSIS.c index 7e08e38f6af..dc259660bd6 100644 --- a/rtos/rtx/TARGET_CORTEX_M/rt_CMSIS.c +++ b/rtos/rtx/TARGET_CORTEX_M/rt_CMSIS.c @@ -392,6 +392,9 @@ extern osThreadId osThreadId_osTimerThread; extern const osMessageQDef_t os_messageQ_def_osTimerMessageQ; extern osMessageQId osMessageQId_osTimerMessageQ; +// Thread creation and destruction mutex +osMutexDef(osThreadMutex); +osMutexId osMutexId_osThreadMutex; // ==== Helper Functions ==== @@ -490,6 +493,8 @@ osStatus svcKernelInitialize (void) { // Create OS Timers resources (Message Queue & Thread) osMessageQId_osTimerMessageQ = svcMessageCreate (&os_messageQ_def_osTimerMessageQ, NULL); osThreadId_osTimerThread = svcThreadCreate(&os_thread_def_osTimerThread, NULL, NULL); + // Initialize thread mutex + osMutexId_osThreadMutex = osMutexCreate(osMutex(osThreadMutex)); } sysThreadError(osOK); @@ -806,7 +811,12 @@ osThreadId osThreadContextCreate (const osThreadDef_t *thread_def, void *argumen // Privileged and not running return svcThreadCreate(thread_def, argument, context); } else { - return __svcThreadCreate(thread_def, argument, context); + osThreadId id; + osMutexWait(osMutexId_osThreadMutex, osWaitForever); + // Thread mutex must be held when a thread is created or terminated + id = __svcThreadCreate(thread_def, argument, context); + osMutexRelease(osMutexId_osThreadMutex); + return id; } } @@ -820,10 +830,15 @@ osThreadId osThreadGetId (void) { /// Terminate execution of a thread and remove it from ActiveThreads osStatus osThreadTerminate (osThreadId thread_id) { + osStatus status; if (__get_IPSR() != 0U) { return osErrorISR; // Not allowed in ISR } - return __svcThreadTerminate(thread_id); + osMutexWait(osMutexId_osThreadMutex, osWaitForever); + // Thread mutex must be held when a thread is created or terminated + status = __svcThreadTerminate(thread_id); + osMutexRelease(osMutexId_osThreadMutex); + return status; } /// Pass control to next thread that is in state READY @@ -852,8 +867,12 @@ osPriority osThreadGetPriority (osThreadId thread_id) { /// INTERNAL - Not Public /// Auto Terminate Thread on exit (used implicitly when thread exists) -__NO_RETURN void osThreadExit (void) { - __svcThreadTerminate(__svcThreadGetId()); +__NO_RETURN void osThreadExit (void) { + // Thread mutex must be held when a thread is created or terminated + // Note - the mutex will be released automatically by the os when + // the thread is terminated + osMutexWait(osMutexId_osThreadMutex, osWaitForever); + __svcThreadTerminate(__svcThreadGetId()); for (;;); // Should never come here } @@ -871,6 +890,38 @@ uint8_t osThreadGetState (osThreadId thread_id) { } #endif +osThreadEnumId osThreadsEnumStart() { + static uint32_t thread_enum_index; + osMutexWait(osMutexId_osThreadMutex, osWaitForever); + thread_enum_index = 0; + return &thread_enum_index; +} + +osThreadId osThreadEnumNext(osThreadEnumId enum_id) { + uint32_t i; + osThreadId id = NULL; + uint32_t *index = (uint32_t*)enum_id; + for (i = *index; i < os_maxtaskrun; i++) { + if (os_active_TCB[i] != NULL) { + id = (osThreadId)os_active_TCB[i]; + break; + } + } + if (i == os_maxtaskrun) { + // Include the idle task at the end of the enumeration + id = &os_idle_TCB; + } + *index = i + 1; + return id; +} + +osStatus osThreadEnumFree(osThreadEnumId enum_id) { + uint32_t *index = (uint32_t*)enum_id; + *index = 0; + osMutexRelease(osMutexId_osThreadMutex); + return osOK; +} + // ==== Generic Wait Functions ==== // Generic Wait Service Calls declarations From c31929669265b51abd2d3a5102a161fdcf77108d Mon Sep 17 00:00:00 2001 From: Russ Butler Date: Wed, 7 Sep 2016 00:39:22 -0500 Subject: [PATCH 2/7] Add RTX API to report stack usage Add the function osThreadGetInfo to allow various Thread information to be queried. This includes stack size and maximum stack usage among other things. Note - for Cortex-A devices the worst case stack usage is not available. --- rtos/rtx/TARGET_CORTEX_A/cmsis_os.h | 17 ++++++ rtos/rtx/TARGET_CORTEX_A/rt_CMSIS.c | 85 +++++++++++++++++++++++++++ rtos/rtx/TARGET_CORTEX_A/rt_TypeDef.h | 1 + rtos/rtx/TARGET_CORTEX_M/cmsis_os.h | 17 ++++++ rtos/rtx/TARGET_CORTEX_M/rt_CMSIS.c | 73 +++++++++++++++++++++++ rtos/rtx/TARGET_CORTEX_M/rt_Task.c | 1 + rtos/rtx/TARGET_CORTEX_M/rt_TypeDef.h | 1 + 7 files changed, 195 insertions(+) diff --git a/rtos/rtx/TARGET_CORTEX_A/cmsis_os.h b/rtos/rtx/TARGET_CORTEX_A/cmsis_os.h index d1449cc77c3..5bd60ed23a7 100644 --- a/rtos/rtx/TARGET_CORTEX_A/cmsis_os.h +++ b/rtos/rtx/TARGET_CORTEX_A/cmsis_os.h @@ -224,6 +224,16 @@ typedef enum { osTimerPeriodic = 1 ///< repeating timer } os_timer_type; +typedef enum { + osThreadInfoState, + osThreadInfoStackSize, + osThreadInfoStackMax, + osThreadInfoEntry, + osThreadInfoArg, + + osThreadInfo_reserved = 0x7FFFFFFF ///< prevent from enum down-size compiler optimization. +} osThreadInfo; + /// Entry point of a thread. /// \note MUST REMAIN UNCHANGED: \b os_pthread shall be consistent in every CMSIS-RTOS. typedef void (*os_pthread) (void const *argument); @@ -451,6 +461,13 @@ osPriority osThreadGetPriority (osThreadId thread_id); uint8_t osThreadGetState (osThreadId thread_id); #endif +/// Get into from an active thread. +/// \param[in] thread_id thread ID obtained by \ref osThreadCreate or \ref osThreadGetId. +/// \param[in] info information to read. +/// \return current state of the thread function. +/// \return requested info that includes the status code. +os_InRegs osEvent osThreadGetInfo(osThreadId thread_id, osThreadInfo info); + // ==== Generic Wait Functions ==== /// Wait for Timeout (Time Delay). diff --git a/rtos/rtx/TARGET_CORTEX_A/rt_CMSIS.c b/rtos/rtx/TARGET_CORTEX_A/rt_CMSIS.c index 2a23d1958e8..7f71b8e0ffc 100644 --- a/rtos/rtx/TARGET_CORTEX_A/rt_CMSIS.c +++ b/rtos/rtx/TARGET_CORTEX_A/rt_CMSIS.c @@ -713,6 +713,7 @@ SVC_1_1(svcThreadTerminate, osStatus, osThreadId, RET SVC_0_1(svcThreadYield, osStatus, RET_osStatus) SVC_2_1(svcThreadSetPriority, osStatus, osThreadId, osPriority, RET_osStatus) SVC_1_1(svcThreadGetPriority, osPriority, osThreadId, RET_osPriority) +SVC_2_3(svcThreadGetInfo, os_InRegs osEvent, osThreadId, osThreadInfo, RET_osEvent) // Thread Service Calls @@ -851,6 +852,80 @@ osPriority svcThreadGetPriority (osThreadId thread_id) { return (osPriority)(ptcb->prio - 1 + osPriorityIdle); } +/// Get info from an active thread +os_InRegs osEvent_type svcThreadGetInfo (osThreadId thread_id, osThreadInfo info) { + P_TCB ptcb; + osEvent ret; + ret.status = osOK; + + ptcb = rt_tid2ptcb(thread_id); // Get TCB pointer + if (ptcb == NULL) { + ret.status = osErrorValue; +#if defined (__GNUC__) && defined (__ARM_PCS_VFP) + osEvent_ret_status; + return; +#else + return osEvent_ret_status; +#endif + } + + if (osThreadInfoStackSize == info) { + uint32_t size; + size = ptcb->priv_stack; + if (0 == size) { + // This is an OS task - always a fixed size + size = os_stackinfo & 0x3FFFF; + } + ret.value.v = size; +#if defined (__GNUC__) && defined (__ARM_PCS_VFP) + osEvent_ret_value; + return; +#else + return osEvent_ret_value; +#endif + } + + if (osThreadInfoStackMax == info) { + // Cortex-A RTX does not have stack init so + // the maximum stack usage cannot be obtained. + ret.status = osErrorResource; +#if defined (__GNUC__) && defined (__ARM_PCS_VFP) + osEvent_ret_status; + return; +#else + return osEvent_ret_status; +#endif + } + + if (osThreadInfoEntry == info) { + ret.value.p = (void*)ptcb->ptask; +#if defined (__GNUC__) && defined (__ARM_PCS_VFP) + osEvent_ret_value; + return; +#else + return osEvent_ret_value; +#endif + } + + if (osThreadInfoArg == info) { + ret.value.p = (void*)ptcb->argv; +#if defined (__GNUC__) && defined (__ARM_PCS_VFP) + osEvent_ret_value; + return; +#else + return osEvent_ret_value; +#endif + } + + // Unsupported option so return error + ret.status = osErrorParameter; +#if defined (__GNUC__) && defined (__ARM_PCS_VFP) + osEvent_ret_status; + return; +#else + return osEvent_ret_status; +#endif +} // Thread Public API @@ -930,6 +1005,16 @@ uint8_t osThreadGetState (osThreadId thread_id) { } #endif +/// Get the requested info from the specified active thread +os_InRegs osEvent osThreadGetInfo(osThreadId thread_id, osThreadInfo info) { + osEvent ret; + if (__exceptional_mode()) { + ret.status = osErrorISR; + return ret; // Not allowed in ISR + } + return __svcThreadGetInfo(thread_id, info); +} + osThreadEnumId osThreadsEnumStart() { static uint32_t thread_enum_index; osMutexWait(osMutexId_osThreadMutex, osWaitForever); diff --git a/rtos/rtx/TARGET_CORTEX_A/rt_TypeDef.h b/rtos/rtx/TARGET_CORTEX_A/rt_TypeDef.h index 832f9ad0083..568c4389ef4 100644 --- a/rtos/rtx/TARGET_CORTEX_A/rt_TypeDef.h +++ b/rtos/rtx/TARGET_CORTEX_A/rt_TypeDef.h @@ -85,6 +85,7 @@ typedef struct OS_TCB { /* Task entry point used for uVision debugger */ FUNCP ptask; /* Task entry address */ + void *argv; /* Task argument */ } *P_TCB; #define TCB_TID 3 /* 'task id' offset */ #define TCB_STACKF 37 /* 'stack_frame' offset */ diff --git a/rtos/rtx/TARGET_CORTEX_M/cmsis_os.h b/rtos/rtx/TARGET_CORTEX_M/cmsis_os.h index c59ff189949..44cc2ebf7a2 100644 --- a/rtos/rtx/TARGET_CORTEX_M/cmsis_os.h +++ b/rtos/rtx/TARGET_CORTEX_M/cmsis_os.h @@ -160,6 +160,16 @@ typedef enum { osTimerPeriodic = 1 ///< repeating timer } os_timer_type; +typedef enum { + osThreadInfoState, + osThreadInfoStackSize, + osThreadInfoStackMax, + osThreadInfoEntry, + osThreadInfoArg, + + osThreadInfo_reserved = 0x7FFFFFFF ///< prevent from enum down-size compiler optimization. +} osThreadInfo; + /// Entry point of a thread. typedef void (*os_pthread) (void const *argument); @@ -361,6 +371,13 @@ osPriority osThreadGetPriority (osThreadId thread_id); uint8_t osThreadGetState (osThreadId thread_id); #endif +/// Get into from an active thread. +/// \param[in] thread_id thread ID obtained by \ref osThreadCreate or \ref osThreadGetId. +/// \param[in] info information to read. +/// \return current state of the thread function. +/// \return requested info that includes the status code. +os_InRegs osEvent osThreadGetInfo(osThreadId thread_id, osThreadInfo info); + // ==== Generic Wait Functions ==== /// Wait for Timeout (Time Delay). diff --git a/rtos/rtx/TARGET_CORTEX_M/rt_CMSIS.c b/rtos/rtx/TARGET_CORTEX_M/rt_CMSIS.c index dc259660bd6..092539bb632 100644 --- a/rtos/rtx/TARGET_CORTEX_M/rt_CMSIS.c +++ b/rtos/rtx/TARGET_CORTEX_M/rt_CMSIS.c @@ -637,6 +637,7 @@ SVC_1_1(svcThreadTerminate, osStatus, osThreadId, RET SVC_0_1(svcThreadYield, osStatus, RET_osStatus) SVC_2_1(svcThreadSetPriority, osStatus, osThreadId, osPriority, RET_osStatus) SVC_1_1(svcThreadGetPriority, osPriority, osThreadId, RET_osPriority) +SVC_2_3(svcThreadGetInfo, os_InRegs osEvent, osThreadId, osThreadInfo, RET_osEvent) // Thread Service Calls @@ -796,6 +797,67 @@ osPriority svcThreadGetPriority (osThreadId thread_id) { return (osPriority)(ptcb->prio - 1 + osPriorityIdle); } +/// Get info from an active thread +os_InRegs osEvent_type svcThreadGetInfo (osThreadId thread_id, osThreadInfo info) { + P_TCB ptcb; + osEvent ret; + ret.status = osOK; + + ptcb = rt_tid2ptcb(thread_id); // Get TCB pointer + if (ptcb == NULL) { + ret.status = osErrorValue; + return osEvent_ret_status; + } + + if (osThreadInfoStackSize == info) { + uint32_t size; + size = ptcb->priv_stack; + if (0 == size) { + // This is an OS task - always a fixed size + size = os_stackinfo & 0x3FFFF; + } + ret.value.v = size; + return osEvent_ret_value; + } + + if (osThreadInfoStackMax == info) { + uint32_t i; + uint32_t *stack_ptr; + uint32_t stack_size; + if (!(os_stackinfo & (1 << 28))) { + // Stack init must be turned on for max stack usage + ret.status = osErrorResource; + return osEvent_ret_status; + } + stack_ptr = (uint32_t*)ptcb->stack; + stack_size = ptcb->priv_stack; + if (0 == stack_size) { + // This is an OS task - always a fixed size + stack_size = os_stackinfo & 0x3FFFF; + } + for (i = 1; i ptask; + return osEvent_ret_value; + } + + if (osThreadInfoArg == info) { + ret.value.p = (void*)ptcb->argv; + return osEvent_ret_value; + } + + // Unsupported option so return error + ret.status = osErrorParameter; + return osEvent_ret_status; +} // Thread Public API @@ -890,6 +952,17 @@ uint8_t osThreadGetState (osThreadId thread_id) { } #endif +/// Get the requested info from the specified active thread +os_InRegs osEvent osThreadGetInfo(osThreadId thread_id, osThreadInfo info) { + osEvent ret; + + if (__get_IPSR() != 0U) { // Not allowed in ISR + ret.status = osErrorISR; + return ret; + } + return __svcThreadGetInfo(thread_id, info); +} + osThreadEnumId osThreadsEnumStart() { static uint32_t thread_enum_index; osMutexWait(osMutexId_osThreadMutex, osWaitForever); diff --git a/rtos/rtx/TARGET_CORTEX_M/rt_Task.c b/rtos/rtx/TARGET_CORTEX_M/rt_Task.c index 3179a5192f4..812978fdc67 100644 --- a/rtos/rtx/TARGET_CORTEX_M/rt_Task.c +++ b/rtos/rtx/TARGET_CORTEX_M/rt_Task.c @@ -247,6 +247,7 @@ OS_TID rt_tsk_create (FUNCP task, U32 prio_stksz, void *stk, void *argv) { task_context->task_id = (U8)i; /* Pass parameter 'argv' to 'rt_init_context' */ task_context->msg = argv; + task_context->argv = argv; /* For 'size == 0' system allocates the user stack from the memory pool. */ rt_init_context (task_context, (U8)(prio_stksz & 0xFFU), task); diff --git a/rtos/rtx/TARGET_CORTEX_M/rt_TypeDef.h b/rtos/rtx/TARGET_CORTEX_M/rt_TypeDef.h index 39cb9d477ed..f16b96ec203 100644 --- a/rtos/rtx/TARGET_CORTEX_M/rt_TypeDef.h +++ b/rtos/rtx/TARGET_CORTEX_M/rt_TypeDef.h @@ -79,6 +79,7 @@ typedef struct OS_TCB { /* Task entry point used for uVision debugger */ FUNCP ptask; /* Task entry address */ + void *argv; /* Task argument */ void *context; /* Pointer to thread context */ } *P_TCB; #define TCB_STACKF 37 /* 'stack_frame' offset */ From 6fd9154d75a7fc618391acfea16c8218758427bd Mon Sep 17 00:00:00 2001 From: Russ Butler Date: Wed, 7 Sep 2016 00:19:48 -0500 Subject: [PATCH 3/7] Add task terminate hook Add an RTX hook which gets called when a thread terminates. Add the function Thread::attach_terminate_hook() to allow users to attach a hook to this event at runtime. --- rtos/rtos/Thread.cpp | 13 +++++++++++++ rtos/rtos/Thread.h | 5 +++++ rtos/rtx/TARGET_CORTEX_A/RTX_Conf_CA.c | 8 ++++++++ rtos/rtx/TARGET_CORTEX_A/rt_CMSIS.c | 9 +++++++-- rtos/rtx/TARGET_CORTEX_M/RTX_Conf_CM.c | 9 +++++++++ rtos/rtx/TARGET_CORTEX_M/rt_CMSIS.c | 9 +++++++-- 6 files changed, 49 insertions(+), 4 deletions(-) diff --git a/rtos/rtos/Thread.cpp b/rtos/rtos/Thread.cpp index 91e80afe2b5..80eae63bc35 100644 --- a/rtos/rtos/Thread.cpp +++ b/rtos/rtos/Thread.cpp @@ -30,6 +30,15 @@ extern "C" P_TCB rt_tid2ptcb(osThreadId thread_id); + +static void (*terminate_hook)(osThreadId id) = 0; +extern "C" void thread_terminate_hook(osThreadId id) +{ + if (terminate_hook != (void (*)(osThreadId))NULL) { + terminate_hook(id); + } +} + namespace rtos { void Thread::constructor(osPriority priority, @@ -336,6 +345,10 @@ void Thread::attach_idle_hook(void (*fptr)(void)) { rtos_attach_idle_hook(fptr); } +void Thread::attach_terminate_hook(void (*fptr)(osThreadId id)) { + terminate_hook = fptr; +} + Thread::~Thread() { // terminate is thread safe terminate(); diff --git a/rtos/rtos/Thread.h b/rtos/rtos/Thread.h index f2b5cb9300a..5c6114f355b 100644 --- a/rtos/rtos/Thread.h +++ b/rtos/rtos/Thread.h @@ -320,6 +320,11 @@ class Thread { */ static void attach_idle_hook(void (*fptr)(void)); + /** Attach a function to be called when a task is killed + @param fptr pointer to the function to be called + */ + static void attach_terminate_hook(void (*fptr)(osThreadId id)); + virtual ~Thread(); private: diff --git a/rtos/rtx/TARGET_CORTEX_A/RTX_Conf_CA.c b/rtos/rtx/TARGET_CORTEX_A/RTX_Conf_CA.c index 83b5820e090..7ef9f6ca051 100644 --- a/rtos/rtx/TARGET_CORTEX_A/RTX_Conf_CA.c +++ b/rtos/rtx/TARGET_CORTEX_A/RTX_Conf_CA.c @@ -320,6 +320,14 @@ void os_error (uint32_t err_code) { for (;;); } +/*---------------------------------------------------------------------------- + * RTX Hooks + *---------------------------------------------------------------------------*/ +extern void thread_terminate_hook(osThreadId id); + +void sysThreadTerminate(osThreadId id) { + thread_terminate_hook(id); +} /*---------------------------------------------------------------------------- * RTX Configuration Functions diff --git a/rtos/rtx/TARGET_CORTEX_A/rt_CMSIS.c b/rtos/rtx/TARGET_CORTEX_A/rt_CMSIS.c index 7f71b8e0ffc..9d1eb2645d2 100644 --- a/rtos/rtx/TARGET_CORTEX_A/rt_CMSIS.c +++ b/rtos/rtx/TARGET_CORTEX_A/rt_CMSIS.c @@ -479,9 +479,10 @@ extern osMessageQId osMessageQId_osTimerMessageQ; extern U32 IRQNestLevel; /* Indicates whether inside an ISR, and the depth of nesting. 0 = not in ISR. */ -// Thread creation and destruction mutex +// Thread creation and destruction osMutexDef(osThreadMutex); osMutexId osMutexId_osThreadMutex; +void sysThreadTerminate(osThreadId id); // ==== Helper Functions ==== @@ -956,6 +957,7 @@ osStatus osThreadTerminate (osThreadId thread_id) { osStatus status; if (__exceptional_mode()) return osErrorISR; // Not allowed in ISR osMutexWait(osMutexId_osThreadMutex, osWaitForever); + sysThreadTerminate(thread_id); // Thread mutex must be held when a thread is created or terminated status = __svcThreadTerminate(thread_id); osMutexRelease(osMutexId_osThreadMutex); @@ -983,11 +985,14 @@ osPriority osThreadGetPriority (osThreadId thread_id) { /// INTERNAL - Not Public /// Auto Terminate Thread on exit (used implicitly when thread exists) __NO_RETURN void osThreadExit (void) { + osThreadId id; // Thread mutex must be held when a thread is created or terminated // Note - the mutex will be released automatically by the os when // the thread is terminated osMutexWait(osMutexId_osThreadMutex, osWaitForever); - __svcThreadTerminate(__svcThreadGetId()); + id = __svcThreadGetId(); + sysThreadTerminate(id); + __svcThreadTerminate(id); for (;;); // Should never come here } diff --git a/rtos/rtx/TARGET_CORTEX_M/RTX_Conf_CM.c b/rtos/rtx/TARGET_CORTEX_M/RTX_Conf_CM.c index 562f1addd09..2c548ce31fb 100755 --- a/rtos/rtx/TARGET_CORTEX_M/RTX_Conf_CM.c +++ b/rtos/rtx/TARGET_CORTEX_M/RTX_Conf_CM.c @@ -407,6 +407,15 @@ void sysThreadError(osStatus status) { } } +/*---------------------------------------------------------------------------- + * RTX Hooks + *---------------------------------------------------------------------------*/ +extern void thread_terminate_hook(osThreadId id); + +void sysThreadTerminate(osThreadId id) { + thread_terminate_hook(id); +} + /*---------------------------------------------------------------------------- * RTX Configuration Functions *---------------------------------------------------------------------------*/ diff --git a/rtos/rtx/TARGET_CORTEX_M/rt_CMSIS.c b/rtos/rtx/TARGET_CORTEX_M/rt_CMSIS.c index 092539bb632..c857153e3b5 100644 --- a/rtos/rtx/TARGET_CORTEX_M/rt_CMSIS.c +++ b/rtos/rtx/TARGET_CORTEX_M/rt_CMSIS.c @@ -392,9 +392,10 @@ extern osThreadId osThreadId_osTimerThread; extern const osMessageQDef_t os_messageQ_def_osTimerMessageQ; extern osMessageQId osMessageQId_osTimerMessageQ; -// Thread creation and destruction mutex +// Thread creation and destruction osMutexDef(osThreadMutex); osMutexId osMutexId_osThreadMutex; +void sysThreadTerminate(osThreadId id); // ==== Helper Functions ==== @@ -897,6 +898,7 @@ osStatus osThreadTerminate (osThreadId thread_id) { return osErrorISR; // Not allowed in ISR } osMutexWait(osMutexId_osThreadMutex, osWaitForever); + sysThreadTerminate(thread_id); // Thread mutex must be held when a thread is created or terminated status = __svcThreadTerminate(thread_id); osMutexRelease(osMutexId_osThreadMutex); @@ -930,11 +932,14 @@ osPriority osThreadGetPriority (osThreadId thread_id) { /// INTERNAL - Not Public /// Auto Terminate Thread on exit (used implicitly when thread exists) __NO_RETURN void osThreadExit (void) { + osThreadId id; // Thread mutex must be held when a thread is created or terminated // Note - the mutex will be released automatically by the os when // the thread is terminated osMutexWait(osMutexId_osThreadMutex, osWaitForever); - __svcThreadTerminate(__svcThreadGetId()); + id = __svcThreadGetId(); + sysThreadTerminate(id); + __svcThreadTerminate(id); for (;;); // Should never come here } From 104190491487f42e5c32bc335098696301383bb9 Mon Sep 17 00:00:00 2001 From: Russ Butler Date: Wed, 7 Sep 2016 01:22:40 -0500 Subject: [PATCH 4/7] Turn on stack init so stack usage is correct Initialize stack memory since this is required for stack metrics to work. --- rtos/rtx/TARGET_CORTEX_M/RTX_Conf_CM.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/rtos/rtx/TARGET_CORTEX_M/RTX_Conf_CM.c b/rtos/rtx/TARGET_CORTEX_M/RTX_Conf_CM.c index 2c548ce31fb..36378726577 100755 --- a/rtos/rtx/TARGET_CORTEX_M/RTX_Conf_CM.c +++ b/rtos/rtx/TARGET_CORTEX_M/RTX_Conf_CM.c @@ -135,7 +135,11 @@ // Initialize thread stack with watermark pattern for analyzing stack usage (current/maximum) in System and Thread Viewer. // Enabling this option increases significantly the execution time of osThreadCreate. #ifndef OS_STKINIT -#define OS_STKINIT 0 + #if (defined(MBED_STACK_STATS_ENABLED) && MBED_STACK_STATS_ENABLED) + #define OS_STKINIT 1 + #else + #define OS_STKINIT 0 + #endif #endif // Processor mode for thread execution From 1921b1aa965517ab98f310481c79fe40e6d2db83 Mon Sep 17 00:00:00 2001 From: Russ Butler Date: Wed, 7 Sep 2016 00:14:28 -0500 Subject: [PATCH 5/7] Add stack metrics to greentea Update greentea to report thread information when a thread exits or is terminated and when testing completes. Also move metrics into a dedicated greentea file. --- .../greentea-client/greentea_metrics.h | 14 ++ .../source/greentea_metrics.cpp | 207 ++++++++++++++++++ .../greentea-client/source/test_env.cpp | 3 + .../utest/source/utest_greentea_handlers.cpp | 4 - 4 files changed, 224 insertions(+), 4 deletions(-) create mode 100644 features/frameworks/greentea-client/greentea-client/greentea_metrics.h create mode 100644 features/frameworks/greentea-client/source/greentea_metrics.cpp diff --git a/features/frameworks/greentea-client/greentea-client/greentea_metrics.h b/features/frameworks/greentea-client/greentea-client/greentea_metrics.h new file mode 100644 index 00000000000..ff5a451cb51 --- /dev/null +++ b/features/frameworks/greentea-client/greentea-client/greentea_metrics.h @@ -0,0 +1,14 @@ +#ifndef GREENTEA_METRICS_H +#define GREENTEA_METRICS_H + +/** + * Setup platform specific metrics + */ +void greentea_metrics_setup(void); + +/** + * Report and cleanup platform specifc metrics + */ +void greentea_metrics_report(void); + +#endif diff --git a/features/frameworks/greentea-client/source/greentea_metrics.cpp b/features/frameworks/greentea-client/source/greentea_metrics.cpp new file mode 100644 index 00000000000..d6186d927ed --- /dev/null +++ b/features/frameworks/greentea-client/source/greentea_metrics.cpp @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2013-2016, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mbed.h" +#include "rtos.h" +#include "mbed_stats.h" +#include "cmsis_os.h" +#include "greentea-client/test_env.h" +#include "greentea-client/greentea_metrics.h" +#include "SingletonPtr.h" +#include "CircularBuffer.h" + +#define THREAD_BUF_COUNT 16 + +typedef struct { + uint32_t entry; + uint32_t arg; + uint32_t stack_size; + uint32_t max_stack; +} thread_info_t; + +// Mutex to protect "buf" +SingletonPtr mutex; +static char buf[128]; +static SingletonPtr > queue; + +static void send_heap_info(void); +static void send_stack_info(void); +static void on_thread_terminate(osThreadId id); +static void enqeue_thread_info(osThreadId id); +static void deque_and_print_thread_info(void); + +// sprintf uses a lot of stack so use these instead +static uint32_t print_hex(char *buf, uint32_t value); +static uint32_t print_dec(char *buf, uint32_t value); + +void greentea_metrics_setup() +{ +#if defined(MBED_STACK_STATS_ENABLED) && MBED_STACK_STATS_ENABLED + Thread::attach_terminate_hook(on_thread_terminate); +#endif +} + +void greentea_metrics_report() +{ + send_heap_info(); +#if defined(MBED_STACK_STATS_ENABLED) && MBED_STACK_STATS_ENABLED + send_stack_info(); + Thread::attach_terminate_hook(NULL); +#endif +} + +static void send_heap_info() +{ + mbed_stats_heap_t heap_stats; + mbed_stats_heap_get(&heap_stats); + greentea_send_kv("max_heap_usage",heap_stats.max_size); +} + +MBED_UNUSED static void send_stack_info() +{ + mutex->lock(); + + // Flush any queued stack entries + while (!queue->empty()) { + deque_and_print_thread_info(); + } + + // Print info for all other threads + osThreadEnumId enum_id = osThreadsEnumStart(); + while (true) { + osThreadId thread_id = osThreadEnumNext(enum_id); + if (NULL == thread_id) { + // End of enumeration + break; + } + enqeue_thread_info(thread_id); + deque_and_print_thread_info(); + } + osThreadEnumFree(enum_id); + + mutex->unlock(); +} + +MBED_UNUSED static void on_thread_terminate(osThreadId id) +{ + mutex->lock(); + + // There should always be space in the queue + enqeue_thread_info(id); + + // If queue is full then print out a single entry + if (queue->full()) { + deque_and_print_thread_info(); + } + + mutex->unlock(); +} + +static void enqeue_thread_info(osThreadId id) +{ + osEvent info; + thread_info_t thread_info = {}; + info = osThreadGetInfo(id, osThreadInfoEntry); + if (info.status != osOK) { + return; + } + thread_info.entry = (uint32_t)info.value.p; + info = osThreadGetInfo(id, osThreadInfoArg); + if (info.status != osOK) { + return; + } + thread_info.arg = (uint32_t)info.value.p; + info = osThreadGetInfo(id, osThreadInfoStackSize); + if (info.status != osOK) { + return; + } + thread_info.stack_size = (uint32_t)info.value.v; + info = osThreadGetInfo(id, osThreadInfoStackMax); + if (info.status != osOK) { + return; + } + thread_info.max_stack = (uint32_t)info.value.v; + queue->push(thread_info); +} + +static void deque_and_print_thread_info() +{ + thread_info_t thread_info; + bool ret = queue->pop(thread_info); + MBED_ASSERT(ret); + uint32_t pos = 0; + buf[pos++] = '\"'; + pos += print_hex(buf + pos, thread_info.entry); + buf[pos++] = '-'; + pos += print_hex(buf + pos, thread_info.arg); + buf[pos++] = '\"'; + buf[pos++] = ','; + pos += print_dec(buf + pos, thread_info.max_stack); + buf[pos++] = ','; + pos += print_dec(buf + pos, thread_info.stack_size); + buf[pos++] = 0; + greentea_send_kv("__thread_info", buf); +} + +static uint32_t print_hex(char *buf, uint32_t value) +{ + uint32_t pos = 0; + buf[pos] = '0'; + pos++; + buf[pos] = 'x'; + pos++; + for (int i = 8; i >= 0; i--) { + uint32_t val = (value >> (4 * i)) & 0xF; + if (val <= 9) { + buf[pos] = '0' + val; + pos++; + } else { + buf[pos] = 'a' + val - 10; + pos++; + } + } + return pos; +} + +static uint32_t print_dec(char *buf, uint32_t value) +{ + uint32_t pos = 0; + + // The value 0 is special case + if (0 == value) { + buf[pos] = '0'; + pos++; + return pos; + } + + // Write out value in reverse order + while (value != 0) { + uint32_t next = value / 10; + buf[pos] = '0' + (value - next * 10); + value = next; + pos++; + } + + // Reverse order + for (uint32_t i = 0; i < pos / 2; i++) { + char temp = buf[i]; + buf[i] = buf[pos - 1 - i]; + buf[pos - 1 - i] = temp; + } + + return pos; +} diff --git a/features/frameworks/greentea-client/source/test_env.cpp b/features/frameworks/greentea-client/source/test_env.cpp index bf994fbfb94..083452a59de 100644 --- a/features/frameworks/greentea-client/source/test_env.cpp +++ b/features/frameworks/greentea-client/source/test_env.cpp @@ -21,6 +21,7 @@ #include "mbed.h" #include "greentea-client/test_env.h" #include "greentea-client/greentea_serial.h" +#include "greentea-client/greentea_metrics.h" /** @@ -65,6 +66,7 @@ static void greentea_notify_version(); * This function is blocking. */ void GREENTEA_SETUP(const int timeout, const char *host_test_name) { + greentea_metrics_setup(); // Key-value protocol handshake function. Waits for {{__sync;...}} message // Sync preamble: "{{__sync;0dad4a9d-59a3-4aec-810d-d5fb09d852c1}}" // Example value of sync_uuid == "0dad4a9d-59a3-4aec-810d-d5fb09d852c1" @@ -451,6 +453,7 @@ static void greentea_notify_completion(const int result) { __gcov_flush(); coverage_report = false; #endif + greentea_metrics_report(); greentea_send_kv(GREENTEA_TEST_ENV_END, val); greentea_send_kv(GREENTEA_TEST_ENV_EXIT, 0); } diff --git a/features/frameworks/utest/source/utest_greentea_handlers.cpp b/features/frameworks/utest/source/utest_greentea_handlers.cpp index bfc6858221d..b55407d098f 100644 --- a/features/frameworks/utest/source/utest_greentea_handlers.cpp +++ b/features/frameworks/utest/source/utest_greentea_handlers.cpp @@ -21,7 +21,6 @@ #include "greentea-client/test_env.h" #include "utest/utest_stack_trace.h" #include "utest/utest_serial.h" -#include "mbed_stats.h" using namespace utest::v1; @@ -106,10 +105,7 @@ utest::v1::status_t utest::v1::greentea_test_setup_handler(const size_t number_o void utest::v1::greentea_test_teardown_handler(const size_t passed, const size_t failed, const failure_t failure) { UTEST_LOG_FUNCTION(); - mbed_stats_heap_t heap_stats; verbose_test_teardown_handler(passed, failed, failure); - mbed_stats_heap_get(&heap_stats); - greentea_send_kv("max_heap_usage",heap_stats.max_size); greentea_send_kv(TEST_ENV_TESTCASE_SUMMARY, passed, failed); int result = !(failed || (failure.reason && !(failure.reason & REASON_IGNORE))); GREENTEA_TESTSUITE_RESULT(result); From 9e4a479794b5e9001b361b84b2c76fa7ead9262d Mon Sep 17 00:00:00 2001 From: Russ Butler Date: Tue, 13 Sep 2016 15:42:04 -0500 Subject: [PATCH 6/7] Add prefix to mark cmsis_os functions as private Add a leading underscore to give an indication that the new cmsis_os API functions are not official. --- .../greentea-client/source/greentea_metrics.cpp | 14 +++++++------- rtos/rtx/TARGET_CORTEX_A/cmsis_os.h | 10 +++++----- rtos/rtx/TARGET_CORTEX_A/rt_CMSIS.c | 8 ++++---- rtos/rtx/TARGET_CORTEX_M/cmsis_os.h | 10 +++++----- rtos/rtx/TARGET_CORTEX_M/rt_CMSIS.c | 8 ++++---- 5 files changed, 25 insertions(+), 25 deletions(-) diff --git a/features/frameworks/greentea-client/source/greentea_metrics.cpp b/features/frameworks/greentea-client/source/greentea_metrics.cpp index d6186d927ed..0dc700ec089 100644 --- a/features/frameworks/greentea-client/source/greentea_metrics.cpp +++ b/features/frameworks/greentea-client/source/greentea_metrics.cpp @@ -81,9 +81,9 @@ MBED_UNUSED static void send_stack_info() } // Print info for all other threads - osThreadEnumId enum_id = osThreadsEnumStart(); + osThreadEnumId enum_id = _osThreadsEnumStart(); while (true) { - osThreadId thread_id = osThreadEnumNext(enum_id); + osThreadId thread_id = _osThreadEnumNext(enum_id); if (NULL == thread_id) { // End of enumeration break; @@ -91,7 +91,7 @@ MBED_UNUSED static void send_stack_info() enqeue_thread_info(thread_id); deque_and_print_thread_info(); } - osThreadEnumFree(enum_id); + _osThreadEnumFree(enum_id); mutex->unlock(); } @@ -115,22 +115,22 @@ static void enqeue_thread_info(osThreadId id) { osEvent info; thread_info_t thread_info = {}; - info = osThreadGetInfo(id, osThreadInfoEntry); + info = _osThreadGetInfo(id, osThreadInfoEntry); if (info.status != osOK) { return; } thread_info.entry = (uint32_t)info.value.p; - info = osThreadGetInfo(id, osThreadInfoArg); + info = _osThreadGetInfo(id, osThreadInfoArg); if (info.status != osOK) { return; } thread_info.arg = (uint32_t)info.value.p; - info = osThreadGetInfo(id, osThreadInfoStackSize); + info = _osThreadGetInfo(id, osThreadInfoStackSize); if (info.status != osOK) { return; } thread_info.stack_size = (uint32_t)info.value.v; - info = osThreadGetInfo(id, osThreadInfoStackMax); + info = _osThreadGetInfo(id, osThreadInfoStackMax); if (info.status != osOK) { return; } diff --git a/rtos/rtx/TARGET_CORTEX_A/cmsis_os.h b/rtos/rtx/TARGET_CORTEX_A/cmsis_os.h index 5bd60ed23a7..5535cdaccea 100644 --- a/rtos/rtx/TARGET_CORTEX_A/cmsis_os.h +++ b/rtos/rtx/TARGET_CORTEX_A/cmsis_os.h @@ -466,7 +466,7 @@ uint8_t osThreadGetState (osThreadId thread_id); /// \param[in] info information to read. /// \return current state of the thread function. /// \return requested info that includes the status code. -os_InRegs osEvent osThreadGetInfo(osThreadId thread_id, osThreadInfo info); +os_InRegs osEvent _osThreadGetInfo(osThreadId thread_id, osThreadInfo info); // ==== Generic Wait Functions ==== @@ -849,16 +849,16 @@ osStatus osMailFree (osMailQId queue_id, void *mail); /// Start a thread enumeration. /// \return an enumeration ID or NULL on error. -osThreadEnumId osThreadsEnumStart(void); +osThreadEnumId _osThreadsEnumStart(void); /// Get the next task ID in the enumeration. /// \return a thread ID or NULL on if the end of the enumeration has been reached. -osThreadId osThreadEnumNext(osThreadEnumId enum_id); +osThreadId _osThreadEnumNext(osThreadEnumId enum_id); /// Free the enumeration structure. -/// \param[in] enum_id pointer to the enumeration ID that was obtained with \ref osThreadsEnumStart. +/// \param[in] enum_id pointer to the enumeration ID that was obtained with \ref _osThreadsEnumStart. /// \return status code that indicates the execution status of the function. -osStatus osThreadEnumFree(osThreadEnumId enum_id); +osStatus _osThreadEnumFree(osThreadEnumId enum_id); #endif // Thread Enumeration available diff --git a/rtos/rtx/TARGET_CORTEX_A/rt_CMSIS.c b/rtos/rtx/TARGET_CORTEX_A/rt_CMSIS.c index 9d1eb2645d2..51093fa8afb 100644 --- a/rtos/rtx/TARGET_CORTEX_A/rt_CMSIS.c +++ b/rtos/rtx/TARGET_CORTEX_A/rt_CMSIS.c @@ -1011,7 +1011,7 @@ uint8_t osThreadGetState (osThreadId thread_id) { #endif /// Get the requested info from the specified active thread -os_InRegs osEvent osThreadGetInfo(osThreadId thread_id, osThreadInfo info) { +os_InRegs osEvent _osThreadGetInfo(osThreadId thread_id, osThreadInfo info) { osEvent ret; if (__exceptional_mode()) { ret.status = osErrorISR; @@ -1020,14 +1020,14 @@ os_InRegs osEvent osThreadGetInfo(osThreadId thread_id, osThreadInfo info) { return __svcThreadGetInfo(thread_id, info); } -osThreadEnumId osThreadsEnumStart() { +osThreadEnumId _osThreadsEnumStart() { static uint32_t thread_enum_index; osMutexWait(osMutexId_osThreadMutex, osWaitForever); thread_enum_index = 0; return &thread_enum_index; } -osThreadId osThreadEnumNext(osThreadEnumId enum_id) { +osThreadId _osThreadEnumNext(osThreadEnumId enum_id) { uint32_t i; osThreadId id = NULL; uint32_t *index = (uint32_t*)enum_id; @@ -1045,7 +1045,7 @@ osThreadId osThreadEnumNext(osThreadEnumId enum_id) { return id; } -osStatus osThreadEnumFree(osThreadEnumId enum_id) { +osStatus _osThreadEnumFree(osThreadEnumId enum_id) { uint32_t *index = (uint32_t*)enum_id; *index = 0; osMutexRelease(osMutexId_osThreadMutex); diff --git a/rtos/rtx/TARGET_CORTEX_M/cmsis_os.h b/rtos/rtx/TARGET_CORTEX_M/cmsis_os.h index 44cc2ebf7a2..f399f1431bb 100644 --- a/rtos/rtx/TARGET_CORTEX_M/cmsis_os.h +++ b/rtos/rtx/TARGET_CORTEX_M/cmsis_os.h @@ -376,7 +376,7 @@ uint8_t osThreadGetState (osThreadId thread_id); /// \param[in] info information to read. /// \return current state of the thread function. /// \return requested info that includes the status code. -os_InRegs osEvent osThreadGetInfo(osThreadId thread_id, osThreadInfo info); +os_InRegs osEvent _osThreadGetInfo(osThreadId thread_id, osThreadInfo info); // ==== Generic Wait Functions ==== @@ -706,16 +706,16 @@ osStatus osMailFree (osMailQId queue_id, void *mail); /// Start a thread enumeration. /// \return an enumeration ID or NULL on error. -osThreadEnumId osThreadsEnumStart(void); +osThreadEnumId _osThreadsEnumStart(void); /// Get the next task ID in the enumeration. /// \return a thread ID or NULL on if the end of the enumeration has been reached. -osThreadId osThreadEnumNext(osThreadEnumId enum_id); +osThreadId _osThreadEnumNext(osThreadEnumId enum_id); /// Free the enumeration structure. -/// \param[in] enum_id pointer to the enumeration ID that was obtained with \ref osThreadsEnumStart. +/// \param[in] enum_id pointer to the enumeration ID that was obtained with \ref _osThreadsEnumStart. /// \return status code that indicates the execution status of the function. -osStatus osThreadEnumFree(osThreadEnumId enum_id); +osStatus _osThreadEnumFree(osThreadEnumId enum_id); #endif // Thread Enumeration available diff --git a/rtos/rtx/TARGET_CORTEX_M/rt_CMSIS.c b/rtos/rtx/TARGET_CORTEX_M/rt_CMSIS.c index c857153e3b5..bd24bca59e4 100644 --- a/rtos/rtx/TARGET_CORTEX_M/rt_CMSIS.c +++ b/rtos/rtx/TARGET_CORTEX_M/rt_CMSIS.c @@ -958,7 +958,7 @@ uint8_t osThreadGetState (osThreadId thread_id) { #endif /// Get the requested info from the specified active thread -os_InRegs osEvent osThreadGetInfo(osThreadId thread_id, osThreadInfo info) { +os_InRegs osEvent _osThreadGetInfo(osThreadId thread_id, osThreadInfo info) { osEvent ret; if (__get_IPSR() != 0U) { // Not allowed in ISR @@ -968,14 +968,14 @@ os_InRegs osEvent osThreadGetInfo(osThreadId thread_id, osThreadInfo info) { return __svcThreadGetInfo(thread_id, info); } -osThreadEnumId osThreadsEnumStart() { +osThreadEnumId _osThreadsEnumStart() { static uint32_t thread_enum_index; osMutexWait(osMutexId_osThreadMutex, osWaitForever); thread_enum_index = 0; return &thread_enum_index; } -osThreadId osThreadEnumNext(osThreadEnumId enum_id) { +osThreadId _osThreadEnumNext(osThreadEnumId enum_id) { uint32_t i; osThreadId id = NULL; uint32_t *index = (uint32_t*)enum_id; @@ -993,7 +993,7 @@ osThreadId osThreadEnumNext(osThreadEnumId enum_id) { return id; } -osStatus osThreadEnumFree(osThreadEnumId enum_id) { +osStatus _osThreadEnumFree(osThreadEnumId enum_id) { uint32_t *index = (uint32_t*)enum_id; *index = 0; osMutexRelease(osMutexId_osThreadMutex); From 8447843c680c8b2173530a9a26541bb371437b72 Mon Sep 17 00:00:00 2001 From: Russ Butler Date: Mon, 12 Sep 2016 17:56:52 -0500 Subject: [PATCH 7/7] Fix error handling when thread cannot be created Update the Thread::start function to gracefully handle the failed creation of a thread when there are no TCBs left. This patch does the following: 1. Set memory handles to NULL after free to prevent double free 2. Post to the release semaphore so anything that tries to join this thread will join immediately 3. Remove dead return path since the new operator should never return NULL (it should trap instead) --- rtos/rtos/Thread.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/rtos/rtos/Thread.cpp b/rtos/rtos/Thread.cpp index 80eae63bc35..0822872f784 100644 --- a/rtos/rtos/Thread.cpp +++ b/rtos/rtos/Thread.cpp @@ -83,10 +83,7 @@ osStatus Thread::start(Callback task) { _thread_def.pthread = Thread::_thunk; if (_thread_def.stack_pointer == NULL) { _thread_def.stack_pointer = new uint32_t[_thread_def.stacksize/sizeof(uint32_t)]; - if (_thread_def.stack_pointer == NULL) { - _mutex.unlock(); - return osErrorNoMemory; - } + MBED_ASSERT(_thread_def.stack_pointer != NULL); } //Fill the stack with a magic word for maximum usage checking @@ -97,8 +94,12 @@ osStatus Thread::start(Callback task) { _task = task; _tid = osThreadCreate(&_thread_def, this); if (_tid == NULL) { - if (_dynamic_stack) delete[] (_thread_def.stack_pointer); + if (_dynamic_stack) { + delete[] (_thread_def.stack_pointer); + _thread_def.stack_pointer = (uint32_t*)NULL; + } _mutex.unlock(); + _join_sem.release(); return osErrorResource; } @@ -355,6 +356,7 @@ Thread::~Thread() { #ifdef __MBED_CMSIS_RTOS_CM if (_dynamic_stack) { delete[] (_thread_def.stack_pointer); + _thread_def.stack_pointer = (uint32_t*)NULL; } #endif }