Fix building under FreeBSD. Make sure alloca.h exists before including it.
[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 #include "asterisk.h"
32
33 #include <sys/types.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <stdarg.h>
37 #ifdef HAVE_ALLOCA_H
38 #include <alloca.h>
39 #endif
40 #include <string.h>
41 #include <pthread.h>
42 #include <sys/stat.h>
43 #include <signal.h>
44 #include <errno.h>
45 #include <unistd.h>
46
47 #include "asterisk/lock.h"
48 #include "asterisk/astobj2.h"
49 #include "asterisk/channel.h"
50 #include "asterisk/utils.h"
51 #include "asterisk/module.h"
52
53 int testno = 2;
54
55 /* stuff we need to make this work with the astobj2 stuff */
56
57 void ast_cli(int *fd, char *str, ...)
58 {
59 }
60
61 int64_t ast_mark(int prof_id, int x)
62 {
63 }
64
65 /* my OBJECT */
66 struct ht_element 
67 {
68         char *key;
69         char *val;
70 };
71
72
73 static int hash_string(const void *obj, const int flags)
74 {
75         unsigned char *str = ((struct ht_element*)obj)->key;
76         int total;
77
78         for (total=0; *str; str++)
79         {
80                 unsigned int tmp = total;
81                 total <<= 1; /* multiply by 2 */
82                 total += tmp; /* multiply by 3 */
83                 total <<= 2; /* multiply by 12 */
84                 total += tmp; /* multiply by 13 */
85         
86                 total += ((unsigned int)(*str));
87         }
88         if (total < 0)
89                 total = -total;
90         return total;
91 }
92
93 static int hashtab_compare_strings(void *a, void *b, int flags)
94 {
95         const struct ht_element *ae = a, *be = b;
96         return !strcmp(ae->key, be->key) ? CMP_MATCH : 0;
97 }
98
99 /* random numbers */
100
101 my_rand(int incl_low, int incl_high, unsigned int *seedp)
102 {
103         if (incl_high == 0)
104                 return 0;
105         
106         return incl_low + (rand_r(seedp) % incl_high);
107 }
108
109
110
111
112 /* the testing routines */
113
114 static int glob_highwater = 0;
115 struct ao2_container *glob_hashtab = 0;
116 unsigned int glob_seed = 0;
117 int els_removed = 0;
118 int els_added = 0;
119 int els_lookedup = 0;
120 int els_found = 0;
121 int els_traversals = 0;
122
123 /* all the operations to perform on the hashtab */
124
125 void ht_destroy(void *obj)
126 {
127         const struct ht_element *o = obj;
128         if (o->key)
129                 free(o->key);
130         if (o->val)
131                 free(o->val);
132 }
133
134
135 static void add_element(void)
136 {
137         char keybuf[100];
138         struct ht_element *x = ao2_alloc(sizeof(struct ht_element), ht_destroy);
139         sprintf(keybuf,"key%08d", glob_highwater++);
140         x->key = strdup(keybuf);
141         x->val = strdup("interesting data");
142 #ifdef DEBUG
143         printf("+ %s\n", keybuf);
144 #endif  
145         ao2_link(glob_hashtab, x);
146         
147         els_added++; /* unprotected, sometimes off, but, not really important, either */
148 }
149
150 static void traverse_elements(void)
151 {
152         struct ht_element *el;
153         struct ao2_iterator it = ao2_iterator_init(glob_hashtab, 0);
154 #ifdef DEBUG
155         printf("Traverse hashtab\n");
156 #endif
157         while ((el = ao2_iterator_next(&it))) {
158                 ao2_ref(el,-1);
159         }
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         
223         /* data will be a random number == use as a seed for random numbers */
224         unsigned long seed = (unsigned long)data;
225         printf("hashtest thread created... test beginning\n");
226         
227         /* main test routine-- a global hashtab exists, pound it like crazy  */
228         int its;
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 }
271
272 void run_hashtest(int numthr)
273 {
274         pthread_t thr[numthr];
275         void *thrres[numthr];
276         int i;
277         
278         /* init a single global hashtab, then... */
279         glob_hashtab = ao2_container_alloc(180000, hash_string, hashtab_compare_strings);
280
281         /* set a random seed  */
282         glob_seed = (unsigned int)time(0);
283         srand(glob_seed);
284         
285         /* create threads, each running hashtest */
286         for(i=0;i<numthr;i++)
287         {
288                 unsigned long z = rand();
289                 
290                 printf("starting hashtest thread %d....\n",i+1);
291                 if (ast_pthread_create(&thr[i], NULL, hashtest, (void*)z)) {
292                         printf("Sorry, couldn't create thread #%d\n", i+1);
293                 }
294                 printf("hashtest thread spawned.... \n");
295         }
296         /* collect threads, each running hashtest */
297         for(i=0;i<numthr;i++)
298         {
299                 printf("waiting for thread %d....\n", i+1);
300                 if (pthread_join(thr[i], &thrres[i])) {
301                         printf("Sorry, couldn't join thread #%d\n", i+1);
302                 }
303                 printf("hashtest thread %d done.... \n",i+1);
304         }
305         /* user has to kill/intr the process to stop the test? */
306 }
307
308
309 int main(int argc,char **argv)
310 {
311         if (argc < 2 || argc > 2 || atoi(argv[1]) < 1)
312         {
313                 printf("Usage: hashtest <number of threads>\n");
314                 exit(1);
315         }
316         
317         /* one arg == number of threads to create */
318         run_hashtest(atoi(argv[1]));
319         
320         return 0;
321 }
322
323
324 struct ast_app *pbx_findapp(const char *app)
325 {
326         return (struct ast_app*)1; /* so as not to trigger an error */
327 }
328
329 int  ast_add_profile(const char *x, uint64_t scale)
330 {
331 }
332
333 int ast_loader_register(int (*updater)(void))
334 {
335         return 1;
336 }
337
338 int ast_loader_unregister(int (*updater)(void))
339 {
340         return 1;
341 }
342 void ast_module_register(const struct ast_module_info *x)
343 {
344 }
345
346 void ast_module_unregister(const struct ast_module_info *x)
347 {
348 }
349
350
351 void ast_cli_register_multiple(void)
352 {
353 }
354
355 void ast_register_file_version(const char *file, const char *version)
356 {
357 }
358
359 void ast_unregister_file_version(const char *file)
360 {
361
362 }
363
364 void ast_cli_unregister_multiple(void)
365 {
366 }
367
368 void ast_context_destroy(void)
369 {
370 }
371
372 void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...)
373 {
374         va_list vars;
375         va_start(vars,fmt);
376         printf("LOG: lev:%d file:%s  line:%d func: %s  ",
377                    level, file, line, function);
378         vprintf(fmt, vars);
379         fflush(stdout);
380         va_end(vars);
381 }
382
383 void ast_verbose(const char *fmt, ...)
384 {
385         va_list vars;
386         va_start(vars,fmt);
387
388         printf("VERBOSE: ");
389         vprintf(fmt, vars);
390         fflush(stdout);
391         va_end(vars);
392 }
393
394 void ast_register_thread(char *name)
395 {
396
397 }
398
399 void ast_unregister_thread(void *id)
400 {
401 }