test code v1
This commit is contained in:
@@ -0,0 +1,356 @@
|
||||
#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);
|
||||
}
|
||||
Reference in New Issue
Block a user