#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include #include #include #include #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); }