fix(worker): real maxswap/maxtmp detection; feat(scheduler): annotate multi-slot assigned jobs
bug-1554: init_host_attrs() replaces hardcoded placeholder stubs with real
detection -- maxswap from sysinfo()'s totalswap (reusing the same syscall
snapshot already taken for maxmem), maxtmp from a fresh statvfs("/tmp")
total-capacity measurement. No synthetic fallback on detection failure, so
a system with swap genuinely disabled correctly reports 0.
bug-1776: the scheduler simulator's "info worker" assigned-jobs listing now
annotates a job consuming more than one "slots" unit (e.g. "jobid*2"),
using per-host allocation data that was already being walked but discarded.
Falls back to the pre-existing per-job listing when jobs on the same worker
have differing slot counts, since a single suffix on a compressed range
would be ambiguous.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Hb6Edtj4KbS8CrgEWYdA2E
This commit is contained in:
parent
a6b735b6f2
commit
a35f597e06
9 changed files with 660 additions and 28 deletions
|
|
@ -23,7 +23,13 @@ double hardware_monitor_get_total_memory(hardware_monitor_t* monitor);
|
|||
|
||||
double hardware_monitor_get_free_memory(hardware_monitor_t* monitor);
|
||||
|
||||
double hardware_monitor_get_total_swap(hardware_monitor_t* monitor);
|
||||
|
||||
bool hardware_monitor_get_memory_info(int64_t* total_memory,
|
||||
int64_t* free_memory);
|
||||
int64_t* free_memory,
|
||||
int64_t* total_swap);
|
||||
|
||||
// 获取指定路径所在文件系统的总容量(字节)
|
||||
bool hardware_monitor_get_disk_info(const char* path, int64_t* total_bytes);
|
||||
|
||||
#endif // HARDWARE_MONITOR_H
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
#else
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/statvfs.h>
|
||||
#include <sys/sysinfo.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
|
@ -22,6 +23,7 @@ struct hardware_monitor {
|
|||
int last_cpu_cores;
|
||||
double total_memory;
|
||||
double free_memory;
|
||||
double total_swap;
|
||||
time_t last_update_time;
|
||||
};
|
||||
|
||||
|
|
@ -46,6 +48,7 @@ hardware_monitor_t* hardware_monitor_create(const char* acct_file)
|
|||
monitor->last_update_time = 0;
|
||||
monitor->total_memory = 0;
|
||||
monitor->free_memory = 0;
|
||||
monitor->total_swap = 0;
|
||||
|
||||
XCECD_LOG(XCE_LOG_INFO, "Hardware monitor created.");
|
||||
return monitor;
|
||||
|
|
@ -83,10 +86,12 @@ bool hardware_monitor_update(hardware_monitor_t* monitor)
|
|||
monitor->last_cpu_cores = current_cores;
|
||||
}
|
||||
|
||||
int64_t total_memory, free_memory;
|
||||
if (hardware_monitor_get_memory_info(&total_memory, &free_memory)) {
|
||||
int64_t total_memory, free_memory, total_swap;
|
||||
if (hardware_monitor_get_memory_info(&total_memory, &free_memory,
|
||||
&total_swap)) {
|
||||
monitor->total_memory = total_memory;
|
||||
monitor->free_memory = free_memory;
|
||||
monitor->total_swap = total_swap;
|
||||
}
|
||||
|
||||
if (hwloc_available) {
|
||||
|
|
@ -107,9 +112,10 @@ int hardware_monitor_get_cpu_cores(hardware_monitor_t* monitor)
|
|||
|
||||
#ifdef _WIN32
|
||||
bool hardware_monitor_get_memory_info(int64_t* total_memory,
|
||||
int64_t* free_memory)
|
||||
int64_t* free_memory,
|
||||
int64_t* total_swap)
|
||||
{
|
||||
if (!total_memory || !free_memory) {
|
||||
if (!total_memory || !free_memory || !total_swap) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -123,14 +129,42 @@ bool hardware_monitor_get_memory_info(int64_t* total_memory,
|
|||
|
||||
*total_memory = status.ullTotalPhys;
|
||||
*free_memory = status.ullAvailPhys;
|
||||
/* ullTotalPageFile is RAM + page file(s) combined (the commit limit),
|
||||
* not the page file alone -- subtracting ullTotalPhys approximates the
|
||||
* page-file-only (i.e. "swap") portion, same convention sysinfo's
|
||||
* totalram/totalswap split gives natively on Linux below. */
|
||||
*total_swap = (status.ullTotalPageFile > status.ullTotalPhys)
|
||||
? (int64_t)(status.ullTotalPageFile
|
||||
- status.ullTotalPhys)
|
||||
: 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool hardware_monitor_get_disk_info(const char* path, int64_t* total_bytes)
|
||||
{
|
||||
ULARGE_INTEGER total;
|
||||
|
||||
if (!path || !total_bytes) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!GetDiskFreeSpaceExA(path, NULL, &total, NULL)) {
|
||||
XCECD_LOG(XCE_LOG_ERROR, "Failed to get disk info for '%s': %d", path,
|
||||
GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
*total_bytes = (int64_t)total.QuadPart;
|
||||
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
bool hardware_monitor_get_memory_info(int64_t* total_memory,
|
||||
int64_t* free_memory)
|
||||
int64_t* free_memory,
|
||||
int64_t* total_swap)
|
||||
{
|
||||
if (!total_memory || !free_memory) {
|
||||
if (!total_memory || !free_memory || !total_swap) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -158,6 +192,26 @@ bool hardware_monitor_get_memory_info(int64_t* total_memory,
|
|||
}
|
||||
|
||||
*free_memory = (int64_t)info.freeram * info.mem_unit;
|
||||
*total_swap = (int64_t)info.totalswap * info.mem_unit;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool hardware_monitor_get_disk_info(const char* path, int64_t* total_bytes)
|
||||
{
|
||||
struct statvfs fs_stat;
|
||||
|
||||
if (!path || !total_bytes) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (statvfs(path, &fs_stat) != 0) {
|
||||
XCECD_LOG(XCE_LOG_ERROR, "Failed to get filesystem info for '%s': %s",
|
||||
path, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
*total_bytes = (int64_t)fs_stat.f_blocks * (int64_t)fs_stat.f_frsize;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -177,4 +231,12 @@ double hardware_monitor_get_free_memory(hardware_monitor_t* monitor)
|
|||
return 0;
|
||||
}
|
||||
return monitor->free_memory;
|
||||
}
|
||||
|
||||
double hardware_monitor_get_total_swap(hardware_monitor_t* monitor)
|
||||
{
|
||||
if (!monitor) {
|
||||
return 0;
|
||||
}
|
||||
return monitor->total_swap;
|
||||
}
|
||||
|
|
@ -341,6 +341,18 @@ controller_connector_init(xcecd_context_t* ctx)
|
|||
|
||||
static void init_host_attrs(void)
|
||||
{
|
||||
/* bug-1554: maxswap/maxtmp real detection. maxswap comes from the same
|
||||
* sysinfo() snapshot hardware_monitor_update() already took for
|
||||
* maxmem (hardware_monitor.c); maxtmp is a fresh statvfs("/tmp")
|
||||
* measurement, same path and same total-vs-free distinction
|
||||
* node_information.c's node_info_get_tmp() already established for the
|
||||
* dynamic "tmp" load index (that one reports free space; this one
|
||||
* reports total capacity). Left at 0 (matching maxcpus/maxmem's own
|
||||
* existing behavior) if detection is unavailable -- no synthetic
|
||||
* fallback, since a real system with swap genuinely disabled reports 0
|
||||
* too and a fabricated non-zero number would misrepresent that. */
|
||||
int64_t tmp_total_bytes = 0;
|
||||
|
||||
host_attrs.cpufactor = 1.0;
|
||||
|
||||
if (xcecd_ctx.hardware_monitor) {
|
||||
|
|
@ -348,15 +360,14 @@ static void init_host_attrs(void)
|
|||
= hardware_monitor_get_cpu_cores(xcecd_ctx.hardware_monitor);
|
||||
host_attrs.maxmem
|
||||
= hardware_monitor_get_total_memory(xcecd_ctx.hardware_monitor);
|
||||
host_attrs.maxswap
|
||||
= hardware_monitor_get_total_swap(xcecd_ctx.hardware_monitor);
|
||||
}
|
||||
|
||||
/* Placeholder stub values (bug-1554, real swap/tmp-disk detection not
|
||||
* yet implemented) -- kept as bytes to match every other memory-scale
|
||||
* host attribute's canonical unit (maxmem, mem, tmp). Magnitude chosen
|
||||
* to match the pre-existing MB-scale placeholders (1024MB/2048MB). */
|
||||
host_attrs.maxswap = 1024.0 * 1024.0 * 1024.0;
|
||||
host_attrs.maxtmp = 2048.0 * 1024.0 * 1024.0;
|
||||
host_attrs.ndisks = 1;
|
||||
if (hardware_monitor_get_disk_info("/tmp", &tmp_total_bytes)) {
|
||||
host_attrs.maxtmp = (double)tmp_total_bytes;
|
||||
}
|
||||
host_attrs.ndisks = 1;
|
||||
|
||||
host_attrs.hostmodel = strdup("Intel64");
|
||||
host_attrs.hosttype = strdup("LINUX64");
|
||||
|
|
|
|||
|
|
@ -41,6 +41,20 @@ test('worker resource_sync test', resource_sync_test,
|
|||
timeout: 60,
|
||||
env: ['ASAN_OPTIONS=detect_leaks=0'])
|
||||
|
||||
# bug-1554: real maxswap (sysinfo() totalswap)/maxtmp (statvfs("/tmp"))
|
||||
# detection -- white-box #include of hardware_monitor.c, same convention as
|
||||
# resource_sync_test above.
|
||||
hardware_monitor_test = executable('hardware_monitor_test',
|
||||
'test_hardware_monitor.c',
|
||||
include_directories: [worker_test_incdir],
|
||||
dependencies: worker_test_deps,
|
||||
install: false)
|
||||
|
||||
test('worker hardware_monitor test', hardware_monitor_test,
|
||||
suite: 'worker',
|
||||
timeout: 60,
|
||||
env: ['ASAN_OPTIONS=detect_leaks=0'])
|
||||
|
||||
# component-failover-adoption task 2.2: worker startup-ordering white-box
|
||||
# test. #include's xcecd.c directly (same technique as controller_connector_
|
||||
# test above, but a separate binary/file -- xcecd.c and controller_connector.c
|
||||
|
|
|
|||
151
src/worker/testsuite/test_hardware_monitor.c
Normal file
151
src/worker/testsuite/test_hardware_monitor.c
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
* test_hardware_monitor.c - bug-1554: maxswap/maxtmp used to be hardcoded
|
||||
* placeholder stubs in xcecd.c's init_host_attrs() (real detection was never
|
||||
* implemented). This white-box #includes the real hardware_monitor.c so
|
||||
* these tests exercise the actual sysinfo()/statvfs() detection logic
|
||||
* against this machine's real values, cross-checking each function's output
|
||||
* against an independent syscall made directly in the test -- not mocks,
|
||||
* since the whole point is proving the real detection is wired up and
|
||||
* arithmetically correct, matching resource_sync_test's own white-box
|
||||
* #include convention for this file family.
|
||||
*/
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/statvfs.h>
|
||||
#include <sys/sysinfo.h>
|
||||
|
||||
#include <unity/unity.h>
|
||||
|
||||
#include "../src/hardware_monitor.c"
|
||||
|
||||
void setUp(void) { return; }
|
||||
void tearDown(void) { return; }
|
||||
|
||||
/* ────────────────────────────────────────────────────────────────────────
|
||||
* hardware_monitor_get_memory_info() -- total_swap output param
|
||||
* ──────────────────────────────────────────────────────────────────────── */
|
||||
|
||||
static void test_get_memory_info_reports_total_swap_matching_sysinfo(void)
|
||||
{
|
||||
int64_t total_memory = -1;
|
||||
int64_t free_memory = -1;
|
||||
int64_t total_swap = -1;
|
||||
struct sysinfo expected;
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0, sysinfo(&expected));
|
||||
|
||||
TEST_ASSERT_TRUE(
|
||||
hardware_monitor_get_memory_info(&total_memory, &free_memory,
|
||||
&total_swap));
|
||||
|
||||
TEST_ASSERT_TRUE(total_swap >= 0);
|
||||
TEST_ASSERT_EQUAL_INT64(
|
||||
(int64_t)expected.totalswap * expected.mem_unit, total_swap);
|
||||
|
||||
printf("test_get_memory_info_reports_total_swap_matching_sysinfo: "
|
||||
"PASS\n");
|
||||
}
|
||||
|
||||
static void test_get_memory_info_null_total_swap_returns_false(void)
|
||||
{
|
||||
int64_t total_memory = -1;
|
||||
int64_t free_memory = -1;
|
||||
|
||||
TEST_ASSERT_FALSE(
|
||||
hardware_monitor_get_memory_info(&total_memory, &free_memory, NULL));
|
||||
|
||||
printf("test_get_memory_info_null_total_swap_returns_false: PASS\n");
|
||||
}
|
||||
|
||||
/* ────────────────────────────────────────────────────────────────────────
|
||||
* hardware_monitor_update()/hardware_monitor_get_total_swap() -- the
|
||||
* monitor-cached accessor init_host_attrs() actually calls.
|
||||
* ──────────────────────────────────────────────────────────────────────── */
|
||||
|
||||
static void test_update_populates_monitor_total_swap(void)
|
||||
{
|
||||
hardware_monitor_t* monitor = hardware_monitor_create("/tmp/acct");
|
||||
int64_t total_memory, free_memory, total_swap;
|
||||
|
||||
TEST_ASSERT_NOT_NULL(monitor);
|
||||
TEST_ASSERT_TRUE(hardware_monitor_update(monitor));
|
||||
|
||||
TEST_ASSERT_TRUE(
|
||||
hardware_monitor_get_memory_info(&total_memory, &free_memory,
|
||||
&total_swap));
|
||||
TEST_ASSERT_EQUAL_FLOAT((float)total_swap,
|
||||
(float)hardware_monitor_get_total_swap(monitor));
|
||||
|
||||
hardware_monitor_destroy(monitor);
|
||||
|
||||
printf("test_update_populates_monitor_total_swap: PASS\n");
|
||||
}
|
||||
|
||||
static void test_get_total_swap_null_monitor_returns_zero(void)
|
||||
{
|
||||
TEST_ASSERT_EQUAL_FLOAT(0.0f, (float)hardware_monitor_get_total_swap(NULL));
|
||||
|
||||
printf("test_get_total_swap_null_monitor_returns_zero: PASS\n");
|
||||
}
|
||||
|
||||
/* ────────────────────────────────────────────────────────────────────────
|
||||
* hardware_monitor_get_disk_info() -- real statvfs()-based capacity
|
||||
* detection for maxtmp (mirrors node_information.c's node_info_get_tmp()
|
||||
* own statvfs("/tmp") convention, but reports total capacity, not free
|
||||
* space).
|
||||
* ──────────────────────────────────────────────────────────────────────── */
|
||||
|
||||
static void test_get_disk_info_tmp_matches_statvfs(void)
|
||||
{
|
||||
int64_t total_bytes = -1;
|
||||
struct statvfs expected;
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0, statvfs("/tmp", &expected));
|
||||
|
||||
TEST_ASSERT_TRUE(hardware_monitor_get_disk_info("/tmp", &total_bytes));
|
||||
|
||||
TEST_ASSERT_EQUAL_INT64(
|
||||
(int64_t)expected.f_blocks * (int64_t)expected.f_frsize,
|
||||
total_bytes);
|
||||
|
||||
printf("test_get_disk_info_tmp_matches_statvfs: PASS\n");
|
||||
}
|
||||
|
||||
static void test_get_disk_info_null_args_return_false(void)
|
||||
{
|
||||
int64_t total_bytes = -1;
|
||||
|
||||
TEST_ASSERT_FALSE(hardware_monitor_get_disk_info(NULL, &total_bytes));
|
||||
TEST_ASSERT_FALSE(hardware_monitor_get_disk_info("/tmp", NULL));
|
||||
|
||||
printf("test_get_disk_info_null_args_return_false: PASS\n");
|
||||
}
|
||||
|
||||
static void test_get_disk_info_nonexistent_path_returns_false(void)
|
||||
{
|
||||
int64_t total_bytes = -1;
|
||||
|
||||
TEST_ASSERT_FALSE(hardware_monitor_get_disk_info(
|
||||
"/this/path/does/not/exist/xce-bug-1554", &total_bytes));
|
||||
|
||||
printf("test_get_disk_info_nonexistent_path_returns_false: PASS\n");
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
UNITY_BEGIN();
|
||||
|
||||
RUN_TEST(test_get_memory_info_reports_total_swap_matching_sysinfo);
|
||||
RUN_TEST(test_get_memory_info_null_total_swap_returns_false);
|
||||
RUN_TEST(test_update_populates_monitor_total_swap);
|
||||
RUN_TEST(test_get_total_swap_null_monitor_returns_zero);
|
||||
RUN_TEST(test_get_disk_info_tmp_matches_statvfs);
|
||||
RUN_TEST(test_get_disk_info_null_args_return_false);
|
||||
RUN_TEST(test_get_disk_info_nonexistent_path_returns_false);
|
||||
|
||||
return UNITY_END();
|
||||
}
|
||||
|
|
@ -125,6 +125,17 @@ double hardware_monitor_get_total_memory(hardware_monitor_t* monitor)
|
|||
(void)monitor;
|
||||
return 16384.0;
|
||||
}
|
||||
double hardware_monitor_get_total_swap(hardware_monitor_t* monitor)
|
||||
{
|
||||
(void)monitor;
|
||||
return 4096.0;
|
||||
}
|
||||
bool hardware_monitor_get_disk_info(const char* path, int64_t* total_bytes)
|
||||
{
|
||||
(void)path;
|
||||
*total_bytes = 8192;
|
||||
return true;
|
||||
}
|
||||
|
||||
network_handler_t* network_handler_create(uint16_t port)
|
||||
{
|
||||
|
|
@ -358,6 +369,29 @@ static void test_no_peers_cluster_cfg_stays_empty_before_first_join(void)
|
|||
"test_no_peers_cluster_cfg_stays_empty_before_first_join: PASS\n");
|
||||
}
|
||||
|
||||
/* ════════════════════════════════════════════════════════════════════════
|
||||
* bug-1554: init_host_attrs() must source maxswap/maxtmp from real
|
||||
* detection (hardware_monitor_get_total_swap()/hardware_monitor_get_disk_
|
||||
* info(), both stubbed above) instead of the old hardcoded placeholder
|
||||
* constants (1024MB/2048MB) -- this test would have kept passing under the
|
||||
* old stub-constant code too (it doesn't assert the old magic numbers are
|
||||
* ABSENT), but it does pin that host_attrs picks up whatever these two
|
||||
* functions report, which the placeholder code structurally could not do.
|
||||
* ════════════════════════════════════════════════════════════════════════ */
|
||||
|
||||
static void test_init_host_attrs_uses_real_swap_and_tmp_detection(void)
|
||||
{
|
||||
xcecd_config_t config = base_test_config();
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0, xcecd_init(&config));
|
||||
TEST_ASSERT_EQUAL_INT(0, xcecd_start());
|
||||
|
||||
TEST_ASSERT_EQUAL_FLOAT(4096.0f, (float)host_attrs.maxswap);
|
||||
TEST_ASSERT_EQUAL_FLOAT(8192.0f, (float)host_attrs.maxtmp);
|
||||
|
||||
printf("test_init_host_attrs_uses_real_swap_and_tmp_detection: PASS\n");
|
||||
}
|
||||
|
||||
/* ════════════════════════════════════════════════════════════════════════
|
||||
* main
|
||||
* ════════════════════════════════════════════════════════════════════════ */
|
||||
|
|
@ -370,6 +404,7 @@ int main(void)
|
|||
|
||||
RUN_TEST(test_multi_candidate_cluster_cfg_populated_before_first_join);
|
||||
RUN_TEST(test_no_peers_cluster_cfg_stays_empty_before_first_join);
|
||||
RUN_TEST(test_init_host_attrs_uses_real_swap_and_tmp_detection);
|
||||
|
||||
return UNITY_END();
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue