terom@48: #define _GNU_SOURCE terom@48: #include terom@48: #include terom@48: #include terom@48: #include terom@48: #include terom@48: #include terom@48: #include terom@38: terom@38: #include terom@38: #include terom@38: #include terom@38: terom@38: #include "memcache.h" terom@38: #include "config.h" terom@38: #include "common.h" terom@38: terom@48: /* terom@48: * Test: terom@48: * * zero-length entries terom@48: */ terom@48: terom@48: static struct event_base *ev_base; terom@38: static struct memcache *mc; terom@38: static struct config_endpoint server_endpoint; terom@46: static char *data_1 = "rei4quohV8Oocio1ua0co8ni4Ae1re4houcheixahchoh3ioghie0aShooShoh6Ahboequ9eiX5eashuu6Chu1quo" terom@46: "o0suph7cheiyai1ea0ooh7Aevoo4feihubupohDeephahwee2Ooz7chiediec7neit7keTh6xuheash8chaeKa5vi" terom@46: "ekooqu7ooj6Eezooroi6Nequ9ca2yi6iSoigh3loowaey9eiphaphaiJ0souy7wohpa9eXo5Ahu2sa"; terom@46: static char *data_2 = "iefaek7ighi5UpueThageish5ieshohyeil1raiceerahjahng5ui7vuzie9quu4dai5ar2aiXi5ieth4looweigi" terom@46: "e3fo5ieri1queengaiphuaghaic1xahvoo9joo6baiNaig8puCootheowah4moocohDoiquoh3quieka5ao3aeNg9" terom@46: "Aimei1soangu4Duch5pho5buu2ohzaich4chahz9iTh3Pei4beep1ongie6au1aafoosh2vierei5E"; terom@48: terom@48: #define BENCHMARK_KEY "memcache_benchmark" terom@48: #define BENCHMARK_KEY_MAX 256 terom@48: #define BENCHMARK_DATA_MAX 1024 * 1024 terom@46: terom@48: #define MIN(a, b) ((a) < (b) ? (a) : (b)) terom@48: #define MAX(a, b) ((a) > (b) ? (a) : (b)) terom@48: terom@48: struct common { terom@48: unsigned int max_connections; terom@49: char pipeline_requests; terom@46: terom@48: struct timeval start; terom@48: } common; terom@46: terom@48: struct benchmark_fetch { terom@48: unsigned int concurrent_ops; terom@48: unsigned int total_ops; terom@46: terom@48: const char *key_prefix; terom@48: unsigned int key_len_min, key_len_max, key_count; terom@48: unsigned int data_len_min, data_len_max; terom@48: terom@48: int keys_stored; terom@48: int cur_ops; terom@48: int op_count; terom@48: terom@48: struct key_buf { terom@48: char buf[BENCHMARK_KEY_MAX]; terom@48: struct memcache_key key; terom@48: } *keys; terom@48: terom@48: } benchmark_fetch; terom@48: terom@48: void benchmark_cb (struct memcache_req *req, void *arg); terom@48: void benchmark_fetch_fn (void); terom@48: terom@49: enum option_test { terom@49: TEST_INVALID, terom@49: TEST_COMMON, terom@49: terom@49: TEST_BENCH_FETCH, terom@49: }; terom@49: terom@48: enum option_code { terom@48: OPT_CODE_INVALID, terom@48: terom@48: COMMON_CONN_MAX, terom@49: COMMON_PIPELINE, terom@48: BENCH_FETCH_REQ_CONCURRENCY, terom@48: BENCH_FETCH_REQ_AMOUNT, terom@48: BENCH_FETCH_KEY_PREFIX, terom@48: BENCH_FETCH_KEY_LEN_MIN, terom@48: BENCH_FETCH_KEY_LEN_MAX, terom@48: BENCH_FETCH_KEY_COUNT, terom@48: BENCH_FETCH_DATA_LEN_MIN, terom@48: BENCH_FETCH_DATA_LEN_MAX, terom@48: terom@48: OPT_CODE_MAX, terom@48: }; terom@48: terom@48: enum option_type { terom@48: OPT_TYPE_NONE, terom@49: OPT_TYPE_BOOL, terom@48: OPT_TYPE_UINT, terom@48: OPT_TYPE_STR, terom@48: }; terom@48: terom@48: static struct test { terom@48: char *name; terom@48: memcache_cb cb_fn; terom@48: terom@48: void (*test_fn) (void); terom@48: terom@49: enum option_test code; terom@49: terom@49: char *descr; terom@49: terom@48: } test_list[] = { terom@49: { "benchmark_fetch", &benchmark_cb, &benchmark_fetch_fn, TEST_BENCH_FETCH, terom@49: "Measure the speed of fetch requests" }, terom@49: { 0, 0, 0, 0, 0 } terom@48: }; terom@48: terom@48: static struct option options[] = { terom@48: { "conn-max", required_argument, NULL, COMMON_CONN_MAX }, terom@49: { "pipeline", required_argument, NULL, COMMON_PIPELINE }, terom@48: { "req-concurrency", required_argument, NULL, BENCH_FETCH_REQ_CONCURRENCY }, terom@48: { "req-amount", required_argument, NULL, BENCH_FETCH_REQ_AMOUNT }, terom@48: { "key-prefix", required_argument, NULL, BENCH_FETCH_KEY_PREFIX }, terom@48: { "key-len-min", required_argument, NULL, BENCH_FETCH_KEY_LEN_MIN }, terom@48: { "key-len-max", required_argument, NULL, BENCH_FETCH_KEY_LEN_MAX }, terom@48: { "key-count", required_argument, NULL, BENCH_FETCH_KEY_COUNT }, terom@48: { "data-len-min", required_argument, NULL, BENCH_FETCH_DATA_LEN_MIN }, terom@48: { "data-len-max", required_argument, NULL, BENCH_FETCH_DATA_LEN_MAX }, terom@48: { 0, 0, 0, 0 }, terom@48: }; terom@48: terom@48: static struct opt { terom@49: enum option_test test; terom@48: enum option_code code; terom@48: enum option_type type; terom@48: terom@48: union opt_type_data { terom@49: struct { terom@49: char *value; terom@49: char default_value; terom@49: } bool; terom@49: terom@48: struct { terom@48: unsigned int *value; terom@48: unsigned int default_value; terom@48: } uint; terom@48: terom@48: struct { terom@48: const char **value; terom@48: const char *default_value; terom@48: } str; terom@48: } data; terom@49: terom@49: const char *name; terom@49: const char *descr; terom@48: } option_info[OPT_CODE_MAX] = { terom@49: { TEST_INVALID, OPT_CODE_INVALID, OPT_TYPE_NONE }, terom@49: terom@49: { TEST_COMMON, COMMON_CONN_MAX, OPT_TYPE_UINT, { .uint = { &common.max_connections, 1 }}, terom@49: "conn-max", "number of connections to use" }, terom@49: terom@49: { TEST_COMMON, COMMON_PIPELINE, OPT_TYPE_BOOL, { .bool = { &common.pipeline_requests, 1 }}, terom@49: "pipeline", "pipeline requests" }, terom@49: terom@49: { TEST_BENCH_FETCH, BENCH_FETCH_REQ_CONCURRENCY, OPT_TYPE_UINT, { .uint = { &benchmark_fetch.concurrent_ops, 1 }}, terom@49: "req-concurrency", "number of requests to have running" }, terom@49: terom@49: { TEST_BENCH_FETCH, BENCH_FETCH_REQ_AMOUNT, OPT_TYPE_UINT, { .uint = { &benchmark_fetch.total_ops, 500 }}, terom@49: "req-amount", "number of requests to issue" }, terom@49: terom@49: { TEST_BENCH_FETCH, BENCH_FETCH_KEY_PREFIX, OPT_TYPE_STR, { .str = { &benchmark_fetch.key_prefix, "bf_" }}, terom@49: "key-prefix", "key prefix" }, terom@49: terom@49: { TEST_BENCH_FETCH, BENCH_FETCH_KEY_LEN_MIN, OPT_TYPE_UINT, { .uint = { &benchmark_fetch.key_len_min, 8 }}, terom@49: "key-len-min", "minimum key length" }, terom@49: terom@49: { TEST_BENCH_FETCH, BENCH_FETCH_KEY_LEN_MAX, OPT_TYPE_UINT, { .uint = { &benchmark_fetch.key_len_max, 8 }}, terom@49: "key-len-max", "maximum key length" }, terom@49: terom@49: { TEST_BENCH_FETCH, BENCH_FETCH_KEY_COUNT, OPT_TYPE_UINT, { .uint = { &benchmark_fetch.key_count, 1 }}, terom@49: "key-count", "how many keys to use" }, terom@49: terom@49: { TEST_BENCH_FETCH, BENCH_FETCH_DATA_LEN_MIN, OPT_TYPE_UINT, { .uint = { &benchmark_fetch.data_len_min, 64 }}, terom@49: "data-len-min", "minimum data length" }, terom@49: terom@49: { TEST_BENCH_FETCH, BENCH_FETCH_DATA_LEN_MAX, OPT_TYPE_UINT, { .uint = { &benchmark_fetch.data_len_max, 64 }}, terom@49: "data-len-max", "maximum data length" }, terom@48: }; terom@48: terom@48: void time_reset () { terom@48: // start timing terom@48: assert(gettimeofday(&common.start, NULL) == 0); terom@38: } terom@38: terom@48: double time_offset () { terom@48: struct timeval time; terom@46: terom@48: assert(gettimeofday(&time, NULL) == 0); terom@48: terom@48: return ((double) (time.tv_sec - common.start.tv_sec)) + ((double) (time.tv_usec - common.start.tv_usec)) / 1000000; terom@48: } terom@48: terom@49: int ratelimit (int count, int total) { terom@49: return ((total / 10) ? (count % (total / 10) == 0) : 1); terom@49: } terom@49: terom@48: terom@48: terom@48: void mc_init (int max_connections, memcache_cb cb_fn) { terom@48: // memcache init terom@49: if ((mc = memcache_alloc(cb_fn, common.pipeline_requests)) == NULL) terom@38: ERROR("memcache_alloc"); terom@38: terom@44: // fix up the endpoint terom@38: endpoint_init(&server_endpoint, 11211); terom@38: terom@38: if (endpoint_parse(&server_endpoint, "localhost")) terom@38: ERROR("config_endpoint_parse"); terom@44: terom@44: // add the server terom@49: if (memcache_add_server(mc, &server_endpoint, common.max_connections)) terom@38: ERROR("memcache_add_server"); terom@48: terom@49: INFO("[memcache] initialized with pipeline_requests=%c max_connections=%d", common.pipeline_requests ? 't' : 'f', common.max_connections); terom@48: terom@48: return; terom@48: terom@48: error: terom@48: assert(0); terom@48: } terom@48: terom@48: void dump_req (struct memcache_req *req, void *unused) { terom@48: const struct memcache_obj *obj; terom@48: const struct memcache_buf *buf; terom@48: terom@49: INFO("[%.*s]: cmd=%s state=%s reply=%s", terom@48: (int) memcache_req_key(req)->len, memcache_req_key(req)->buf, terom@48: memcache_command_str(memcache_req_cmd(req)), terom@48: memcache_state_str(memcache_req_state(req)), terom@48: memcache_reply_str(memcache_req_reply(req)) terom@48: ); terom@48: terom@48: if ((obj = memcache_req_obj(req))) terom@48: INFO("\tobj: flags=0x%04X exptime=%zu bytes=%zu cas=%llu", obj->flags, obj->exptime, obj->bytes, obj->cas); terom@48: terom@48: if ((buf = memcache_req_buf(req))) terom@48: INFO("\tbuf: data=%p len=%zu offset=%zu", buf->data, buf->len, buf->offset); terom@48: terom@48: INFO("%s", ""); terom@48: } terom@48: terom@48: size_t random_value (size_t min, size_t max) { terom@48: return ((max == min) ? min : (random() % (max - min)) + min); terom@48: } terom@48: terom@48: size_t random_data (char *buf, size_t min, size_t max) { terom@48: #define CHAR_TABLE_MAX (('z' - 'a' + 1) + ('Z' - 'A' + 1) + ('9' - '0' + 1)) terom@48: terom@48: static char char_table[CHAR_TABLE_MAX] = { terom@48: 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', terom@48: 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', terom@48: '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' terom@48: }; terom@48: terom@48: assert(max >= min); terom@48: terom@48: size_t size = random_value(min, max), i; terom@48: terom@48: for (i = 0; i < size; i++) { terom@48: buf[i] = char_table[random_value(0, CHAR_TABLE_MAX)]; terom@48: terom@48: assert(isalnum(buf[i])); terom@48: /* terom@48: switch (MIN((size - i), 4)) { terom@48: case 4: * ((u_int32_t*) &buf[i]) = random(); i += 4; break; terom@48: case 3: terom@48: case 2: * ((u_int16_t*) &buf[i]) = random(); i += 2; break; terom@48: case 1: * ((u_int8_t*) &buf[i]) = random(); i += 1; break; terom@48: default: assert(0); terom@48: } terom@48: */ terom@48: } terom@48: terom@48: return size; terom@48: } terom@48: terom@48: void test_cb (struct memcache_req *req, void *arg) { terom@48: dump_req(req, arg); terom@48: } terom@48: terom@48: void begin_test () { terom@48: struct memcache_key key_1, key_2; terom@48: struct memcache_obj obj_1, obj_2; terom@48: struct memcache_buf buf_1, buf_2; terom@48: struct memcache_req *req_1s, *req_2s, *req_1f, *req_2f; terom@48: terom@48: mc_init(1, &test_cb); terom@48: terom@44: // add a request or two terom@46: key_1.buf = "memcache_test_k1"; terom@46: key_2.buf = "memcache_test_k2"; terom@46: key_1.len = key_2.len = 0; terom@46: terom@46: obj_1.flags = 0x1A; terom@46: obj_2.flags = 0x2B; terom@46: terom@46: obj_1.exptime = 0; terom@46: obj_2.exptime = 3600; terom@46: terom@46: obj_1.bytes = strlen(data_1); terom@46: obj_2.bytes = strlen(data_2); terom@46: terom@46: buf_1.data = data_1; terom@46: buf_1.len = strlen(data_1); terom@46: buf_1.offset = buf_1.len; terom@46: terom@46: buf_2.data = data_2; terom@46: buf_2.len = strlen(data_2); terom@46: buf_2.offset = buf_2.len; terom@46: terom@48: if ((req_1s = memcache_store(mc, MEMCACHE_CMD_STORE_SET, &key_1, &obj_1, &buf_1, key_1.buf)) == NULL) terom@46: ERROR("memcache_store: key_1"); terom@38: terom@48: if ((req_2s = memcache_store(mc, MEMCACHE_CMD_STORE_ADD, &key_2, &obj_2, &buf_2, key_2.buf)) == NULL) terom@46: ERROR("memcache_store: key_2"); terom@44: terom@48: if ((req_1f = memcache_fetch(mc, &key_1, key_1.buf)) == NULL) terom@46: ERROR("memcache_fetch: key_1"); terom@46: terom@48: if ((req_2f = memcache_fetch(mc, &key_2, key_2.buf)) == NULL) terom@46: ERROR("memcache_fetch: key_2"); terom@38: terom@38: error: terom@38: return; terom@38: } terom@38: terom@48: void benchmark_continue () { terom@48: while (benchmark_fetch.cur_ops < benchmark_fetch.concurrent_ops && (benchmark_fetch.op_count + benchmark_fetch.cur_ops) < benchmark_fetch.total_ops) { terom@48: // launch terom@48: assert(memcache_fetch(mc, &benchmark_fetch.keys[random_value(0, benchmark_fetch.key_count)].key, NULL) != NULL); terom@48: terom@48: benchmark_fetch.cur_ops++; terom@49: terom@49: if (ratelimit(benchmark_fetch.op_count + benchmark_fetch.cur_ops, benchmark_fetch.total_ops)) terom@48: INFO("[benchmark] %0.6f: %d+%d/%d requests", terom@48: time_offset(), terom@48: benchmark_fetch.op_count, benchmark_fetch.cur_ops, benchmark_fetch.total_ops terom@48: ); terom@48: } terom@48: terom@48: if (benchmark_fetch.op_count == benchmark_fetch.total_ops) { terom@48: // done terom@48: assert(event_base_loopexit(ev_base, NULL) == 0); terom@48: terom@48: INFO("[benchmark] %.6f: %.6f req/s", terom@48: time_offset(), terom@48: benchmark_fetch.op_count / time_offset() terom@48: ); terom@48: } terom@48: } terom@48: terom@48: void benchmark_fetch_start () { terom@48: INFO( terom@48: "[benchmark] %0.6f: starting\n" terom@48: "\tconcurrent_ops = %u\n" terom@48: "\ttotal_ops = %u\n" terom@48: "\tkey_prefix = %s\n" terom@48: "\tkey_len_min = %u\n" terom@48: "\tkey_len_max = %u\n" terom@48: "\tkey_count = %u\n" terom@48: "\tdata_len_min = %u\n" terom@48: "\tdata_len_max = %u\n" terom@48: , time_offset(), terom@48: benchmark_fetch.concurrent_ops, benchmark_fetch.total_ops, terom@48: benchmark_fetch.key_prefix, benchmark_fetch.key_len_min, benchmark_fetch.key_len_max, benchmark_fetch.key_count, terom@48: benchmark_fetch.data_len_min, benchmark_fetch.data_len_max terom@48: ); terom@48: terom@48: time_reset(); terom@48: terom@48: benchmark_continue(); terom@48: terom@48: INFO("[benchmark] %0.6f: running", terom@48: time_offset() terom@48: ); terom@48: } terom@48: terom@48: terom@48: void benchmark_cb (struct memcache_req *req, void *arg) { terom@48: enum memcache_command cmd = memcache_req_cmd(req); terom@48: enum memcache_state state = memcache_req_state(req); terom@48: terom@48: if (state == MEMCACHE_STATE_ERROR) { terom@48: dump_req(req, arg); terom@48: FATAL("request failed"); terom@48: } terom@48: terom@48: if (state == MEMCACHE_STATE_DONE || state == MEMCACHE_STATE_DONE_DATA) { terom@48: if (cmd == MEMCACHE_CMD_FETCH_GET) { terom@48: benchmark_fetch.cur_ops--; terom@48: benchmark_fetch.op_count++; terom@48: terom@48: benchmark_continue(); terom@48: terom@48: } else if (cmd == MEMCACHE_CMD_STORE_SET) { terom@48: if (memcache_req_reply(req) != MEMCACHE_RPL_STORED) { terom@48: dump_req(req, arg); terom@48: WARNING("value was not stored"); terom@48: } terom@48: terom@48: benchmark_fetch.keys_stored++; terom@49: terom@49: if (ratelimit(benchmark_fetch.keys_stored, benchmark_fetch.key_count)) terom@48: INFO("[benchmark] %.6f: key %u/%u stored: %.*s", terom@48: time_offset(), benchmark_fetch.keys_stored, benchmark_fetch.key_count, (int) memcache_req_key(req)->len, memcache_req_key(req)->buf terom@48: ); terom@48: terom@48: if (benchmark_fetch.keys_stored == benchmark_fetch.key_count) terom@48: benchmark_fetch_start(); terom@48: } terom@48: terom@48: memcache_req_free(req); terom@48: } terom@48: } terom@48: terom@48: /* terom@48: * Run ops in parrallel, until we have completed total_ops, at which point we shut down. terom@48: */ terom@48: void benchmark_fetch_fn () { terom@48: static char data[BENCHMARK_DATA_MAX]; terom@48: char key_postfix[BENCHMARK_KEY_MAX]; terom@48: int i, key_len, data_len; terom@48: struct memcache_obj obj; terom@48: struct memcache_buf buf; terom@48: terom@48: assert(benchmark_fetch.key_len_min > 0 && (strlen(benchmark_fetch.key_prefix) + benchmark_fetch.key_len_max) < BENCHMARK_KEY_MAX); terom@48: assert(benchmark_fetch.data_len_min > 0 && benchmark_fetch.data_len_max < BENCHMARK_DATA_MAX); terom@48: terom@48: benchmark_fetch.cur_ops = benchmark_fetch.op_count = benchmark_fetch.keys_stored = 0; terom@48: terom@48: if ((benchmark_fetch.keys = calloc(benchmark_fetch.key_count, sizeof(struct key_buf))) == NULL) terom@48: FATAL("calloc"); terom@48: terom@48: // pregenerate the data terom@48: data_len = random_data(data, benchmark_fetch.data_len_min, benchmark_fetch.data_len_max); terom@48: terom@48: obj.flags = 0x1234; terom@48: obj.exptime = 0; terom@48: obj.bytes = data_len; terom@48: buf.data = data; terom@48: buf.len = buf.offset = data_len; terom@48: terom@48: // insert keys terom@48: INFO("[benchmark] %0.6f: inserting %u keys with prefix=%s and len=(%u -> %u)", terom@48: time_offset(), terom@48: benchmark_fetch.key_count, benchmark_fetch.key_prefix, benchmark_fetch.key_len_min, benchmark_fetch.key_len_max terom@48: ); terom@48: terom@48: for (i = 0; i < benchmark_fetch.key_count; i++) { terom@48: struct key_buf *keybuf = &benchmark_fetch.keys[i]; terom@48: terom@48: key_len = random_data(key_postfix, benchmark_fetch.key_len_min, benchmark_fetch.key_len_max); terom@48: terom@48: key_postfix[key_len] = '\0'; terom@48: terom@48: assert((keybuf->key.len = snprintf(keybuf->buf, BENCHMARK_KEY_MAX, "%s%*s", benchmark_fetch.key_prefix, key_len, key_postfix)) < BENCHMARK_KEY_MAX); terom@48: terom@48: keybuf->key.buf = keybuf->buf; terom@48: terom@48: assert(memcache_store(mc, MEMCACHE_CMD_STORE_SET, &keybuf->key, &obj, &buf, NULL) != NULL); terom@48: } terom@48: } terom@48: terom@49: void usage_opt (enum option_test test) { terom@49: struct opt *opt; terom@49: terom@49: for (opt = option_info + 1; opt->code; opt++) { terom@49: if (opt->test == test) { terom@49: switch (opt->type) { terom@49: case OPT_TYPE_BOOL: terom@49: INFO("\t%-20s %c %s", opt->name, opt->data.bool.default_value ? 't' : 'f', opt->descr); terom@49: break; terom@49: terom@49: case OPT_TYPE_UINT: terom@49: INFO("\t%-20s %-6d %s", opt->name, opt->data.uint.default_value, opt->descr); terom@49: break; terom@49: terom@49: case OPT_TYPE_STR: terom@49: INFO("\t%-20s %-6s %s", opt->name, opt->data.str.default_value, opt->descr); terom@49: break; terom@49: terom@49: default: terom@49: assert(0); terom@49: } terom@49: } terom@49: } terom@49: } terom@49: terom@48: void usage (char *cmd) { terom@49: struct test *test; terom@48: terom@49: INFO("Usage: %s [ ... ]", cmd); terom@49: terom@49: INFO("\nCOMMON OPTIONS\n"); terom@49: terom@49: usage_opt(TEST_COMMON); terom@49: terom@49: INFO("\nCOMMANDS\n"); terom@49: terom@49: for (test = test_list; test->name; test++) { terom@49: INFO("%s:", test->name); terom@49: INFO("\t%s", test->descr); terom@49: INFO("\t"); terom@49: terom@49: usage_opt(test->code); terom@49: terom@49: INFO("\t"); terom@49: } terom@48: terom@48: exit(1); terom@48: } terom@48: terom@38: int main (int argc, char **argv) { terom@48: char *name, *invalid; terom@48: struct test *test; terom@48: int c, option_index; terom@48: terom@48: // argument-parsing terom@48: if (argc < 2) { terom@48: WARNING("No command given"); terom@48: usage(argv[0]); terom@48: } terom@48: terom@48: // look up the test terom@48: name = argv[1]; terom@48: test = test_list; terom@48: terom@48: while (test->name && strcmp(test->name, name) != 0) terom@48: test++; terom@48: terom@48: if (!test->name) { terom@48: WARNING("Unknown cmd '%s'", name); terom@48: usage(argv[0]); terom@48: } terom@48: terom@48: // default values terom@48: for (c = OPT_CODE_INVALID; c < OPT_CODE_MAX; c++) { terom@48: switch (option_info[c].type) { terom@48: case OPT_TYPE_NONE: terom@48: break; terom@49: terom@49: case OPT_TYPE_BOOL: terom@49: *option_info[c].data.bool.value = option_info[c].data.bool.default_value; terom@49: terom@49: break; terom@48: terom@48: case OPT_TYPE_UINT: terom@48: *option_info[c].data.uint.value = option_info[c].data.uint.default_value; terom@48: terom@48: break; terom@48: terom@48: case OPT_TYPE_STR: terom@48: *option_info[c].data.str.value = option_info[c].data.str.default_value; terom@48: terom@48: break; terom@48: terom@48: default: terom@48: assert(0); terom@48: } terom@48: } terom@48: terom@48: while ((c = getopt_long(argc, argv, "", options, &option_index)) != -1) { terom@48: if (c <= OPT_CODE_INVALID || c >= OPT_CODE_MAX) terom@48: FATAL("invalid argument %s", options[option_index].name); terom@49: terom@49: if (option_info[c].test != test->code && option_info[c].test != TEST_COMMON) terom@49: FATAL("invalid option %s for test %s", options[option_index].name, test->name); terom@48: terom@48: switch (option_info[c].type) { terom@49: case OPT_TYPE_BOOL: terom@49: assert(optarg); terom@49: terom@49: switch (optarg[0]) { terom@49: case 't': terom@49: case '1': terom@49: *option_info[c].data.bool.value = 1; terom@49: break; terom@49: terom@49: case 'f': terom@49: case '0': terom@49: *option_info[c].data.bool.value = 0; terom@49: break; terom@49: terom@49: default: terom@49: FATAL("invalid true/false value: %s: %s", options[option_index].name, optarg); terom@49: } terom@49: terom@49: break; terom@49: terom@48: case OPT_TYPE_UINT: terom@48: assert(optarg); terom@48: terom@48: *option_info[c].data.uint.value = strtol(optarg, &invalid, 10); terom@48: terom@48: if (*invalid) terom@48: FATAL("invalid argument value: %s: %s (%s)", options[option_index].name, optarg, invalid); terom@48: terom@48: break; terom@48: terom@48: case OPT_TYPE_STR: terom@48: assert(optarg); terom@48: terom@48: *option_info[c].data.str.value = optarg; terom@48: terom@48: break; terom@48: terom@48: case OPT_TYPE_NONE: terom@48: default: terom@48: FATAL("invalid argument type %s", options[option_index].name); terom@48: terom@48: break; terom@48: } terom@48: } terom@48: terom@48: // libevent init terom@48: ev_base = event_init(); terom@38: terom@38: if (!ev_base) terom@38: FATAL("event_init"); terom@38: terom@48: // set up the memcache terom@48: mc_init(common.max_connections, test->cb_fn); terom@48: terom@48: // start timing terom@48: time_reset(); terom@48: terom@48: // start the test terom@48: test->test_fn(); terom@48: terom@48: INFO("[libevent] run"); terom@38: terom@38: // run the libevent mainloop terom@38: if (event_base_dispatch(ev_base)) terom@38: WARNING("event_dispatch"); terom@38: terom@48: INFO("[libevent] shutdown"); terom@38: terom@38: // clean up terom@38: event_base_free(ev_base); terom@38: terom@38: // successfull exit terom@38: return 0; terom@38: }