1 /*****************************************************************************/
2 /* */
3 /* Copyright (c) International Business Machines Corp., 2001 */
4 /* */
5 /* This program is free software; you can redistribute it and/or modify */
6 /* it under the terms of the GNU General Public License as published by */
7 /* the Free Software Foundation; either version 2 of the License, or */
8 /* (at your option) any later version. */
9 /* */
10 /* This program is distributed in the hope that it will be useful, */
11 /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
12 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See */
13 /* the GNU General Public License for more details. */
14 /* */
15 /* You should have received a copy of the GNU General Public License */
16 /* along with this program; if not, write to the Free Software */
17 /* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
18 /* */
19 /******************************************************************************/
20
21 /******************************************************************************/
22 /* */
23 /* History: July - 16 - 2001 Created by Manoj Iyer, IBM Austin TX. */
24 /* email:manjo@austin.ibm.com */
25 /* */
26 /* July - 30 - 2001 Modified - Added function write_to_mem. */
27 /* */
28 /* Aug - 14 - 2001 Modified - Added code to remove the shared */
29 /* memory segment ids. */
30 /* */
31 /* Aug - 15 - 2001 Modified - Added for loop to run the test */
32 /* repeatedly. */
33 /* */
34 /* Oct - 22 - 2001 Modified - Fixed bad code in main(). */
35 /* removed stray code and options. Pthread_join */
36 /* part fixed, older version was completely bad */
37 /* */
38 /* Nov - 09 - 2001 Modified - Removed compile errors */
39 /* - added missing header file string.h */
40 /* - removed unused variables. */
41 /* - made read_ndx and write_ndx static variable*/
42 /* */
43 /* Nov - 91 - 2001 Modified - Changed scope of status variable */
44 /* - change the status of status variable from */
45 /* int *status to int status[1] */
46 /* */
47 /* File: shmat1.c */
48 /* */
49 /* Description: Test the LINUX memory manager. The program is aimed at */
50 /* stressing the memory manager by repeaded shmat/write/read/ */
51 /* shmatd of file/memory of random size (maximum 1000 * 4096) */
52 /* done by multiple processes. */
53 /* */
54 /* Create a file of random size upto 1000 times 4096. */
55 /* process X shmats and un-shmats this file in memory. */
56 /* process Y changes content of the file to Y, ie writes to it. */
57 /* process Z reads from this memory location, and varifies the */
58 /* the content of the file. */
59 /* */
60 /******************************************************************************/
61
62 /* Include Files */
63 #include <stdio.h> /* definitions for standard I/O */
64 #include <unistd.h> /* required by usleep() */
65 #include <errno.h> /* definitions for errno */
66 #include <sched.h> /* definitions for sched_yield() */
67 #include <stdlib.h> /* definitions for WEXIT macros */
68 #include <signal.h> /* required by sigaction & sig handling fncs */
69 #include <sys/time.h> /* definitions of settimer() */
70 #include <sys/wait.h> /* definitions of itimer structure */
71 #include <pthread.h> /* definitions for pthread_create etc */
72 #include <setjmp.h> /* required by setjmp longjmp */
73 #include <sys/ucontext.h> /* required by the signal handler */
74 #include <sys/ipc.h> /* required by shmat shmget etc */
75 #include <sys/shm.h> /* required by shmat shmget etc */
76 #include <string.h> /* required by strncmp */
77
78 /* Defines */
79 #ifndef TRUE
80 #define TRUE 1
81 #endif
82 #ifndef FALSE
83 #define FALSE 0
84 #endif
85 #define prtln() printf(" I AM HERE ==> %s %d\n", __FILE__, __LINE__);
86
87 #define STR_SHMAT " "
88 #define STR_WRITER " "
89 #define STR_READER " "
90
91 /* Global Variables */
92 void *map_address; /* pointer to file in memory */
93 sigjmp_buf jmpbuf; /* argument to setjmp and longjmp */
94 int fsize; /* size of the file to be created. */
95 int done_shmat = 0; /* disallow read and writes before shmat */
96
97 /******************************************************************************/
98 /* */
99 /* Function: sig_handler */
100 /* */
101 /* Description: handle SIGALRM raised by set_timer(), SIGALRM is raised when */
102 /* the timer expires. If any other signal is received exit the */
103 /* test. */
104 /* */
105 /* Input: signal - signal number, intrested in SIGALRM! */
106 /* */
107 /* Return: exit -1 if unexpected signal is received */
108 /* exit 0 if SIGALRM is received */
109 /* */
110 /******************************************************************************/
sig_handler(int signal,int code,ucontext_t * ut)111 static void sig_handler(int signal, /* signal number, set to handle SIGALRM */
112 int code, ucontext_t *ut)
113 { /* contains pointer to sigcontext structure */
114 #ifdef __i386__
115 unsigned long except; /* exception type. */
116 int ret = 0; /* exit code from signal handler. */
117 struct sigcontext *scp = /* pointer to sigcontext structure */
118 (struct sigcontext *)&ut->uc_mcontext;
119 #endif
120
121 if (signal == SIGALRM) {
122 fprintf(stdout, "Test ended, success\n");
123 exit(0);
124 }
125 #ifdef __i386__
126 else {
127 except = scp->trapno;
128 fprintf(stderr, "signal caught - [%d] ", signal);
129 }
130
131 switch (except) {
132 case 10:
133 fprintf(stderr,
134 "Exception - invalid TSS, exception #[%ld]\n", except);
135 break;
136 case 11:
137 fprintf(stderr,
138 "Exception - segment not present, exception #[%ld]\n",
139 except);
140 break;
141 case 12:
142 fprintf(stderr,
143 "Exception - stack segment not present, exception #[%ld]\n",
144 except);
145 break;
146 case 13:
147 fprintf(stderr,
148 "Exception - general protection, exception #[%ld]\n",
149 except);
150 break;
151 case 14:
152 fprintf(stderr,
153 "Exception - page fault, exception #[%ld]\n", except);
154 ret = 1;
155 break;
156 default:
157 fprintf(stderr,
158 "Exception type not handled... unknown exception #[%ld]\n",
159 except);
160 break;
161 }
162
163 if (ret) {
164 if (scp->edi == (int)map_address) {
165 fprintf(stdout,
166 "page fault at [%#lx] - ignore\n", scp->edi);
167 siglongjmp(jmpbuf, 1);
168 } else if (scp->esi == (int)map_address) {
169 fprintf(stdout,
170 "page fault at [%#lx] - ignore\n", scp->esi);
171 siglongjmp(jmpbuf, 1);
172 } else {
173 fprintf(stderr,
174 "address at which sigfault occured: [%lx]\n"
175 "address at which sigfault occured: [%lx]\n"
176 "address at which memory was shmat: [%p]\n",
177 (unsigned long)scp->edi,
178 (unsigned long)scp->esi, map_address);
179 fprintf(stderr, "bad page fault exit test\n");
180 exit(-1);
181 }
182 } else
183 exit(-1);
184 #else
185 fprintf(stderr, "caught signal %d -- exiting.\n", signal);
186 exit(-1);
187 #endif
188 }
189
190 /******************************************************************************//* */
191 /* Function: usage */
192 /* */
193 /* Description: Print the usage message. */
194 /* */
195 /* Return: exits with -1 */
196 /* */
197 /******************************************************************************/
usage(char * progname)198 static void usage(char *progname)
199 { /* name of this program */
200 fprintf(stderr,
201 "Usage: %s -h -l -x\n"
202 "\t -h help, usage message.\n"
203 "\t -l number of map - write - unmap. default: 1000\n"
204 "\t -x time for which test is to be run. default: 24 Hrs\n",
205 progname);
206 exit(-1);
207 }
208
209 /******************************************************************************/
210 /* */
211 /* Function: shmat_shmdt */
212 /* */
213 /* Description: Thread X function. */
214 /* shmat a random size file and shm-detach this file, this is */
215 /* done for user defined number of times. */
216 /* */
217 /* Input: arg[0] number of times shmat shmdt is done */
218 /* */
219 /* Return: -1 on error. */
220 /* 0 on errorless completion of the loop. */
221 /* */
222 /******************************************************************************/
shmat_shmdt(void * args)223 void *shmat_shmdt(void *args)
224 { /* arguments to the thread X function. */
225 int shm_ndx = 0; /* index to number of shmat/shmdt */
226 key_t shmkey = 0; /* IPC_PRIVATE (key for shmget) */
227 int shmid; /* shared memory id */
228 long *locargs = /* local pointer to arguments */
229 (long *)args;
230
231 while (shm_ndx++ < (int)locargs[0]) {
232 /* put the reader and writer threads to sleep */
233 done_shmat = 0;
234
235 /* generate a random size, we will ask for this amount of shared mem */
236 srand(time(NULL) % 100);
237 fsize = (1 + (int)(1000.0 * rand() / (RAND_MAX + 1.0))) * 4096;
238
239 if ((shmid = shmget(shmkey, fsize, IPC_CREAT | 0666)) == -1) {
240 perror("shmat_shmdt(): shmget()");
241 pthread_exit((void *)-1);
242 } else {
243 fprintf(stdout,
244 "%s[%#lx]: shmget(): success, got segment of size %d\n",
245 STR_SHMAT, pthread_self(), fsize);
246 }
247
248 if ((map_address = shmat(shmid, NULL, 0))
249 == (void *)-1) {
250 fprintf(stderr, "shmat_shmat(): map address = %p\n",
251 map_address);
252 perror("shmat_shmdt(): shmat()");
253 pthread_exit((void *)-1);
254 } else {
255 /* Wake up the reader and writer threads. */
256 /* Write 'X' into map_address. We are not sure about
257 reader/writer interleaving. So the reader may expect
258 to find 'X' or 'Y'
259 */
260 memset(map_address, 'X', 1);
261 done_shmat = 1;
262 usleep(0);
263 }
264
265 fprintf(stdout, "%s[%#lx]: Map address = %p\n",
266 STR_SHMAT, pthread_self(), map_address);
267 fprintf(stdout,
268 "%s[%#lx]: Num iter: [%d] Total Num Iter: [%d]\n",
269 STR_SHMAT, pthread_self(), shm_ndx, (int)locargs[0]);
270 usleep(0);
271 sched_yield();
272
273 /* put the threads to sleep before un-shmatting */
274 done_shmat = 0;
275 if (shmdt((void *)map_address) == -1) {
276 perror("shmat_shmdt(): shmdt()");
277 pthread_exit((void *)-1);
278 }
279 if (shmctl(shmid, IPC_RMID, NULL)) {
280 perror("shmat_shmdt(): shmctl()");
281 pthread_exit((void *)-1);
282 }
283 }
284 pthread_exit(NULL);
285 }
286
287 /******************************************************************************/
288 /* */
289 /* Function: write_to_mem */
290 /* */
291 /* Description: Thread Y function. */
292 /* Writes 'Y' to the memory location shmat by process X. */
293 /* */
294 /* Input: arg[0] number of times write is performed */
295 /* */
296 /* Return: -1 on error. */
297 /* 0 on errorless completion of the loop. */
298 /* */
299 /******************************************************************************/
write_to_mem(void * args)300 void *write_to_mem(void *args)
301 {
302 static int write_ndx = 0; /* index to the number of writes to perform */
303 long *locargs = /* local pointer to the arguments */
304 (long *)args;
305
306 while (write_ndx++ < (int)locargs[0]) {
307 /* wait for the thread to shmat, and dont sleep on the processor. */
308 while (!done_shmat)
309 usleep(0);
310
311 if (sigsetjmp(jmpbuf, 1) == 1) {
312 fprintf(stdout,
313 "page fault ocurred due a write after an shmdt from [%p]\n",
314 map_address);
315 }
316
317 fprintf(stdout,
318 "%s[%#lx]: write_to_mem(): memory address: [%p]\n",
319 STR_WRITER, pthread_self(), map_address);
320 memset(map_address, 'Y', 1);
321 usleep(1);
322 sched_yield();
323 }
324 pthread_exit(NULL);
325 }
326
327 /******************************************************************************/
328 /* */
329 /* Function: read_from_mem */
330 /* */
331 /* Description: Thread Z function. */
332 /* reads from the memory location shmat by process X. */
333 /* */
334 /* Input: arg[0] number of times read is performed */
335 /* */
336 /* Return: -1 on error. */
337 /* 0 on errorless completion of the loop. */
338 /* */
339 /******************************************************************************/
read_from_mem(void * args)340 void *read_from_mem(void *args)
341 {
342 static int read_ndx = 0; /* index to the number of writes to perform */
343 long *locargs = /* local pointer to the arguments */
344 (long *)args;
345
346 while (read_ndx++ < (int)locargs[0]) {
347 /* wait for the shmat to happen */
348 while (!done_shmat)
349 usleep(0);
350
351 fprintf(stdout,
352 "%s[%#lx]: read_from_mem(): memory address: [%p]\n",
353 STR_READER, pthread_self(), map_address);
354 if (sigsetjmp(jmpbuf, 1) == 1) {
355 fprintf(stdout,
356 "page fault ocurred due a read after an shmdt from %p\n",
357 map_address);
358 }
359
360 fprintf(stdout, "%s[%#lx]: read_mem(): content of memory: %s\n",
361 STR_READER, pthread_self(), (char *)map_address);
362
363 if (strncmp(map_address, "Y", 1) != 0) {
364 if (strncmp(map_address, "X", 1) != 0) {
365 pthread_exit((void *)-1);
366 }
367 }
368 usleep(1);
369 sched_yield();
370 }
371 pthread_exit(NULL);
372 }
373
374 /******************************************************************************/
375 /* */
376 /* Function: main */
377 /* */
378 /* Descrption: Create a large file of size up to a Giga Bytes. write to it */
379 /* lower case alphabet 'a'. Map the file and change the contents */
380 /* to 'A's (upper case alphabet), write the contents to the file,*/
381 /* and unmap the file from memory. Spwan a certian number of */
382 /* LWP's that will do the above. */
383 /* */
384 /* Return: exits with -1 on error */
385 /* exits with a 0 on success. */
386 /* */
387 /******************************************************************************/
main(int argc,char ** argv)388 int main(int argc, /* number of input parameters. */
389 char **argv)
390 { /* pointer to the command line arguments. */
391 int c; /* command line options */
392 int num_iter; /* number of iteration to perform */
393 int thrd_ndx; /* index into the array of threads. */
394 double exec_time; /* period for which the test is executed */
395 void *status; /* exit status for light weight process */
396 int sig_ndx; /* index into signal handler structure. */
397 pthread_t thid[1000]; /* pids of process that will map/write/unmap */
398 long chld_args[3]; /* arguments to funcs execed by child process */
399 extern char *optarg; /* arguments passed to each option */
400 struct sigaction sigptr; /* set up signal, for interval timer */
401
402 static struct signal_info {
403 int signum; /* signal number that hasto be handled */
404 char *signame; /* name of the signal to be handled. */
405 } sig_info[] = {
406 {
407 SIGHUP, "SIGHUP"}, {
408 SIGINT, "SIGINT"}, {
409 SIGQUIT, "SIGQUIT"}, {
410 SIGABRT, "SIGABRT"}, {
411 SIGBUS, "SIGBUS"}, {
412 SIGSEGV, "SIGSEGV"}, {
413 SIGALRM, "SIGALRM"}, {
414 SIGUSR1, "SIGUSR1"}, {
415 SIGUSR2, "SIGUSR2"}, {
416 -1, "ENDSIG"}
417 };
418
419 /* set up the default values */
420 num_iter = 1000; /* repeate map - write - unmap operation 1000 times */
421 exec_time = 24.0; /* minimum time period for which to run the tests */
422
423 while ((c = getopt(argc, argv, "h:l:x:")) != -1) {
424 switch (c) {
425 case 'h':
426 usage(argv[0]);
427 break;
428 case 'l': /* number of times to loop in the thread function */
429 if ((num_iter = atoi(optarg)) == 0)
430 num_iter = 1000;
431 break;
432 case 'x': /* time in hrs to run this test. */
433 if ((exec_time = atof(optarg)) == 0)
434 exec_time = 24;
435 break;
436 default:
437 usage(argv[0]);
438 break;
439 }
440 }
441
442 fprintf(stdout,
443 "\n\n\nTest is set to run with the following parameters:\n"
444 "\tDuration of test: [%f]hrs\n"
445 "\tnumber of shmat shm detach: [%d]\n", exec_time, num_iter);
446
447 /* set up signals */
448 sigptr.sa_handler = (void (*)(int signal))sig_handler;
449 sigfillset(&sigptr.sa_mask);
450 sigptr.sa_flags = SA_SIGINFO;
451 for (sig_ndx = 0; sig_info[sig_ndx].signum != -1; sig_ndx++) {
452 sigaddset(&sigptr.sa_mask, sig_info[sig_ndx].signum);
453 if (sigaction(sig_info[sig_ndx].signum, &sigptr,
454 NULL) == -1) {
455 perror("man(): sigaction()");
456 fprintf(stderr,
457 "could not set handler for SIGALRM, errno = %d\n",
458 errno);
459 exit(-1);
460 }
461 }
462
463 chld_args[0] = num_iter;
464 alarm(exec_time * 3600.00);
465
466 for (;;) {
467 /* create 3 threads */
468 if (pthread_create(&thid[0], NULL, shmat_shmdt, chld_args)) {
469 perror("main(): pthread_create()");
470 exit(-1);
471 } else {
472 fprintf(stdout,
473 "created thread id[%#lx], execs fn shmat_shmdt()\n",
474 thid[0]);
475 }
476 sched_yield();
477
478 if (pthread_create(&thid[1], NULL, write_to_mem, chld_args)) {
479 perror("main(): pthread_create()");
480 exit(-1);
481 } else {
482 fprintf(stdout,
483 "created thread id[%#lx], execs fn write_to_mem()\n",
484 thid[1]);
485 }
486 sched_yield();
487
488 if (pthread_create(&thid[2], NULL, read_from_mem, chld_args)) {
489 perror("main(): pthread_create()");
490 exit(-1);
491 } else {
492 fprintf(stdout,
493 "created thread id[%#lx], execs fn read_from_mem()\n",
494 thid[2]);
495 }
496 sched_yield();
497
498 /* wait for the children to terminate */
499 for (thrd_ndx = 0; thrd_ndx < 3; thrd_ndx++) {
500 if (pthread_join(thid[thrd_ndx], &status)) {
501 perror("main(): pthread_create()");
502 exit(-1);
503 }
504 if (status == (void *)-1) {
505 fprintf(stderr,
506 "thread [%#lx] - process exited with errors %ld\n",
507 thid[thrd_ndx], (long)status);
508 exit(-1);
509 }
510 }
511 }
512 fprintf(stdout, "TEST PASSED\n");
513 return 0;
514 }
515