zephyr/rims_app/src/utilization.cpp
2025-06-18 13:28:59 +02:00

112 lines
3.4 KiB
C++

#include "utilization.hpp"
#include "log.hpp"
#include <cstdint>
#include <thread>
#include <zephyr/debug/thread_analyzer.h>
#include <zephyr/kernel.h>
namespace rims {
K_FIFO_DEFINE(utilizationIngress);
K_FIFO_DEFINE(utilizationEggress);
fifo_queue<utilization_IngressMessage, 2> utilizationIngressQueue{utilizationIngress};
fifo_queue<utilization_EgressMessage, 2> utilizationEgressQueue{utilizationEggress};
struct StatsAccumulator {
uint32_t total_cpu = 0;
uint32_t total_threads = 0;
uint32_t max_stack_percent = 0;
uint32_t threads_over_stack_threshold = 0;
uint32_t idle_thread_cpu = 0;
uint32_t heap_free_percent = 0; // Optional, left as placeholder
};
// Threshold for stack usage warning (e.g. 80%)
constexpr uint32_t STACK_WARN_THRESHOLD = 80;
// We'll store intermediate values here
static StatsAccumulator stats{};
// Simple utility to calculate percent with safety
static uint32_t percent(uint32_t used, uint32_t total) {
if (total == 0) return 0;
return (used * 100) / total;
}
// Called once per thread by thread_analyzer_run()
extern "C" void mythread_analyzer_cb(struct thread_analyzer_info *info) {
if (!info || info->stack_size == 0) return;
if (strcmp(info->name, "idle") == 0) {
stats.idle_thread_cpu = info->utilization;
return;
}
stats.total_threads++;
uint32_t stack_used = info->stack_size - info->stack_used;
uint32_t stack_used_percent = percent(stack_used, info->stack_size);
if (stack_used_percent > stats.max_stack_percent) {
stats.max_stack_percent = stack_used_percent;
}
if (stack_used_percent >= STACK_WARN_THRESHOLD) {
stats.threads_over_stack_threshold++;
}
stats.total_cpu += info->utilization;
}
// Call after `thread_analyzer_run(mythread_analyzer_cb)`
void finalize_utilization(utilization_UtilizationSummary &summary, utilization_SystemStats &sysstats) {
summary.avg_cpu_percent = percent(stats.total_cpu, stats.total_threads * 100);
summary.max_stack_percent = stats.max_stack_percent;
summary.thread_count = stats.total_threads;
summary.cpu_idle = stats.idle_thread_cpu;
sysstats.cpu_utilization_percent = stats.total_cpu;
sysstats.max_stack_utilization_percent = stats.max_stack_percent;
sysstats.heap_free_percent = stats.heap_free_percent; // Fill from heap stats if available
sysstats.num_threads_over_threshold = stats.threads_over_stack_threshold;
// Reset accumulator if reused
stats = StatsAccumulator{};
}
// void mythread_analyzer_cb(struct thread_analyzer_info *info) {
// ULOG_DEBUG(
// "thread name, memfree, cpu : %s, %d, %d",
// info->name,
// info->stack_size - info->stack_used,
// info->utilization // cpu usage in %
// );
// }
void run_stats_collection() {
thread_analyzer_run(mythread_analyzer_cb, 0);
utilization_UtilizationSummary summary{};
utilization_SystemStats sysstats{};
finalize_utilization(summary, sysstats);
// Now encode with nanopb and send
// pb_encode(..., &summary);
// pb_encode(..., &sysstats);
}
void ThreadAnalyzer::loop() {
while (true) {
std::this_thread::sleep_for(std::chrono::seconds{5});
thread_analyzer_run(mythread_analyzer_cb, 0);
}
}
void ThreadAnalyzerThread::threadMain() {
ThreadAnalyzer ta;
ta.loop();
}
} // namespace rims