Merge pull request #2642 from c1728p9/stack_stats

Stack stats
pull/2734/head
Sam Grove 2016-09-16 17:24:20 -05:00 committed by GitHub
commit 52fd7b6864
15 changed files with 631 additions and 17 deletions

View File

@ -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

View File

@ -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> mutex;
static char buf[128];
static SingletonPtr<CircularBuffer<thread_info_t, THREAD_BUF_COUNT> > 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;
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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,
@ -74,10 +83,7 @@ osStatus Thread::start(Callback<void()> 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
@ -88,8 +94,12 @@ osStatus Thread::start(Callback<void()> 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;
}
@ -336,12 +346,17 @@ 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();
#ifdef __MBED_CMSIS_RTOS_CM
if (_dynamic_stack) {
delete[] (_thread_def.stack_pointer);
_thread_def.stack_pointer = (uint32_t*)NULL;
}
#endif
}

View File

@ -320,6 +320,11 @@ public:
*/
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:

View File

@ -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

View File

@ -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
@ -223,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);
@ -261,6 +272,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.
@ -448,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).
@ -823,6 +843,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

View File

@ -479,6 +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
osMutexDef(osThreadMutex);
osMutexId osMutexId_osThreadMutex;
void sysThreadTerminate(osThreadId id);
// ==== Helper Functions ====
@ -596,6 +600,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);
@ -708,6 +714,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
@ -846,6 +853,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
@ -856,7 +937,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 +954,14 @@ 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);
sysThreadTerminate(thread_id);
// 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,7 +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) {
__svcThreadTerminate(__svcThreadGetId());
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);
id = __svcThreadGetId();
sysThreadTerminate(id);
__svcThreadTerminate(id);
for (;;); // Should never come here
}
@ -911,6 +1010,48 @@ 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);
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

View File

@ -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 */

View File

@ -136,7 +136,11 @@
// <i> Initialize thread stack with watermark pattern for analyzing stack usage (current/maximum) in System and Thread Viewer.
// <i> 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
// <o>Processor mode for thread execution
@ -405,6 +409,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
*---------------------------------------------------------------------------*/

View File

@ -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
@ -159,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);
@ -188,6 +199,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 {
@ -358,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).
@ -680,6 +700,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.

View File

@ -392,6 +392,10 @@ extern osThreadId osThreadId_osTimerThread;
extern const osMessageQDef_t os_messageQ_def_osTimerMessageQ;
extern osMessageQId osMessageQId_osTimerMessageQ;
// Thread creation and destruction
osMutexDef(osThreadMutex);
osMutexId osMutexId_osThreadMutex;
void sysThreadTerminate(osThreadId id);
// ==== Helper Functions ====
@ -490,6 +494,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);
@ -632,6 +638,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
@ -791,6 +798,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 <stack_size / 4; i++) {
if (stack_ptr[i] != MAGIC_PATTERN) {
break;
}
}
ret.value.v = stack_size - i * 4;
return osEvent_ret_value;
}
if (osThreadInfoEntry == info) {
ret.value.p = (void*)ptcb->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
@ -806,7 +874,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 +893,16 @@ 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);
sysThreadTerminate(thread_id);
// 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 +931,15 @@ 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) {
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);
id = __svcThreadGetId();
sysThreadTerminate(id);
__svcThreadTerminate(id);
for (;;); // Should never come here
}
@ -871,6 +957,49 @@ 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);
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

View File

@ -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);

View File

@ -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 */