Complete the A-to-Z Lardon3D maintenance and coherence pass. Generalize host resource policy, remove the global CPU12 ceiling, preserve host CPU/RAM reserves, scale Task capabilities through the Resource Governor, and validate deterministic parallel GV execution. Migrate Project DB to v23 with data-driven camera, lens, optical configuration and calibration profiles, including manual lenses without EXIF. Integrate safe optional LARDON SSD swap/scratch control with Governor and F10 drain/safe-to-unplug semantics. Refactor the ncurses TUI into a runtime observatory with durable progress, elapsed time, smoothed ETA, throughput, resource telemetry, Governor state, optics workflow, colors and compact/no-color fallbacks. Reconcile Queue lifetime, persistence, concurrency, comments, tests, README, AGENTS and canonical documentation. GLOBAL_MAINTENANCE_AUDIT=PASS/FROZEN
316 lines
9.1 KiB
C
316 lines
9.1 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>
|
|
|
|
#include "resource_snapshot_internal.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
|
|
read_exact_decimal_u64(const char *path, uint64_t *value)
|
|
{
|
|
if (!path || !value) return false;
|
|
int descriptor = open(path, O_RDONLY | O_CLOEXEC | O_NOFOLLOW);
|
|
if (descriptor < 0) return false;
|
|
char buffer[64];
|
|
size_t total = 0;
|
|
bool success = true;
|
|
while (total + 1 < sizeof(buffer)) {
|
|
ssize_t count = read(descriptor, buffer + total,
|
|
sizeof(buffer) - total - 1);
|
|
if (count < 0 && errno == EINTR) continue;
|
|
if (count < 0) {
|
|
success = false;
|
|
break;
|
|
}
|
|
if (count == 0) break;
|
|
total += (size_t)count;
|
|
}
|
|
if (success && total + 1 == sizeof(buffer)) {
|
|
char extra;
|
|
ssize_t count;
|
|
do {
|
|
count = read(descriptor, &extra, 1);
|
|
} while (count < 0 && errno == EINTR);
|
|
success = count == 0;
|
|
}
|
|
if (close(descriptor) != 0) success = false;
|
|
if (!success || total == 0) return false;
|
|
buffer[total] = '\0';
|
|
const unsigned char *cursor = (const unsigned char *)buffer;
|
|
uint64_t parsed = 0;
|
|
size_t digits = 0;
|
|
while (*cursor >= '0' && *cursor <= '9') {
|
|
uint64_t digit = (uint64_t)(*cursor - '0');
|
|
if (parsed > (UINT64_MAX - digit) / 10U) return false;
|
|
parsed = parsed * 10U + digit;
|
|
++cursor;
|
|
++digits;
|
|
}
|
|
if (digits == 0) return false;
|
|
while (*cursor == ' ' || *cursor == '\t' || *cursor == '\n'
|
|
|| *cursor == '\r' || *cursor == '\f' || *cursor == '\v') {
|
|
++cursor;
|
|
}
|
|
if (*cursor) return false;
|
|
*value = parsed;
|
|
return true;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
void
|
|
lardon3d_resource_snapshot_capture_gpu_at_root(
|
|
const Lardon3DHardwareProfile *profile,
|
|
Lardon3DResourceSnapshot *snapshot,
|
|
const char *drm_root
|
|
)
|
|
{
|
|
if (!profile || !snapshot || !drm_root || !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;
|
|
}
|
|
char path[256];
|
|
int written = snprintf(
|
|
path,
|
|
sizeof(path),
|
|
"%s/card%u/device/mem_info_vram_used",
|
|
drm_root,
|
|
profile->gpu_drm_card_index
|
|
);
|
|
if (written < 0 || (size_t)written >= sizeof(path)) {
|
|
return;
|
|
}
|
|
uint64_t used;
|
|
if (read_exact_decimal_u64(path, &used)
|
|
&& used <= profile->gpu_memory_total_bytes) {
|
|
snapshot->gpu_memory_available_known = true;
|
|
snapshot->gpu_memory_available_bytes =
|
|
profile->gpu_memory_total_bytes - used;
|
|
}
|
|
}
|
|
|
|
static bool
|
|
capture_resource_snapshot(
|
|
const Lardon3DHardwareProfile *profile,
|
|
Lardon3DResourceSnapshot *snapshot,
|
|
uint64_t *swap_total_bytes,
|
|
char *error_message,
|
|
size_t error_message_size
|
|
)
|
|
{
|
|
set_error(error_message, error_message_size, "");
|
|
if (!profile || !snapshot || !swap_total_bytes
|
|
|| 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, "SwapTotal", swap_total_bytes)
|
|
|| !meminfo_bytes(buffer, "SwapFree", &snapshot->swap_available_bytes)
|
|
|| !capture_load(snapshot)
|
|
|| clock_gettime(CLOCK_MONOTONIC, &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);
|
|
}
|
|
lardon3d_resource_snapshot_capture_gpu_at_root(
|
|
profile,
|
|
snapshot,
|
|
"/sys/class/drm"
|
|
);
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
lardon3d_resource_snapshot_capture(
|
|
const Lardon3DHardwareProfile *profile,
|
|
Lardon3DResourceSnapshot *snapshot,
|
|
char *error_message,
|
|
size_t error_message_size
|
|
)
|
|
{
|
|
uint64_t ignored_swap_total = 0;
|
|
/* CONTRACT: this historical entry point writes exactly the frozen
|
|
* ResourceSnapshot size; extended telemetry must use observation_capture.
|
|
*/
|
|
return capture_resource_snapshot(profile, snapshot, &ignored_swap_total,
|
|
error_message, error_message_size);
|
|
}
|
|
|
|
bool
|
|
lardon3d_resource_observation_capture(
|
|
const Lardon3DHardwareProfile *profile,
|
|
Lardon3DResourceObservation *observation,
|
|
char *error_message,
|
|
size_t error_message_size
|
|
)
|
|
{
|
|
if (observation) {
|
|
*observation = (Lardon3DResourceObservation) {0};
|
|
}
|
|
if (!observation) {
|
|
set_error(error_message, error_message_size,
|
|
"Instantané système invalide.");
|
|
return false;
|
|
}
|
|
if (!capture_resource_snapshot(profile, &observation->snapshot,
|
|
&observation->swap_total_bytes, error_message,
|
|
error_message_size)) {
|
|
return false;
|
|
}
|
|
observation->swap_total_known = true;
|
|
return true;
|
|
}
|