From 3d528ab093189ae2bee0aa7e79f07789d82c02d9 Mon Sep 17 00:00:00 2001 From: Brian Aker Date: Wed, 27 Jul 2011 03:13:33 -0700 Subject: [PATCH] Add test for memslap --- .bzrignore | 1 + clients/execute.cc | 13 ++- clients/memslap.cc | 250 ++++++++++++++++++++++------------------ libmemcached/connect.cc | 2 +- libtest/cmdline.cc | 9 ++ tests/include.am | 10 +- tests/memslap.cc | 168 +++++++++++++++++++++++++++ 7 files changed, 329 insertions(+), 124 deletions(-) create mode 100644 tests/memslap.cc diff --git a/.bzrignore b/.bzrignore index f091ff10..e1a61fbc 100644 --- a/.bzrignore +++ b/.bzrignore @@ -113,3 +113,4 @@ unittests/unittests out *.orig tests/memcapable +tests/memslap diff --git a/clients/execute.cc b/clients/execute.cc index 7f89f773..1ada836f 100644 --- a/clients/execute.cc +++ b/clients/execute.cc @@ -14,25 +14,28 @@ Return the number of rows set. */ -#include "config.h" +#include #include "execute.h" unsigned int execute_set(memcached_st *memc, pairs_st *pairs, unsigned int number_of) { - memcached_return_t rc; unsigned int x; unsigned int pairs_sent; for (x= 0, pairs_sent= 0; x < number_of; x++) { - rc= memcached_set(memc, pairs[x].key, pairs[x].key_length, - pairs[x].value, pairs[x].value_length, - 0, 0); + memcached_return_t rc= memcached_set(memc, pairs[x].key, pairs[x].key_length, + pairs[x].value, pairs[x].value_length, + 0, 0); if (rc != MEMCACHED_SUCCESS && rc != MEMCACHED_BUFFERED) + { fprintf(stderr, "Failured on insert of %.*s\n", (unsigned int)pairs[x].key_length, pairs[x].key); + } else + { pairs_sent++; + } } return pairs_sent; diff --git a/clients/memslap.cc b/clients/memslap.cc index d8927e7e..095cd088 100644 --- a/clients/memslap.cc +++ b/clients/memslap.cc @@ -37,18 +37,20 @@ #include -#include -#include -#include -#include -#include -#include -#include + +#include +#include +#include +#include #include -#include #include +#include #include -#include +#include +#include +#include +#include +#include #include @@ -65,23 +67,16 @@ #define PROGRAM_DESCRIPTION "Generates a load against a memcached custer of servers." /* Global Thread counter */ -volatile unsigned int thread_counter; -pthread_mutex_t counter_mutex; -pthread_cond_t count_threshhold; volatile unsigned int master_wakeup; pthread_mutex_t sleeper_mutex; pthread_cond_t sleep_threshhold; -void *run_task(void *p); - /* Types */ -typedef struct conclusions_st conclusions_st; -typedef struct thread_context_st thread_context_st; -typedef enum { +enum test_t { SET_TEST, GET_TEST, MGET_TEST -} test_type; +}; struct thread_context_st { unsigned int key_count; @@ -91,8 +86,37 @@ struct thread_context_st { unsigned int execute_number; char **keys; size_t *key_lengths; - test_type test; + test_t test; memcached_st *memc; + const memcached_st* root; + + thread_context_st(const memcached_st* memc_arg, test_t test_arg) : + key_count(0), + initial_pairs(NULL), + initial_number(0), + execute_pairs(NULL), + execute_number(0), + keys(0), + key_lengths(NULL), + test(test_arg), + memc(NULL), + root(memc_arg) + { + } + + void init() + { + memc= memcached_clone(NULL, root); + } + + ~thread_context_st() + { + if (execute_pairs) + { + pairs_free(execute_pairs); + } + memcached_free(memc); + } }; struct conclusions_st { @@ -100,6 +124,13 @@ struct conclusions_st { long int read_time; unsigned int rows_loaded; unsigned int rows_read; + + conclusions_st() : + load_time(0), + read_time(0), + rows_loaded(0), + rows_read() + { } }; /* Prototypes */ @@ -121,35 +152,72 @@ static unsigned int opt_concurrency= 0; static int opt_displayflag= 0; static char *opt_servers= NULL; static int opt_udp_io= 0; -test_type opt_test= SET_TEST; +test_t opt_test= SET_TEST; + +extern "C" { + +static void *run_task(void *p) +{ + thread_context_st *context= (thread_context_st *)p; + + context->init(); + + pthread_mutex_lock(&sleeper_mutex); + while (master_wakeup) + { + pthread_cond_wait(&sleep_threshhold, &sleeper_mutex); + } + pthread_mutex_unlock(&sleeper_mutex); + + /* Do Stuff */ + switch (context->test) + { + case SET_TEST: + assert(context->execute_pairs); + execute_set(context->memc, context->execute_pairs, context->execute_number); + break; + + case GET_TEST: + execute_get(context->memc, context->initial_pairs, context->initial_number); + break; + + case MGET_TEST: + execute_mget(context->memc, (const char*const*)context->keys, context->key_lengths, context->initial_number); + break; + } + + delete context; + + pthread_exit(0); +} + +} + int main(int argc, char *argv[]) { conclusions_st conclusion; - memcached_server_st *servers; - - memset(&conclusion, 0, sizeof(conclusions_st)); srandom((unsigned int)time(NULL)); options_parse(argc, argv); - if (!opt_servers) + if (opt_servers == NULL) { char *temp; if ((temp= getenv("MEMCACHED_SERVERS"))) + { opt_servers= strdup(temp); + } else { fprintf(stderr, "No Servers provided\n"); - exit(1); + return EXIT_FAILURE; } } - servers= memcached_servers_parse(opt_servers); + memcached_server_st *servers= memcached_servers_parse(opt_servers); - pthread_mutex_init(&counter_mutex, NULL); - pthread_cond_init(&count_threshhold, NULL); pthread_mutex_init(&sleeper_mutex, NULL); pthread_cond_init(&sleep_threshhold, NULL); @@ -157,31 +225,22 @@ int main(int argc, char *argv[]) free(opt_servers); - (void)pthread_mutex_destroy(&counter_mutex); - (void)pthread_cond_destroy(&count_threshhold); (void)pthread_mutex_destroy(&sleeper_mutex); (void)pthread_cond_destroy(&sleep_threshhold); conclusions_print(&conclusion); memcached_server_list_free(servers); - return 0; + return EXIT_SUCCESS; } void scheduler(memcached_server_st *servers, conclusions_st *conclusion) { unsigned int actual_loaded= 0; /* Fix warning */ - memcached_st *memc; struct timeval start_time, end_time; - pthread_t mainthread; /* Thread descriptor */ - pthread_attr_t attr; /* Thread attributes */ pairs_st *pairs= NULL; - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, - PTHREAD_CREATE_DETACHED); - - memc= memcached_create(NULL); + memcached_st *memc= memcached_create(NULL); /* We need to set udp behavior before adding servers to the client */ if (opt_udp_io) @@ -200,13 +259,14 @@ void scheduler(memcached_server_st *servers, conclusions_st *conclusion) if (opt_flush) flush_all(memc); + if (opt_createial_load) pairs= load_create_data(memc, opt_createial_load, &actual_loaded); char **keys= static_cast(calloc(actual_loaded, sizeof(char*))); size_t *key_lengths= static_cast(calloc(actual_loaded, sizeof(size_t))); - if (keys == NULL || key_lengths == NULL) + if (keys == NULL or key_lengths == NULL) { free(keys); free(key_lengths); @@ -226,23 +286,25 @@ void scheduler(memcached_server_st *servers, conclusions_st *conclusion) { if (opt_non_blocking_io) memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_NO_BLOCK, 1); + if (opt_tcp_nodelay) memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_TCP_NODELAY, 1); } - pthread_mutex_lock(&counter_mutex); - thread_counter= 0; - pthread_mutex_lock(&sleeper_mutex); master_wakeup= 1; pthread_mutex_unlock(&sleeper_mutex); - for (uint32_t x= 0; x < opt_concurrency; x++) + pthread_t *threads= new (std::nothrow) pthread_t[opt_concurrency]; + + if (not threads) { - thread_context_st *context; - context= (thread_context_st *)calloc(1, sizeof(thread_context_st)); + exit(EXIT_FAILURE); + } - context->memc= memcached_clone(NULL, memc); + for (uint32_t x= 0; x < opt_concurrency; x++) + { + thread_context_st *context= new thread_context_st(memc, opt_test); context->test= opt_test; context->initial_pairs= pairs; @@ -257,31 +319,25 @@ void scheduler(memcached_server_st *servers, conclusions_st *conclusion) } /* now you create the thread */ - if (pthread_create(&mainthread, &attr, run_task, - (void *)context) != 0) + if (pthread_create(threads +x, NULL, run_task, (void *)context) != 0) { fprintf(stderr,"Could not create thread\n"); exit(1); } - thread_counter++; } - pthread_mutex_unlock(&counter_mutex); - pthread_attr_destroy(&attr); - pthread_mutex_lock(&sleeper_mutex); master_wakeup= 0; pthread_mutex_unlock(&sleeper_mutex); pthread_cond_broadcast(&sleep_threshhold); - gettimeofday(&start_time, NULL); - /* - We loop until we know that all children have cleaned up. - */ - pthread_mutex_lock(&counter_mutex); - while (thread_counter) - pthread_cond_wait(&count_threshhold, &counter_mutex); - pthread_mutex_unlock(&counter_mutex); + + for (uint32_t x= 0; x < opt_concurrency; x++) + { + void *retval; + pthread_join(threads[x], &retval); + } + delete [] threads; gettimeofday(&end_time, NULL); @@ -331,6 +387,7 @@ void options_parse(int argc, char *argv[]) { case 0: break; + case OPT_UDP: if (opt_test == GET_TEST) { @@ -340,24 +397,31 @@ void options_parse(int argc, char *argv[]) } opt_udp_io= 1; break; + case OPT_BINARY: opt_binary = 1; break; + case OPT_VERBOSE: /* --verbose or -v */ opt_verbose = OPT_VERBOSE; break; + case OPT_DEBUG: /* --debug or -d */ opt_verbose = OPT_DEBUG; break; + case OPT_VERSION: /* --version or -V */ version_command(PROGRAM_NAME); break; + case OPT_HELP: /* --help or -h */ help_command(PROGRAM_NAME, PROGRAM_DESCRIPTION, long_options, help_options); break; + case OPT_SERVERS: /* --servers or -s */ opt_servers= strdup(optarg); break; + case OPT_SLAP_TEST: if (!strcmp(optarg, "get")) { @@ -370,7 +434,9 @@ void options_parse(int argc, char *argv[]) opt_test= GET_TEST ; } else if (!strcmp(optarg, "set")) + { opt_test= SET_TEST; + } else if (!strcmp(optarg, "mget")) { opt_test= MGET_TEST; @@ -381,24 +447,29 @@ void options_parse(int argc, char *argv[]) exit(1); } break; + case OPT_SLAP_CONCURRENCY: opt_concurrency= (unsigned int)strtoul(optarg, (char **)NULL, 10); break; + case OPT_SLAP_EXECUTE_NUMBER: opt_execute_number= (unsigned int)strtoul(optarg, (char **)NULL, 10); break; + case OPT_SLAP_INITIAL_LOAD: opt_createial_load= (unsigned int)strtoul(optarg, (char **)NULL, 10); break; + case '?': /* getopt_long already printed an error message. */ exit(1); + default: abort(); } } - if ((opt_test == GET_TEST || opt_test == MGET_TEST) && opt_createial_load == 0) + if ((opt_test == GET_TEST or opt_test == MGET_TEST) && opt_createial_load == 0) opt_createial_load= DEFAULT_INITIAL_LOAD; if (opt_execute_number == 0) @@ -423,54 +494,6 @@ void conclusions_print(conclusions_st *conclusion) conclusion->read_time % 1000); } -void *run_task(void *p) -{ - thread_context_st *context= (thread_context_st *)p; - memcached_st *memc; - - memc= context->memc; - - pthread_mutex_lock(&sleeper_mutex); - while (master_wakeup) - { - pthread_cond_wait(&sleep_threshhold, &sleeper_mutex); - } - pthread_mutex_unlock(&sleeper_mutex); - - /* Do Stuff */ - switch (context->test) - { - case SET_TEST: - assert(context->execute_pairs); - execute_set(memc, context->execute_pairs, context->execute_number); - break; - case GET_TEST: - execute_get(memc, context->initial_pairs, context->initial_number); - break; - case MGET_TEST: - execute_mget(memc, (const char*const*)context->keys, context->key_lengths, - context->initial_number); - break; - default: - WATCHPOINT_ASSERT(context->test); - break; - } - - memcached_free(memc); - - if (context->execute_pairs) - pairs_free(context->execute_pairs); - - free(context); - - pthread_mutex_lock(&counter_mutex); - thread_counter--; - pthread_cond_signal(&count_threshhold); - pthread_mutex_unlock(&counter_mutex); - - return NULL; -} - void flush_all(memcached_st *memc) { memcached_flush(memc, 0); @@ -479,14 +502,11 @@ void flush_all(memcached_st *memc) pairs_st *load_create_data(memcached_st *memc, unsigned int number_of, unsigned int *actual_loaded) { - memcached_st *memc_clone; - pairs_st *pairs; - - memc_clone= memcached_clone(NULL, memc); + memcached_st *memc_clone= memcached_clone(NULL, memc); /* We always used non-blocking IO for load since it is faster */ memcached_behavior_set(memc_clone, MEMCACHED_BEHAVIOR_NO_BLOCK, 0); - pairs= pairs_generate(number_of, 400); + pairs_st *pairs= pairs_generate(number_of, 400); *actual_loaded= execute_set(memc_clone, pairs, number_of); memcached_free(memc_clone); diff --git a/libmemcached/connect.cc b/libmemcached/connect.cc index 8727b289..e3638b46 100644 --- a/libmemcached/connect.cc +++ b/libmemcached/connect.cc @@ -131,7 +131,7 @@ static memcached_return_t set_hostinfo(memcached_server_st *server) char str_port[NI_MAXSERV]; int length= snprintf(str_port, NI_MAXSERV, "%u", (uint32_t)server->port); - if (length >= NI_MAXSERV || length < 0) + if (length >= NI_MAXSERV or length < 0) { return MEMCACHED_FAILURE; } diff --git a/libtest/cmdline.cc b/libtest/cmdline.cc index 2b67a47d..32d0809d 100644 --- a/libtest/cmdline.cc +++ b/libtest/cmdline.cc @@ -48,13 +48,22 @@ bool exec_cmdline(const std::string& executable, const char *args[]) arg_buffer << "./libtool --mode=execute "; + if (getenv("LIBTEST_TEST_ENVIRONMENT")) + { + arg_buffer << getenv("LIBTEST_TEST_ENVIRONMENT"); + arg_buffer << " "; + } + arg_buffer << executable; for (const char **ptr= args; *ptr; ++ptr) { arg_buffer << " " << *ptr; } +#if 0 arg_buffer << " > /dev/null 2>&1"; +#endif + std::cerr << std::endl << arg_buffer.str() << std::endl; if (system(arg_buffer.str().c_str()) == -1) { return false; diff --git a/tests/include.am b/tests/include.am index 954f32b2..2dfb8f38 100644 --- a/tests/include.am +++ b/tests/include.am @@ -124,6 +124,13 @@ tests_memcapable_LDADD= $(tests_memcapable_DEPENDENCIES) check_PROGRAMS+= tests/memcapable noinst_PROGRAMS+= tests/memcapable +tests_memslap_SOURCES= tests/memslap.cc +tests_memslap_CXXFLAGS= $(AM_CXXFLAGS) $(NO_EFF_CXX) +tests_memslap_DEPENDENCIES= libtest/libtest.la $(TESTS_LDADDS) +tests_memslap_LDADD= $(tests_memslap_DEPENDENCIES) +check_PROGRAMS+= tests/memslap +noinst_PROGRAMS+= tests/memslap + test: check check-local: tests/var $(TEST_DOCS) @@ -284,9 +291,6 @@ gdb-hashplus: tests/hash_plus gdb-cycle: tests/cycle @$(DEBUG_COMMAND) tests/cycle -gdb-memslap: clients/memslap - @$(DEBUG_COMMAND) clients/memslap - valgrind-cycle: tests/cycle $(VALGRIND_COMMAND) tests/cycle diff --git a/tests/memslap.cc b/tests/memslap.cc new file mode 100644 index 00000000..45bb6e5b --- /dev/null +++ b/tests/memslap.cc @@ -0,0 +1,168 @@ +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * Test memslap + * + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +/* + Test that we are cycling the servers we are creating during testing. +*/ + +#include + +#include +#include + +using namespace libtest; + +#ifndef __INTEL_COMPILER +#pragma GCC diagnostic ignored "-Wstrict-aliasing" +#endif + +static std::string executable; + +static test_return_t help_test(void *) +{ + const char *args[]= { "--help", 0 }; + + test_success(exec_cmdline(executable, args)); + return TEST_SUCCESS; +} + +static test_return_t server_test(void *) +{ + char buffer[1024]; + snprintf(buffer, sizeof(buffer), "--servers=localhost:%d", int(default_port())); + const char *args[]= { buffer, 0 }; + + test_success(exec_cmdline(executable, args)); + return TEST_SUCCESS; +} + +static test_return_t server_concurrency_test(void *) +{ + char buffer[1024]; + snprintf(buffer, sizeof(buffer), "--servers=localhost:%d", int(default_port())); + const char *args[]= { buffer, "--concurrency=10", 0 }; + + test_success(exec_cmdline(executable, args)); + return TEST_SUCCESS; +} + +static test_return_t server_concurrency_initial_load_test(void *) +{ + char buffer[1024]; + snprintf(buffer, sizeof(buffer), "--servers=localhost:%d", int(default_port())); + const char *args[]= { buffer, "--concurrency=10", "--initial-load=1000", 0 }; + + test_success(exec_cmdline(executable, args)); + return TEST_SUCCESS; +} + +static test_return_t server_concurrency_initial_load_execute_number_test(void *) +{ + char buffer[1024]; + snprintf(buffer, sizeof(buffer), "--servers=localhost:%d", int(default_port())); + const char *args[]= { buffer, "--concurrency=10", "--initial-load=1000", "--execute-number=10", 0 }; + + test_success(exec_cmdline(executable, args)); + return TEST_SUCCESS; +} + +static test_return_t server_concurrency_initial_load_execute_number_test_get_test(void *) +{ + char buffer[1024]; + snprintf(buffer, sizeof(buffer), "--servers=localhost:%d", int(default_port())); + const char *args[]= { buffer, "--concurrency=10", "--initial-load=1000", "--execute-number=10", "--test=get", 0 }; + + test_success(exec_cmdline(executable, args)); + return TEST_SUCCESS; +} + +static test_return_t server_concurrency_initial_load_execute_number_test_set_test(void *) +{ + char buffer[1024]; + snprintf(buffer, sizeof(buffer), "--servers=localhost:%d", int(default_port())); + const char *args[]= { buffer, "--concurrency=10", "--initial-load=1000", "--execute-number=10", "--test=set", 0 }; + + test_success(exec_cmdline(executable, args)); + return TEST_SUCCESS; +} + +static test_return_t server_concurrency_initial_load_execute_number_test_set_non_blocking_test(void *) +{ + char buffer[1024]; + snprintf(buffer, sizeof(buffer), "--servers=localhost:%d", int(default_port())); + const char *args[]= { buffer, "--concurrency=10", "--initial-load=1000", "--execute-number=10", "--test=set", "--non-blocking", 0 }; + + test_success(exec_cmdline(executable, args)); + return TEST_SUCCESS; +} + +test_st memslap_tests[] ={ + {"--help", true, help_test }, + {"--server_test", true, server_test }, + {"--concurrency=10", true, server_concurrency_test }, + {"--initial-load=1000", true, server_concurrency_initial_load_test }, + {"--execute-number=10", true, server_concurrency_initial_load_execute_number_test }, + {"--test=get", true, server_concurrency_initial_load_execute_number_test_get_test }, + {"--test=set", true, server_concurrency_initial_load_execute_number_test_set_test }, + {"--test=set --non-blockin", true, server_concurrency_initial_load_execute_number_test_set_non_blocking_test }, + {0, 0, 0} +}; + +collection_st collection[] ={ + {"memslap", 0, 0, memslap_tests }, + {0, 0, 0, 0} +}; + +static void *world_create(server_startup_st& servers, test_return_t& error) +{ + const char *argv[1]= { "memslap" }; + if (not server_startup(servers, "memcached", MEMCACHED_DEFAULT_PORT +10, 1, argv)) + { + error= TEST_FAILURE; + } + + return &servers; +} + + +void get_world(Framework *world) +{ + executable= "./clients/memslap"; + world->collections= collection; + world->_create= world_create; +} + -- 2.30.2