mirror of https://github.com/ARMmbed/mbed-os.git
				
				
				
			Add stack metrics to greentea
Update greentea to report thread information when a thread exits or is terminated and when testing completes. Also move metrics into a dedicated greentea file.pull/2642/head
							parent
							
								
									1041904914
								
							
						
					
					
						commit
						1921b1aa96
					
				| 
						 | 
				
			
			@ -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);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue