Skip to content

Commit 1982e18

Browse files
committed
esp32/modesp32: Implement idf_task_stats().
Signed-off-by: Daniël van de Giessen <daniel@dvdgiessen.nl>
1 parent 3883f29 commit 1982e18

File tree

2 files changed

+106
-0
lines changed

2 files changed

+106
-0
lines changed

docs/library/esp32.rst

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,68 @@ Functions
8080
The result of :func:`gc.mem_free()` is the total of the current "free"
8181
and "max new split" values printed by :func:`micropython.mem_info()`.
8282

83+
.. function:: idf_task_stats()
84+
85+
Returns information about running ESP-IDF/FreeRTOS tasks, which include
86+
MicroPython threads. This data is useful to gain insight into how much time
87+
tasks spend running or if they are blocked for signficant parts of time, and
88+
to determine if allocated stacks are fully utilized or might be reduced.
89+
90+
``CONFIG_FREERTOS_USE_TRACE_FACILITY=y`` must be set in the board
91+
configuration to make this method available. Additionally setting
92+
``CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID=y`` and
93+
``CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=y`` is recommended to be able to
94+
retrieve the core id and runtime respectively.
95+
96+
The return value is a 2-tuple where the first value is the total runtime,
97+
and the second a list of tasks. Each task is a 7-tuple containing: the task
98+
name, ID, current state, priority, runtime, stack high water mark, and the
99+
ID of the core it is running on.
100+
101+
Example displaying a constantly updating task list::
102+
103+
import esp32
104+
import time
105+
def print_task_stats(timeout_seconds=None):
106+
if not hasattr(esp32, 'idf_task_stats'):
107+
raise RuntimeError('esp32.idf_task_stats() is not supported!')
108+
time_start = time.ticks_ms()
109+
previous_total_runtime = None
110+
previous_task_runtimes = {}
111+
previous_line_count = 0
112+
task_state_names = ['running', 'ready', 'blocked', 'suspended', 'deleted', 'invalid']
113+
print('')
114+
while timeout_seconds is None or abs(time.ticks_diff(time.ticks_ms(), time_start)) < 1000 * timeout_seconds:
115+
total_runtime, tasks = esp32.idf_task_stats()
116+
tasks.sort(key=lambda t: t[1])
117+
print('\x1B[{}A'.format(previous_line_count), end='')
118+
print(' CPU% CORE PRIORITY STATE STACKWATERMARK NAME\x1B[K')
119+
previous_line_count = 1
120+
for task_name, task_id, task_state, task_priority, task_runtime, task_stackhighwatermark, task_coreid in tasks:
121+
task_runtime_percentage = '-'
122+
if total_runtime > 0:
123+
if previous_total_runtime is not None and task_id in previous_task_runtimes:
124+
task_cpu_percentage = 100 * (task_runtime - previous_task_runtimes[task_id]) / (total_runtime - previous_total_runtime)
125+
else:
126+
task_cpu_percentage = 100 * task_runtime / total_runtime
127+
task_runtime_percentage = '{:.2f}%'.format(task_cpu_percentage)
128+
task_state_name = 'unknown'
129+
if task_state >= 0 and task_state < len(task_state_names):
130+
task_state_name = task_state_names[task_state]
131+
print('{:>7} {:>4d} {:>8d} {:<9} {:<14d} {}\x1B[K'.format(
132+
task_runtime_percentage,
133+
task_coreid,
134+
task_priority,
135+
task_state_name,
136+
task_stackhighwatermark,
137+
task_name
138+
))
139+
previous_task_runtimes[task_id] = task_runtime
140+
previous_line_count += 1
141+
print('\x1B[K')
142+
previous_line_count += 1
143+
previous_total_runtime = total_runtime
144+
time.sleep_ms(1000)
83145

84146
Flash partitions
85147
----------------

ports/esp32/modesp32.c

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,47 @@ STATIC mp_obj_t esp32_idf_heap_info(const mp_obj_t cap_in) {
192192
}
193193
STATIC MP_DEFINE_CONST_FUN_OBJ_1(esp32_idf_heap_info_obj, esp32_idf_heap_info);
194194

195+
#if configUSE_TRACE_FACILITY
196+
STATIC mp_obj_t esp32_idf_task_stats(void) {
197+
const size_t task_count_max = uxTaskGetNumberOfTasks();
198+
if (task_count_max == 0) {
199+
return mp_const_none;
200+
}
201+
202+
TaskStatus_t *task_array = m_new(TaskStatus_t, task_count_max);
203+
if (task_array == NULL) {
204+
return mp_const_none;
205+
}
206+
207+
uint32_t total_time;
208+
const size_t task_count = uxTaskGetSystemState(task_array, task_count_max, &total_time);
209+
210+
mp_obj_t task_list = mp_obj_new_list(0, 0);
211+
for (size_t i = 0; i < task_count; i++) {
212+
mp_obj_t task_data[] = {
213+
mp_obj_new_str(task_array[i].pcTaskName, strlen(task_array[i].pcTaskName)),
214+
mp_obj_new_int_from_uint(task_array[i].xTaskNumber),
215+
MP_OBJ_NEW_SMALL_INT(task_array[i].eCurrentState),
216+
MP_OBJ_NEW_SMALL_INT(task_array[i].uxCurrentPriority),
217+
mp_obj_new_int_from_uint(task_array[i].ulRunTimeCounter),
218+
mp_obj_new_int_from_uint(task_array[i].usStackHighWaterMark),
219+
#if configTASKLIST_INCLUDE_COREID
220+
MP_OBJ_NEW_SMALL_INT(task_array[i].xCoreID),
221+
#else
222+
mp_const_none,
223+
#endif
224+
};
225+
mp_obj_t task = mp_obj_new_tuple(7, task_data);
226+
mp_obj_list_append(task_list, task);
227+
}
228+
229+
m_del(TaskStatus_t, task_array, task_count_max);
230+
mp_obj_t task_stats[] = { MP_OBJ_NEW_SMALL_INT(total_time), task_list };
231+
return mp_obj_new_tuple(2, task_stats);
232+
}
233+
STATIC MP_DEFINE_CONST_FUN_OBJ_0(esp32_idf_task_stats_obj, esp32_idf_task_stats);
234+
#endif
235+
195236
STATIC const mp_rom_map_elem_t esp32_module_globals_table[] = {
196237
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_esp32) },
197238

@@ -204,6 +245,9 @@ STATIC const mp_rom_map_elem_t esp32_module_globals_table[] = {
204245
{ MP_ROM_QSTR(MP_QSTR_raw_temperature), MP_ROM_PTR(&esp32_raw_temperature_obj) },
205246
#endif
206247
{ MP_ROM_QSTR(MP_QSTR_idf_heap_info), MP_ROM_PTR(&esp32_idf_heap_info_obj) },
248+
#if configUSE_TRACE_FACILITY
249+
{ MP_ROM_QSTR(MP_QSTR_idf_task_stats), MP_ROM_PTR(&esp32_idf_task_stats_obj) },
250+
#endif
207251

208252
{ MP_ROM_QSTR(MP_QSTR_NVS), MP_ROM_PTR(&esp32_nvs_type) },
209253
{ MP_ROM_QSTR(MP_QSTR_Partition), MP_ROM_PTR(&esp32_partition_type) },

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy