lardon3d/src/resource_snapshot.c

222 lines
6.4 KiB
C

#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <lardon3d/resource_snapshot.h>
enum {
PROC_BUFFER_CAPACITY = 32768,
};
static void
set_error(char *message, size_t size, const char *text)
{
if (message && size > 0) {
(void)snprintf(message, size, "%s", text);
}
}
static bool
read_file(const char *path, char *buffer, size_t capacity)
{
int descriptor = open(path, O_RDONLY | O_CLOEXEC);
if (descriptor < 0) {
return false;
}
size_t total = 0;
while (total + 1 < capacity) {
ssize_t count = read(descriptor, buffer + total, capacity - total - 1);
if (count < 0 && errno == EINTR) {
continue;
}
if (count < 0) {
(void)close(descriptor);
return false;
}
if (count == 0) {
break;
}
total += (size_t)count;
}
bool success = close(descriptor) == 0;
buffer[total] = '\0';
return success;
}
static bool
meminfo_bytes(const char *buffer, const char *key, uint64_t *bytes)
{
const char *line = buffer;
size_t key_length = strlen(key);
while (*line) {
if (strncmp(line, key, key_length) == 0 && line[key_length] == ':') {
const char *number = line + key_length + 1;
while (*number == ' ' || *number == '\t') {
++number;
}
errno = 0;
char *end;
unsigned long long kibibytes = strtoull(number, &end, 10);
if (errno != 0 || end == number || kibibytes > UINT64_MAX / 1024) {
return false;
}
while (*end == ' ' || *end == '\t') {
++end;
}
if (strncmp(end, "kB", 2) != 0) {
return false;
}
*bytes = (uint64_t)kibibytes * 1024;
return true;
}
const char *newline = strchr(line, '\n');
if (!newline) {
break;
}
line = newline + 1;
}
return false;
}
static bool
capture_load(Lardon3DResourceSnapshot *snapshot)
{
char buffer[256];
if (!read_file("/proc/loadavg", buffer, sizeof(buffer))) {
return false;
}
return sscanf(
buffer,
"%lf %lf %lf",
&snapshot->cpu_load_1m,
&snapshot->cpu_load_5m,
&snapshot->cpu_load_15m
) == 3;
}
static bool
capture_pressure(const char *path, double *average)
{
char buffer[512];
if (!read_file(path, buffer, sizeof(buffer))) {
return false;
}
return sscanf(buffer, "some avg10=%lf", average) == 1
&& *average >= 0.0 && *average <= 100.0;
}
static bool
vmstat_counter(const char *buffer, const char *key, uint64_t *value)
{
const char *line = buffer;
size_t key_length = strlen(key);
while (*line) {
if (strncmp(line, key, key_length) == 0 && line[key_length] == ' ') {
errno = 0;
char *end;
unsigned long long parsed = strtoull(line + key_length + 1, &end, 10);
if (errno == 0 && end != line + key_length + 1) {
*value = (uint64_t)parsed;
return true;
}
return false;
}
const char *newline = strchr(line, '\n');
if (!newline) break;
line = newline + 1;
}
return false;
}
static void
capture_gpu_memory(
const Lardon3DHardwareProfile *profile,
Lardon3DResourceSnapshot *snapshot
)
{
if (!profile->gpu_available) {
return;
}
if (profile->gpu_uses_shared_memory && !profile->gpu_memory_known) {
snapshot->gpu_memory_available_known = true;
snapshot->gpu_memory_available_bytes = snapshot->memory_available_bytes;
return;
}
if (!profile->gpu_memory_known) {
return;
}
for (unsigned int index = 0; index < 64; ++index) {
char path[256];
int written = snprintf(
path,
sizeof(path),
"/sys/class/drm/card%u/device/mem_info_vram_used",
index
);
if (written < 0 || (size_t)written >= sizeof(path)) {
continue;
}
char buffer[64];
if (!read_file(path, buffer, sizeof(buffer))) {
continue;
}
errno = 0;
char *end;
unsigned long long used = strtoull(buffer, &end, 10);
if (errno == 0 && end != buffer
&& (uint64_t)used <= profile->gpu_memory_total_bytes) {
snapshot->gpu_memory_available_known = true;
snapshot->gpu_memory_available_bytes =
profile->gpu_memory_total_bytes - (uint64_t)used;
return;
}
}
}
bool
lardon3d_resource_snapshot_capture(
const Lardon3DHardwareProfile *profile,
Lardon3DResourceSnapshot *snapshot,
char *error_message,
size_t error_message_size
)
{
set_error(error_message, error_message_size, "");
if (!profile || !snapshot || profile->logical_cpu_count == 0
|| profile->memory_total_bytes == 0) {
set_error(error_message, error_message_size, "Profil matériel invalide.");
return false;
}
*snapshot = (Lardon3DResourceSnapshot) {0};
char buffer[PROC_BUFFER_CAPACITY];
if (!read_file("/proc/meminfo", buffer, sizeof(buffer))
|| !meminfo_bytes(
buffer,
"MemAvailable",
&snapshot->memory_available_bytes
)
|| !meminfo_bytes(buffer, "MemFree", &snapshot->memory_free_bytes)
|| !meminfo_bytes(buffer, "SwapFree", &snapshot->swap_available_bytes)
|| !capture_load(snapshot)
|| clock_gettime(CLOCK_REALTIME, &snapshot->captured_at) != 0) {
set_error(error_message, error_message_size, "Instantané système impossible.");
return false;
}
snapshot->cpu_pressure_known = capture_pressure(
"/proc/pressure/cpu", &snapshot->cpu_pressure_avg10);
snapshot->memory_pressure_known = capture_pressure(
"/proc/pressure/memory", &snapshot->memory_pressure_avg10);
snapshot->io_pressure_known = capture_pressure(
"/proc/pressure/io", &snapshot->io_pressure_avg10);
if (read_file("/proc/vmstat", buffer, sizeof(buffer))) {
snapshot->swap_activity_known = vmstat_counter(
buffer, "pswpin", &snapshot->swap_pages_in)
&& vmstat_counter(buffer, "pswpout", &snapshot->swap_pages_out);
}
capture_gpu_memory(profile, snapshot);
return true;
}