e1780791fbbd7f5a186374c11f1208b85c1f9529
[asterisk/asterisk.git] / utils / hashtest.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2007, Steve Murphy
5  *
6  * Steve Murphy <murf@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18 /*! \file
19  *
20  *  \brief A program to thoroughly thrash a hash table, testing
21  *         out locking safety, and making sure all functionality
22  *         is functioning. Run with 5 or more threads to get that
23  *         fully intense firestorm of activity. If your
24  *         hash tables don't crash, lock up, or go weird, it must
25  *         be good code! Even features some global counters
26  *         that will get slightly behind because they aren't lock-protected.
27  *
28  *  \author Steve Murphy <murf@digium.com>
29  */
30
31 #include "asterisk.h"
32 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
33
34 #include <pthread.h>
35 #include <sys/stat.h>
36 #include <signal.h>
37 #include <errno.h>
38 #include "asterisk/lock.h"
39 #include "asterisk/hashtab.h"
40 #include "asterisk/channel.h"
41 #include "asterisk/utils.h"
42 #include "asterisk/module.h"
43 int testno = 1;
44
45 /* stuff we need to make this work with the hashtab stuff */
46
47 int64_t ast_mark(int prof_id, int x)
48 {
49         return 0;
50 }
51
52 struct ht_element 
53 {
54         char *key;
55         char *val;
56 };
57
58 static int hashtab_compare_strings_nocase(const void *a, const void *b)
59 {
60         const struct ht_element *ae = a, *be = b;
61         return ast_hashtab_compare_strings_nocase(ae->key, be->key);
62 }
63
64 #if 0
65 static int hashtab_compare_strings(const void *a, const void *b)
66 {
67         const struct ht_element *ae = a, *be = b;
68         return ast_hashtab_compare_strings(ae->key, be->key);
69 }
70
71 static unsigned int hashtab_hash_string(const void *obj)
72 {
73         const struct ht_element *o = obj;
74         return ast_hashtab_hash_string((const void *)o->key);
75 }
76 #endif
77
78 static unsigned int hashtab_hash_string_nocase(const void *obj)
79 {
80         const struct ht_element *o = obj;
81         return ast_hashtab_hash_string_nocase((const void*)o->key);
82 }
83
84 /* random numbers */
85
86 static int my_rand(int incl_low, int incl_high, unsigned int *seedp)
87 {
88         if (incl_high == 0)
89                 return 0;
90         
91         return incl_low + (rand_r(seedp) % incl_high);
92 }
93
94
95
96
97 /* the testing routines */
98
99 static int glob_highwater = 0;
100 struct ast_hashtab *glob_hashtab = 0;
101 unsigned int glob_seed = 0;
102 int els_removed = 0;
103 int els_added = 0;
104 int els_lookedup = 0;
105 int els_found = 0;
106 int els_traversals = 0;
107
108 /* all the operations to perform on the hashtab */
109
110 static void add_element(void)
111 {
112         char keybuf[100];
113         struct ht_element *x = malloc(sizeof(struct ht_element));
114         sprintf(keybuf,"key%08d", glob_highwater++);
115         x->key = strdup(keybuf);
116         x->val = strdup("interesting data");
117         ast_hashtab_insert_immediate(glob_hashtab, x);
118         els_added++;
119 }
120
121 static void traverse_elements(void)
122 {
123         struct ht_element *el;
124         int c=0;
125         struct ast_hashtab_iter *it = ast_hashtab_start_write_traversal(glob_hashtab);
126 #ifdef DEBUG
127         printf("Traverse hashtab\n");
128 #endif
129         while ((el = ast_hashtab_next(it))) {
130                 c++;
131         }
132         ast_hashtab_end_traversal(it);
133         els_traversals++; /* unprotected, sometimes off, but, not really important, either */
134 }
135
136 static void * del_element(unsigned int *seedp)
137 {
138         char keybuf[100];
139         struct ht_element *el, lookup;
140         int x;
141         
142         /* pick a random element from 0 to highwater-1 */
143         x = my_rand(0,glob_highwater-1,seedp);
144         sprintf(keybuf, "key%08d", x);
145 #ifdef DEBUG
146         printf("Removing %s", keybuf);
147 #endif
148         lookup.key = keybuf;
149         el = ast_hashtab_remove_object_via_lookup(glob_hashtab, &lookup);
150         
151         if (el) {
152 #ifdef DEBUG
153                 printf("...YES (el=%x)\n", (unsigned long)el);
154 #endif
155                 free(el->key);
156                 free(el->val);
157                 free(el);
158                 els_removed++;
159         } else {
160 #ifdef DEBUG
161                 printf("...NO.\n");
162 #endif
163                 return 0;
164         }
165         
166         
167         return el;
168 }
169
170 static int lookup_element(unsigned int *seedp)
171 {
172         char keybuf[100];
173         struct ht_element *el, lookup;
174         int x;
175         
176         /* pick a random element from 0 to highwater-1 */
177         x = my_rand(0,glob_highwater-1,seedp);
178         sprintf(keybuf, "key%08d", x);
179         lookup.key = keybuf;
180         el = ast_hashtab_lookup(glob_hashtab, &lookup);
181         els_lookedup++;
182         if (el)
183                 els_found++;
184         if (el)
185                 return 1;
186         return 0;
187 }
188
189
190 static void *hashtest(void *data)
191 {
192         int my_els_removed = 0;
193         int my_els_added = 0;
194         int my_els_lookedup = 0;
195         int my_els_found = 0;
196         int my_els_traversals = 0;
197         int my_testno = testno++;
198         int its;
199         
200         /* data will be a random number == use as a seed for random numbers */
201         unsigned long seed = (unsigned long)data;
202
203         printf("hashtest thread created... test beginning\n");
204         
205         /* main test routine-- a global hashtab exists, pound it like crazy  */
206         for(its=0;its<100000;its++)
207         {
208                 void *seed2 = &seed;
209                 int op = my_rand(0,100, seed2);
210                 if (op<60) {
211                         my_els_lookedup++;
212 #ifdef DEBUG
213                         printf("%d[%d]: LOOKUP\n", my_testno, its);
214 #endif                  
215                         if ((my_els_lookedup%1000)==0) {
216                                 printf(".");
217                                 fflush(stdout);
218                         }
219                         if (lookup_element(seed2))
220                                 my_els_found++;
221                 } else if (op < 61) { /* make this 61 and it'll take 15 minutes to run */
222 #ifdef DEBUG
223                         printf("%d[%d]: TRAVERSE\n", my_testno, its);
224 #endif
225                         traverse_elements();
226                         my_els_traversals++;
227                         
228                 } else if (op < 80) {
229 #ifdef DEBUG
230                         printf("%d[%d]: REMOVE\n", my_testno, its);
231 #endif
232                         if (del_element(seed2))
233                                 my_els_removed++;
234                 } else {
235                         my_els_added++;
236 #ifdef DEBUG
237                         printf("%d[%d]: ADD\n", my_testno, its);
238 #endif
239                         add_element();
240                 }
241         }
242         printf("\nhashtest thread %d exiting.... lookups=%d/%d, added=%d, removed=%d, traversals=%d;\n",
243                    my_testno, my_els_found, my_els_lookedup, my_els_added, my_els_removed, my_els_traversals);
244         printf("\ntotals..................... lookups=%d/%d, added=%d, removed=%d, traversals=%d;\n",
245                    els_found, els_lookedup, els_added, els_removed,els_traversals);
246         pthread_exit(0);
247 }
248
249 static void run_hashtest(int numthr)
250 {
251         pthread_t thr[numthr];
252         void *thrres[numthr];
253         int i, biggest, resize_cnt, numobjs, numbuckets;
254         
255         /* init a single global hashtab, then... */
256         glob_hashtab = ast_hashtab_create(180000, hashtab_compare_strings_nocase, ast_hashtab_resize_java, ast_hashtab_newsize_java, hashtab_hash_string_nocase, 1);
257         printf("starting with %d elements in the hashtable...\n", ast_hashtab_capacity(glob_hashtab));
258         /* set a random seed  */
259         glob_seed = (unsigned int)time(0);
260         srand(glob_seed);
261         
262         /* create threads, each running hashtest */
263         for(i=0;i<numthr;i++)
264         {
265                 unsigned long z = rand();
266                 
267                 printf("starting hashtest thread %d....\n",i+1);
268                 if (ast_pthread_create(&thr[i], NULL, hashtest, (void*)z)) {
269                         printf("Sorry, couldn't create thread #%d\n", i+1);
270                 }
271                 printf("hashtest thread spawned.... \n");
272         }
273         /* collect threads, each running hashtest */
274         for(i=0;i<numthr;i++)
275         {
276                 printf("waiting for thread %d....\n", i+1);
277                 if (pthread_join(thr[i], &thrres[i])) {
278                         printf("Sorry, couldn't join thread #%d\n", i+1);
279                 }
280                 printf("hashtest thread %d done.... \n",i+1);
281         }
282         /* user has to kill/intr the process to stop the test? */
283         ast_hashtab_get_stats(glob_hashtab, &biggest, &resize_cnt, &numobjs, &numbuckets);
284         printf("Some stats: longest bucket chain: %d;  number of resizes: %d; number of objects: %d;  capacity: %d\n",
285                         biggest, resize_cnt, numobjs, numbuckets);
286 }
287
288
289 int main(int argc,char **argv)
290 {
291         if (argc < 2 || argc > 2 || atoi(argv[1]) < 1)
292         {
293                 printf("Usage: hashtest <number of threads>\n");
294                 exit(1);
295         }
296         
297         /* one arg == number of threads to create */
298         run_hashtest(atoi(argv[1]));
299         
300         return 0;
301 }
302
303 int  ast_add_profile(const char *x, uint64_t scale)
304 {
305         return 0;
306 }
307
308 int ast_loader_register(int (*updater)(void))
309 {
310         return 1;
311 }
312
313 int ast_loader_unregister(int (*updater)(void))
314 {
315         return 1;
316 }
317 void ast_module_register(const struct ast_module_info *x)
318 {
319 }
320
321 void ast_module_unregister(const struct ast_module_info *x)
322 {
323 }
324
325
326 void ast_register_file_version(const char *file, const char *version)
327 {
328 }
329
330 void ast_unregister_file_version(const char *file)
331 {
332
333 }
334
335 void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...)
336 {
337         va_list vars;
338         va_start(vars,fmt);
339         printf("LOG: lev:%d file:%s  line:%d func: %s  ",
340                    level, file, line, function);
341         vprintf(fmt, vars);
342         fflush(stdout);
343         va_end(vars);
344 }
345
346 void ast_verbose(const char *fmt, ...)
347 {
348         va_list vars;
349         va_start(vars,fmt);
350
351         printf("VERBOSE: ");
352         vprintf(fmt, vars);
353         fflush(stdout);
354         va_end(vars);
355 }
356
357 void ast_register_thread(char *name)
358 {
359
360 }
361
362 void ast_unregister_thread(void *id)
363 {
364 }