Import Mbed OS hard-float snapshot
This commit is contained in:
249
features/frameworks/greentea-client/source/greentea_metrics.cpp
Normal file
249
features/frameworks/greentea-client/source/greentea_metrics.cpp
Normal file
@@ -0,0 +1,249 @@
|
||||
/*
|
||||
* 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 "greentea-client/test_env.h"
|
||||
#include "greentea-client/greentea_metrics.h"
|
||||
#include "platform/mbed_stats.h"
|
||||
#include <stdint.h>
|
||||
|
||||
#if defined(MBED_STACK_STATS_ENABLED) && MBED_STACK_STATS_ENABLED && defined(MBED_CONF_RTOS_PRESENT)
|
||||
|
||||
#include "rtos/Mutex.h"
|
||||
#include "rtos/Thread.h"
|
||||
#include "rtos/Kernel.h"
|
||||
#include "mbed_stats.h"
|
||||
#include "cmsis_os2.h"
|
||||
#include "platform/SingletonPtr.h"
|
||||
#include "platform/CircularBuffer.h"
|
||||
using namespace mbed;
|
||||
using namespace rtos;
|
||||
|
||||
#define THREAD_BUF_COUNT 16
|
||||
|
||||
typedef struct {
|
||||
uint32_t entry;
|
||||
uint32_t stack_size;
|
||||
uint32_t max_stack;
|
||||
const char *stack_name;
|
||||
} thread_info_t;
|
||||
|
||||
// Mutex to protect "buf"
|
||||
static SingletonPtr<Mutex> mutex;
|
||||
static char buf[128];
|
||||
static SingletonPtr<CircularBuffer<thread_info_t, THREAD_BUF_COUNT> > queue;
|
||||
#endif
|
||||
|
||||
#if defined(MBED_CPU_STATS_ENABLED)
|
||||
static void send_CPU_info(void);
|
||||
#endif
|
||||
|
||||
#if defined(MBED_HEAP_STATS_ENABLED ) && MBED_HEAP_STATS_ENABLED
|
||||
static void send_heap_info(void);
|
||||
#endif
|
||||
#if defined(MBED_STACK_STATS_ENABLED) && MBED_STACK_STATS_ENABLED && defined(MBED_CONF_RTOS_PRESENT)
|
||||
static void send_stack_info(void);
|
||||
static void on_thread_terminate(osThreadId_t id);
|
||||
static void enqeue_thread_info(osThreadId_t id);
|
||||
static void deque_and_print_thread_info(void);
|
||||
|
||||
// sprintf uses a lot of stack so use these instead
|
||||
static uint32_t print_str(char *buf, const char *value);
|
||||
static uint32_t print_hex(char *buf, uint32_t value);
|
||||
static uint32_t print_dec(char *buf, uint32_t value);
|
||||
#endif
|
||||
|
||||
void greentea_metrics_setup()
|
||||
{
|
||||
#if defined(MBED_STACK_STATS_ENABLED) && MBED_STACK_STATS_ENABLED && defined(MBED_CONF_RTOS_PRESENT)
|
||||
Kernel::attach_thread_terminate_hook(on_thread_terminate);
|
||||
#endif
|
||||
}
|
||||
|
||||
void greentea_metrics_report()
|
||||
{
|
||||
#if defined(MBED_HEAP_STATS_ENABLED ) && MBED_HEAP_STATS_ENABLED
|
||||
send_heap_info();
|
||||
#endif
|
||||
#if defined(MBED_STACK_STATS_ENABLED) && MBED_STACK_STATS_ENABLED && defined(MBED_CONF_RTOS_PRESENT)
|
||||
send_stack_info();
|
||||
Kernel::attach_thread_terminate_hook(NULL);
|
||||
#endif
|
||||
#if defined(MBED_CPU_STATS_ENABLED)
|
||||
send_CPU_info();
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(MBED_CPU_STATS_ENABLED)
|
||||
static void send_CPU_info()
|
||||
{
|
||||
mbed_stats_cpu_t stats;
|
||||
mbed_stats_cpu_get(&stats);
|
||||
|
||||
greentea_send_kv("__cpu_info up time", stats.uptime);
|
||||
greentea_send_kv("__cpu_info sleep time", stats.sleep_time);
|
||||
greentea_send_kv("__cpu_info deepsleep time", stats.deep_sleep_time);
|
||||
greentea_send_kv("__cpu_info % sleep/deep", (stats.sleep_time * 100) / stats.uptime, (stats.deep_sleep_time * 100) / stats.uptime);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(MBED_HEAP_STATS_ENABLED ) && MBED_HEAP_STATS_ENABLED
|
||||
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);
|
||||
greentea_send_kv("reserved_heap",heap_stats.reserved_size);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(MBED_STACK_STATS_ENABLED) && MBED_STACK_STATS_ENABLED && defined(MBED_CONF_RTOS_PRESENT)
|
||||
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
|
||||
uint32_t thread_n = osThreadGetCount();
|
||||
osThreadId_t *threads = new (std::nothrow) osThreadId_t[thread_n];
|
||||
// Don't fail on lack of memory
|
||||
if (!threads) {
|
||||
goto end;
|
||||
}
|
||||
thread_n = osThreadEnumerate(threads, thread_n);
|
||||
|
||||
for(size_t i = 0; i < thread_n; i++) {
|
||||
enqeue_thread_info(threads[i]);
|
||||
deque_and_print_thread_info();
|
||||
}
|
||||
|
||||
delete[] threads;
|
||||
|
||||
end:
|
||||
mutex->unlock();
|
||||
}
|
||||
|
||||
MBED_UNUSED static void on_thread_terminate(osThreadId_t 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_t id)
|
||||
{
|
||||
thread_info_t thread_info = {};
|
||||
|
||||
thread_info.entry = (uint32_t)id;
|
||||
thread_info.stack_size = osThreadGetStackSize(id);
|
||||
thread_info.max_stack = thread_info.stack_size - osThreadGetStackSpace(id);
|
||||
thread_info.stack_name = osThreadGetName(id);
|
||||
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_str(buf + pos, thread_info.stack_name);
|
||||
buf[pos++] = '\"';
|
||||
buf[pos++] = ',';
|
||||
buf[pos++] = '\"';
|
||||
pos += print_hex(buf + pos, thread_info.entry);
|
||||
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_str(char *buf, const char *value)
|
||||
{
|
||||
uint32_t pos = 0;
|
||||
for (pos = 0; pos < strlen(value); pos++) {
|
||||
buf[pos] = value[pos];
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user