Skip to content

Commit 52fd7b6

Browse files
authored
Merge pull request #2642 from c1728p9/stack_stats
Stack stats
2 parents 27eb9c0 + 8447843 commit 52fd7b6

File tree

15 files changed

+631
-17
lines changed

15 files changed

+631
-17
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#ifndef GREENTEA_METRICS_H
2+
#define GREENTEA_METRICS_H
3+
4+
/**
5+
* Setup platform specific metrics
6+
*/
7+
void greentea_metrics_setup(void);
8+
9+
/**
10+
* Report and cleanup platform specifc metrics
11+
*/
12+
void greentea_metrics_report(void);
13+
14+
#endif
Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
/*
2+
* Copyright (c) 2013-2016, ARM Limited, All Rights Reserved
3+
* SPDX-License-Identifier: Apache-2.0
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License"); you may
6+
* not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
#include "mbed.h"
19+
#include "rtos.h"
20+
#include "mbed_stats.h"
21+
#include "cmsis_os.h"
22+
#include "greentea-client/test_env.h"
23+
#include "greentea-client/greentea_metrics.h"
24+
#include "SingletonPtr.h"
25+
#include "CircularBuffer.h"
26+
27+
#define THREAD_BUF_COUNT 16
28+
29+
typedef struct {
30+
uint32_t entry;
31+
uint32_t arg;
32+
uint32_t stack_size;
33+
uint32_t max_stack;
34+
} thread_info_t;
35+
36+
// Mutex to protect "buf"
37+
SingletonPtr<Mutex> mutex;
38+
static char buf[128];
39+
static SingletonPtr<CircularBuffer<thread_info_t, THREAD_BUF_COUNT> > queue;
40+
41+
static void send_heap_info(void);
42+
static void send_stack_info(void);
43+
static void on_thread_terminate(osThreadId id);
44+
static void enqeue_thread_info(osThreadId id);
45+
static void deque_and_print_thread_info(void);
46+
47+
// sprintf uses a lot of stack so use these instead
48+
static uint32_t print_hex(char *buf, uint32_t value);
49+
static uint32_t print_dec(char *buf, uint32_t value);
50+
51+
void greentea_metrics_setup()
52+
{
53+
#if defined(MBED_STACK_STATS_ENABLED) && MBED_STACK_STATS_ENABLED
54+
Thread::attach_terminate_hook(on_thread_terminate);
55+
#endif
56+
}
57+
58+
void greentea_metrics_report()
59+
{
60+
send_heap_info();
61+
#if defined(MBED_STACK_STATS_ENABLED) && MBED_STACK_STATS_ENABLED
62+
send_stack_info();
63+
Thread::attach_terminate_hook(NULL);
64+
#endif
65+
}
66+
67+
static void send_heap_info()
68+
{
69+
mbed_stats_heap_t heap_stats;
70+
mbed_stats_heap_get(&heap_stats);
71+
greentea_send_kv("max_heap_usage",heap_stats.max_size);
72+
}
73+
74+
MBED_UNUSED static void send_stack_info()
75+
{
76+
mutex->lock();
77+
78+
// Flush any queued stack entries
79+
while (!queue->empty()) {
80+
deque_and_print_thread_info();
81+
}
82+
83+
// Print info for all other threads
84+
osThreadEnumId enum_id = _osThreadsEnumStart();
85+
while (true) {
86+
osThreadId thread_id = _osThreadEnumNext(enum_id);
87+
if (NULL == thread_id) {
88+
// End of enumeration
89+
break;
90+
}
91+
enqeue_thread_info(thread_id);
92+
deque_and_print_thread_info();
93+
}
94+
_osThreadEnumFree(enum_id);
95+
96+
mutex->unlock();
97+
}
98+
99+
MBED_UNUSED static void on_thread_terminate(osThreadId id)
100+
{
101+
mutex->lock();
102+
103+
// There should always be space in the queue
104+
enqeue_thread_info(id);
105+
106+
// If queue is full then print out a single entry
107+
if (queue->full()) {
108+
deque_and_print_thread_info();
109+
}
110+
111+
mutex->unlock();
112+
}
113+
114+
static void enqeue_thread_info(osThreadId id)
115+
{
116+
osEvent info;
117+
thread_info_t thread_info = {};
118+
info = _osThreadGetInfo(id, osThreadInfoEntry);
119+
if (info.status != osOK) {
120+
return;
121+
}
122+
thread_info.entry = (uint32_t)info.value.p;
123+
info = _osThreadGetInfo(id, osThreadInfoArg);
124+
if (info.status != osOK) {
125+
return;
126+
}
127+
thread_info.arg = (uint32_t)info.value.p;
128+
info = _osThreadGetInfo(id, osThreadInfoStackSize);
129+
if (info.status != osOK) {
130+
return;
131+
}
132+
thread_info.stack_size = (uint32_t)info.value.v;
133+
info = _osThreadGetInfo(id, osThreadInfoStackMax);
134+
if (info.status != osOK) {
135+
return;
136+
}
137+
thread_info.max_stack = (uint32_t)info.value.v;
138+
queue->push(thread_info);
139+
}
140+
141+
static void deque_and_print_thread_info()
142+
{
143+
thread_info_t thread_info;
144+
bool ret = queue->pop(thread_info);
145+
MBED_ASSERT(ret);
146+
uint32_t pos = 0;
147+
buf[pos++] = '\"';
148+
pos += print_hex(buf + pos, thread_info.entry);
149+
buf[pos++] = '-';
150+
pos += print_hex(buf + pos, thread_info.arg);
151+
buf[pos++] = '\"';
152+
buf[pos++] = ',';
153+
pos += print_dec(buf + pos, thread_info.max_stack);
154+
buf[pos++] = ',';
155+
pos += print_dec(buf + pos, thread_info.stack_size);
156+
buf[pos++] = 0;
157+
greentea_send_kv("__thread_info", buf);
158+
}
159+
160+
static uint32_t print_hex(char *buf, uint32_t value)
161+
{
162+
uint32_t pos = 0;
163+
buf[pos] = '0';
164+
pos++;
165+
buf[pos] = 'x';
166+
pos++;
167+
for (int i = 8; i >= 0; i--) {
168+
uint32_t val = (value >> (4 * i)) & 0xF;
169+
if (val <= 9) {
170+
buf[pos] = '0' + val;
171+
pos++;
172+
} else {
173+
buf[pos] = 'a' + val - 10;
174+
pos++;
175+
}
176+
}
177+
return pos;
178+
}
179+
180+
static uint32_t print_dec(char *buf, uint32_t value)
181+
{
182+
uint32_t pos = 0;
183+
184+
// The value 0 is special case
185+
if (0 == value) {
186+
buf[pos] = '0';
187+
pos++;
188+
return pos;
189+
}
190+
191+
// Write out value in reverse order
192+
while (value != 0) {
193+
uint32_t next = value / 10;
194+
buf[pos] = '0' + (value - next * 10);
195+
value = next;
196+
pos++;
197+
}
198+
199+
// Reverse order
200+
for (uint32_t i = 0; i < pos / 2; i++) {
201+
char temp = buf[i];
202+
buf[i] = buf[pos - 1 - i];
203+
buf[pos - 1 - i] = temp;
204+
}
205+
206+
return pos;
207+
}

features/frameworks/greentea-client/source/test_env.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "mbed.h"
2222
#include "greentea-client/test_env.h"
2323
#include "greentea-client/greentea_serial.h"
24+
#include "greentea-client/greentea_metrics.h"
2425

2526

2627
/**
@@ -65,6 +66,7 @@ static void greentea_notify_version();
6566
* This function is blocking.
6667
*/
6768
void GREENTEA_SETUP(const int timeout, const char *host_test_name) {
69+
greentea_metrics_setup();
6870
// Key-value protocol handshake function. Waits for {{__sync;...}} message
6971
// Sync preamble: "{{__sync;0dad4a9d-59a3-4aec-810d-d5fb09d852c1}}"
7072
// Example value of sync_uuid == "0dad4a9d-59a3-4aec-810d-d5fb09d852c1"
@@ -451,6 +453,7 @@ static void greentea_notify_completion(const int result) {
451453
__gcov_flush();
452454
coverage_report = false;
453455
#endif
456+
greentea_metrics_report();
454457
greentea_send_kv(GREENTEA_TEST_ENV_END, val);
455458
greentea_send_kv(GREENTEA_TEST_ENV_EXIT, 0);
456459
}

features/frameworks/utest/source/utest_greentea_handlers.cpp

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
#include "greentea-client/test_env.h"
2222
#include "utest/utest_stack_trace.h"
2323
#include "utest/utest_serial.h"
24-
#include "mbed_stats.h"
2524

2625
using namespace utest::v1;
2726

@@ -106,10 +105,7 @@ utest::v1::status_t utest::v1::greentea_test_setup_handler(const size_t number_o
106105
void utest::v1::greentea_test_teardown_handler(const size_t passed, const size_t failed, const failure_t failure)
107106
{
108107
UTEST_LOG_FUNCTION();
109-
mbed_stats_heap_t heap_stats;
110108
verbose_test_teardown_handler(passed, failed, failure);
111-
mbed_stats_heap_get(&heap_stats);
112-
greentea_send_kv("max_heap_usage",heap_stats.max_size);
113109
greentea_send_kv(TEST_ENV_TESTCASE_SUMMARY, passed, failed);
114110
int result = !(failed || (failure.reason && !(failure.reason & REASON_IGNORE)));
115111
GREENTEA_TESTSUITE_RESULT(result);

rtos/rtos/Thread.cpp

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,15 @@
3030

3131
extern "C" P_TCB rt_tid2ptcb(osThreadId thread_id);
3232

33+
34+
static void (*terminate_hook)(osThreadId id) = 0;
35+
extern "C" void thread_terminate_hook(osThreadId id)
36+
{
37+
if (terminate_hook != (void (*)(osThreadId))NULL) {
38+
terminate_hook(id);
39+
}
40+
}
41+
3342
namespace rtos {
3443

3544
void Thread::constructor(osPriority priority,
@@ -74,10 +83,7 @@ osStatus Thread::start(Callback<void()> task) {
7483
_thread_def.pthread = Thread::_thunk;
7584
if (_thread_def.stack_pointer == NULL) {
7685
_thread_def.stack_pointer = new uint32_t[_thread_def.stacksize/sizeof(uint32_t)];
77-
if (_thread_def.stack_pointer == NULL) {
78-
_mutex.unlock();
79-
return osErrorNoMemory;
80-
}
86+
MBED_ASSERT(_thread_def.stack_pointer != NULL);
8187
}
8288

8389
//Fill the stack with a magic word for maximum usage checking
@@ -88,8 +94,12 @@ osStatus Thread::start(Callback<void()> task) {
8894
_task = task;
8995
_tid = osThreadCreate(&_thread_def, this);
9096
if (_tid == NULL) {
91-
if (_dynamic_stack) delete[] (_thread_def.stack_pointer);
97+
if (_dynamic_stack) {
98+
delete[] (_thread_def.stack_pointer);
99+
_thread_def.stack_pointer = (uint32_t*)NULL;
100+
}
92101
_mutex.unlock();
102+
_join_sem.release();
93103
return osErrorResource;
94104
}
95105

@@ -336,12 +346,17 @@ void Thread::attach_idle_hook(void (*fptr)(void)) {
336346
rtos_attach_idle_hook(fptr);
337347
}
338348

349+
void Thread::attach_terminate_hook(void (*fptr)(osThreadId id)) {
350+
terminate_hook = fptr;
351+
}
352+
339353
Thread::~Thread() {
340354
// terminate is thread safe
341355
terminate();
342356
#ifdef __MBED_CMSIS_RTOS_CM
343357
if (_dynamic_stack) {
344358
delete[] (_thread_def.stack_pointer);
359+
_thread_def.stack_pointer = (uint32_t*)NULL;
345360
}
346361
#endif
347362
}

rtos/rtos/Thread.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,11 @@ class Thread {
320320
*/
321321
static void attach_idle_hook(void (*fptr)(void));
322322

323+
/** Attach a function to be called when a task is killed
324+
@param fptr pointer to the function to be called
325+
*/
326+
static void attach_terminate_hook(void (*fptr)(osThreadId id));
327+
323328
virtual ~Thread();
324329

325330
private:

rtos/rtx/TARGET_CORTEX_A/RTX_Conf_CA.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,14 @@ void os_error (uint32_t err_code) {
320320
for (;;);
321321
}
322322

323+
/*----------------------------------------------------------------------------
324+
* RTX Hooks
325+
*---------------------------------------------------------------------------*/
326+
extern void thread_terminate_hook(osThreadId id);
327+
328+
void sysThreadTerminate(osThreadId id) {
329+
thread_terminate_hook(id);
330+
}
323331

324332
/*----------------------------------------------------------------------------
325333
* RTX Configuration Functions

0 commit comments

Comments
 (0)