Files
erofs-freebsd-out-tree/tests/g7_probe.c
T
2026-08-13 10:44:59 +02:00

357 lines
9.4 KiB
C

#define _POSIX_C_SOURCE 200809L
#include <sys/resource.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#define PRESSURE_CHUNK (8U * 1024U * 1024U)
static void
usage(void)
{
fprintf(stderr,
"usage:\n"
" g7_probe empty FILE\n"
" g7_probe eof FILE OFFSET\n"
" g7_probe range SOURCE TARGET OFFSET LENGTH\n"
" g7_probe random SOURCE TARGET SEED OPERATIONS BLOCK_SIZE\n"
" g7_probe pressure GO_FILE STOP_FILE REQUEST_MIB\n");
}
static int
parse_u64(const char *value, uint64_t *result)
{
char *end;
unsigned long long parsed;
errno = 0;
parsed = strtoull(value, &end, 0);
if (errno != 0 || *value == '\0' || *end != '\0')
return (-1);
*result = parsed;
return (0);
}
static int
read_exact_at(int descriptor, unsigned char *buffer, size_t length,
off_t offset)
{
size_t total;
ssize_t amount;
total = 0;
while (total < length) {
amount = pread(descriptor, buffer + total, length - total,
offset + (off_t)total);
if (amount < 0 && errno == EINTR)
continue;
if (amount <= 0)
return (-1);
total += (size_t)amount;
}
return (0);
}
static uint64_t
digest_bytes(uint64_t digest, const unsigned char *buffer, size_t length)
{
size_t index;
for (index = 0; index < length; index++) {
digest ^= buffer[index];
digest *= UINT64_C(1099511628211);
}
return (digest);
}
static int
empty_probe(const char *path)
{
struct stat status;
unsigned char byte;
ssize_t amount;
int descriptor;
descriptor = open(path, O_RDONLY);
if (descriptor < 0)
return (1);
if (fstat(descriptor, &status) != 0 || status.st_size != 0) {
close(descriptor);
return (1);
}
amount = read(descriptor, &byte, sizeof(byte));
if (amount != 0 || lseek(descriptor, 0, SEEK_SET) != 0 ||
lseek(descriptor, 0, SEEK_END) != 0) {
close(descriptor);
return (1);
}
close(descriptor);
printf("size=0 read_bytes=0 seek_set=0 seek_end=0 exit=0\n");
return (0);
}
static int
eof_probe(const char *path, const char *offset_value)
{
unsigned char byte;
uint64_t offset;
ssize_t amount;
int descriptor;
if (parse_u64(offset_value, &offset) != 0 || offset > INT64_MAX)
return (1);
descriptor = open(path, O_RDONLY);
if (descriptor < 0)
return (1);
amount = pread(descriptor, &byte, sizeof(byte), (off_t)offset);
close(descriptor);
if (amount != 0)
return (1);
printf("offset=%" PRIu64 " read_bytes=0 errno=0 exit=0\n", offset);
return (0);
}
static int
range_probe(const char *source_path, const char *target_path,
const char *offset_value, const char *length_value)
{
unsigned char *source_buffer, *target_buffer;
uint64_t digest, length, offset;
int source, target, result;
if (parse_u64(offset_value, &offset) != 0 ||
parse_u64(length_value, &length) != 0 || length == 0 ||
length > SIZE_MAX || offset > INT64_MAX)
return (1);
source_buffer = malloc((size_t)length);
target_buffer = malloc((size_t)length);
if (source_buffer == NULL || target_buffer == NULL) {
free(source_buffer);
free(target_buffer);
return (1);
}
source = open(source_path, O_RDONLY);
target = open(target_path, O_RDONLY);
result = 1;
if (source >= 0 && target >= 0 &&
read_exact_at(source, source_buffer, (size_t)length,
(off_t)offset) == 0 &&
read_exact_at(target, target_buffer, (size_t)length,
(off_t)offset) == 0 &&
memcmp(source_buffer, target_buffer, (size_t)length) == 0) {
digest = digest_bytes(UINT64_C(1469598103934665603),
source_buffer, (size_t)length);
printf("offset=%" PRIu64 " length=%" PRIu64
" mismatches=0 digest=%016" PRIx64 " exit=0\n",
offset, length, digest);
result = 0;
}
if (source >= 0)
close(source);
if (target >= 0)
close(target);
free(source_buffer);
free(target_buffer);
return (result);
}
static uint64_t
xorshift64star(uint64_t *state)
{
uint64_t value;
value = *state;
value ^= value >> 12;
value ^= value << 25;
value ^= value >> 27;
*state = value;
return (value * UINT64_C(2685821657736338717));
}
static double
elapsed_seconds(const struct timespec *start, const struct timespec *end)
{
return ((double)(end->tv_sec - start->tv_sec) +
(double)(end->tv_nsec - start->tv_nsec) / 1000000000.0);
}
static int
random_probe(int argc, char **argv)
{
struct stat source_status, target_status;
struct timespec start, end;
unsigned char *source_buffer, *target_buffer;
uint64_t block_size, digest, index, input_seed, operations, offset, seed;
uint64_t slots;
double elapsed;
int source, target, result;
if (argc != 7 || parse_u64(argv[4], &seed) != 0 || seed == 0 ||
parse_u64(argv[5], &operations) != 0 || operations == 0 ||
parse_u64(argv[6], &block_size) != 0 || block_size == 0 ||
block_size > SIZE_MAX)
return (1);
input_seed = seed;
source = open(argv[2], O_RDONLY);
target = open(argv[3], O_RDONLY);
if (source < 0 || target < 0 || fstat(source, &source_status) != 0 ||
fstat(target, &target_status) != 0 || source_status.st_size <= 0 ||
source_status.st_size != target_status.st_size ||
(uint64_t)source_status.st_size < block_size) {
if (source >= 0)
close(source);
if (target >= 0)
close(target);
return (1);
}
source_buffer = malloc((size_t)block_size);
target_buffer = malloc((size_t)block_size);
if (source_buffer == NULL || target_buffer == NULL) {
close(source);
close(target);
free(source_buffer);
free(target_buffer);
return (1);
}
slots = (uint64_t)source_status.st_size / block_size;
digest = UINT64_C(1469598103934665603);
result = 1;
clock_gettime(CLOCK_MONOTONIC, &start);
for (index = 0; index < operations; index++) {
offset = (xorshift64star(&seed) % slots) * block_size;
if (read_exact_at(source, source_buffer, (size_t)block_size,
(off_t)offset) != 0 ||
read_exact_at(target, target_buffer, (size_t)block_size,
(off_t)offset) != 0 ||
memcmp(source_buffer, target_buffer, (size_t)block_size) != 0)
goto out;
digest = digest_bytes(digest, target_buffer, (size_t)block_size);
}
clock_gettime(CLOCK_MONOTONIC, &end);
elapsed = elapsed_seconds(&start, &end);
printf("seed=0x%016" PRIx64 " operations=%" PRIu64
" block_size=%" PRIu64
" bytes=%" PRIu64 " mismatches=0 digest=%016" PRIx64
" elapsed_seconds=%.6f iops=%.2f mean_us=%.2f exit=0\n",
input_seed, operations, block_size, operations * block_size, digest, elapsed,
(double)operations / elapsed, elapsed * 1000000.0 / operations);
result = 0;
out:
close(source);
close(target);
free(source_buffer);
free(target_buffer);
return (result);
}
static int
wait_for_path(const char *path, unsigned int attempts)
{
struct timespec delay;
unsigned int attempt;
delay.tv_sec = 0;
delay.tv_nsec = 100000000;
for (attempt = 0; attempt < attempts; attempt++) {
if (access(path, F_OK) == 0)
return (0);
nanosleep(&delay, NULL);
}
return (-1);
}
static int
pressure_probe(const char *go_path, const char *stop_path,
const char *request_value)
{
struct rusage usage;
unsigned char *recovery;
unsigned char **chunks;
uint64_t allocated, index, page_size, request_mib, requested;
int failure_errno, recovery_ok;
if (parse_u64(request_value, &request_mib) != 0 || request_mib == 0 ||
request_mib > 4096)
return (1);
requested = request_mib * 1024 * 1024;
chunks = calloc((size_t)(requested / PRESSURE_CHUNK + 1), sizeof(*chunks));
if (chunks == NULL)
return (1);
printf("state=waiting pid=%ld requested_bytes=%" PRIu64 "\n",
(long)getpid(), requested);
fflush(stdout);
if (wait_for_path(go_path, 600) != 0) {
free(chunks);
return (1);
}
page_size = (uint64_t)sysconf(_SC_PAGESIZE);
allocated = 0;
failure_errno = 0;
for (index = 0; allocated < requested; index++) {
size_t offset;
errno = 0;
chunks[index] = malloc(PRESSURE_CHUNK);
if (chunks[index] == NULL) {
failure_errno = errno;
break;
}
for (offset = 0; offset < PRESSURE_CHUNK; offset += page_size)
chunks[index][offset] = (unsigned char)(index + offset);
allocated += PRESSURE_CHUNK;
}
getrusage(RUSAGE_SELF, &usage);
printf("state=holding pid=%ld allocated_bytes=%" PRIu64
" allocation_failure=%s failure_errno=%d maxrss=%ld\n",
(long)getpid(), allocated,
failure_errno == ENOMEM ? "ENOMEM" : "none", failure_errno,
usage.ru_maxrss);
fflush(stdout);
if (wait_for_path(stop_path, 3000) != 0) {
for (index = 0; index < allocated / PRESSURE_CHUNK; index++)
free(chunks[index]);
free(chunks);
return (1);
}
for (index = 0; index < allocated / PRESSURE_CHUNK; index++)
free(chunks[index]);
free(chunks);
recovery = malloc(1024 * 1024);
recovery_ok = recovery != NULL;
if (recovery != NULL) {
recovery[0] = 1;
free(recovery);
}
printf("state=released pid=%ld released_bytes=%" PRIu64
" recovery=%s exit=%d\n",
(long)getpid(), allocated, recovery_ok ? "ok" : "failed",
failure_errno == ENOMEM && recovery_ok ? 0 : 1);
return (failure_errno == ENOMEM && recovery_ok ? 0 : 1);
}
int
main(int argc, char **argv)
{
if (argc == 3 && strcmp(argv[1], "empty") == 0)
return (empty_probe(argv[2]));
if (argc == 4 && strcmp(argv[1], "eof") == 0)
return (eof_probe(argv[2], argv[3]));
if (argc == 6 && strcmp(argv[1], "range") == 0)
return (range_probe(argv[2], argv[3], argv[4], argv[5]));
if (argc == 7 && strcmp(argv[1], "random") == 0)
return (random_probe(argc, argv));
if (argc == 5 && strcmp(argv[1], "pressure") == 0)
return (pressure_probe(argv[2], argv[3], argv[4]));
usage();
return (64);
}