221 lines
6.5 KiB
C
221 lines
6.5 KiB
C
/**
|
|
* @file lv_linux.c
|
|
*
|
|
*/
|
|
|
|
/*********************
|
|
* INCLUDES
|
|
*********************/
|
|
|
|
#include "lv_os_private.h"
|
|
|
|
#if defined(__linux__)
|
|
|
|
#include "../core/lv_global.h"
|
|
#include "../misc/lv_log.h"
|
|
#include "lv_linux.h"
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
/*********************
|
|
* DEFINES
|
|
*********************/
|
|
|
|
#define LV_UPTIME_MONITOR_FILE "/proc/stat"
|
|
#define LV_UPTIME_MONITOR_SELF_FILE "/proc/self/stat"
|
|
|
|
#define LV_PROC_STAT_VAR_FORMAT " %" PRIu32
|
|
#define LV_PROC_STAT_IGNORE_VAR_FORMAT " %*" PRIu32
|
|
|
|
#define last_proc_stat LV_GLOBAL_DEFAULT()->linux_last_proc_stat
|
|
#if LV_SYSMON_PROC_IDLE_AVAILABLE
|
|
#define last_self_ticks LV_GLOBAL_DEFAULT()->linux_last_self_proc_time_ticks
|
|
#define last_system_total_ticks_stat LV_GLOBAL_DEFAULT()->linux_last_system_total_ticks_stat
|
|
#endif
|
|
|
|
#define LV_SELF_PROC_STAT_BUFFER_SIZE 512
|
|
/**********************
|
|
* TYPEDEFS
|
|
**********************/
|
|
|
|
/**********************
|
|
* STATIC PROTOTYPES
|
|
**********************/
|
|
|
|
static lv_result_t lv_read_proc_stat(lv_linux_proc_stat_t * result);
|
|
static uint32_t lv_proc_stat_get_total(const lv_linux_proc_stat_t * p);
|
|
|
|
/**********************
|
|
* STATIC VARIABLES
|
|
**********************/
|
|
|
|
/**********************
|
|
* MACROS
|
|
**********************/
|
|
|
|
/**********************
|
|
* GLOBAL FUNCTIONS
|
|
**********************/
|
|
|
|
uint32_t lv_os_get_idle_percent(void)
|
|
{
|
|
lv_linux_proc_stat_t proc_stat;
|
|
{
|
|
lv_result_t err = lv_read_proc_stat(&proc_stat);
|
|
|
|
if(err == LV_RESULT_INVALID) {
|
|
return UINT32_MAX;
|
|
}
|
|
|
|
for(size_t i = 0; i < LV_PROC_STAT_PARAMS_LEN; ++i) {
|
|
uint32_t delta =
|
|
proc_stat.buffer[i] - last_proc_stat.buffer[i];
|
|
/* Update old for next call*/
|
|
last_proc_stat.buffer[i] = proc_stat.buffer[i];
|
|
/* Store delta in new */
|
|
proc_stat.buffer[i] = delta;
|
|
}
|
|
}
|
|
|
|
/* From here onwards, there's no risk of overflowing as long as we call this function regularly */
|
|
const uint32_t total = lv_proc_stat_get_total(&proc_stat);
|
|
|
|
if(total == 0) {
|
|
return 0;
|
|
}
|
|
|
|
return (proc_stat.fields.idle * 100) / total;
|
|
}
|
|
|
|
#if LV_SYSMON_PROC_IDLE_AVAILABLE
|
|
|
|
uint32_t lv_os_get_proc_idle_percent(void)
|
|
{
|
|
uint64_t self_current_time_ticks = 0;
|
|
lv_linux_proc_stat_t stat_current_system_total_ticks;
|
|
lv_linux_proc_stat_t stat_delta_system_ticks;
|
|
|
|
FILE * self = fopen(LV_UPTIME_MONITOR_SELF_FILE, "r");
|
|
if(!self) {
|
|
LV_LOG_ERROR("Failed to open " LV_UPTIME_MONITOR_SELF_FILE);
|
|
return UINT32_MAX;
|
|
}
|
|
|
|
char self_stat_buffer[LV_SELF_PROC_STAT_BUFFER_SIZE];
|
|
|
|
if(!fgets(self_stat_buffer, sizeof(self_stat_buffer), self)) {
|
|
fclose(self);
|
|
LV_LOG_ERROR("Failed to read " LV_UPTIME_MONITOR_SELF_FILE);
|
|
return UINT32_MAX;
|
|
}
|
|
|
|
fclose(self);
|
|
|
|
/* The comm field can contain spaces and parentheses, so we find the last ')'
|
|
* Skip the whitespace after finding the last ')' */
|
|
char * p = strrchr(self_stat_buffer, ')');
|
|
if(!p) {
|
|
LV_LOG_ERROR(LV_UPTIME_MONITOR_SELF_FILE " is missing the closing ')'");
|
|
return UINT32_MAX;
|
|
}
|
|
p++; /* move past the ')' */
|
|
while(*p && (*p == ' ' || *p == '\t'))
|
|
p++; /* skip whitespace after ')' */
|
|
if(!*p) {
|
|
LV_LOG_ERROR(LV_UPTIME_MONITOR_SELF_FILE " unexpectedly ends after the closing ')'");
|
|
return UINT32_MAX;
|
|
}
|
|
|
|
uint64_t utime = 0;
|
|
uint64_t stime = 0;
|
|
|
|
int scanned_items = sscanf(p,
|
|
"%*c " // state (field 3)
|
|
"%*d %*d %*d %*d %*d " // ppid, pgrp, session, tty_nr, tpgid (fields 4-8)
|
|
"%*u " // flags (field 9)
|
|
"%*u %*u %*u %*u " // minflt, cminflt, majflt, cmajflt (fields 10-13)
|
|
"%" SCNu64 " %" SCNu64, // utime, stime (fields 14-15)
|
|
&utime, &stime);
|
|
|
|
if(scanned_items != 2) {
|
|
LV_LOG_ERROR("Failed to parse utime/stime");
|
|
return UINT32_MAX;
|
|
}
|
|
|
|
self_current_time_ticks = utime + stime;
|
|
|
|
if(lv_read_proc_stat(&stat_current_system_total_ticks) != LV_RESULT_OK) {
|
|
LV_LOG_ERROR("lv_read_proc_stat failed");
|
|
return UINT32_MAX;
|
|
}
|
|
|
|
/* no delta on the first call so return 0, next call will have actual values*/
|
|
if(last_self_ticks == 0) {
|
|
last_self_ticks = self_current_time_ticks;
|
|
last_system_total_ticks_stat = stat_current_system_total_ticks;
|
|
|
|
return 100; /* 100% idle = 0% CPU usage*/
|
|
}
|
|
|
|
uint64_t delta_self_proc_ticks = self_current_time_ticks - last_self_ticks;
|
|
|
|
for(size_t i = 0; i < LV_PROC_STAT_PARAMS_LEN; ++i) {
|
|
stat_delta_system_ticks.buffer[i] = stat_current_system_total_ticks.buffer[i] - last_system_total_ticks_stat.buffer[i];
|
|
}
|
|
|
|
uint64_t delta_total_system_ticks = lv_proc_stat_get_total(&stat_delta_system_ticks);
|
|
|
|
last_self_ticks = self_current_time_ticks;
|
|
last_system_total_ticks_stat = stat_current_system_total_ticks;
|
|
|
|
if(delta_total_system_ticks == 0) return 100;
|
|
|
|
uint32_t cpu_usage_percent = (uint32_t)((delta_self_proc_ticks * 100ULL) / delta_total_system_ticks);
|
|
|
|
return 100 - cpu_usage_percent;
|
|
}
|
|
|
|
#endif /*LV_SYSMON_PROC_IDLE_AVAILABLE*/
|
|
|
|
/**********************
|
|
* STATIC FUNCTIONS
|
|
**********************/
|
|
|
|
static lv_result_t lv_read_proc_stat(lv_linux_proc_stat_t * result)
|
|
{
|
|
FILE * fp = fopen(LV_UPTIME_MONITOR_FILE, "r");
|
|
|
|
if(!fp) {
|
|
LV_LOG_ERROR("Failed to open " LV_UPTIME_MONITOR_FILE);
|
|
return LV_RESULT_INVALID;
|
|
}
|
|
const char * fmt = "cpu " LV_PROC_STAT_VAR_FORMAT LV_PROC_STAT_VAR_FORMAT
|
|
LV_PROC_STAT_VAR_FORMAT LV_PROC_STAT_VAR_FORMAT
|
|
LV_PROC_STAT_IGNORE_VAR_FORMAT LV_PROC_STAT_VAR_FORMAT
|
|
LV_PROC_STAT_VAR_FORMAT LV_PROC_STAT_VAR_FORMAT;
|
|
|
|
int err = fscanf(fp, fmt, &result->fields.user, &result->fields.nice,
|
|
&result->fields.system, &result->fields.idle,
|
|
&result->fields.irq, &result->fields.softirq,
|
|
&result->fields.steal);
|
|
|
|
fclose(fp);
|
|
|
|
if(err != LV_PROC_STAT_PARAMS_LEN) {
|
|
LV_LOG_ERROR("Failed to parse " LV_UPTIME_MONITOR_FILE);
|
|
return LV_RESULT_INVALID;
|
|
}
|
|
return LV_RESULT_OK;
|
|
}
|
|
|
|
static uint32_t lv_proc_stat_get_total(const lv_linux_proc_stat_t * p)
|
|
{
|
|
uint32_t sum = 0;
|
|
for(size_t i = 0; i < LV_PROC_STAT_PARAMS_LEN; ++i) {
|
|
sum += p->buffer[i];
|
|
}
|
|
return sum;
|
|
}
|
|
|
|
#endif
|