mirror of https://github.com/ARMmbed/mbed-os.git
commit
52fd7b6864
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
*---------------------------------------------------------------------------*/
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
Loading…
Reference in New Issue