1 /* ``Licensed under the Apache License, Version 2.0 (the "License");
2  * you may not use this file except in compliance with the License.
3  * You may obtain a copy of the License at
4  *
5  *     http://www.apache.org/licenses/LICENSE-2.0
6  *
7  * Unless required by applicable law or agreed to in writing, software
8  * distributed under the License is distributed on an "AS IS" BASIS,
9  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10  * See the License for the specific language governing permissions and
11  * limitations under the License.
12  *
13  * The Initial Developer of the Original Code is Ericsson Utvecklings AB.
14  * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
15  * AB. All Rights Reserved.''
16  *
17  *     $Id$
18  */
19 
20 
21 #ifndef __WIN32__
22 #include <sys/types.h>
23 #include <unistd.h>
24 #include <errno.h>
25 #endif
26 #include <stdio.h>
27 #include <stdarg.h>
28 #include <string.h>
29 #include "testcase_driver.h"
30 #include "allocator_test.h"
31 
32 #ifdef __WIN32__
33 #undef HAVE_VSNPRINTF
34 #define HAVE_VSNPRINTF 1
35 #define vsnprintf _vsnprintf
36 #endif
37 
38 #ifndef HAVE_VSNPRINTF
39 #define HAVE_VSNPRINTF 0
40 #endif
41 
42 #define NO_OF_ALLOC_SEQS 6
43 #define NO_OF_THREADS (18)
44 #define NO_OF_BLOCKS 10
45 #define NO_OF_OPS_PER_BL 200
46 #define SBC_THRESHOLD 8192
47 
48 #define BLOCK_ID(TN, BN) ((TN) << 4 | (BN))
49 
50 #define ERR_BUF_SZ 4096
51 static char err_buf[ERR_BUF_SZ];
52 static volatile int tc_failed;
53 static int dead_thread_no;
54 static erts_mutex tc_mutex;
55 static erts_cond tc_cond;
56 
exit_thread(int t_no,int do_lock)57 static void exit_thread(int t_no, int do_lock)
58 {
59     if (do_lock)
60 	THR_MTX_LOCK(tc_mutex);
61 
62     while (dead_thread_no >= 0)
63 	THR_COND_WAIT(tc_cond, tc_mutex);
64     dead_thread_no = t_no;
65 
66     THR_COND_BCAST(tc_cond);
67     THR_MTX_UNLOCK(tc_mutex);
68     THR_EXIT(NULL);
69 }
70 
fail(int t_no,char * frmt,...)71 static void fail(int t_no, char *frmt, ...)
72 {
73     char buf[10];
74     size_t bufsz = sizeof(buf);
75     va_list va;
76 
77     THR_MTX_LOCK(tc_mutex);
78 
79     va_start(va, frmt);
80 #if HAVE_VSNPRINTF
81     vsnprintf(err_buf, ERR_BUF_SZ, frmt, va);
82 #else
83     vsprintf(err_buf, frmt, va);
84 #endif
85     va_end(va);
86 
87     tc_failed = 1;
88 
89     if (enif_getenv("ERL_ABORT_ON_FAILURE", buf, &bufsz) == 0
90 	&& strcmp("true", buf) == 0) {
91 	fprintf(stderr, "Testcase \"%s\" failed: %s\n",
92 		testcase_name(), err_buf);
93 	abort();
94     }
95 
96     exit_thread(t_no, 0);
97 }
98 
99 static Allctr_t *alloc_ts_1 = NULL;
100 static Allctr_t *alloc_ts_2 = NULL;
101 
stop_allocators(void)102 static void stop_allocators(void)
103 {
104     if (alloc_ts_1) {
105 	STOP_ALC(alloc_ts_1);
106 	alloc_ts_1 = NULL;
107     }
108     if (alloc_ts_2) {
109 	STOP_ALC(alloc_ts_2);
110 	alloc_ts_2 = NULL;
111     }
112 }
113 
114 
115 void *thread_func(void *arg);
116 
117 typedef struct {
118     Allctr_t *a;
119     int t_no;
120     int no_ops_per_bl;
121 } ThreadData;
122 
123 
124 char *
testcase_name(void)125 testcase_name(void)
126 {
127     return "threads";
128 }
129 
130 void
testcase_cleanup(TestCaseState_t * tcs)131 testcase_cleanup(TestCaseState_t *tcs)
132 {
133     stop_allocators();
134 }
135 
136 void
testcase_run(TestCaseState_t * tcs)137 testcase_run(TestCaseState_t *tcs)
138 {
139     struct {
140 	erts_thread tid;
141 	ThreadData arg;
142     } threads[NO_OF_THREADS+1] = {{0}};
143     int no_threads;
144     int i;
145     char sbct_buf[10];
146     char *argv_org[] = {"-tasaobf", "-tmmsbc5000", "-tmmmbc5000", "-tsbct",
147 			&sbct_buf[0], NULL};
148     char *argv[sizeof(argv_org)/sizeof(argv_org[0])];
149 
150     if (!IS_THREADS_ENABLED)
151 	testcase_skipped(tcs, "Threads not enabled");
152 
153     alloc_ts_1 = NULL;
154     alloc_ts_2 = NULL;
155 
156     err_buf[0] = '\0';
157 
158     sprintf(sbct_buf, "%d", SBC_THRESHOLD/1024);
159 
160     memcpy((void *) argv, argv_org, sizeof(argv_org));
161     alloc_ts_1 = START_ALC("threads_ts_1", 1, argv);
162     ASSERT(tcs, alloc_ts_1);
163     memcpy((void *) argv, argv_org, sizeof(argv_org));
164     alloc_ts_2 = START_ALC("threads_ts_2", 1, argv);
165     ASSERT(tcs, alloc_ts_2);
166 
167     ASSERT(tcs, IS_ALLOC_THREAD_SAFE(alloc_ts_1));
168     ASSERT(tcs, IS_ALLOC_THREAD_SAFE(alloc_ts_2));
169 
170     tc_mutex = THR_MTX_CREATE();
171     tc_cond = THR_COND_CREATE();
172 
173     THR_MTX_LOCK(tc_mutex);
174 
175     dead_thread_no = -1;
176     no_threads = 0;
177 
178     for(i = 1; i <= NO_OF_THREADS; i++) {
179 	char *alc;
180 
181 	threads[i].arg.no_ops_per_bl = NO_OF_OPS_PER_BL;
182 
183 	if (i % 2 == 0) {
184 	    alc = "threads_ts_1";
185 	    threads[i].arg.a = alloc_ts_1;
186 	}
187 	else {
188 	    alc = "threads_ts_2";
189 	    threads[i].arg.a = alloc_ts_2;
190 	}
191 	threads[i].arg.t_no = i;
192 
193 	threads[i].tid = THR_CREATE(thread_func, (void *) &threads[i].arg);
194 	if (threads[i].tid) {
195 	    testcase_printf(tcs, "Successfully created thread %d "
196 			    "using %s_alloc\n", i, alc);
197 	    no_threads++;
198 	}
199 	else {
200 	    tc_failed = 1;
201 	    sprintf(err_buf, "Failed to create thread %d\n", i);
202 	    break;
203 	}
204 
205     }
206 
207     while (no_threads) {
208 	THR_COND_WAIT(tc_cond, tc_mutex);
209 	if (dead_thread_no >= 0) {
210 	    no_threads--;
211 	    THR_JOIN(threads[dead_thread_no].tid);
212 	    testcase_printf(tcs, "Thread %d died\n", dead_thread_no);
213 	    dead_thread_no = -1;
214 	    THR_COND_BCAST(tc_cond);
215 	}
216     }
217 
218     THR_MTX_UNLOCK(tc_mutex);
219     THR_MTX_DESTROY(tc_mutex);
220     THR_COND_DESTROY(tc_cond);
221 
222     stop_allocators();
223 
224     if (tc_failed)
225 	testcase_failed(tcs, "%s", err_buf);
226 }
227 
228 Ulong alloc_seq_1[] = {
229   17,
230   SBC_THRESHOLD*2,
231   SBC_THRESHOLD*20,
232   SBC_THRESHOLD*2,
233   17,
234   0
235 };
236 
237 Ulong alloc_seq_2[] = {
238   SBC_THRESHOLD*20,
239   SBC_THRESHOLD*2,
240   17,
241   SBC_THRESHOLD*2,
242   SBC_THRESHOLD*20,
243   0
244 };
245 
246 Ulong alloc_seq_3[] = {
247   1,
248   SBC_THRESHOLD/10,
249   SBC_THRESHOLD/9,
250   SBC_THRESHOLD/8,
251   SBC_THRESHOLD/7,
252   SBC_THRESHOLD/6,
253   SBC_THRESHOLD/5,
254   SBC_THRESHOLD/4,
255   SBC_THRESHOLD/3,
256   SBC_THRESHOLD/2,
257   SBC_THRESHOLD/1,
258   SBC_THRESHOLD*1,
259   SBC_THRESHOLD*2,
260   SBC_THRESHOLD*3,
261   SBC_THRESHOLD*4,
262   SBC_THRESHOLD*5,
263   SBC_THRESHOLD*6,
264   SBC_THRESHOLD*7,
265   SBC_THRESHOLD*8,
266   SBC_THRESHOLD*9,
267   SBC_THRESHOLD*10,
268   SBC_THRESHOLD*9,
269   SBC_THRESHOLD*8,
270   SBC_THRESHOLD*7,
271   SBC_THRESHOLD*6,
272   SBC_THRESHOLD*5,
273   SBC_THRESHOLD*4,
274   SBC_THRESHOLD*3,
275   SBC_THRESHOLD*2,
276   SBC_THRESHOLD*1,
277   SBC_THRESHOLD/2,
278   SBC_THRESHOLD/3,
279   SBC_THRESHOLD/4,
280   SBC_THRESHOLD/5,
281   SBC_THRESHOLD/6,
282   SBC_THRESHOLD/7,
283   SBC_THRESHOLD/8,
284   SBC_THRESHOLD/9,
285   SBC_THRESHOLD/10,
286   1,
287   0
288 };
289 
290 Ulong alloc_seq_4[] = {
291   SBC_THRESHOLD*2,
292   SBC_THRESHOLD*3,
293   SBC_THRESHOLD*7,
294   SBC_THRESHOLD*8,
295   SBC_THRESHOLD*5,
296   SBC_THRESHOLD*6,
297   SBC_THRESHOLD*1,
298   SBC_THRESHOLD*10,
299   SBC_THRESHOLD*4,
300   SBC_THRESHOLD*2,
301   0
302 };
303 
304 Ulong alloc_seq_5[] = {
305   SBC_THRESHOLD/2,
306   SBC_THRESHOLD/3,
307   SBC_THRESHOLD/7,
308   SBC_THRESHOLD/8,
309   SBC_THRESHOLD/5,
310   SBC_THRESHOLD/6,
311   SBC_THRESHOLD/1,
312   SBC_THRESHOLD/10,
313   SBC_THRESHOLD/4,
314   SBC_THRESHOLD/2,
315   SBC_THRESHOLD/3,
316   SBC_THRESHOLD/7,
317   SBC_THRESHOLD/8,
318   SBC_THRESHOLD/5,
319   SBC_THRESHOLD/6,
320   SBC_THRESHOLD/1,
321   SBC_THRESHOLD/10,
322   SBC_THRESHOLD/4,
323   SBC_THRESHOLD/2,
324   0
325 };
326 
327 Ulong alloc_seq_6[] = {
328     1, 50, 100, 50, 23, 46, 2345, 23, 54, 2, 0
329 };
330 
331 Ulong *alloc_seqs[NO_OF_ALLOC_SEQS] = {
332   alloc_seq_1,
333   alloc_seq_2,
334   alloc_seq_3,
335   alloc_seq_4,
336   alloc_seq_5,
337   alloc_seq_6
338 };
339 
340 typedef struct {
341     unsigned char *p;
342     Ulong  s;
343     int   i;
344     Ulong *as;
345 } block;
346 
347 #define CHECK_BLOCK_DATA(T, P, S, D) \
348   check_block_data(__FILE__, __LINE__, (T), (P), (S), (D))
349 
350 static void
check_block_data(char * file,int line,int t_no,unsigned char * p,Ulong sz,int d)351 check_block_data(char *file, int line, int t_no,
352 		 unsigned char *p, Ulong sz, int d)
353 {
354     Ulong i;
355     for (i = 0; i < sz; i++)
356 	if (p[i] != (unsigned char) d)
357 	    fail(t_no, "%s:%d: Thread no %d found clobbered data! "
358 		 "found id=%d; expected id=%d\n",
359 		 file, line, t_no, (int) p[i], d);
360 }
361 
362 static void
alloc_op(int t_no,Allctr_t * a,block * bp,int id,int clean_up)363 alloc_op(int t_no, Allctr_t *a, block *bp, int id, int clean_up)
364 {
365     if (tc_failed)
366 	exit_thread(t_no, 1);
367 
368     if(bp->p)
369 	CHECK_BLOCK_DATA(t_no, bp->p, bp->s, id);
370 
371     if(bp->as[bp->i] == 0 || clean_up) {
372 	FREE(a, bp->p);
373 	bp->p = NULL;
374 	bp->s = 0;
375 	bp->i = 0; /* start from the beginning again */
376 	return;
377     }
378 
379     if(!bp->p) {
380 	bp->s = bp->as[bp->i];
381 	bp->p = (unsigned char *) ALLOC(a, bp->s);
382 	if(!bp->p)
383 	    fail(t_no, "ALLOC(%lu) failed [id=%d])\n", bp->s, id);
384 	memset((void *) bp->p, (unsigned char)id, (size_t) bp->s);
385     }
386     else {
387 	unsigned char *p = (unsigned char *) REALLOC(a, bp->p, bp->as[bp->i]);
388 	if(!p)
389 	    fail(t_no, "REALLOC(0x%lx, %lu) failed [id=%d]\n",
390 		 (Ulong) bp->p, bp->as[bp->i], id);
391 
392 	if(bp->s < bp->as[bp->i]) {
393 	    CHECK_BLOCK_DATA(t_no, p, bp->s, id);
394 	    memset((void *) p, (unsigned char)id, (size_t) bp->as[bp->i]);
395 	}
396 	else
397 	    CHECK_BLOCK_DATA(t_no, p, bp->as[bp->i], id);
398 
399 	bp->s = bp->as[bp->i];
400 	bp->p = p;
401     }
402 
403     bp->i++;
404 }
405 
406 
407 void *
thread_func(void * arg)408 thread_func(void *arg)
409 {
410     int i, j;
411     ThreadData *td = ((ThreadData *) arg);
412     block bs[NO_OF_BLOCKS];
413 
414     for(i = 0; i < NO_OF_BLOCKS; i++) {
415 	bs[i].p = NULL;
416 	bs[i].s = 0;
417 	bs[i].i = 0;
418 	bs[i].as = alloc_seqs[i % NO_OF_ALLOC_SEQS];
419     }
420 
421     for(i = 0; i < td->no_ops_per_bl; i++) {
422 
423 	for(j = 0; j < NO_OF_BLOCKS; j++)
424 	    alloc_op(td->t_no, td->a, &bs[j], BLOCK_ID(td->t_no, j), 0);
425     }
426 
427     for(j = 0; j < NO_OF_BLOCKS; j++)
428 	alloc_op(td->t_no, td->a, &bs[j], BLOCK_ID(td->t_no, j), 1);
429 
430     exit_thread(td->t_no, 1);
431     return NULL;
432 }
433 
434 ERL_NIF_INIT(threads, testcase_nif_funcs, testcase_nif_init,
435 	     NULL, NULL, NULL);
436