Skip to content
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

Stack stats #2642

Merged
merged 7 commits into from
Sep 16, 2016
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
Original file line number Diff line number Diff line change
@@ -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
207 changes: 207 additions & 0 deletions features/frameworks/greentea-client/source/greentea_metrics.cpp
Original file line number Diff line number Diff line change
@@ -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;
}
3 changes: 3 additions & 0 deletions features/frameworks/greentea-client/source/test_env.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"


/**
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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);
}
Expand Down
4 changes: 0 additions & 4 deletions features/frameworks/utest/source/utest_greentea_handlers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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);
Expand Down
25 changes: 20 additions & 5 deletions rtos/rtos/Thread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand All @@ -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;
}

Expand Down Expand Up @@ -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
}
Expand Down
5 changes: 5 additions & 0 deletions rtos/rtos/Thread.h
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
8 changes: 8 additions & 0 deletions rtos/rtx/TARGET_CORTEX_A/RTX_Conf_CA.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading