Skip to content

esp32/modesp32: Implement idf_task_stats(). #12732

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions docs/library/esp32.rst
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,29 @@ Functions
The result of :func:`gc.mem_free()` is the total of the current "free"
and "max new split" values printed by :func:`micropython.mem_info()`.

.. function:: idf_task_info()

Returns information about running ESP-IDF/FreeRTOS tasks, which include
MicroPython threads. This data is useful to gain insight into how much time
tasks spend running or if they are blocked for significant parts of time,
and to determine if allocated stacks are fully utilized or might be reduced.

``CONFIG_FREERTOS_USE_TRACE_FACILITY=y`` must be set in the board
configuration to make this method available. Additionally configuring
``CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=y`` and
``CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID=y`` is recommended to be able to
retrieve the total and per-task runtime and the core ID respectively.

The return value is a 2-tuple where the first value is the total runtime,
and the second a list of tasks. Each task is a 7-tuple containing: the task
ID, name, current state, priority, runtime, stack high water mark, and the
ID of the core it is running on. Runtime and core ID will be None when the
respective FreeRTOS configuration option is not enabled.

.. note:: For an easier to use output based on this function you can use the
`utop library <https://github.com/micropython/micropython-lib/tree/master/micropython/utop>`_,
which implements a live overview similar to the Unix ``top`` command.


Flash partitions
----------------
Expand Down
46 changes: 46 additions & 0 deletions ports/esp32/modesp32.c
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,49 @@ static mp_obj_t esp32_idf_heap_info(const mp_obj_t cap_in) {
}
static MP_DEFINE_CONST_FUN_OBJ_1(esp32_idf_heap_info_obj, esp32_idf_heap_info);

#if CONFIG_FREERTOS_USE_TRACE_FACILITY
static mp_obj_t esp32_idf_task_info(void) {
const size_t task_count_max = uxTaskGetNumberOfTasks();
TaskStatus_t *task_array = m_new(TaskStatus_t, task_count_max);
uint32_t total_time;
const size_t task_count = uxTaskGetSystemState(task_array, task_count_max, &total_time);

mp_obj_list_t *task_list = MP_OBJ_TO_PTR(mp_obj_new_list(task_count, NULL));
for (size_t i = 0; i < task_count; i++) {
mp_obj_t task_data[] = {
mp_obj_new_int_from_uint((mp_uint_t)task_array[i].xHandle),
mp_obj_new_str(task_array[i].pcTaskName, strlen(task_array[i].pcTaskName)),
MP_OBJ_NEW_SMALL_INT(task_array[i].eCurrentState),
MP_OBJ_NEW_SMALL_INT(task_array[i].uxCurrentPriority),
#if CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS
mp_obj_new_int_from_uint(task_array[i].ulRunTimeCounter),
#else
mp_const_none,
#endif
mp_obj_new_int_from_uint(task_array[i].usStackHighWaterMark),
#if CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID
MP_OBJ_NEW_SMALL_INT(task_array[i].xCoreID),
#else
mp_const_none,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The utop module expects this entry to be an integer, so if CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID is disabled then utop crashes.

Maybe -1 can be used here instead of None, so that it's uniformly an integer?

(I did see that FreeRTOS returns -1 for some tasks' xCoreID, such as "Tmr Svc", so maybe a different sentinel integer like 255, or -255?)

Copy link
Contributor Author

@DvdGiessen DvdGiessen May 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: -1 is the constant tskNO_AFFINITY, meaning the task is not pinned to one specific core. Mapping it in utop (perhaps listing it as any instead of -1?) could make that clearer for the casual user.

Partly because there's already different integer values to interpret I think explicitly using None makes it clearer that the value is not available at all. I've now explicitly documented that behaviour and fixed utop to correctly handle that. If you're getting an integer value, that's a valid value returned by the IDF, and when you get None you have not build with the required configuration options. Let me know what you think!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I think that's fine to keep it as None when the feature is not supported.

#endif
};
task_list->items[i] = mp_obj_new_tuple(7, task_data);
}

m_del(TaskStatus_t, task_array, task_count_max);
mp_obj_t task_stats[] = {
#if CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS
MP_OBJ_NEW_SMALL_INT(total_time),
#else
mp_const_none,
#endif
task_list
};
return mp_obj_new_tuple(2, task_stats);
}
static MP_DEFINE_CONST_FUN_OBJ_0(esp32_idf_task_info_obj, esp32_idf_task_info);
#endif

static const mp_rom_map_elem_t esp32_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_esp32) },

Expand All @@ -228,6 +271,9 @@ static const mp_rom_map_elem_t esp32_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR_mcu_temperature), MP_ROM_PTR(&esp32_mcu_temperature_obj) },
#endif
{ MP_ROM_QSTR(MP_QSTR_idf_heap_info), MP_ROM_PTR(&esp32_idf_heap_info_obj) },
#if CONFIG_FREERTOS_USE_TRACE_FACILITY
{ MP_ROM_QSTR(MP_QSTR_idf_task_info), MP_ROM_PTR(&esp32_idf_task_info_obj) },
#endif

{ MP_ROM_QSTR(MP_QSTR_NVS), MP_ROM_PTR(&esp32_nvs_type) },
{ MP_ROM_QSTR(MP_QSTR_Partition), MP_ROM_PTR(&esp32_partition_type) },
Expand Down
Loading
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