Fix compilation of utilities (caught by Bamboo).
[asterisk/asterisk.git] / utils / hashtest2.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 /*** MODULEINFO
32         <support_level>extended</support_level>
33  ***/
34
35 #include "asterisk.h"
36
37 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
38
39 #include <pthread.h>
40 #include <sys/stat.h>
41 #include <signal.h>
42
43 #include "asterisk/lock.h"
44 #include "asterisk/astobj2.h"
45 #include "asterisk/channel.h"
46 #include "asterisk/utils.h"
47 #include "asterisk/module.h"
48
49 int testno = 2;
50
51 /* stuff we need to make this work with the astobj2 stuff */
52 #if !defined(LOW_MEMORY)
53 int64_t ast_mark(int prof_id, int x)
54 {
55         return 0;
56 }
57 #endif
58
59 /* my OBJECT */
60 struct ht_element 
61 {
62         char *key;
63         char *val;
64 };
65
66 char *pbx_substitute_variables_helper_full(struct ast_channel *chan, struct varshead *head, const char *cp1, char *cp2, int maxlen, size_t *used);
67 char *pbx_substitute_variables_helper_full(struct ast_channel *chan, struct varshead *head, const char *cp1, char *cp2, int maxlen, size_t *used)
68 {
69         return NULL;
70 }
71
72 static int hash_string(const void *obj, const int flags)
73 {
74         char *str = ((struct ht_element*)obj)->key;
75         int total;
76
77         for (total=0; *str; str++)
78         {
79                 unsigned int tmp = total;
80                 total <<= 1; /* multiply by 2 */
81                 total += tmp; /* multiply by 3 */
82                 total <<= 2; /* multiply by 12 */
83                 total += tmp; /* multiply by 13 */
84         
85                 total += ((unsigned int)(*str));
86         }
87         if (total < 0)
88                 total = -total;
89         return total;
90 }
91
92 static int hashtab_compare_strings(void *a, void *b, int flags)
93 {
94         const struct ht_element *ae = a, *be = b;
95         return !strcmp(ae->key, be->key) ? CMP_MATCH | CMP_STOP : 0;
96 }
97
98 /* random numbers */
99
100 static int my_rand(int incl_low, int incl_high, unsigned int *seedp)
101 {
102         if (incl_high == 0)
103                 return 0;
104         
105         return incl_low + (rand_r(seedp) % incl_high);
106 }
107
108
109
110
111 /* the testing routines */
112
113 static int glob_highwater = 0;
114 struct ao2_container *glob_hashtab = 0;
115 unsigned int glob_seed = 0;
116 int els_removed = 0;
117 int els_added = 0;
118 int els_lookedup = 0;
119 int els_found = 0;
120 int els_traversals = 0;
121
122 /* all the operations to perform on the hashtab */
123
124 static void ht_destroy(void *obj)
125 {
126         const struct ht_element *o = obj;
127         if (o->key)
128                 free(o->key);
129         if (o->val)
130                 free(o->val);
131 }
132
133
134 static void add_element(void)
135 {
136         char keybuf[100];
137         struct ht_element *x = ao2_alloc(sizeof(struct ht_element), ht_destroy);
138         sprintf(keybuf,"key%08d", glob_highwater++);
139         x->key = strdup(keybuf);
140         x->val = strdup("interesting data");
141 #ifdef DEBUG
142         printf("+ %s\n", keybuf);
143 #endif  
144         ao2_link(glob_hashtab, x);
145         
146         els_added++; /* unprotected, sometimes off, but, not really important, either */
147 }
148
149 static int do_nothing_cb(void *obj, void *arg, int flags)
150 {
151         return 0;
152 }
153
154 static void traverse_elements(void)
155 {
156 #ifdef DEBUG
157         printf("Traverse hashtab\n");
158 #endif
159         ao2_callback(glob_hashtab, OBJ_NODATA, do_nothing_cb, NULL);
160         els_traversals++; /* unprotected, sometimes off, but, not really important, either */
161 }
162
163 static void * del_element(unsigned int *seedp)
164 {
165         char keybuf[100];
166         struct ht_element *el, lookup;
167         int x;
168         
169         /* pick a random element from 0 to highwater-1 */
170         x = my_rand(0,glob_highwater-1,seedp);
171         sprintf(keybuf, "key%08d", x);
172 #ifdef DEBUG
173         printf("- %s", keybuf);
174 #endif
175         lookup.key = keybuf;
176         el = ao2_find(glob_hashtab, &lookup, OBJ_POINTER);
177         if (el) {
178 #ifdef DEBUG
179                 printf("...YES (el=%x)\n", (unsigned long)el);
180 #endif
181                 ao2_unlink(glob_hashtab, el); /* mistakenly tried to use ao2_ref(c,-2) here to unlink. Bad Boy! */
182                 els_removed++;
183         } else {
184 #ifdef DEBUG
185                 printf("...NO.\n");
186 #endif
187                 return 0;
188         }
189         return el;
190 }
191
192 static int lookup_element(unsigned int *seedp)
193 {
194         char keybuf[100];
195         struct ht_element *el, lookup;
196         int x;
197         
198         /* pick a random element from 0 to highwater-1 */
199         x = my_rand(0,glob_highwater-1,seedp);
200         sprintf(keybuf, "key%08d", x);
201         lookup.key = keybuf;
202         el = ao2_find(glob_hashtab, &lookup, OBJ_POINTER);
203         els_lookedup++;
204         if (el) {
205                 els_found++;
206                 ao2_ref(el, -1); /* toss out this ref, no longer needed */
207                 return 1;
208         } else {
209                 return 0;
210         }
211 }
212
213
214 static void *hashtest(void *data)
215 {
216         int my_els_removed = 0;
217         int my_els_added = 0;
218         int my_els_lookedup = 0;
219         int my_els_found = 0;
220         int my_els_traversals = 0;
221         int my_testno = testno++;
222         int its;
223         
224         /* data will be a random number == use as a seed for random numbers */
225         unsigned long seed = (unsigned long)data;
226         printf("hashtest thread created... test beginning\n");
227         
228         /* main test routine-- a global hashtab exists, pound it like crazy  */
229         for(its=0;its<100000;its++)
230         {
231                 void *seed2 = &seed;
232                 int op = my_rand(0,100, seed2);
233                 if (op<60) {
234                         my_els_lookedup++;
235 #ifdef DEBUG
236                         printf("%d[%d]: LOOKUP\n", my_testno, its);
237 #endif                  
238                         if ((my_els_lookedup%1000)==0) {
239                                 printf(".");
240                                 fflush(stdout);
241                         }
242                         if (lookup_element(seed2))
243                                 my_els_found++;
244                 } else if (op < 61) { /* make this 61 and it'll take 16 minutes to run */
245 #ifdef DEBUG
246                         printf("%d[%d]: TRAVERSE\n", my_testno, its);
247 #endif
248                         traverse_elements();
249                         my_els_traversals++;
250                         
251                 } else if (op < 80) {
252 #ifdef DEBUG
253                         printf("%d[%d]: REMOVE\n", my_testno, its);
254 #endif
255                         if (del_element(seed2))
256                                 my_els_removed++;
257                 } else {
258                         my_els_added++;
259 #ifdef DEBUG
260                         printf("%d[%d]: ADD\n", my_testno, its);
261 #endif
262                         add_element();
263                 }
264         }
265         printf("\nhashtest thread %d exiting.... lookups=%d/%d, added=%d, removed=%d, traversals=%d;\n",
266                    my_testno, my_els_found, my_els_lookedup, my_els_added, my_els_removed, my_els_traversals);
267         printf("\ntotals..................... lookups=%d/%d, added=%d, removed=%d; traversals=%d\n",
268                    els_found, els_lookedup, els_added, els_removed, els_traversals);
269         pthread_exit(0);
270         return NULL;
271 }
272
273 static void run_hashtest(int numthr)
274 {
275         pthread_t thr[numthr];
276         void *thrres[numthr];
277         int i;
278         
279         /* init a single global hashtab, then... */
280         glob_hashtab = ao2_container_alloc(180000, hash_string, hashtab_compare_strings);
281
282         /* set a random seed  */
283         glob_seed = (unsigned int)time(0);
284         srand(glob_seed);
285         
286         /* create threads, each running hashtest */
287         for(i=0;i<numthr;i++)
288         {
289                 unsigned long z = rand();
290                 
291                 printf("starting hashtest thread %d....\n",i+1);
292                 if (ast_pthread_create(&thr[i], NULL, hashtest, (void*)z)) {
293                         printf("Sorry, couldn't create thread #%d\n", i+1);
294                 }
295                 printf("hashtest thread spawned.... \n");
296         }
297         /* collect threads, each running hashtest */
298         for(i=0;i<numthr;i++)
299         {
300                 printf("waiting for thread %d....\n", i+1);
301                 if (pthread_join(thr[i], &thrres[i])) {
302                         printf("Sorry, couldn't join thread #%d\n", i+1);
303                 }
304                 printf("hashtest thread %d done.... \n",i+1);
305         }
306         /* user has to kill/intr the process to stop the test? */
307 }
308
309
310 int main(int argc,char **argv)
311 {
312         if (argc < 2 || argc > 2 || atoi(argv[1]) < 1)
313         {
314                 printf("Usage: hashtest <number of threads>\n");
315                 exit(1);
316         }
317         
318         /* one arg == number of threads to create */
319         run_hashtest(atoi(argv[1]));
320         
321         return 0;
322 }
323
324 #if !defined(LOW_MEMORY)
325 int ast_add_profile(const char *x, uint64_t scale)
326 {
327         return 0;
328 }
329 #endif
330
331 int ast_loader_register(int (*updater)(void))
332 {
333         return 1;
334 }
335
336 int ast_loader_unregister(int (*updater)(void))
337 {
338         return 1;
339 }
340 void ast_module_register(const struct ast_module_info *x)
341 {
342 }
343
344 void ast_module_unregister(const struct ast_module_info *x)
345 {
346 }
347
348
349 void ast_register_file_version(const char *file, const char *version);
350 void ast_register_file_version(const char *file, const char *version)
351 {
352 }
353
354 void ast_unregister_file_version(const char *file);
355 void ast_unregister_file_version(const char *file)
356 {
357
358 }
359
360 void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...)
361 {
362         va_list vars;
363         va_start(vars,fmt);
364         printf("LOG: lev:%d file:%s  line:%d func: %s  ",
365                    level, file, line, function);
366         vprintf(fmt, vars);
367         fflush(stdout);
368         va_end(vars);
369 }
370
371 void __ast_verbose(const char *file, int line, const char *func, int level, const char *fmt, ...)
372 {
373         va_list vars;
374         va_start(vars,fmt);
375
376         printf("VERBOSE: ");
377         vprintf(fmt, vars);
378         fflush(stdout);
379         va_end(vars);
380 }
381
382 void ast_register_thread(char *name)
383 {
384
385 }
386
387 void ast_unregister_thread(void *id)
388 {
389 }
390
391 #ifdef HAVE_BKTR
392 struct ast_bt* ast_bt_create(void)
393 {
394         return NULL;
395 }
396
397 int ast_bt_get_addresses(struct ast_bt *bt)
398 {
399         return -1;
400 }
401
402 char **ast_bt_get_symbols(void **addresses, size_t num_frames)
403 {
404         char **foo = calloc(num_frames, sizeof(char *) + 1);
405         if (foo) {
406                 int i;
407                 for (i = 0; i < num_frames; i++) {
408                         foo[i] = (char *) foo + sizeof(char *) * num_frames;
409                 }
410         }
411         return foo;
412 }
413
414 void *ast_bt_destroy(struct ast_bt *bt)
415 {
416         return NULL;
417 }
418 #endif