8000 esp32/modesp32: Implement idf_task_stats(). · micropython/micropython@e2316bd · GitHub
[go: up one dir, main page]

Skip to content

Commit e2316bd

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

File tree

2 files changed

+98
-0
lines changed

2 files changed

+98
-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 significant parts of time,
88+
and 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: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,39 @@ 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 CONFIG_FREERTOS_USE_TRACE_FACILITY
196+
STATIC mp_obj_t esp32_idf_task_stats(void) {
197+
const size_t task_count_max = uxTaskGetNumberOfTasks();
198+
TaskStatus_t *task_array = m_new(TaskStatus_t, task_count_max);
199+
uint32_t total_time;
200+
const size_t task_count = uxTaskGetSystemState(task_array, task_count_max, &total_time);
201+
202+
mp_obj_t task_list = mp_obj_new_list(0, 0);
203+
for (size_t i = 0; i < task_count; i++) {
204+
mp_obj_t task_data[] = {
205+
mp_obj_new_str(task_array[i].pcTaskName, strlen(task_array[i].pcTaskName)),
206+
mp_obj_new_int_from_uint(task_array[i].xTaskNumber),
207+
MP_OBJ_NEW_SMALL_INT(task_array[i].eCurrentState),
208+
MP_OBJ_NEW_SMALL_INT(task_array[i].uxCurrentPriority),
209+
mp_obj_new_int_from_uint(task_array[i].ulRunTimeCounter),
210+
mp_obj_new_int_from_uint(task_array[i].usStackHighWaterMark),
211+
#if CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID
212+
MP_OBJ_NEW_SMALL_INT(task_array[i].xCoreID),
213+
#else
214+
mp_const_none,
215+
#endif
216+
};
217+
mp_obj_t task = mp_obj_new_tuple(7, task_data);
218+
mp_obj_list_append(task_list, task);
219+
}
220+
221+
m_del(TaskStatus_t, task_array, task_count_max);
222+
mp_obj_t task_stats[] = { MP_OBJ_NEW_SMALL_INT(total_time), task_list };
223+
return mp_obj_new_tuple(2, task_stats);
224+
}
225+
STATIC MP_DEFINE_CONST_FUN_OBJ_0(esp32_idf_task_stats_obj, esp32_idf_task_stats);
226+
#endif
227+
195228
STATIC const mp_rom_map_elem_t esp32_module_globals_table[] = {
196229
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_esp32) },
197230

@@ -204,6 +237,9 @@ STATIC const mp_rom_map_elem_t esp32_module_globals_table[] = {
204237
{ MP_ROM_QSTR(MP_QSTR_raw_temperature), MP_ROM_PTR(&esp32_raw_temperature_obj) },
205238
#endif
206239
{ MP_ROM_QSTR(MP_QSTR_idf_heap_info), MP_ROM_PTR(&esp32_idf_heap_info_obj) },
240+
#if CONFIG_FREERTOS_USE_TRACE_FACILITY
241+
{ MP_ROM_QSTR(MP_QSTR_idf_task_stats), MP_ROM_PTR(&esp32_idf_task_stats_obj) },
242+
#endif
207243

208244
{ MP_ROM_QSTR(MP_QSTR_NVS), MP_ROM_PTR(&esp32_nvs_type) },
209245
{ MP_ROM_QSTR(MP_QSTR_Partition), MP_ROM_PTR(&esp32_partition_type) },

0 commit comments

Comments
 (0)
0