mirror of
https://github.com/isar/libmdbx.git
synced 2024-10-30 11:29:19 +08:00
72557af2b5
Change-Id: I2a91b986964d96bb3e5f848e3c17408bd7745866
398 lines
13 KiB
C++
398 lines
13 KiB
C++
/*
|
|
* Copyright 2017-2018 Leonid Yuriev <leo@yuriev.ru>
|
|
* and other libmdbx authors: please see AUTHORS file.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted only as authorized by the OpenLDAP
|
|
* Public License.
|
|
*
|
|
* A copy of this license is available in the file LICENSE in the
|
|
* top-level directory of the distribution or, alternatively, at
|
|
* <http://www.OpenLDAP.org/license.html>.
|
|
*/
|
|
|
|
#include "test.h"
|
|
|
|
void __noreturn usage(void) {
|
|
printf("usage:\n"
|
|
"\tFIXME\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void actor_params::set_defaults(void) {
|
|
pathname_log = "";
|
|
loglevel =
|
|
#ifdef NDEBUG
|
|
logging::notice;
|
|
#else
|
|
logging::trace;
|
|
#endif
|
|
|
|
pathname_db =
|
|
#ifdef __linux__
|
|
"/dev/shm/test_tmpdb.mdbx";
|
|
#else
|
|
"test_tmpdb.mdbx";
|
|
#endif
|
|
mode_flags = MDBX_NOSUBDIR | MDBX_WRITEMAP | MDBX_MAPASYNC | MDBX_NORDAHEAD |
|
|
MDBX_NOMEMINIT | MDBX_COALESCE | MDBX_LIFORECLAIM;
|
|
table_flags = MDBX_DUPSORT;
|
|
size = 1024 * 1024 * 4;
|
|
|
|
keygen.seed = 1;
|
|
keygen.keycase = kc_random;
|
|
keygen.width = 32;
|
|
keygen.mesh = 32;
|
|
keygen.split = keygen.width / 2;
|
|
keygen.rotate = 0;
|
|
keygen.offset = 0;
|
|
|
|
test_duration = 0;
|
|
test_nops = 1000;
|
|
nrepeat = 1;
|
|
nthreads = 1;
|
|
|
|
keylen_min = 0;
|
|
keylen_max = 42;
|
|
datalen_min = 0;
|
|
datalen_max = 256;
|
|
|
|
batch_read = 4;
|
|
batch_write = 4;
|
|
|
|
delaystart = 0;
|
|
waitfor_nops = 0;
|
|
|
|
drop_table = false;
|
|
|
|
max_readers = 42;
|
|
max_tables = 42;
|
|
|
|
global::config::timeout_duration_seconds = 0 /* infinite */;
|
|
global::config::dump_config = true;
|
|
global::config::cleanup_before = true;
|
|
global::config::cleanup_after = true;
|
|
global::config::failfast = true;
|
|
global::config::progress_indicator = osal_istty(STDERR_FILENO);
|
|
}
|
|
|
|
namespace global {
|
|
|
|
std::vector<actor_config> actors;
|
|
std::unordered_map<unsigned, actor_config *> events;
|
|
std::unordered_map<mdbx_pid_t, actor_config *> pid2actor;
|
|
std::set<std::string> databases;
|
|
unsigned nactors;
|
|
chrono::time start_motonic;
|
|
chrono::time deadline_motonic;
|
|
bool singlemode;
|
|
|
|
namespace config {
|
|
unsigned timeout_duration_seconds;
|
|
bool dump_config;
|
|
bool cleanup_before;
|
|
bool cleanup_after;
|
|
bool failfast;
|
|
bool progress_indicator;
|
|
} /* namespace config */
|
|
|
|
} /* namespace global */
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
const char global::thunk_param_prefix[] = "--execute=";
|
|
|
|
std::string thunk_param(const actor_config &config) {
|
|
return config.serialize(global::thunk_param_prefix);
|
|
}
|
|
|
|
void cleanup() {
|
|
log_trace(">> cleanup");
|
|
/* TODO: remove each database */
|
|
log_trace("<< cleanup");
|
|
}
|
|
|
|
int main(int argc, char *const argv[]) {
|
|
|
|
#ifdef _DEBUG
|
|
log_trace("#argc = %d", argc);
|
|
for (int i = 0; i < argc; ++i)
|
|
log_trace("#argv[%d] = %s", i, argv[i]);
|
|
#endif /* _DEBUG */
|
|
|
|
if (argc < 2)
|
|
failure("No parameters given\n");
|
|
|
|
if (argc == 2 &&
|
|
strncmp(argv[1], global::thunk_param_prefix,
|
|
strlen(global::thunk_param_prefix)) == 0)
|
|
return test_execute(
|
|
actor_config(argv[1] + strlen(global::thunk_param_prefix)))
|
|
? EXIT_SUCCESS
|
|
: EXIT_FAILURE;
|
|
|
|
actor_params params;
|
|
params.set_defaults();
|
|
global::config::dump_config = true;
|
|
logging::setup((logging::loglevel)params.loglevel, "main");
|
|
unsigned last_space_id = 0;
|
|
|
|
for (int narg = 1; narg < argc; ++narg) {
|
|
const char *value = nullptr;
|
|
|
|
if (config::parse_option(argc, argv, narg, "case", &value)) {
|
|
testcase_setup(value, params, last_space_id);
|
|
continue;
|
|
}
|
|
if (config::parse_option(argc, argv, narg, "pathname", params.pathname_db))
|
|
continue;
|
|
if (config::parse_option(argc, argv, narg, "mode", params.mode_flags,
|
|
config::mode_bits))
|
|
continue;
|
|
if (config::parse_option(argc, argv, narg, "table", params.table_flags,
|
|
config::table_bits))
|
|
continue;
|
|
if (config::parse_option(argc, argv, narg, "size", params.size,
|
|
config::binary, 4096 * 4))
|
|
continue;
|
|
|
|
if (config::parse_option(argc, argv, narg, "keygen.width",
|
|
params.keygen.width, 1, 64))
|
|
continue;
|
|
if (config::parse_option(argc, argv, narg, "keygen.mesh",
|
|
params.keygen.mesh, 1, 64))
|
|
continue;
|
|
if (config::parse_option(argc, argv, narg, "keygen.seed",
|
|
params.keygen.seed, config::no_scale))
|
|
continue;
|
|
if (config::parse_option(argc, argv, narg, "keygen.split",
|
|
params.keygen.split, 1, 64))
|
|
continue;
|
|
if (config::parse_option(argc, argv, narg, "keygen.rotate",
|
|
params.keygen.rotate, 1, 64))
|
|
continue;
|
|
if (config::parse_option(argc, argv, narg, "keygen.offset",
|
|
params.keygen.offset, config::binary))
|
|
continue;
|
|
if (config::parse_option(argc, argv, narg, "keygen.case", &value)) {
|
|
keycase_setup(value, params);
|
|
continue;
|
|
}
|
|
|
|
if (config::parse_option(argc, argv, narg, "repeat", params.nrepeat,
|
|
config::no_scale))
|
|
continue;
|
|
if (config::parse_option(argc, argv, narg, "threads", params.nthreads,
|
|
config::no_scale, 1, 64))
|
|
continue;
|
|
if (config::parse_option(argc, argv, narg, "timeout",
|
|
global::config::timeout_duration_seconds,
|
|
config::duration, 1))
|
|
continue;
|
|
if (config::parse_option(argc, argv, narg, "keylen.min", params.keylen_min,
|
|
config::no_scale, 0, params.keylen_max))
|
|
continue;
|
|
if (config::parse_option(argc, argv, narg, "keylen.max", params.keylen_max,
|
|
config::no_scale, params.keylen_min,
|
|
mdbx_get_maxkeysize(0)))
|
|
continue;
|
|
if (config::parse_option(argc, argv, narg, "datalen.min",
|
|
params.datalen_min, config::no_scale, 0,
|
|
params.datalen_max))
|
|
continue;
|
|
if (config::parse_option(argc, argv, narg, "datalen.max",
|
|
params.datalen_max, config::no_scale,
|
|
params.datalen_min, MDBX_MAXDATASIZE))
|
|
continue;
|
|
if (config::parse_option(argc, argv, narg, "batch.read", params.batch_read,
|
|
config::no_scale, 1))
|
|
continue;
|
|
if (config::parse_option(argc, argv, narg, "batch.write",
|
|
params.batch_write, config::no_scale, 1))
|
|
continue;
|
|
if (config::parse_option(argc, argv, narg, "delay", params.delaystart,
|
|
config::duration))
|
|
continue;
|
|
if (config::parse_option(argc, argv, narg, "wait4ops", params.waitfor_nops,
|
|
config::decimal))
|
|
continue;
|
|
if (config::parse_option(argc, argv, narg, "drop", params.drop_table))
|
|
continue;
|
|
if (config::parse_option(argc, argv, narg, "dump-config",
|
|
global::config::dump_config))
|
|
continue;
|
|
if (config::parse_option(argc, argv, narg, "cleanup-before",
|
|
global::config::cleanup_before))
|
|
continue;
|
|
if (config::parse_option(argc, argv, narg, "cleanup-after",
|
|
global::config::cleanup_after))
|
|
continue;
|
|
if (config::parse_option(argc, argv, narg, "max-readers",
|
|
params.max_readers, config::no_scale, 1, 255))
|
|
continue;
|
|
if (config::parse_option(argc, argv, narg, "max-tables", params.max_tables,
|
|
config::no_scale, 1, INT16_MAX))
|
|
continue;
|
|
|
|
if (config::parse_option(argc, argv, narg, "no-delay", nullptr)) {
|
|
params.delaystart = 0;
|
|
continue;
|
|
}
|
|
if (config::parse_option(argc, argv, narg, "no-wait", nullptr)) {
|
|
params.waitfor_nops = 0;
|
|
continue;
|
|
}
|
|
if (config::parse_option(argc, argv, narg, "duration", params.test_duration,
|
|
config::duration, 1)) {
|
|
params.test_nops = 0;
|
|
continue;
|
|
}
|
|
if (config::parse_option(argc, argv, narg, "nops", params.test_nops,
|
|
config::decimal, 1)) {
|
|
params.test_duration = 0;
|
|
continue;
|
|
}
|
|
if (config::parse_option(argc, argv, narg, "hill", &value, "auto")) {
|
|
configure_actor(last_space_id, ac_hill, value, params);
|
|
continue;
|
|
}
|
|
if (config::parse_option(argc, argv, narg, "jitter", nullptr)) {
|
|
configure_actor(last_space_id, ac_jitter, value, params);
|
|
continue;
|
|
}
|
|
if (config::parse_option(argc, argv, narg, "dead.reader", nullptr)) {
|
|
configure_actor(last_space_id, ac_deadread, value, params);
|
|
continue;
|
|
}
|
|
if (config::parse_option(argc, argv, narg, "dead.writer", nullptr)) {
|
|
configure_actor(last_space_id, ac_deadwrite, value, params);
|
|
continue;
|
|
}
|
|
if (config::parse_option(argc, argv, narg, "failfast",
|
|
global::config::failfast))
|
|
continue;
|
|
if (config::parse_option(argc, argv, narg, "progress",
|
|
global::config::progress_indicator))
|
|
continue;
|
|
|
|
if (*argv[narg] != '-')
|
|
testcase_setup(argv[narg], params, last_space_id);
|
|
else
|
|
failure("Unknown option '%s'\n", argv[narg]);
|
|
}
|
|
|
|
if (global::config::dump_config)
|
|
config::dump();
|
|
|
|
//--------------------------------------------------------------------------
|
|
|
|
if (global::actors.empty()) {
|
|
log_notice("no testcase(s) configured, exiting");
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
bool failed = false;
|
|
global::start_motonic = chrono::now_motonic();
|
|
global::deadline_motonic.fixedpoint =
|
|
(global::config::timeout_duration_seconds == 0)
|
|
? chrono::infinite().fixedpoint
|
|
: global::start_motonic.fixedpoint +
|
|
chrono::from_seconds(global::config::timeout_duration_seconds)
|
|
.fixedpoint;
|
|
|
|
if (global::config::cleanup_before)
|
|
cleanup();
|
|
|
|
if (global::actors.size() == 1) {
|
|
logging::setup("main");
|
|
global::singlemode = true;
|
|
if (!test_execute(global::actors.front()))
|
|
failed = true;
|
|
} else {
|
|
logging::setup("overlord");
|
|
|
|
log_trace("=== preparing...");
|
|
log_trace(">> osal_setup");
|
|
osal_setup(global::actors);
|
|
log_trace("<< osal_setup");
|
|
|
|
for (auto &a : global::actors) {
|
|
mdbx_pid_t pid;
|
|
log_trace(">> actor_start");
|
|
int rc = osal_actor_start(a, pid);
|
|
log_trace("<< actor_start");
|
|
if (rc) {
|
|
log_trace(">> killall_actors: (%s)", "start failed");
|
|
osal_killall_actors();
|
|
log_trace("<< killall_actors");
|
|
failure("Failed to start actor #%u (%s)\n", a.actor_id,
|
|
test_strerror(rc));
|
|
}
|
|
global::pid2actor[pid] = &a;
|
|
}
|
|
|
|
log_trace("=== ready to start...");
|
|
atexit(osal_killall_actors);
|
|
log_trace(">> wait4barrier");
|
|
osal_wait4barrier();
|
|
log_trace("<< wait4barrier");
|
|
|
|
size_t left = global::actors.size();
|
|
log_trace("=== polling...");
|
|
while (left > 0) {
|
|
unsigned timeout_seconds_left = INT_MAX;
|
|
chrono::time now_motonic = chrono::now_motonic();
|
|
if (now_motonic.fixedpoint >= global::deadline_motonic.fixedpoint)
|
|
timeout_seconds_left = 0;
|
|
else {
|
|
chrono::time left_motonic;
|
|
left_motonic.fixedpoint =
|
|
global::deadline_motonic.fixedpoint - now_motonic.fixedpoint;
|
|
timeout_seconds_left = left_motonic.seconds();
|
|
}
|
|
|
|
mdbx_pid_t pid;
|
|
int rc = osal_actor_poll(pid, timeout_seconds_left);
|
|
if (rc)
|
|
failure("Poll error: %s (%d)\n", test_strerror(rc), rc);
|
|
|
|
if (pid) {
|
|
actor_status status = osal_actor_info(pid);
|
|
actor_config *actor = global::pid2actor.at(pid);
|
|
if (!actor)
|
|
continue;
|
|
|
|
log_info("actor #%u, id %d, pid %u: %s\n", actor->actor_id,
|
|
actor->space_id, pid, status2str(status));
|
|
if (status > as_running) {
|
|
left -= 1;
|
|
if (status != as_successful) {
|
|
if (global::config::failfast && !failed) {
|
|
log_trace(">> killall_actors: (%s)", "failfast");
|
|
osal_killall_actors();
|
|
log_trace("<< killall_actors");
|
|
}
|
|
failed = true;
|
|
}
|
|
}
|
|
} else {
|
|
if (timeout_seconds_left == 0)
|
|
failure("Timeout\n");
|
|
}
|
|
}
|
|
log_trace("=== done...");
|
|
}
|
|
|
|
log_notice("RESULT: %s\n", failed ? "Failed" : "Successful");
|
|
if (global::config::cleanup_before) {
|
|
if (failed)
|
|
log_info("skip cleanup");
|
|
else
|
|
cleanup();
|
|
}
|
|
return failed ? EXIT_FAILURE : EXIT_SUCCESS;
|
|
}
|