blob: e85c827b1bd9891499cf4f88a22d09bca21d4edc [file] [edit]
/******************************************************************************
* Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
*
* Add tests validating use of uid/username cache and negative cache
*****************************************************************************/
#define _GNU_SOURCE
#include <check.h>
#include <fcntl.h>
#include <grp.h>
#include <pwd.h>
#include "src/common/log.h"
#include "src/common/read_config.h"
#include "src/common/slurm_protocol_api.h"
#include "src/common/timers.h"
#include "src/common/xstring.h"
#include "src/common/uid.h"
static bool slow_lookup_enabled = false;
static long lookup_count = 0;
#define SLOW_LOOKUP_USEC 50000 /* 50 msec */
#define UID_BASE 100000
#define NUSERS 130000
static int fill_synthetic_pwd_entry(struct passwd *pwd, char *buf,
size_t buflen, int i,
struct passwd **result)
{
/*
* buf layout:
* "ckuserNNNNNNN\0" "x\0" "\0" "\0" "\0" "\0"
*/
char *ptr = NULL;
int n = snprintf(buf, buflen, "ckuser%07d", i);
if ((n < 0) || ((size_t) n + 6 >= buflen)) {
*result = NULL;
return ERANGE;
}
ptr = buf;
pwd->pw_name = ptr;
ptr += n + 1;
pwd->pw_passwd = ptr;
*ptr++ = 'x';
*ptr++ = '\0';
pwd->pw_gecos = ptr;
pwd->pw_dir = ptr;
pwd->pw_shell = ptr;
*ptr = '\0';
pwd->pw_uid = UID_BASE + i;
pwd->pw_gid = UID_BASE + i;
*result = pwd;
return 0;
}
int getpwnam_r(const char *name, struct passwd *pwd, char *buf, size_t buflen,
struct passwd **result)
{
lookup_count += 1;
if (slow_lookup_enabled)
usleep(SLOW_LOOKUP_USEC);
if (!xstrncmp(name, "ckuser", 6)) {
int i = atoi(name + 6);
if ((i >= 0) && (i < NUSERS))
return fill_synthetic_pwd_entry(pwd, buf, buflen, i,
result);
}
*result = NULL;
return 0; /* not found */
}
int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf, size_t buflen,
struct passwd **result)
{
lookup_count += 1;
if (slow_lookup_enabled)
usleep(SLOW_LOOKUP_USEC);
if ((uid >= UID_BASE) && (uid < UID_BASE + NUSERS))
return fill_synthetic_pwd_entry(pwd, buf, buflen,
uid - UID_BASE, result);
*result = NULL;
return 0;
}
static void setup(void)
{
log_options_t log_opts = LOG_OPTS_INITIALIZER;
log_init("uid-test", log_opts, 0, NULL);
}
static void teardown(void)
{
log_fini();
}
START_TEST(test_uid_lookup_time)
{
#if SLURM_VERSION_NUMBER >= SLURM_VERSION_NUM(26, 5, 0)
DEF_TIMERS;
int rc;
long timer_us = 0;
uid_t u;
char username[16];
long sum = 0;
uid_from_string_cache_enable();
slow_lookup_enabled = false;
lookup_count = 0;
uid_cache_clear();
START_TIMER;
ck_assert(uid_from_string_cached("ckuser0000042", &u) == SLURM_SUCCESS);
END_TIMER;
ck_assert(u == UID_BASE + 42);
timer_us = TIMER_DURATION_USEC();
printf("uncached fast lookup: %s (%ld us)\n", TIMER_STR(), timer_us);
uid_cache_clear();
slow_lookup_enabled = true;
START_TIMER;
ck_assert(uid_from_string_cached("ckuser0000042", &u) == SLURM_SUCCESS);
END_TIMER;
ck_assert(u == UID_BASE + 42);
timer_us = TIMER_DURATION_USEC();
printf("uncached slow lookup: %s (%ld us)\n", TIMER_STR(), timer_us);
ck_assert(timer_us > 50000);
START_TIMER;
ck_assert(uid_from_string_cached("ckuser0000042", &u) == SLURM_SUCCESS);
END_TIMER;
ck_assert(u == UID_BASE + 42);
timer_us = TIMER_DURATION_USEC();
printf("cached fast lookup: %s (%ld us)\n", TIMER_STR(), timer_us);
ck_assert(timer_us < 50);
uid_cache_clear();
slow_lookup_enabled = false;
lookup_count = 0;
/* load cache up to 130000 users */
sum = 0;
for (int i = 0; i < NUSERS; i++) {
START_TIMER;
snprintf(username, sizeof(username), "ckuser%07d", i);
rc = uid_from_string_cached(username, &u);
END_TIMER;
sum += TIMER_DURATION_USEC();
ck_assert(rc == SLURM_SUCCESS);
ck_assert(u == i + UID_BASE);
}
ck_assert(lookup_count == NUSERS);
printf("Cache Load Time, Average: %0.2f us\n",
(double) sum / (double) NUSERS);
sum = 0;
for (int i = 0; i < NUSERS; i++) {
START_TIMER;
snprintf(username, sizeof(username), "ckuser%07d", i);
rc = uid_from_string_cached(username, &u);
END_TIMER;
ck_assert(rc == SLURM_SUCCESS);
ck_assert(u == i + UID_BASE);
sum += TIMER_DURATION_USEC();
}
ck_assert(lookup_count == NUSERS);
printf("Cache Read Time, Average: %0.2f us\n",
(double) sum / (double) NUSERS);
/* measure time for uid-as-string search */
START_TIMER;
snprintf(username, sizeof(username), "%d", UID_BASE + 15151);
rc = uid_from_string_cached(username, &u);
END_TIMER;
ck_assert(rc == SLURM_SUCCESS);
ck_assert(u == UID_BASE + 15151);
printf("Cache search time: %0.2f us\n", (double) TIMER_DURATION_USEC());
uid_from_string_cache_disable();
#else
ck_abort_msg(
"uid_from_string caching does not exist until Slurm 26.05.0");
#endif
}
END_TEST
START_TEST(test_negative_cache)
{
#if SLURM_VERSION_NUMBER >= SLURM_VERSION_NUM(26, 5, 0)
uid_t uid = 50;
slow_lookup_enabled = true;
lookup_count = 0;
for (int i = 0; i < 10; i++) {
int rc = uid_from_string_cached("fake_username", &uid);
ck_assert(rc == SLURM_ERROR);
ck_assert(uid == 50); /* make sure uid not updated */
}
ck_assert(lookup_count == 10);
lookup_count = 0;
uid_from_string_cache_enable();
/* warm cache */
uid_from_string_cached("fake_username", &uid);
/* test cache */
for (int i = 0; i < 10000; i++) {
int rc = uid_from_string_cached("fake_username", &uid);
ck_assert(rc == SLURM_ERROR);
ck_assert(uid == 50); /* make sure uid not updated */
}
ck_assert(lookup_count == 1);
uid_from_string_cache_disable();
slow_lookup_enabled = false;
#else
ck_abort_msg("uid negative cache does not exist until Slurm 26.05.0");
#endif
}
END_TEST
extern int main(int argc, char **argv)
{
int failures;
/* Create main test case with fixtures and the main suite*/
TCase *tcase = tcase_create("uid");
tcase_add_unchecked_fixture(tcase, setup, teardown);
tcase_add_test(tcase, test_negative_cache);
tcase_add_test(tcase, test_uid_lookup_time);
tcase_set_timeout(tcase, 20);
Suite *suite = suite_create("uid");
suite_add_tcase(suite, tcase);
/* Create and run the runner */
SRunner *sr = srunner_create(suite);
srunner_run_all(sr, CK_VERBOSE);
failures = srunner_ntests_failed(sr);
srunner_free(sr);
return failures;
}