Skip to content

Thread stats API #6795

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
May 15, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
176 changes: 176 additions & 0 deletions TESTS/mbed_platform/stats_thread/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@

/* mbed Microcontroller Library
* Copyright (c) 2018 ARM Limited
*
* 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 "greentea-client/test_env.h"
#include "unity/unity.h"
#include "utest/utest.h"

#include "mbed.h"

#if !defined(MBED_THREAD_STATS_ENABLED)
#error [NOT_SUPPORTED] test not supported
#endif

using namespace utest::v1;

static EventFlags ef;
static int32_t counter = 0;

#define TEST_STACK_SIZE 320
#define FLAG_SIGNAL_DEC 0x2
#define MAX_THREAD_STATS 0x8

void decrement_on_event()
{
uint32_t ret = ef.wait_all(FLAG_SIGNAL_DEC);
TEST_ASSERT_FALSE(ret & osFlagsError);
TEST_ASSERT_EQUAL(FLAG_SIGNAL_DEC, ret);
counter--;
}

void increment_with_delay()
{
while (1) {
counter++;
Thread::wait(500);
}
}

void test_case_single_thread_stats()
{
mbed_stats_thread_t *stats = new mbed_stats_thread_t[MAX_THREAD_STATS];
int old_count = mbed_stats_thread_get_each(stats, MAX_THREAD_STATS);
Thread t1(osPriorityNormal, TEST_STACK_SIZE, NULL, "Th1");
t1.start(increment_with_delay);

// Read stats
int count = mbed_stats_thread_get_each(stats, MAX_THREAD_STATS);
TEST_ASSERT_EQUAL(1, (count - old_count));

for (int i = 0; i < count; i++) {
if (0 == strcmp(stats[i].name, "Th1")) {
TEST_ASSERT_EQUAL(TEST_STACK_SIZE, stats[i].stack_size);
TEST_ASSERT_EQUAL(osPriorityNormal, stats[i].priority);
break;
}
}

t1.terminate();
delete[] stats;
}

#define SINGLE_ELEMENT 1
void test_case_less_count()
{
// Default Mbed OS has 3 threads
mbed_stats_thread_t stats;
int count = mbed_stats_thread_get_each(&stats, SINGLE_ELEMENT);
TEST_ASSERT_EQUAL(SINGLE_ELEMENT, count);
}

void test_case_multi_threads_blocked()
{
mbed_stats_thread_t *stats = new mbed_stats_thread_t[MAX_THREAD_STATS];
int old_count = mbed_stats_thread_get_each(stats, MAX_THREAD_STATS);

Thread t1(osPriorityNormal, TEST_STACK_SIZE, NULL, "Th1");
Thread t2(osPriorityNormal1, TEST_STACK_SIZE, NULL, "Th2");
t1.start(increment_with_delay);
t2.start(decrement_on_event);

// Read stats

int count = mbed_stats_thread_get_each(stats, MAX_THREAD_STATS);
TEST_ASSERT_EQUAL(2, (count - old_count));
for (int i = 0; i < count; i++) {
if (0 == strcmp(stats[i].name, "Th2")) {
TEST_ASSERT_EQUAL(TEST_STACK_SIZE, stats[i].stack_size);
TEST_ASSERT_EQUAL(osPriorityNormal1, stats[i].priority);
TEST_ASSERT_EQUAL(osThreadBlocked, stats[i].state);
} else if (0 == strcmp (stats[i].name, "Th1")) {
TEST_ASSERT_EQUAL(TEST_STACK_SIZE, stats[i].stack_size);
TEST_ASSERT_EQUAL(osPriorityNormal, stats[i].priority);
}
}

// Signal blocked thread
uint32_t ret = ef.set(FLAG_SIGNAL_DEC);
TEST_ASSERT_FALSE(ret & osFlagsError);

Thread::wait(100);

count = mbed_stats_thread_get_each(stats, MAX_THREAD_STATS);
TEST_ASSERT_EQUAL(1, (count - old_count));

t1.terminate();
delete[] stats;
}

void test_case_multi_threads_terminate()
{
mbed_stats_thread_t *stats = new mbed_stats_thread_t[MAX_THREAD_STATS];
int old_count = mbed_stats_thread_get_each(stats, MAX_THREAD_STATS);

Thread t1(osPriorityNormal1, TEST_STACK_SIZE, NULL, "Th1");
Thread t2(osPriorityNormal2, TEST_STACK_SIZE, NULL, "Th2");
t2.start(increment_with_delay);
t1.start(decrement_on_event);

// Read stats

int count = mbed_stats_thread_get_each(stats, MAX_THREAD_STATS);
TEST_ASSERT_EQUAL(2, (count - old_count));

for (int i = 0; i < count; i++) {
if (0 == strcmp(stats[i].name, "Th2")) {
TEST_ASSERT_EQUAL(TEST_STACK_SIZE, stats[i].stack_size);
TEST_ASSERT_EQUAL(osPriorityNormal2, stats[i].priority);
} else if (0 == strcmp(stats[i].name, "Th1")) {
TEST_ASSERT_EQUAL(TEST_STACK_SIZE, stats[i].stack_size);
TEST_ASSERT_EQUAL(osPriorityNormal1, stats[i].priority);
TEST_ASSERT_EQUAL(osThreadBlocked, stats[i].state);
}
}

t1.terminate();
t2.terminate();

count = mbed_stats_thread_get_each(stats, MAX_THREAD_STATS);
TEST_ASSERT_EQUAL(count, old_count);

delete[] stats;
}

Case cases[] = {
Case("Single Thread Stats", test_case_single_thread_stats),
Case("Less count value", test_case_less_count),
Case("Multiple Threads blocked", test_case_multi_threads_blocked),
Case("Multiple Threads terminate", test_case_multi_threads_terminate),
};

utest::v1::status_t greentea_test_setup(const size_t number_of_cases)
{
GREENTEA_SETUP(20, "default_auto");
return greentea_test_setup_handler(number_of_cases);
}

Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler);

int main()
{
Harness::run(specification);
}
39 changes: 34 additions & 5 deletions platform/mbed_stats.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

#ifdef MBED_CONF_RTOS_PRESENT
#include "cmsis_os2.h"
#elif defined(MBED_STACK_STATS_ENABLED) || defined(MBED_THREAD_STATS_ENABLED)
#warning Statistics are currently not supported without the rtos.
#endif

// note: mbed_stats_heap_get defined in mbed_alloc_wrappers.cpp
Expand All @@ -25,7 +27,7 @@ void mbed_stats_stack_get(mbed_stats_stack_t *stats)
osKernelLock();
thread_n = osThreadEnumerate(threads, thread_n);

for(i = 0; i < thread_n; i++) {
for (i = 0; i < thread_n; i++) {
uint32_t stack_size = osThreadGetStackSize(threads[i]);
stats->max_size += stack_size - osThreadGetStackSpace(threads[i]);
stats->reserved_size += stack_size;
Expand All @@ -40,7 +42,8 @@ void mbed_stats_stack_get(mbed_stats_stack_t *stats)
size_t mbed_stats_stack_get_each(mbed_stats_stack_t *stats, size_t count)
{
MBED_ASSERT(stats != NULL);
memset(stats, 0, count*sizeof(mbed_stats_stack_t));
memset(stats, 0, count * sizeof(mbed_stats_stack_t));

size_t i = 0;

#if defined(MBED_STACK_STATS_ENABLED) && defined(MBED_CONF_RTOS_PRESENT)
Expand All @@ -52,7 +55,7 @@ size_t mbed_stats_stack_get_each(mbed_stats_stack_t *stats, size_t count)
osKernelLock();
count = osThreadEnumerate(threads, count);

for(i = 0; i < count; i++) {
for (i = 0; i < count; i++) {
uint32_t stack_size = osThreadGetStackSize(threads[i]);
stats[i].max_size = stack_size - osThreadGetStackSpace(threads[i]);
stats[i].reserved_size = stack_size;
Expand All @@ -67,6 +70,32 @@ size_t mbed_stats_stack_get_each(mbed_stats_stack_t *stats, size_t count)
return i;
}

#if defined(MBED_STACK_STATS_ENABLED) && !defined(MBED_CONF_RTOS_PRESENT)
#warning Stack statistics are currently not supported without the rtos.
size_t mbed_stats_thread_get_each(mbed_stats_thread_t *stats, size_t count)
{
MBED_ASSERT(stats != NULL);
memset(stats, 0, count * sizeof(mbed_stats_thread_t));
size_t i = 0;

#if defined(MBED_THREAD_STATS_ENABLED) && defined(MBED_CONF_RTOS_PRESENT)
osThreadId_t *threads;

threads = malloc(sizeof(osThreadId_t) * count);
MBED_ASSERT(threads != NULL);

osKernelLock();
count = osThreadEnumerate(threads, count);

for (i = 0; i < count; i++) {
stats[i].id = (uint32_t)threads[i];
stats[i].state = (uint32_t)osThreadGetState(threads[i]);
stats[i].priority = (uint32_t)osThreadGetPriority(threads[i]);
stats[i].stack_size = osThreadGetStackSize(threads[i]);
stats[i].stack_space = osThreadGetStackSpace(threads[i]);
stats[i].name = osThreadGetName(threads[i]);
}
osKernelUnlock();
free(threads);
#endif

return i;
}
24 changes: 24 additions & 0 deletions platform/mbed_stats.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ extern "C" {
#ifdef MBED_ALL_STATS_ENABLED
#define MBED_STACK_STATS_ENABLED 1
#define MBED_HEAP_STATS_ENABLED 1
#define MBED_THREAD_STATS_ENABLED 1
#endif

/**
Expand Down Expand Up @@ -81,6 +82,29 @@ void mbed_stats_stack_get(mbed_stats_stack_t *stats);
*/
size_t mbed_stats_stack_get_each(mbed_stats_stack_t *stats, size_t count);

/**
* struct mbed_stats_thread_t definition
*/

typedef struct {
uint32_t id; /**< Thread Object Identifier */
uint32_t state; /**< Thread Object State */
uint32_t priority; /**< Thread Priority */
uint32_t stack_size; /**< Thread Stack Size */
uint32_t stack_space; /**< Thread remaining stack size */
const char *name; /**< Thread Object name */
} mbed_stats_thread_t;

/**
* Fill the passed array of stat structures with the thread stats for each available thread.
*
* @param stats A pointer to an array of mbed_stats_thread_t structures to fill
* @param count The number of mbed_stats_thread_t structures in the provided array
* @return The number of mbed_stats_thread_t structures that have been filled,
* this is equal to the number of threads on the system.
*/
size_t mbed_stats_thread_get_each(mbed_stats_thread_t *stats, size_t count);

#ifdef __cplusplus
}
#endif
Expand Down
4 changes: 4 additions & 0 deletions rtos/TARGET_CORTEX/mbed_rtx_conf.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@
#define OS_STACK_WATERMARK 1
#endif

#if !defined(OS_STACK_WATERMARK) && defined(MBED_THREAD_STATS_ENABLED)
#define OS_STACK_WATERMARK 1
#endif

/* Run threads unprivileged when uVisor is enabled. */
#if defined(FEATURE_UVISOR) && defined(TARGET_UVISOR_SUPPORTED)
# define OS_PRIVILEGE_MODE 0
Expand Down