mirror of
https://github.com/DarkFlippers/unleashed-firmware.git
synced 2025-12-12 04:34:43 +04:00
Cli: top (#3707)
* Cli: top command to replace ps. Furi: ThreadList and thread enumeration routine. * Sync API Symbols * Cli: cleanup top output, add memory section. Furi: thread enumeration code cleanup. Fix doxygen and make pvs happy. * Furi: iterator in thread_list instead of M_EACH, fix memory leak * Update documentation * Cli: customizable refres interval for top command * Furi: add consistentency into float declaration in thread list * FreeRTOSConfig: remove invalid comment Co-authored-by: Sergei Gavrilov <who.just.the.doctor@gmail.com>
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
#include "thread.h"
|
||||
#include "thread_list.h"
|
||||
#include "kernel.h"
|
||||
#include "memmgr.h"
|
||||
#include "memmgr_heap.h"
|
||||
@@ -13,6 +14,8 @@
|
||||
#include <stdint.h>
|
||||
#include <task.h>
|
||||
|
||||
#include <task_control_block.h>
|
||||
|
||||
#define TAG "FuriThread"
|
||||
|
||||
#define THREAD_NOTIFY_INDEX (1) // Index 0 is used for stream buffers
|
||||
@@ -547,33 +550,71 @@ uint32_t furi_thread_flags_wait(uint32_t flags, uint32_t options, uint32_t timeo
|
||||
return rflags;
|
||||
}
|
||||
|
||||
uint32_t furi_thread_enumerate(FuriThreadId* thread_array, uint32_t array_item_count) {
|
||||
uint32_t i, count;
|
||||
TaskStatus_t* task;
|
||||
static const char* furi_thread_state_name(eTaskState state) {
|
||||
switch(state) {
|
||||
case eRunning:
|
||||
return "Running";
|
||||
case eReady:
|
||||
return "Ready";
|
||||
case eBlocked:
|
||||
return "Blocked";
|
||||
case eSuspended:
|
||||
return "Suspended";
|
||||
case eDeleted:
|
||||
return "Deleted";
|
||||
case eInvalid:
|
||||
return "Invalid";
|
||||
default:
|
||||
return "?";
|
||||
}
|
||||
}
|
||||
|
||||
if(FURI_IS_IRQ_MODE() || (thread_array == NULL) || (array_item_count == 0U)) {
|
||||
count = 0U;
|
||||
} else {
|
||||
vTaskSuspendAll();
|
||||
bool furi_thread_enumerate(FuriThreadList* thread_list) {
|
||||
furi_check(thread_list);
|
||||
furi_check(!FURI_IS_IRQ_MODE());
|
||||
|
||||
bool result = false;
|
||||
|
||||
vTaskSuspendAll();
|
||||
do {
|
||||
uint32_t tick = furi_get_tick();
|
||||
uint32_t count = uxTaskGetNumberOfTasks();
|
||||
|
||||
TaskStatus_t* task = pvPortMalloc(count * sizeof(TaskStatus_t));
|
||||
|
||||
if(!task) break;
|
||||
|
||||
count = uxTaskGetNumberOfTasks();
|
||||
task = pvPortMalloc(count * sizeof(TaskStatus_t));
|
||||
configRUN_TIME_COUNTER_TYPE total_run_time;
|
||||
count = uxTaskGetSystemState(task, count, &total_run_time);
|
||||
for(uint32_t i = 0U; i < count; i++) {
|
||||
TaskControlBlock* tcb = (TaskControlBlock*)task[i].xHandle;
|
||||
|
||||
if(task != NULL) {
|
||||
count = uxTaskGetSystemState(task, count, &total_run_time);
|
||||
FuriThreadListItem* item =
|
||||
furi_thread_list_get_or_insert(thread_list, (FuriThread*)task[i].xHandle);
|
||||
|
||||
for(i = 0U; (i < count) && (i < array_item_count); i++) {
|
||||
thread_array[i] = (FuriThreadId)task[i].xHandle;
|
||||
}
|
||||
count = i;
|
||||
item->thread = (FuriThreadId)task[i].xHandle;
|
||||
item->app_id = furi_thread_get_appid(item->thread);
|
||||
item->name = task[i].pcTaskName;
|
||||
item->priority = task[i].uxCurrentPriority;
|
||||
item->stack_address = (uint32_t)tcb->pxStack;
|
||||
size_t thread_heap = memmgr_heap_get_thread_memory(item->thread);
|
||||
item->heap = thread_heap == MEMMGR_HEAP_UNKNOWN ? 0u : thread_heap;
|
||||
item->stack_size = (tcb->pxEndOfStack - tcb->pxStack + 1) * sizeof(StackType_t);
|
||||
item->stack_min_free = furi_thread_get_stack_space(item->thread);
|
||||
item->state = furi_thread_state_name(task[i].eCurrentState);
|
||||
item->counter_previous = item->counter_current;
|
||||
item->counter_current = task[i].ulRunTimeCounter;
|
||||
item->tick = tick;
|
||||
}
|
||||
(void)xTaskResumeAll();
|
||||
|
||||
vPortFree(task);
|
||||
}
|
||||
furi_thread_list_process(thread_list, total_run_time, tick);
|
||||
|
||||
return count;
|
||||
result = true;
|
||||
} while(false);
|
||||
(void)xTaskResumeAll();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const char* furi_thread_get_name(FuriThreadId thread_id) {
|
||||
|
||||
@@ -46,6 +46,9 @@ typedef enum {
|
||||
*/
|
||||
typedef struct FuriThread FuriThread;
|
||||
|
||||
/** FuriThreadList type */
|
||||
typedef struct FuriThreadList FuriThreadList;
|
||||
|
||||
/**
|
||||
* @brief Unique thread identifier type (used by the OS kernel).
|
||||
*/
|
||||
@@ -379,13 +382,13 @@ uint32_t furi_thread_flags_get(void);
|
||||
uint32_t furi_thread_flags_wait(uint32_t flags, uint32_t options, uint32_t timeout);
|
||||
|
||||
/**
|
||||
* @brief Enumerate all threads.
|
||||
*
|
||||
* @param[out] thread_array pointer to the output array (must be properly allocated)
|
||||
* @param[in] array_item_count output array capacity in elements (NOT bytes)
|
||||
* @return total thread count (array_item_count or less)
|
||||
* @brief Enumerate all threads.
|
||||
*
|
||||
* @param[out] thread_list pointer to the FuriThreadList container
|
||||
*
|
||||
* @return true on success, false otherwise
|
||||
*/
|
||||
uint32_t furi_thread_enumerate(FuriThreadId* thread_array, uint32_t array_item_count);
|
||||
bool furi_thread_enumerate(FuriThreadList* thread_list);
|
||||
|
||||
/**
|
||||
* @brief Get the name of a thread based on its unique identifier.
|
||||
|
||||
110
furi/core/thread_list.c
Normal file
110
furi/core/thread_list.c
Normal file
@@ -0,0 +1,110 @@
|
||||
#include "thread_list.h"
|
||||
#include "check.h"
|
||||
|
||||
#include <m-array.h>
|
||||
#include <m-dict.h>
|
||||
|
||||
ARRAY_DEF(FuriThreadListItemArray, FuriThreadListItem*, M_PTR_OPLIST) // NOLINT
|
||||
|
||||
#define M_OPL_FuriThreadListItemArray_t() ARRAY_OPLIST(FuriThreadListItemArray, M_PTR_OPLIST)
|
||||
|
||||
DICT_DEF2(
|
||||
FuriThreadListItemDict,
|
||||
uint32_t,
|
||||
M_DEFAULT_OPLIST,
|
||||
FuriThreadListItem*,
|
||||
M_PTR_OPLIST) // NOLINT
|
||||
|
||||
#define M_OPL_FuriThreadListItemDict_t() \
|
||||
DICT_OPLIST(FuriThreadListItemDict, M_DEFAULT_OPLIST, M_PTR_OPLIST)
|
||||
|
||||
struct FuriThreadList {
|
||||
FuriThreadListItemArray_t items;
|
||||
FuriThreadListItemDict_t search;
|
||||
uint32_t runtime_previous;
|
||||
uint32_t runtime_current;
|
||||
};
|
||||
|
||||
FuriThreadList* furi_thread_list_alloc(void) {
|
||||
FuriThreadList* instance = malloc(sizeof(FuriThreadList));
|
||||
|
||||
FuriThreadListItemArray_init(instance->items);
|
||||
FuriThreadListItemDict_init(instance->search);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
void furi_thread_list_free(FuriThreadList* instance) {
|
||||
furi_check(instance);
|
||||
|
||||
FuriThreadListItemArray_it_t it;
|
||||
FuriThreadListItemArray_it(it, instance->items);
|
||||
while(!FuriThreadListItemArray_end_p(it)) {
|
||||
FuriThreadListItem* item = *FuriThreadListItemArray_cref(it);
|
||||
free(item);
|
||||
FuriThreadListItemArray_next(it);
|
||||
}
|
||||
|
||||
FuriThreadListItemDict_clear(instance->search);
|
||||
FuriThreadListItemArray_clear(instance->items);
|
||||
|
||||
free(instance);
|
||||
}
|
||||
|
||||
size_t furi_thread_list_size(FuriThreadList* instance) {
|
||||
furi_check(instance);
|
||||
return FuriThreadListItemArray_size(instance->items);
|
||||
}
|
||||
|
||||
FuriThreadListItem* furi_thread_list_get_at(FuriThreadList* instance, size_t position) {
|
||||
furi_check(instance);
|
||||
furi_check(position < furi_thread_list_size(instance));
|
||||
|
||||
return *FuriThreadListItemArray_get(instance->items, position);
|
||||
}
|
||||
|
||||
FuriThreadListItem* furi_thread_list_get_or_insert(FuriThreadList* instance, FuriThread* thread) {
|
||||
furi_check(instance);
|
||||
|
||||
FuriThreadListItem** item_ptr = FuriThreadListItemDict_get(instance->search, (uint32_t)thread);
|
||||
if(item_ptr) {
|
||||
return *item_ptr;
|
||||
}
|
||||
|
||||
FuriThreadListItem* item = malloc(sizeof(FuriThreadListItem));
|
||||
|
||||
FuriThreadListItemArray_push_back(instance->items, item);
|
||||
FuriThreadListItemDict_set_at(instance->search, (uint32_t)thread, item);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
void furi_thread_list_process(FuriThreadList* instance, uint32_t runtime, uint32_t tick) {
|
||||
furi_check(instance);
|
||||
|
||||
instance->runtime_previous = instance->runtime_current;
|
||||
instance->runtime_current = runtime;
|
||||
|
||||
uint32_t runtime_counter = instance->runtime_current - instance->runtime_previous;
|
||||
|
||||
FuriThreadListItemArray_it_t it;
|
||||
FuriThreadListItemArray_it(it, instance->items);
|
||||
while(!FuriThreadListItemArray_end_p(it)) {
|
||||
FuriThreadListItem* item = *FuriThreadListItemArray_cref(it);
|
||||
if(item->tick != tick) {
|
||||
FuriThreadListItemArray_remove(instance->items, it);
|
||||
(void)FuriThreadListItemDict_erase(instance->search, (uint32_t)item->thread);
|
||||
free(item);
|
||||
} else {
|
||||
uint32_t item_counter = item->counter_current - item->counter_previous;
|
||||
if(item_counter && item->counter_previous && item->counter_current) {
|
||||
item->cpu = (float)item_counter / (float)runtime_counter * 100.0f;
|
||||
if(item->cpu > 200.0f) item->cpu = 0.0f;
|
||||
} else {
|
||||
item->cpu = 0.0f;
|
||||
}
|
||||
|
||||
FuriThreadListItemArray_next(it);
|
||||
}
|
||||
}
|
||||
}
|
||||
81
furi/core/thread_list.h
Normal file
81
furi/core/thread_list.h
Normal file
@@ -0,0 +1,81 @@
|
||||
#pragma once
|
||||
|
||||
#include "base.h"
|
||||
#include "common_defines.h"
|
||||
#include "thread.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
FuriThread* thread; /**< Pointer to FuriThread, valid while it is running */
|
||||
const char* app_id; /**< Thread application id, valid while it is running */
|
||||
const char* name; /**< Thread name, valid while it is running */
|
||||
FuriThreadPriority priority; /**< Thread priority */
|
||||
uint32_t stack_address; /**< Thread stack address */
|
||||
size_t heap; /**< Thread heap size if tracking enabled, 0 - otherwise */
|
||||
uint32_t stack_size; /**< Thread stack size */
|
||||
uint32_t stack_min_free; /**< Thread minimum of the stack size ever reached */
|
||||
const char*
|
||||
state; /**< Thread state, can be: "Running", "Ready", "Blocked", "Suspended", "Deleted", "Invalid" */
|
||||
float cpu; /**< Thread CPU usage time in percents (including interrupts happened while running) */
|
||||
|
||||
// Service variables
|
||||
uint32_t counter_previous; /**< Thread previous runtime counter */
|
||||
uint32_t counter_current; /**< Thread current runtime counter */
|
||||
uint32_t tick; /**< Thread last seen tick */
|
||||
} FuriThreadListItem;
|
||||
|
||||
/** Anonymous FuriThreadList type */
|
||||
typedef struct FuriThreadList FuriThreadList;
|
||||
|
||||
/** Allocate FuriThreadList instance
|
||||
*
|
||||
* @return FuriThreadList instance
|
||||
*/
|
||||
FuriThreadList* furi_thread_list_alloc(void);
|
||||
|
||||
/** Free FuriThreadList instance
|
||||
*
|
||||
* @param instance The FuriThreadList instance to free
|
||||
*/
|
||||
void furi_thread_list_free(FuriThreadList* instance);
|
||||
|
||||
/** Get FuriThreadList instance size
|
||||
*
|
||||
* @param instance The instance
|
||||
*
|
||||
* @return Item count
|
||||
*/
|
||||
size_t furi_thread_list_size(FuriThreadList* instance);
|
||||
|
||||
/** Get item at position
|
||||
*
|
||||
* @param instance The FuriThreadList instance
|
||||
* @param[in] position The position of the item
|
||||
*
|
||||
* @return The FuriThreadListItem instance
|
||||
*/
|
||||
FuriThreadListItem* furi_thread_list_get_at(FuriThreadList* instance, size_t position);
|
||||
|
||||
/** Get item by thread FuriThread pointer
|
||||
*
|
||||
* @param instance The FuriThreadList instance
|
||||
* @param thread The FuriThread pointer
|
||||
*
|
||||
* @return The FuriThreadListItem instance
|
||||
*/
|
||||
FuriThreadListItem* furi_thread_list_get_or_insert(FuriThreadList* instance, FuriThread* thread);
|
||||
|
||||
/** Process items in the FuriThreadList instance
|
||||
*
|
||||
* @param instance The instance
|
||||
* @param[in] runtime The runtime of the system since start
|
||||
* @param[in] tick The tick when processing happened
|
||||
*/
|
||||
void furi_thread_list_process(FuriThreadList* instance, uint32_t runtime, uint32_t tick);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "core/record.h"
|
||||
#include "core/semaphore.h"
|
||||
#include "core/thread.h"
|
||||
#include "core/thread_list.h"
|
||||
#include "core/timer.h"
|
||||
#include "core/string.h"
|
||||
#include "core/stream_buffer.h"
|
||||
|
||||
Reference in New Issue
Block a user