diff --git a/include/asterisk/hashtab.h b/include/asterisk/hashtab.h index 89d293759ab86c04774ccedf2881aa97dab4cc4d..17eff704829e459db189700e088dfd411e81e38b 100644 --- a/include/asterisk/hashtab.h +++ b/include/asterisk/hashtab.h @@ -23,6 +23,9 @@ * \brief Generic (perhaps overly so) hashtable implementation * \ref AstHash */ + +#include "asterisk/lock.h" + /*! \page AstHash Hash Table support in Asterisk A hash table is a structure that allows for an exact-match search diff --git a/tests/test_astobj2_thrash.c b/tests/test_astobj2_thrash.c new file mode 100644 index 0000000000000000000000000000000000000000..f43445a051592348875608467cfc353c0325a600 --- /dev/null +++ b/tests/test_astobj2_thrash.c @@ -0,0 +1,353 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2012, David M. Lee, II + * + * David M. Lee, II <dlee@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/* + *! \file \brief Thrash a astobj2 container, for fun and profit. + * + * \author\verbatim David M. Lee, II <dlee@digium.com> \endverbatim + * + * Inspired by the original hashtest2.c by Steve Murphy <murf@digium.com>. This test runs + * several threads manipulatings a concurrent astobj2 container to see if they maintain + * consistency. While the tests attempt to check consistency and error normally, threading + * errors often result in segfaults. + * \ingroup tests + */ + +/*** MODULEINFO + <depend>TEST_FRAMEWORK</depend> + <support_level>core</support_level> + ***/ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") +#include <pthread.h> +#include "asterisk/astobj2.h" +#include "asterisk/hashtab.h" +#include "asterisk/lock.h" +#include "asterisk/module.h" +#include "asterisk/test.h" +#include "asterisk/time.h" +#include "asterisk/utils.h" + +#define MAX_HASH_ENTRIES 15000 +#define MAX_TEST_SECONDS 60 + +struct hash_test { + /*! Unit under test */ + struct ao2_container *to_be_thrashed; + /*! Number of entries to insert in the grow thread. */ + int max_grow; + /*! Number of enteries added by the grow thread. */ + int grow_count; + /*! Entries preloaded into the hashtab; to be deleted by the shrink thread */ + int preload; + /*! When to give up on the tests */ + struct timeval deadline; +}; + +static int alloc_count = 0; + +static int is_timed_out(struct hash_test const *data) { + return ast_tvdiff_us(data->deadline, ast_tvnow()) < 0; +} + +/*! /brief Free test element */ +static void ht_delete(void *obj) +{ + ast_atomic_fetchadd_int(&alloc_count, -1); +} + +/*! /brief Create test element */ +static char *ht_new(int i) +{ + const int buflen = 12; + char *keybuf = ao2_alloc(buflen, ht_delete); + int needed; + if (keybuf == NULL) { + return NULL; + } + needed = snprintf(keybuf, buflen, "key%08x", i); + ast_atomic_fetchadd_int(&alloc_count, 1); + ast_assert(needed + 1 <= buflen); + return keybuf; +} + +/*! /brief Grow the hash data as specified */ +static void *hash_test_grow(void *d) +{ + struct hash_test *data = d; + int i; + + for (i = 0; i < data->max_grow; ++i) { + char *ht; + if (is_timed_out(data)) { + printf("Growth timed out at %d\n", i); + return "Growth timed out"; + } + ht = ht_new(i); + if (ht == NULL) { + return "Allocation failed"; + } + ao2_link(data->to_be_thrashed, ht); + ao2_ref(ht, -1); + ast_atomic_fetchadd_int(&data->grow_count, 1); + } + return NULL; +} + +/*! Randomly lookup data in the hash */ +static void *hash_test_lookup(void *d) +{ + struct hash_test *data = d; + int max; + unsigned seed = time(NULL); + + /* ast_atomic_fetchadd_int provide a memory fence so that the optimizer doesn't + * optimize away reads. + */ + while ((max = ast_atomic_fetchadd_int(&data->grow_count, 0)) < data->max_grow) { + int i; + char *obj; + char *from_ao2; + + if (is_timed_out(data)) { + return "Lookup timed out"; + } + + if (max == 0) { + /* No data yet; yield and try again */ + sched_yield(); + continue; + } + + /* Randomly lookup one object from the hash */ + i = rand_r(&seed) % max; + obj = ht_new(i); + if (obj == NULL) { + return "Allocation failed"; + } + from_ao2 = ao2_find(data->to_be_thrashed, obj, OBJ_POINTER); + ao2_ref(obj, -1); + ao2_ref(from_ao2, -1); + if (from_ao2 == NULL) { + return "Key unexpectedly missing"; + } + } + + return NULL; +} + +/*! Delete entries from the hash */ +static void *hash_test_shrink(void *d) +{ + const struct hash_test *data = d; + int i; + + for (i = 1; i < data->preload; ++i) { + char *obj = ht_new(-i); + char *from_ao2; + + if (obj == NULL) { + return "Allocation failed"; + } + from_ao2 = ao2_find(data->to_be_thrashed, obj, OBJ_UNLINK | OBJ_POINTER); + + ao2_ref(obj, -1); + if (from_ao2) { + ao2_ref(from_ao2, -1); + } else { + return "Could not find object to delete"; + } + + if (is_timed_out(data)) { + return "Shrink timed out"; + } + } + + return NULL; +} + +/*! ao2_callback for hash_test_count */ +static int increment_count(void *obj, void *arg, int flags) { + char *ht = obj; + int *count = arg; + if (strncmp(ht, "key0", 4) == 0) { + ++(*count); + } + return 0; +} + +/*! Continuously iterate through all the entries in the hash */ +static void *hash_test_count(void *d) +{ + const struct hash_test *data = d; + int count = 0; + int last_count = 0; + + while (count < data->max_grow) { + last_count = count; + count = 0; + ao2_callback(data->to_be_thrashed, OBJ_MULTIPLE, increment_count, &count); + + if (last_count == count) { + /* Allow other threads to run. */ + sched_yield(); + } else if (last_count > count) { + /* Make sure the ao2 container never shrinks */ + return "ao2 container unexpectedly shrank"; + } + + if (is_timed_out(data)) { + return "Count timed out"; + } + } + + /* Successfully iterated over all of the expected elements */ + return NULL; +} + +static int hash_string(const void *obj, const int flags) +{ + return ast_hashtab_hash_string_nocase(obj); +} + +static int compare_strings(void *lhs, void *rhs, int flags) +{ + const char *lhs_str = lhs; + const char *rhs_str = rhs; + if (strcasecmp(lhs_str, rhs_str) == 0) { + return CMP_MATCH | CMP_STOP; + } else { + return 0; + } +} + +AST_TEST_DEFINE(hash_test) +{ + enum ast_test_result_state res = AST_TEST_PASS; + struct hash_test data = {}; + pthread_t grow_thread, count_thread, lookup_thread, shrink_thread; + void *thread_results; + int i; + + switch (cmd) { + case TEST_INIT: + info->name = "thrash"; + info->category = "/main/astobj2/"; + info->summary = "Testing astobj2 container concurrency"; + info->description = "Test astobj2 container concurrency correctness."; + return AST_TEST_NOT_RUN; + case TEST_EXECUTE: + break; + } + + ast_test_status_update(test, "Executing hash concurrency test...\n"); + data.preload = MAX_HASH_ENTRIES / 2; + data.max_grow = MAX_HASH_ENTRIES - data.preload; + data.deadline = ast_tvadd(ast_tvnow(), ast_tv(MAX_TEST_SECONDS, 0)); + data.to_be_thrashed = ao2_container_alloc(MAX_HASH_ENTRIES / 100, hash_string, + compare_strings); + + if (data.to_be_thrashed == NULL) { + ast_test_status_update(test, "Allocation failed\n"); + /* Nothing needs to be freed; early return is fine */ + return AST_TEST_FAIL; + } + + /* preload with data to delete */ + for (i = 1; i < data.preload; ++i) { + char *ht = ht_new(-i); + if (ht == NULL) { + ast_test_status_update(test, "Allocation failed\n"); + ao2_ref(data.to_be_thrashed, -1); + return AST_TEST_FAIL; + } + ao2_link(data.to_be_thrashed, ht); + ao2_ref(ht, -1); + } + + /* add data.max_grow entries to the ao2 container */ + ast_pthread_create(&grow_thread, NULL, hash_test_grow, &data); + /* continually count the keys added by the grow thread */ + ast_pthread_create(&count_thread, NULL, hash_test_count, &data); + /* continually lookup keys added by the grow thread */ + ast_pthread_create(&lookup_thread, NULL, hash_test_lookup, &data); + /* delete all keys preloaded into the ao2 container */ + ast_pthread_create(&shrink_thread, NULL, hash_test_shrink, &data); + + pthread_join(grow_thread, &thread_results); + if (thread_results != NULL) { + ast_test_status_update(test, "Growth thread failed: %s\n", + (char *)thread_results); + res = AST_TEST_FAIL; + } + + pthread_join(count_thread, &thread_results); + if (thread_results != NULL) { + ast_test_status_update(test, "Count thread failed: %s\n", + (char *)thread_results); + res = AST_TEST_FAIL; + } + + pthread_join(lookup_thread, &thread_results); + if (thread_results != NULL) { + ast_test_status_update(test, "Lookup thread failed: %s\n", + (char *)thread_results); + res = AST_TEST_FAIL; + } + + pthread_join(shrink_thread, &thread_results); + if (thread_results != NULL) { + ast_test_status_update(test, "Shrink thread failed: %s\n", + (char *)thread_results); + res = AST_TEST_FAIL; + } + + if (ao2_container_count(data.to_be_thrashed) != data.max_grow) { + ast_test_status_update(test, + "Invalid ao2 container size. Expected: %d, Actual: %d\n", + data.max_grow, ao2_container_count(data.to_be_thrashed)); + res = AST_TEST_FAIL; + } + + ao2_ref(data.to_be_thrashed, -1); + + /* check for object leaks */ + if (ast_atomic_fetchadd_int(&alloc_count, 0) != 0) { + ast_test_status_update(test, "Leaked %d objects!\n", + ast_atomic_fetchadd_int(&alloc_count, 0)); + res = AST_TEST_FAIL; + } + + return res; +} + +static int unload_module(void) +{ + AST_TEST_UNREGISTER(hash_test); + return 0; +} + +static int load_module(void) +{ + AST_TEST_REGISTER(hash_test); + return AST_MODULE_LOAD_SUCCESS; +} + +AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "astobj2 container thrash test"); diff --git a/tests/test_hashtab_thrash.c b/tests/test_hashtab_thrash.c new file mode 100644 index 0000000000000000000000000000000000000000..373922bf84976392e3a1d810344df1e7f4de3974 --- /dev/null +++ b/tests/test_hashtab_thrash.c @@ -0,0 +1,321 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2012, David M. Lee, II + * + * David M. Lee, II <dlee@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/* + *! \file \brief Thrash a hash table, for fun and profit. + * + * \author\verbatim David M. Lee, II <dlee@digium.com> \endverbatim + * + * Inspired by the original hashtest.c by Steve Murphy <murf@digium.com>. This test runs + * several threads manipulatings a concurrent hastab to see if they maintain + * consistency. While the tests attempt to check consistency and error normally, threading + * errors often result in segfaults. + * \ingroup tests + */ + +/*** MODULEINFO + <depend>TEST_FRAMEWORK</depend> + <support_level>core</support_level> + ***/ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") +#include <pthread.h> +#include "asterisk/hashtab.h" +#include "asterisk/lock.h" +#include "asterisk/module.h" +#include "asterisk/test.h" +#include "asterisk/time.h" +#include "asterisk/utils.h" + +#define MAX_HASH_ENTRIES 30000 +#define MAX_TEST_SECONDS 60 + +struct hash_test { + /*! Unit under test */ + struct ast_hashtab *to_be_thrashed; + /*! Number of entries to insert in the grow thread. */ + int max_grow; + /*! Number of enteries added by the grow thread. */ + int grow_count; + /*! Entries preloaded into the hashtab; to be deleted by the shrink thread */ + int preload; + /*! When to give up on the tests */ + struct timeval deadline; +}; + +static int is_timed_out(struct hash_test const *data) { + return ast_tvdiff_us(data->deadline, ast_tvnow()) < 0; +} + +/*! /brief Create test element */ +static char *ht_new(int i) +{ + const int buflen = 12; + char *keybuf = ast_malloc(buflen); + int needed; + if (keybuf == NULL) { + return NULL; + } + needed = snprintf(keybuf, buflen, "key%08x", i); + ast_assert(needed + 1 <= buflen); + return keybuf; +} + +/*! /brief Free test element */ +static void ht_delete(void *obj) +{ + ast_free(obj); +} + +/*! /brief Grow the hash data as specified */ +static void *hash_test_grow(void *d) +{ + struct hash_test *data = d; + int i; + + for (i = 0; i < data->max_grow; ++i) { + char *obj; + if (is_timed_out(data)) { + return "Growth timed out"; + } + obj = ht_new(i); + if (obj == NULL) { + return "Allocation failed"; + } + ast_hashtab_insert_immediate(data->to_be_thrashed, obj); + ast_atomic_fetchadd_int(&data->grow_count, 1); + } + return NULL; +} + +/*! Randomly lookup data in the hash */ +static void *hash_test_lookup(void *d) +{ + struct hash_test *data = d; + int max; + unsigned seed = time(NULL); + + /* ast_atomic_fetchadd_int provide a memory fence so that the optimizer doesn't + * optimize away reads. + */ + while ((max = ast_atomic_fetchadd_int(&data->grow_count, 0)) < data->max_grow) { + int i; + char *obj; + int is_in_hashtab; + + if (is_timed_out(data)) { + return "Lookup timed out"; + } + + if (max == 0) { + /* No data yet; yield and try again */ + sched_yield(); + continue; + } + + /* Randomly lookup one object from the hash */ + i = rand_r(&seed) % max; + obj = ht_new(i); + if (obj == NULL) { + return "Allocation failed."; + } + is_in_hashtab = (ast_hashtab_lookup(data->to_be_thrashed, obj) != NULL); + ht_delete(obj); + if (!is_in_hashtab) { + return "key unexpectedly missing"; + } + } + + return NULL; +} + +/*! Delete entries from the hash */ +static void *hash_test_shrink(void *d) +{ + const struct hash_test *data = d; + int i; + + for (i = 1; i < data->preload; ++i) { + char *obj = ht_new(-i); + char *from_hashtab; + int deleted; + + if (obj == NULL) { + return "Allocation failed"; + } + from_hashtab = ast_hashtab_remove_object_via_lookup(data->to_be_thrashed, obj); + deleted = from_hashtab != NULL; + + ht_delete(obj); + ht_delete(from_hashtab); + if (!deleted) { + return "could not delete object"; + } + if (is_timed_out(data)) { + return "Shrink timed out"; + } + } + return NULL; +} + +/*! Continuously iterate through all the entries in the hash */ +static void *hash_test_count(void *d) +{ + const struct hash_test *data = d; + int count = 0; + int last_count = 0; + + while (count < data->max_grow) { + struct ast_hashtab_iter *it = ast_hashtab_start_write_traversal(data->to_be_thrashed); + char *ht = ast_hashtab_next(it); + last_count = count; + count = 0; + while (ht) { + /* only count keys added by grow thread */ + if (strncmp(ht, "key0", 4) == 0) { + ++count; + } + ht = ast_hashtab_next(it); + } + ast_hashtab_end_traversal(it); + + if (last_count == count) { + /* Allow other threads to run. */ + sched_yield(); + } else if (last_count > count) { + /* Make sure the hashtable never shrinks */ + return "hashtab unexpectedly shrank"; + } + + if (is_timed_out(data)) { + return "Count timed out"; + } + } + + /* Successfully iterated over all of the expected elements */ + return NULL; +} + +AST_TEST_DEFINE(hash_test) +{ + enum ast_test_result_state res = AST_TEST_PASS; + struct hash_test data = {}; + pthread_t grow_thread, count_thread, lookup_thread, shrink_thread; + void *thread_results; + int i; + + switch (cmd) { + case TEST_INIT: + info->name = "thrash"; + info->category = "/main/hashtab/"; + info->summary = "Testing hashtab concurrency"; + info->description = "Test hashtab concurrency correctness."; + return AST_TEST_NOT_RUN; + case TEST_EXECUTE: + break; + } + + ast_test_status_update(test, "Executing hash concurrency test...\n"); + data.preload = MAX_HASH_ENTRIES / 2; + data.max_grow = MAX_HASH_ENTRIES - data.preload; + data.deadline = ast_tvadd(ast_tvnow(), ast_tv(MAX_TEST_SECONDS, 0)); + data.to_be_thrashed = ast_hashtab_create(MAX_HASH_ENTRIES / 100, + ast_hashtab_compare_strings_nocase, ast_hashtab_resize_java, + ast_hashtab_newsize_java, ast_hashtab_hash_string_nocase, 1); + + if (data.to_be_thrashed == NULL) { + ast_test_status_update(test, "Allocation failed\n"); + /* Nothing needs to be freed; early return is fine */ + return AST_TEST_FAIL; + } + + + /* preload with data to delete */ + for (i = 1; i < data.preload; ++i) { + char *obj = ht_new(-i); + if (obj == NULL) { + ast_test_status_update(test, "Allocation failed\n"); + ast_hashtab_destroy(data.to_be_thrashed, ht_delete); + return AST_TEST_FAIL; + } + ast_hashtab_insert_immediate(data.to_be_thrashed, obj); + } + + /* add data.max_grow entries to the hashtab */ + ast_pthread_create(&grow_thread, NULL, hash_test_grow, &data); + /* continually count the keys added by the grow thread */ + ast_pthread_create(&count_thread, NULL, hash_test_count, &data); + /* continually lookup keys added by the grow thread */ + ast_pthread_create(&lookup_thread, NULL, hash_test_lookup, &data); + /* delete all keys preloaded into the hashtab */ + ast_pthread_create(&shrink_thread, NULL, hash_test_shrink, &data); + + pthread_join(grow_thread, &thread_results); + if (thread_results != NULL) { + ast_test_status_update(test, "Growth thread failed: %s\n", + (char *)thread_results); + res = AST_TEST_FAIL; + } + + pthread_join(count_thread, &thread_results); + if (thread_results != NULL) { + ast_test_status_update(test, "Count thread failed: %s\n", + (char *)thread_results); + res = AST_TEST_FAIL; + } + + pthread_join(lookup_thread, &thread_results); + if (thread_results != NULL) { + ast_test_status_update(test, "Lookup thread failed: %s\n", + (char *)thread_results); + res = AST_TEST_FAIL; + } + + pthread_join(shrink_thread, &thread_results); + if (thread_results != NULL) { + ast_test_status_update(test, "Shrink thread failed: %s\n", + (char *)thread_results); + res = AST_TEST_FAIL; + } + + if (ast_hashtab_size(data.to_be_thrashed) != data.max_grow) { + ast_test_status_update(test, + "Invalid hashtab size. Expected: %d, Actual: %d\n", + data.max_grow, ast_hashtab_size(data.to_be_thrashed)); + res = AST_TEST_FAIL; + } + + ast_hashtab_destroy(data.to_be_thrashed, ht_delete); + return res; +} + +static int unload_module(void) +{ + AST_TEST_UNREGISTER(hash_test); + return 0; +} + +static int load_module(void) +{ + AST_TEST_REGISTER(hash_test); + return AST_MODULE_LOAD_SUCCESS; +} + +AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Hash test"); diff --git a/utils/Makefile b/utils/Makefile index aff39ce78e4ab556dc9a8b6b85c6ef2f6c35ce64..4bd898ce798f091770f46a729734cd2e4bdd5107 100644 --- a/utils/Makefile +++ b/utils/Makefile @@ -88,7 +88,7 @@ clean: rm -f *.s *.i rm -f md5.c strcompat.c ast_expr2.c ast_expr2.h ast_expr2f.c pbx_ael.c pval.c hashtab.c lock.c rm -f aelparse.c aelbison.c conf2ael - rm -f utils.c strings.c threadstorage.c sha1.c astobj2.c hashtest2 hashtest refcounter + rm -f utils.c strings.c threadstorage.c sha1.c astobj2.c refcounter rm -f db1-ast/.*.d @$(MAKE) -C db1-ast clean @@ -175,11 +175,6 @@ threadstorage.c: $(ASTTOPDIR)/main/threadstorage.c $(ECHO_PREFIX) echo " [CP] $(subst $(ASTTOPDIR)/,,$<) -> $@" $(CMD_PREFIX) cp "$<" "$@" -hashtest2.o: _ASTCFLAGS+=-O0 -hashtest2: hashtest2.o md5.o lock.o utils.o strings.o astobj2.o sha1.o strcompat.o threadstorage.o clicompat.o poll.o - -hashtest: hashtest.o md5.o hashtab.o lock.o utils.o strings.o sha1.o strcompat.o threadstorage.o clicompat.o poll.o -hashtest.o: _ASTCFLAGS+=-O0 refcounter: refcounter.o md5.o hashtab.o lock.o utils.o strings.o sha1.o strcompat.o threadstorage.o clicompat.o poll.o refcounter.o: _ASTCFLAGS+=-O0 diff --git a/utils/extconf.c b/utils/extconf.c index 43cf2652a9af4d920221634543162999b1bf7cfb..c62001a7768445cc945896f5f57fc1013058f3c1 100644 --- a/utils/extconf.c +++ b/utils/extconf.c @@ -168,6 +168,7 @@ void ast_console_puts(const char *string); #define LOG_DTMF __LOG_DTMF, _A_ /* lock.h */ +#define _ASTERISK_LOCK_H /* A small indication that this is horribly wrong. */ #ifndef HAVE_MTX_PROFILE #define __MTX_PROF(a) return pthread_mutex_lock((a)) diff --git a/utils/hashtest.c b/utils/hashtest.c deleted file mode 100644 index c06d4ac0adadd353054f36bd5f4affa249d8c157..0000000000000000000000000000000000000000 --- a/utils/hashtest.c +++ /dev/null @@ -1,410 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 2007, Steve Murphy - * - * Steve Murphy <murf@digium.com> - * - * See http://www.asterisk.org for more information about - * the Asterisk project. Please do not directly contact - * any of the maintainers of this project for assistance; - * the project provides a web site, mailing lists and IRC - * channels for your use. - * - * This program is free software, distributed under the terms of - * the GNU General Public License Version 2. See the LICENSE file - * at the top of the source tree. - */ -/*! \file - * - * \brief A program to thoroughly thrash a hash table, testing - * out locking safety, and making sure all functionality - * is functioning. Run with 5 or more threads to get that - * fully intense firestorm of activity. If your - * hash tables don't crash, lock up, or go weird, it must - * be good code! Even features some global counters - * that will get slightly behind because they aren't lock-protected. - * - * \author Steve Murphy <murf@digium.com> - */ - -/*** MODULEINFO - <support_level>extended</support_level> - ***/ - -#include "asterisk.h" -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include <pthread.h> -#include <sys/stat.h> -#include <signal.h> -#include <errno.h> -#include "asterisk/lock.h" -#include "asterisk/hashtab.h" -#include "asterisk/channel.h" -#include "asterisk/utils.h" -#include "asterisk/module.h" -int testno = 1; - -/* stuff we need to make this work with the hashtab stuff */ -#if !defined(LOW_MEMORY) -int64_t ast_mark(int prof_id, int x) -{ - return 0; -} -#endif - -void pbx_substitute_variables_helper_full(struct ast_channel *c, struct varshead *headp, const char *cp1, char *cp2, int cp2_size, size_t *used); -void pbx_substitute_variables_helper_full(struct ast_channel *c, struct varshead *headp, const char *cp1, char *cp2, int cp2_size, size_t *used) -{ -} - -struct ht_element -{ - char *key; - char *val; -}; - -static int hashtab_compare_strings_nocase(const void *a, const void *b) -{ - const struct ht_element *ae = a, *be = b; - return ast_hashtab_compare_strings_nocase(ae->key, be->key); -} - -#if 0 -static int hashtab_compare_strings(const void *a, const void *b) -{ - const struct ht_element *ae = a, *be = b; - return ast_hashtab_compare_strings(ae->key, be->key); -} - -static unsigned int hashtab_hash_string(const void *obj) -{ - const struct ht_element *o = obj; - return ast_hashtab_hash_string((const void *)o->key); -} -#endif - -static unsigned int hashtab_hash_string_nocase(const void *obj) -{ - const struct ht_element *o = obj; - return ast_hashtab_hash_string_nocase((const void*)o->key); -} - -/* random numbers */ - -static int my_rand(int incl_low, int incl_high, unsigned int *seedp) -{ - if (incl_high == 0) - return 0; - - return incl_low + (rand_r(seedp) % incl_high); -} - - - - -/* the testing routines */ - -static int glob_highwater = 0; -struct ast_hashtab *glob_hashtab = 0; -unsigned int glob_seed = 0; -int els_removed = 0; -int els_added = 0; -int els_lookedup = 0; -int els_found = 0; -int els_traversals = 0; - -/* all the operations to perform on the hashtab */ - -static void add_element(void) -{ - char keybuf[100]; - struct ht_element *x = malloc(sizeof(struct ht_element)); - sprintf(keybuf,"key%08d", glob_highwater++); - x->key = strdup(keybuf); - x->val = strdup("interesting data"); - ast_hashtab_insert_immediate(glob_hashtab, x); - els_added++; -} - -static void traverse_elements(void) -{ - struct ht_element *el; - int c=0; - struct ast_hashtab_iter *it = ast_hashtab_start_write_traversal(glob_hashtab); -#ifdef DEBUG - printf("Traverse hashtab\n"); -#endif - while ((el = ast_hashtab_next(it))) { - c++; - } - ast_hashtab_end_traversal(it); - els_traversals++; /* unprotected, sometimes off, but, not really important, either */ -} - -static void * del_element(unsigned int *seedp) -{ - char keybuf[100]; - struct ht_element *el, lookup; - int x; - - /* pick a random element from 0 to highwater-1 */ - x = my_rand(0,glob_highwater-1,seedp); - sprintf(keybuf, "key%08d", x); -#ifdef DEBUG - printf("Removing %s", keybuf); -#endif - lookup.key = keybuf; - el = ast_hashtab_remove_object_via_lookup(glob_hashtab, &lookup); - - if (el) { -#ifdef DEBUG - printf("...YES (el=%x)\n", (unsigned long)el); -#endif - free(el->key); - free(el->val); - free(el); - els_removed++; - } else { -#ifdef DEBUG - printf("...NO.\n"); -#endif - return 0; - } - - - return el; -} - -static int lookup_element(unsigned int *seedp) -{ - char keybuf[100]; - struct ht_element *el, lookup; - int x; - - /* pick a random element from 0 to highwater-1 */ - x = my_rand(0,glob_highwater-1,seedp); - sprintf(keybuf, "key%08d", x); - lookup.key = keybuf; - el = ast_hashtab_lookup(glob_hashtab, &lookup); - els_lookedup++; - if (el) - els_found++; - if (el) - return 1; - return 0; -} - - -static void *hashtest(void *data) -{ - int my_els_removed = 0; - int my_els_added = 0; - int my_els_lookedup = 0; - int my_els_found = 0; - int my_els_traversals = 0; - int my_testno = testno++; - int its; - - /* data will be a random number == use as a seed for random numbers */ - unsigned long seed = (unsigned long)data; - - printf("hashtest thread created... test beginning\n"); - - /* main test routine-- a global hashtab exists, pound it like crazy */ - for(its=0;its<100000;its++) - { - void *seed2 = &seed; - int op = my_rand(0,100, seed2); - if (op<60) { - my_els_lookedup++; -#ifdef DEBUG - printf("%d[%d]: LOOKUP\n", my_testno, its); -#endif - if ((my_els_lookedup%1000)==0) { - printf("."); - fflush(stdout); - } - if (lookup_element(seed2)) - my_els_found++; - } else if (op < 61) { /* make this 61 and it'll take 15 minutes to run */ -#ifdef DEBUG - printf("%d[%d]: TRAVERSE\n", my_testno, its); -#endif - traverse_elements(); - my_els_traversals++; - - } else if (op < 80) { -#ifdef DEBUG - printf("%d[%d]: REMOVE\n", my_testno, its); -#endif - if (del_element(seed2)) - my_els_removed++; - } else { - my_els_added++; -#ifdef DEBUG - printf("%d[%d]: ADD\n", my_testno, its); -#endif - add_element(); - } - } - printf("\nhashtest thread %d exiting.... lookups=%d/%d, added=%d, removed=%d, traversals=%d;\n", - my_testno, my_els_found, my_els_lookedup, my_els_added, my_els_removed, my_els_traversals); - printf("\ntotals..................... lookups=%d/%d, added=%d, removed=%d, traversals=%d;\n", - els_found, els_lookedup, els_added, els_removed,els_traversals); - pthread_exit(0); -} - -static void run_hashtest(int numthr) -{ - pthread_t thr[numthr]; - void *thrres[numthr]; - int i, biggest, resize_cnt, numobjs, numbuckets; - - /* init a single global hashtab, then... */ - glob_hashtab = ast_hashtab_create(180000, hashtab_compare_strings_nocase, ast_hashtab_resize_java, ast_hashtab_newsize_java, hashtab_hash_string_nocase, 1); - printf("starting with %d elements in the hashtable...\n", ast_hashtab_capacity(glob_hashtab)); - /* set a random seed */ - glob_seed = (unsigned int)time(0); - srand(glob_seed); - - /* create threads, each running hashtest */ - for(i=0;i<numthr;i++) - { - unsigned long z = rand(); - - printf("starting hashtest thread %d....\n",i+1); - if (ast_pthread_create(&thr[i], NULL, hashtest, (void*)z)) { - printf("Sorry, couldn't create thread #%d\n", i+1); - } - printf("hashtest thread spawned.... \n"); - } - /* collect threads, each running hashtest */ - for(i=0;i<numthr;i++) - { - printf("waiting for thread %d....\n", i+1); - if (pthread_join(thr[i], &thrres[i])) { - printf("Sorry, couldn't join thread #%d\n", i+1); - } - printf("hashtest thread %d done.... \n",i+1); - } - /* user has to kill/intr the process to stop the test? */ - ast_hashtab_get_stats(glob_hashtab, &biggest, &resize_cnt, &numobjs, &numbuckets); - printf("Some stats: longest bucket chain: %d; number of resizes: %d; number of objects: %d; capacity: %d\n", - biggest, resize_cnt, numobjs, numbuckets); -} - - -int main(int argc,char **argv) -{ - if (argc < 2 || argc > 2 || atoi(argv[1]) < 1) - { - printf("Usage: hashtest <number of threads>\n"); - exit(1); - } - - /* one arg == number of threads to create */ - run_hashtest(atoi(argv[1])); - - return 0; -} -#if !defined(LOW_MEMORY) -int ast_add_profile(const char *x, uint64_t scale) -{ - return 0; -} -#endif - -int ast_loader_register(int (*updater)(void)) -{ - return 1; -} - -int ast_loader_unregister(int (*updater)(void)) -{ - return 1; -} -void ast_module_register(const struct ast_module_info *x) -{ -} - -void ast_module_unregister(const struct ast_module_info *x) -{ -} - - -void ast_register_file_version(const char *file, const char *version); -void ast_register_file_version(const char *file, const char *version) -{ -} - -void ast_unregister_file_version(const char *file); -void ast_unregister_file_version(const char *file) -{ - -} - -void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...) -{ - va_list vars; - va_start(vars,fmt); - printf("LOG: lev:%d file:%s line:%d func: %s ", - level, file, line, function); - vprintf(fmt, vars); - fflush(stdout); - va_end(vars); -} - -void __ast_verbose(const char *file, int line, const char *func, int level, const char *fmt, ...) -{ - va_list vars; - va_start(vars,fmt); - - printf("VERBOSE: "); - vprintf(fmt, vars); - fflush(stdout); - va_end(vars); -} - -void ast_register_thread(char *name) -{ - -} - -void ast_unregister_thread(void *id) -{ -} - -#ifdef HAVE_BKTR -struct ast_bt *ast_bt_create(void); -struct ast_bt *ast_bt_create(void) -{ - return NULL; -} - -int ast_bt_get_addresses(struct ast_bt *bt); -int ast_bt_get_addresses(struct ast_bt *bt) -{ - return 0; -} - -char **ast_bt_get_symbols(void **addresses, size_t num_frames); -char **ast_bt_get_symbols(void **addresses, size_t num_frames) -{ - char **foo = calloc(num_frames, sizeof(char *) + 1); - if (foo) { - int i; - for (i = 0; i < num_frames; i++) { - foo[i] = (char *) foo + sizeof(char *) * num_frames; - } - } - return foo; -} - -void *ast_bt_destroy(struct ast_bt *bt); -void *ast_bt_destroy(struct ast_bt *bt) -{ - return NULL; -} -#endif diff --git a/utils/hashtest2.c b/utils/hashtest2.c deleted file mode 100644 index e5b7717986f50ab2b4771e76a4cb8634e534da56..0000000000000000000000000000000000000000 --- a/utils/hashtest2.c +++ /dev/null @@ -1,424 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 2007, Steve Murphy - * - * Steve Murphy <murf@digium.com> - * - * See http://www.asterisk.org for more information about - * the Asterisk project. Please do not directly contact - * any of the maintainers of this project for assistance; - * the project provides a web site, mailing lists and IRC - * channels for your use. - * - * This program is free software, distributed under the terms of - * the GNU General Public License Version 2. See the LICENSE file - * at the top of the source tree. - */ -/*! \file - * - * \brief A program to thoroughly thrash a hash table, testing - * out locking safety, and making sure all functionality - * is functioning. Run with 5 or more threads to get that - * fully intense firestorm of activity. If your - * hash tables don't crash, lock up, or go weird, it must - * be good code! Even features some global counters - * that will get slightly behind because they aren't lock-protected. - * - * \author Steve Murphy <murf@digium.com> - */ - -/*** MODULEINFO - <support_level>extended</support_level> - ***/ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include <pthread.h> -#include <sys/stat.h> -#include <signal.h> - -#include "asterisk/lock.h" -#include "asterisk/astobj2.h" -#include "asterisk/channel.h" -#include "asterisk/utils.h" -#include "asterisk/module.h" - -int testno = 2; - -/* stuff we need to make this work with the astobj2 stuff */ -#if !defined(LOW_MEMORY) -int64_t ast_mark(int prof_id, int x) -{ - return 0; -} -#endif - -/* my OBJECT */ -struct ht_element -{ - char *key; - char *val; -}; - -char *pbx_substitute_variables_helper_full(struct ast_channel *chan, struct varshead *head, const char *cp1, char *cp2, int maxlen, size_t *used); -char *pbx_substitute_variables_helper_full(struct ast_channel *chan, struct varshead *head, const char *cp1, char *cp2, int maxlen, size_t *used) -{ - return NULL; -} - -static int hash_string(const void *obj, const int flags) -{ - char *str = ((struct ht_element*)obj)->key; - int total; - - for (total=0; *str; str++) - { - unsigned int tmp = total; - total <<= 1; /* multiply by 2 */ - total += tmp; /* multiply by 3 */ - total <<= 2; /* multiply by 12 */ - total += tmp; /* multiply by 13 */ - - total += ((unsigned int)(*str)); - } - if (total < 0) - total = -total; - return total; -} - -static int hashtab_compare_strings(void *a, void *b, int flags) -{ - const struct ht_element *ae = a, *be = b; - return !strcmp(ae->key, be->key) ? CMP_MATCH | CMP_STOP : 0; -} - -/* random numbers */ - -static int my_rand(int incl_low, int incl_high, unsigned int *seedp) -{ - if (incl_high == 0) - return 0; - - return incl_low + (rand_r(seedp) % incl_high); -} - - - - -/* the testing routines */ - -static int glob_highwater = 0; -struct ao2_container *glob_hashtab = 0; -unsigned int glob_seed = 0; -int els_removed = 0; -int els_added = 0; -int els_lookedup = 0; -int els_found = 0; -int els_traversals = 0; - -/* all the operations to perform on the hashtab */ - -static void ht_destroy(void *obj) -{ - const struct ht_element *o = obj; - if (o->key) - free(o->key); - if (o->val) - free(o->val); -} - - -static void add_element(void) -{ - char keybuf[100]; - struct ht_element *x = ao2_alloc(sizeof(struct ht_element), ht_destroy); - sprintf(keybuf,"key%08d", glob_highwater++); - x->key = strdup(keybuf); - x->val = strdup("interesting data"); -#ifdef DEBUG - printf("+ %s\n", keybuf); -#endif - ao2_link(glob_hashtab, x); - - els_added++; /* unprotected, sometimes off, but, not really important, either */ -} - -static int do_nothing_cb(void *obj, void *arg, int flags) -{ - return 0; -} - -static void traverse_elements(void) -{ -#ifdef DEBUG - printf("Traverse hashtab\n"); -#endif - ao2_callback(glob_hashtab, OBJ_NODATA, do_nothing_cb, NULL); - els_traversals++; /* unprotected, sometimes off, but, not really important, either */ -} - -static void * del_element(unsigned int *seedp) -{ - char keybuf[100]; - struct ht_element *el, lookup; - int x; - - /* pick a random element from 0 to highwater-1 */ - x = my_rand(0,glob_highwater-1,seedp); - sprintf(keybuf, "key%08d", x); -#ifdef DEBUG - printf("- %s", keybuf); -#endif - lookup.key = keybuf; - el = ao2_find(glob_hashtab, &lookup, OBJ_POINTER); - if (el) { -#ifdef DEBUG - printf("...YES (el=%x)\n", (unsigned long)el); -#endif - ao2_unlink(glob_hashtab, el); /* mistakenly tried to use ao2_ref(c,-2) here to unlink. Bad Boy! */ - els_removed++; - } else { -#ifdef DEBUG - printf("...NO.\n"); -#endif - return 0; - } - return el; -} - -static int lookup_element(unsigned int *seedp) -{ - char keybuf[100]; - struct ht_element *el, lookup; - int x; - - /* pick a random element from 0 to highwater-1 */ - x = my_rand(0,glob_highwater-1,seedp); - sprintf(keybuf, "key%08d", x); - lookup.key = keybuf; - el = ao2_find(glob_hashtab, &lookup, OBJ_POINTER); - els_lookedup++; - if (el) { - els_found++; - ao2_ref(el, -1); /* toss out this ref, no longer needed */ - return 1; - } else { - return 0; - } -} - - -static void *hashtest(void *data) -{ - int my_els_removed = 0; - int my_els_added = 0; - int my_els_lookedup = 0; - int my_els_found = 0; - int my_els_traversals = 0; - int my_testno = testno++; - int its; - - /* data will be a random number == use as a seed for random numbers */ - unsigned long seed = (unsigned long)data; - printf("hashtest thread created... test beginning\n"); - - /* main test routine-- a global hashtab exists, pound it like crazy */ - for(its=0;its<100000;its++) - { - void *seed2 = &seed; - int op = my_rand(0,100, seed2); - if (op<60) { - my_els_lookedup++; -#ifdef DEBUG - printf("%d[%d]: LOOKUP\n", my_testno, its); -#endif - if ((my_els_lookedup%1000)==0) { - printf("."); - fflush(stdout); - } - if (lookup_element(seed2)) - my_els_found++; - } else if (op < 61) { /* make this 61 and it'll take 16 minutes to run */ -#ifdef DEBUG - printf("%d[%d]: TRAVERSE\n", my_testno, its); -#endif - traverse_elements(); - my_els_traversals++; - - } else if (op < 80) { -#ifdef DEBUG - printf("%d[%d]: REMOVE\n", my_testno, its); -#endif - if (del_element(seed2)) - my_els_removed++; - } else { - my_els_added++; -#ifdef DEBUG - printf("%d[%d]: ADD\n", my_testno, its); -#endif - add_element(); - } - } - printf("\nhashtest thread %d exiting.... lookups=%d/%d, added=%d, removed=%d, traversals=%d;\n", - my_testno, my_els_found, my_els_lookedup, my_els_added, my_els_removed, my_els_traversals); - printf("\ntotals..................... lookups=%d/%d, added=%d, removed=%d; traversals=%d\n", - els_found, els_lookedup, els_added, els_removed, els_traversals); - pthread_exit(0); - return NULL; -} - -static void run_hashtest(int numthr) -{ - pthread_t thr[numthr]; - void *thrres[numthr]; - int i; - - /* init a single global hashtab, then... */ - glob_hashtab = ao2_container_alloc(180000, hash_string, hashtab_compare_strings); - - /* set a random seed */ - glob_seed = (unsigned int)time(0); - srand(glob_seed); - - /* create threads, each running hashtest */ - for(i=0;i<numthr;i++) - { - unsigned long z = rand(); - - printf("starting hashtest thread %d....\n",i+1); - if (ast_pthread_create(&thr[i], NULL, hashtest, (void*)z)) { - printf("Sorry, couldn't create thread #%d\n", i+1); - } - printf("hashtest thread spawned.... \n"); - } - /* collect threads, each running hashtest */ - for(i=0;i<numthr;i++) - { - printf("waiting for thread %d....\n", i+1); - if (pthread_join(thr[i], &thrres[i])) { - printf("Sorry, couldn't join thread #%d\n", i+1); - } - printf("hashtest thread %d done.... \n",i+1); - } - /* user has to kill/intr the process to stop the test? */ -} - - -int main(int argc,char **argv) -{ - if (argc < 2 || argc > 2 || atoi(argv[1]) < 1) - { - printf("Usage: hashtest <number of threads>\n"); - exit(1); - } - - /* one arg == number of threads to create */ - run_hashtest(atoi(argv[1])); - - return 0; -} - -#if !defined(LOW_MEMORY) -int ast_add_profile(const char *x, uint64_t scale) -{ - return 0; -} -#endif - -int ast_loader_register(int (*updater)(void)) -{ - return 1; -} - -int ast_loader_unregister(int (*updater)(void)) -{ - return 1; -} -void ast_module_register(const struct ast_module_info *x) -{ -} - -void ast_module_unregister(const struct ast_module_info *x) -{ -} - - -void ast_register_file_version(const char *file, const char *version); -void ast_register_file_version(const char *file, const char *version) -{ -} - -void ast_unregister_file_version(const char *file); -void ast_unregister_file_version(const char *file) -{ - -} - -int ast_register_atexit(void (*func)(void)); -int ast_register_atexit(void (*func)(void)) -{ - return 0; -} - -void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...) -{ - va_list vars; - va_start(vars,fmt); - printf("LOG: lev:%d file:%s line:%d func: %s ", - level, file, line, function); - vprintf(fmt, vars); - fflush(stdout); - va_end(vars); -} - -void __ast_verbose(const char *file, int line, const char *func, int level, const char *fmt, ...) -{ - va_list vars; - va_start(vars,fmt); - - printf("VERBOSE: "); - vprintf(fmt, vars); - fflush(stdout); - va_end(vars); -} - -void ast_register_thread(char *name) -{ - -} - -void ast_unregister_thread(void *id) -{ -} - -#ifdef HAVE_BKTR -struct ast_bt* ast_bt_create(void) -{ - return NULL; -} - -int ast_bt_get_addresses(struct ast_bt *bt) -{ - return -1; -} - -char **ast_bt_get_symbols(void **addresses, size_t num_frames) -{ - char **foo = calloc(num_frames, sizeof(char *) + 1); - if (foo) { - int i; - for (i = 0; i < num_frames; i++) { - foo[i] = (char *) foo + sizeof(char *) * num_frames; - } - } - return foo; -} - -void *ast_bt_destroy(struct ast_bt *bt) -{ - return NULL; -} -#endif diff --git a/utils/utils.xml b/utils/utils.xml index a9b6e28ca13af6cd6915250818a36bb8e9215f28..58d2932e4d2f86394c20dd0741f05bfd419b4cb3 100644 --- a/utils/utils.xml +++ b/utils/utils.xml @@ -32,14 +32,6 @@ <defaultenabled>no</defaultenabled> <support_level>extended</support_level> </member> - <member name="hashtest"> - <defaultenabled>no</defaultenabled> - <support_level>extended</support_level> - </member> - <member name="hashtest2"> - <defaultenabled>no</defaultenabled> - <support_level>extended</support_level> - </member> <member name="muted"> <defaultenabled>no</defaultenabled> <support_level>extended</support_level>