Kev Mann / mbed-dev-OS5_10_4
Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers greentea_metrics.cpp Source File

greentea_metrics.cpp

00001 /*
00002  * Copyright (c) 2013-2016, ARM Limited, All Rights Reserved
00003  * SPDX-License-Identifier: Apache-2.0
00004  *
00005  * Licensed under the Apache License, Version 2.0 (the "License"); you may
00006  * not use this file except in compliance with the License.
00007  * You may obtain a copy of the License at
00008  *
00009  * http://www.apache.org/licenses/LICENSE-2.0
00010  *
00011  * Unless required by applicable law or agreed to in writing, software
00012  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
00013  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00014  * See the License for the specific language governing permissions and
00015  * limitations under the License.
00016  */
00017 
00018 #include "greentea-client/test_env.h"
00019 #include "greentea-client/greentea_metrics.h"
00020 #include "platform/mbed_stats.h"
00021 #include <stdint.h>
00022 
00023 #define THREAD_BUF_COUNT    16
00024 
00025 typedef struct {
00026     uint32_t entry;
00027     uint32_t stack_size;
00028     uint32_t max_stack;
00029 } thread_info_t;
00030 
00031 #if defined(MBED_STACK_STATS_ENABLED) && MBED_STACK_STATS_ENABLED
00032 
00033 #if !defined(MBED_CONF_RTOS_PRESENT) || !(MBED_CONF_RTOS_PRESENT)
00034 #error "RTOS required for Stack stats"
00035 #endif
00036 
00037 #include "rtos/Mutex.h"
00038 #include "rtos/Thread.h"
00039 #include "rtos/Kernel.h"
00040 #include "mbed_stats.h"
00041 #include "cmsis_os2.h"
00042 #include "platform/SingletonPtr.h"
00043 #include "platform/CircularBuffer.h"
00044 using namespace mbed;
00045 using namespace rtos;
00046 
00047 // Mutex to protect "buf"
00048 static SingletonPtr<Mutex> mutex;
00049 static char buf[128];
00050 static SingletonPtr<CircularBuffer<thread_info_t, THREAD_BUF_COUNT> > queue;
00051 #endif
00052 
00053 #if defined(MBED_CPU_STATS_ENABLED)
00054 static void send_CPU_info(void);
00055 #endif
00056 
00057 #if defined(MBED_HEAP_STATS_ENABLED ) && MBED_HEAP_STATS_ENABLED
00058 static void send_heap_info(void);
00059 #endif
00060 #if defined(MBED_STACK_STATS_ENABLED) && MBED_STACK_STATS_ENABLED
00061 static void send_stack_info(void);
00062 static void on_thread_terminate(osThreadId_t id);
00063 static void enqeue_thread_info(osThreadId_t id);
00064 static void deque_and_print_thread_info(void);
00065 
00066 // sprintf uses a lot of stack so use these instead
00067 static uint32_t print_hex(char *buf, uint32_t value);
00068 static uint32_t print_dec(char *buf, uint32_t value);
00069 #endif
00070 
00071 void greentea_metrics_setup()
00072 {
00073 #if defined(MBED_STACK_STATS_ENABLED) && MBED_STACK_STATS_ENABLED
00074     Thread::attach_terminate_hook(on_thread_terminate);
00075 #endif
00076 }
00077 
00078 void greentea_metrics_report()
00079 {
00080 #if defined(MBED_HEAP_STATS_ENABLED ) && MBED_HEAP_STATS_ENABLED
00081     send_heap_info();
00082 #endif
00083 #if defined(MBED_STACK_STATS_ENABLED) && MBED_STACK_STATS_ENABLED
00084     send_stack_info();
00085     Thread::attach_terminate_hook(NULL);
00086 #endif
00087 #if defined(MBED_CPU_STATS_ENABLED)
00088     send_CPU_info();
00089 #endif
00090 }
00091 
00092 #if defined(MBED_CPU_STATS_ENABLED)
00093 static void send_CPU_info()
00094 {
00095     mbed_stats_cpu_t stats;
00096     mbed_stats_cpu_get(&stats);
00097 
00098     greentea_send_kv("__cpu_info        up time", stats.uptime);
00099     greentea_send_kv("__cpu_info     sleep time", stats.sleep_time);
00100     greentea_send_kv("__cpu_info deepsleep time", stats.deep_sleep_time);
00101     greentea_send_kv("__cpu_info  %  sleep/deep", (stats.sleep_time * 100) / stats.uptime, (stats.deep_sleep_time * 100) / stats.uptime);
00102 }
00103 #endif
00104 
00105 #if defined(MBED_HEAP_STATS_ENABLED ) && MBED_HEAP_STATS_ENABLED
00106 static void send_heap_info()
00107 {
00108     mbed_stats_heap_t heap_stats;
00109     mbed_stats_heap_get(&heap_stats);
00110     greentea_send_kv("max_heap_usage",heap_stats.max_size);
00111     greentea_send_kv("reserved_heap",heap_stats.reserved_size);
00112 }
00113 #endif
00114 
00115 #if defined(MBED_STACK_STATS_ENABLED) && MBED_STACK_STATS_ENABLED
00116 MBED_UNUSED static void send_stack_info()
00117 {
00118     mutex->lock();
00119 
00120     // Flush any queued stack entries
00121     while (!queue->empty()) {
00122         deque_and_print_thread_info();
00123     }
00124 
00125     // Print info for all other threads
00126     uint32_t thread_n = osThreadGetCount();
00127     osThreadId_t *threads = new (std::nothrow) osThreadId_t[thread_n];
00128     // Don't fail on lack of memory
00129     if (!threads) {
00130         goto end;
00131     }
00132     thread_n = osThreadEnumerate(threads, thread_n);
00133 
00134     for(size_t i = 0; i < thread_n; i++) {
00135         enqeue_thread_info(threads[i]);
00136         deque_and_print_thread_info();
00137     }
00138 
00139     delete[] threads;
00140 
00141 end:
00142     mutex->unlock();
00143 }
00144 
00145 MBED_UNUSED static void on_thread_terminate(osThreadId_t id)
00146 {
00147     mutex->lock();
00148 
00149     // There should always be space in the queue
00150     enqeue_thread_info(id);
00151 
00152     // If queue is full then print out a single entry
00153     if (queue->full()) {
00154         deque_and_print_thread_info();
00155     }
00156 
00157     mutex->unlock();
00158 }
00159 
00160 static void enqeue_thread_info(osThreadId_t id)
00161 {
00162     thread_info_t thread_info = {};
00163 
00164     thread_info.entry = (uint32_t)id;
00165     thread_info.stack_size = osThreadGetStackSize(id);
00166     thread_info.max_stack = thread_info.stack_size - osThreadGetStackSpace(id);
00167     queue->push(thread_info);
00168 }
00169 
00170 static void deque_and_print_thread_info()
00171 {
00172     thread_info_t thread_info;
00173     bool ret = queue->pop(thread_info);
00174     MBED_ASSERT(ret);
00175     uint32_t pos = 0;
00176     buf[pos++] = '\"';
00177     pos += print_hex(buf + pos, thread_info.entry);
00178     buf[pos++] = '\"';
00179     buf[pos++] = ',';
00180     pos += print_dec(buf + pos, thread_info.max_stack);
00181     buf[pos++] = ',';
00182     pos += print_dec(buf + pos, thread_info.stack_size);
00183     buf[pos++] = 0;
00184     greentea_send_kv("__thread_info", buf);
00185 }
00186 
00187 static uint32_t print_hex(char *buf, uint32_t value)
00188 {
00189     uint32_t pos = 0;
00190     buf[pos] = '0';
00191     pos++;
00192     buf[pos] = 'x';
00193     pos++;
00194     for (int i = 8; i >= 0; i--) {
00195         uint32_t val = (value >> (4 * i)) & 0xF;
00196         if (val <= 9) {
00197             buf[pos] = '0' + val;
00198             pos++;
00199         } else {
00200             buf[pos] = 'a' + val - 10;
00201             pos++;
00202         }
00203     }
00204     return pos;
00205 }
00206 
00207 static uint32_t print_dec(char *buf, uint32_t value)
00208 {
00209     uint32_t pos = 0;
00210 
00211     // The value 0 is special case
00212     if (0 == value) {
00213         buf[pos] = '0';
00214         pos++;
00215         return pos;
00216     }
00217 
00218     // Write out value in reverse order
00219     while (value != 0) {
00220         uint32_t next = value / 10;
00221         buf[pos] = '0' + (value - next * 10);
00222         value = next;
00223         pos++;
00224     }
00225 
00226     // Reverse order
00227     for (uint32_t i = 0; i < pos / 2; i++) {
00228         char temp = buf[i];
00229         buf[i] = buf[pos - 1 - i];
00230         buf[pos - 1 - i] = temp;
00231     }
00232 
00233     return pos;
00234 }
00235 
00236 #endif