Files
vlibc/tests/test_resource.c
T

257 lines
8.7 KiB
C

/*
* vlibc — sys/resource.h test (todo 31).
*
* Exercises the whole <sys/resource.h> surface end to end:
*
* 1. getrlimit(RLIMIT_NOFILE): returns 0 with a finite soft limit in
* (0, max] — the kernel's own NOFILE ceiling, so cur is never
* RLIM_INFINITY. getrlimit(RLIMIT_STACK) is the QA happy shape and
* must also report a positive soft limit.
* 2. setrlimit round-trip: re-installing the just-read RLIMIT_NOFILE
* pair must succeed without privilege (identical values never raise
* the hard limit), and a following getrlimit must reflect it.
* 3. getrlimit64/setrlimit64 parity: the LFS aliases return the same
* values and accept the same writes as the base functions.
* 4. getrusage(RUSAGE_SELF) and RUSAGE_THREAD: ru_utime is a sane
* timeval and ru_maxrss is non-negative.
* 5. getpriority(PRIO_PROCESS, 0) lands in PRIO_MIN..PRIO_MAX, and
* setpriority to the same value round-trips (equal values are never
* a denied raise).
* 6. nice(0) returns the current nice value: no change, no error.
* 7. Constant sanity: RLIM_INFINITY is ~0UL, RLIMIT_NLIMITS is 16 and
* PRIO_MIN/PRIO_MAX bound the range [-20, 19].
*
* The -f mode runs the failure shapes — setrlimit/getrusage/getpriority/
* setpriority with a bogus resource kind or `which` — asserting return
* values only (errno is never read: vlibc's errno slot collides with the
* host TCB). It leaves via a raw SYS_exit_group so no host cleanup runs
* after the library's errno writes (house pattern, tests/test_systime.c).
*
* No host libc headers are included (the -Iinclude path would shadow
* GCC's internal headers); diagnostics go through raw SYS_write. The whole
* body mirrors the header's gate: <sys/resource.h> is entirely level 2, so
* at level 1 this TU compiles to a no-op runner.
*
* Standalone build (list the src/resource/ files individually, or the
* shell glob would trip -Wcomment in this header block):
* gcc -Iinclude -DVLIBC_LEVEL=2 -std=c23 -Wall -Wextra -pedantic -O2 \
* -o /tmp/t31 tests/test_resource.c \
* src/resource/getrlimit.c src/resource/setrlimit.c \
* src/resource/getrusage.c src/resource/getpriority.c \
* src/resource/setpriority.c src/resource/nice.c \
* src/internal/errno.c src/internal/syscall_ret.c
*/
#include <sys/resource.h>
#include "../src/internal/syscall.h"
/* Write a NUL-terminated string to fd via the raw syscall layer. The
* optimize attribute keeps GCC from lowering the length loop into a
* strlen call, which would leave a vlibc-owned symbol undefined in this
* host-linked standalone binary (house idiom, see src/string). */
static __attribute__((optimize("no-tree-loop-distribute-patterns"))) void
say(int fd, const char *s)
{
long n = 0;
while (s[n] != '\0')
{
n++;
}
__syscall3(SYS_write, fd, (long)s, n);
}
#if VLIBC_LEVEL_GE(2)
static int failures;
static void
check(int ok, const char *msg)
{
if (ok)
{
say(1, "ok ");
}
else
{
say(1, "FAIL ");
failures++;
}
say(1, msg);
say(1, "\n");
}
/* 1. getrlimit reads sane, finite NOFILE and STACK soft limits. */
static void
rlimit_read_scenario(void)
{
struct rlimit rl;
check(getrlimit(RLIMIT_NOFILE, &rl) == 0, "getrlimit(RLIMIT_NOFILE) returns 0");
check(rl.rlim_cur > 0, "getrlimit NOFILE rlim_cur is positive");
check(rl.rlim_max > 0 && rl.rlim_max >= rl.rlim_cur,
"getrlimit NOFILE rlim_max is positive and at least rlim_cur");
check(getrlimit(RLIMIT_STACK, &rl) == 0, "getrlimit(RLIMIT_STACK) returns 0");
check(rl.rlim_cur > 0, "getrlimit STACK rlim_cur is positive");
}
/* 2. Re-installing the same limit pair succeeds and round-trips. */
static void
setrlimit_roundtrip_scenario(void)
{
struct rlimit before;
struct rlimit after;
if (getrlimit(RLIMIT_NOFILE, &before) != 0)
{
check(0, "getrlimit(RLIMIT_NOFILE) for the round-trip returns 0");
return;
}
check(setrlimit(RLIMIT_NOFILE, &before) == 0, "setrlimit of identical NOFILE pair returns 0");
check(getrlimit(RLIMIT_NOFILE, &after) == 0, "getrlimit after setrlimit returns 0");
check(after.rlim_cur == before.rlim_cur && after.rlim_max == before.rlim_max,
"getrlimit reflects the re-installed NOFILE pair");
}
/* 3. The LFS aliases read and write exactly like the base functions. */
static void
rlimit64_alias_scenario(void)
{
struct rlimit a;
struct rlimit b;
check(getrlimit64(RLIMIT_NOFILE, &a) == 0, "getrlimit64(RLIMIT_NOFILE) returns 0");
check(getrlimit(RLIMIT_NOFILE, &b) == 0, "getrlimit(RLIMIT_NOFILE) returns 0");
check(a.rlim_cur == b.rlim_cur && a.rlim_max == b.rlim_max,
"getrlimit64 agrees with getrlimit");
check(setrlimit64(RLIMIT_NOFILE, &a) == 0, "setrlimit64 of identical pair returns 0");
check(getrlimit(RLIMIT_NOFILE, &b) == 0, "getrlimit after setrlimit64 returns 0");
check(b.rlim_cur == a.rlim_cur && b.rlim_max == a.rlim_max,
"setrlimit64 write is visible through getrlimit");
}
/* 4. getrusage reports sane accounting for the process and the thread. */
static void
getrusage_scenario(void)
{
struct rusage ru;
check(getrusage(RUSAGE_SELF, &ru) == 0, "getrusage(RUSAGE_SELF) returns 0");
check(ru.ru_utime.tv_sec >= 0, "getrusage ru_utime.tv_sec is non-negative");
check(ru.ru_utime.tv_usec >= 0 && ru.ru_utime.tv_usec < 1000000,
"getrusage ru_utime.tv_usec is in [0, 1e6)");
check(ru.ru_maxrss >= 0, "getrusage ru_maxrss is non-negative");
check(getrusage(RUSAGE_THREAD, &ru) == 0, "getrusage(RUSAGE_THREAD) returns 0");
check(ru.ru_utime.tv_sec >= 0, "getrusage THREAD ru_utime.tv_sec is non-negative");
}
/* 5. getpriority/setpriority round-trip on the current process. */
static void
priority_roundtrip_scenario(void)
{
int p;
p = getpriority(PRIO_PROCESS, 0);
check(p >= PRIO_MIN && p <= PRIO_MAX,
"getpriority(PRIO_PROCESS, 0) is in [PRIO_MIN, PRIO_MAX]");
check(setpriority(PRIO_PROCESS, 0, p) == 0, "setpriority to the current value returns 0");
check(getpriority(PRIO_PROCESS, 0) == p, "getpriority round-trips through setpriority");
}
/* 6. nice(0) reports the current nice value without changing it. */
static void
nice_scenario(void)
{
int cur;
int n;
cur = getpriority(PRIO_PROCESS, 0);
check(cur >= PRIO_MIN && cur <= PRIO_MAX, "nice baseline is in [PRIO_MIN, PRIO_MAX]");
n = nice(0);
check(n == cur, "nice(0) returns the current nice value");
check(getpriority(PRIO_PROCESS, 0) == cur, "nice(0) left the nice value unchanged");
}
/* 7. Constant sanity. */
static void
constant_scenario(void)
{
check(RLIM_INFINITY == ~0UL, "RLIM_INFINITY is ~0UL");
check(RLIMIT_NLIMITS == 16, "RLIMIT_NLIMITS is 16");
check(PRIO_MIN == -20 && PRIO_MAX == 19, "PRIO_MIN..PRIO_MAX is -20..19");
check(RUSAGE_SELF == 0 && RUSAGE_CHILDREN == -1 && RUSAGE_THREAD == 1,
"RUSAGE_SELF/CHILDREN/THREAD are 0/-1/1");
}
/* The failure shapes; return values only, never errno. Exit via a raw
* SYS_exit_group: the library's errno writes on these paths corrupt glibc's
* private dtv slot at %fs:0+8, so host cleanup must never run (house
* pattern, tests/test_systime.c). */
static void
failure_scenarios(void)
{
struct rlimit rl;
struct rusage ru;
rl.rlim_cur = 0;
rl.rlim_max = 0;
check(setrlimit(9999, &rl) == -1, "-f setrlimit(9999) returns -1");
check(getrlimit(9999, &rl) == -1, "-f getrlimit(9999) returns -1");
check(getrusage(999, &ru) == -1, "-f getrusage(999) returns -1");
check(getpriority(999, 0) == -1, "-f getpriority(999, 0) returns -1");
check(setpriority(999, 0, 0) == -1, "-f setpriority(999, 0, 0) returns -1");
if (failures == 0)
{
say(1, "all resource failure scenarios passed\n");
}
__syscall1(SYS_exit_group, failures == 0 ? 0 : 1);
/* not reached */
}
int
main(int argc, char **argv)
{
if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'f' && argv[1][2] == '\0')
{
failure_scenarios();
}
rlimit_read_scenario();
setrlimit_roundtrip_scenario();
rlimit64_alias_scenario();
getrusage_scenario();
priority_roundtrip_scenario();
nice_scenario();
constant_scenario();
if (failures == 0)
{
say(1, "all resource tests passed\n");
}
else
{
say(1, "FAILURES\n");
}
return failures == 0 ? 0 : 1;
}
#else /* !VLIBC_LEVEL_GE(2) */
/*
* Level 1: every <sys/resource.h> symbol is gated at level 2, so there is
* nothing to run. Keep the TU compilable at any configured profile.
*/
int
main(void)
{
say(1, "SKIP: <sys/resource.h> is level 2, not available here\n");
return 0;
}
#endif /* VLIBC_LEVEL_GE(2) */