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: Nov - 21 - 2001 Created - Manoj Iyer, IBM Austin TX. */
24 /* email:manjo@austin.ibm.com */
25 /* */
26 /* Nov - 26 - 2001 Modified - Manoj Iyer, IBM Austin Tx. */
27 /* - Added function rm_shared_mem. */
28 /* */
29 /* Dec - 03 - 2001 Modified - Manoj Iyer, IBM Austin Tx. */
30 /* - Added code to spawn threads. */
31 /* - Removed dead code. */
32 /* - Checked in the initial version to CVS */
33 /* */
34 /* Feb - 27 - 2001 Modified - Manoj Iyer, IBM Austin TX. */
35 /* - removed compiler warnings. */
36 /* - removed compiler errors. */
37 /* */
38 /* File: shm_test.c */
39 /* */
40 /* Description: This program is designed to stress the Memory management sub -*/
41 /* system of Linux. This program will spawn multiple pairs of */
42 /* reader and writer threads. One thread will create the shared */
43 /* segment of random size and write to this memory, the other */
44 /* pair will read from this memory. */
45 /* */
46 /******************************************************************************/
47 #include <pthread.h> /* required by pthread functions */
48 #include <stdio.h> /* required by fprintf() */
49 #include <stdlib.h> /* required by exit(), atoi() */
50 #include <string.h> /* required by strncpy() */
51 #include <unistd.h> /* required by getopt(), mmap() */
52 #include <sys/types.h> /* required by open(), shmat(), shmdt() */
53 #include <sys/stat.h> /* required by open() */
54 #include <sys/ipc.h> /* required by shmat() shmdt(), shmctl() */
55 #include <sys/shm.h> /* required by shmat() shmdt(), shmctl() */
56 #include <sys/mman.h> /* required by mmap() */
57 #include <fcntl.h> /* required by open() */
58 #include <stdint.h> /* required by uintptr_t */
59
noprintf(char * string,...)60 void noprintf(char *string, ...)
61 {
62 }
63
64 #ifdef DEBUG
65 #define dprt printf
66 #else
67 #define dprt noprintf
68 #endif
69
70 #define PTHREAD_EXIT(val) do {\
71 exit_val = val; \
72 dprt("pid[%d]: exiting with %d\n", getpid(),exit_val); \
73 pthread_exit((void *)(uintptr_t)exit_val); \
74 } while (0)
75
76 #define OPT_MISSING(prog, opt) do{\
77 fprintf(stderr, "%s: option -%c ", prog, opt); \
78 fprintf(stderr, "requires an argument\n"); \
79 usage(prog); \
80 } while (0)
81
82 #define MAXT 30 /* default number of threads to create. */
83 #define MAXR 1000 /* default number of repatetions to execute */
84 #define WRITER 0 /* cause thread function to shmat and write */
85 #define READER 1 /* cause thread function to shmat and read */
86
87 /******************************************************************************/
88 /* */
89 /* Function: usage */
90 /* */
91 /* Description: Print the usage message. */
92 /* */
93 /* Return: exits with -1 */
94 /* */
95 /******************************************************************************/
usage(char * progname)96 static void usage(char *progname)
97 { /* name of this program */
98 fprintf(stderr,
99 "Usage: %s -d NUMDIR -f NUMFILES -h -t NUMTHRD\n"
100 "\t -h Help!\n"
101 "\t -l Number of repatetions to execute: Default: 1000\n"
102 "\t -t Number of threads to generate: Default: 30\n",
103 progname);
104 exit(-1);
105 }
106
107 /******************************************************************************/
108 /* */
109 /* Function: rm_shared_mem */
110 /* */
111 /* Description: This function removes the shared segments that were created */
112 /* This function is called when shmat fails or logical end of */
113 /* the while loop is reached in shmat_rd_wr function. */
114 /* */
115 /* Input: shm_id - id of the shared memory segment to be removed */
116 /* shm_addr - address of the shared memory segment to be removed */
117 /* cmd - remove id only or remove id and detach?? */
118 /* 0 - remove id dont detach segment. */
119 /* 1 - remove id and detach segment. */
120 /* */
121 /* Output: NONE. */
122 /* */
123 /* Return: exits with -1 on error, 0 on success */
124 /* */
125 /******************************************************************************/
rm_shared_mem(key_t shm_id,char * shm_addr,int cmd)126 static int rm_shared_mem(key_t shm_id, /* id of shared memory segment to be removed */
127 char *shm_addr, /* address of shared mem seg to be removed */
128 int cmd)
129 { /* remove id only or remove id and detach seg */
130 struct shmid *shmbuf = NULL; /* info about the segment pointed by shmkey */
131
132 dprt("pid[%d]: rm_shared_mem(): shm_id = %d shm_addr = %#x cmd = %d\n",
133 getpid(), shm_id, shm_addr, cmd);
134 if (shmctl(shm_id, IPC_RMID, (struct shmid_ds *)shmbuf) == -1) {
135 dprt("pid[%d]: rm_shared_mem(): shmctl unable to remove shm_id[%d]\n", getpid(), shm_id);
136 perror("rm_shared_mem(): shmctl()");
137 return -1;
138 }
139
140 if (cmd) {
141 if (shmdt((void *)shm_addr) == -1) {
142 dprt("pid[%d]:rm_shared_mem(): shmdt unable to detach addr = %#x\n", getpid(), shm_addr);
143 perror("rm_shared_mem(): shmdt()");
144 return -1;
145 }
146 }
147 return 0;
148 }
149
150 /******************************************************************************/
151 /* */
152 /* Function: shmat_rd_wr */
153 /* */
154 /* Description: This function repeatedly attaches and detaches the memory */
155 /* The size of the file is a multiple of page size. */
156 /* The function acts as either reader or writer thread depending */
157 /* on arg[3]. The reader and writer thread use the same key so */
158 /* they get access to the same shared memory segment. */
159 /* */
160 /* Input: The argument pointer contains the following. */
161 /* arg[0] - number of repatetions of the above operation */
162 /* arg[1] - shared memory key. */
163 /* arg[2] - size of the memory that is to be attached. */
164 /* arg[3] - reader or writer. */
165 /* */
166 /* Return: exits with -1 on error, 0 on success */
167 /* */
168 /******************************************************************************/
shmat_rd_wr(void * args)169 static void *shmat_rd_wr(void *args)
170 { /* arguments to the thread function */
171 int shmndx = 0; /* index to the number of attach and detach */
172 int index = 0; /* index to the number of blocks touched */
173 int reader = 0; /* this thread is a reader thread if set to 1 */
174 key_t shm_id = 0; /* shared memory id */
175 long *locargs = /* local pointer to arguments */
176 (long *)args;
177 volatile int exit_val = 0; /* exit value of the pthread */
178 char *read_from_mem; /* ptr to touch each (4096) block in memory */
179 char *write_to_mem; /* ptr to touch each (4096) block in memory */
180 char *shmat_addr; /* address of the attached memory */
181 char buff; /* temporary buffer */
182
183 reader = (int)locargs[3];
184 while (shmndx++ < (int)locargs[0]) {
185 dprt("pid[%d]: shmat_rd_wr(): locargs[1] = %#x\n",
186 getpid(), (int)locargs[1]);
187
188 /* get shared memory id */
189 if ((shm_id =
190 shmget((int)locargs[1], (int)locargs[2], IPC_CREAT | 0666))
191 == -1) {
192 dprt("pid[%d]: shmat_rd_wr(): shmget failed\n",
193 getpid());
194 perror("do_shmat_shmadt(): shmget()");
195 PTHREAD_EXIT(-1);
196 }
197
198 fprintf(stdout, "pid[%d]: shmat_rd_wr(): shmget():"
199 "success got segment id %d\n", getpid(), shm_id);
200
201 /* get shared memory segment */
202 if ((shmat_addr = shmat(shm_id, NULL, 0)) == (void *)-1) {
203 rm_shared_mem(shm_id, shmat_addr, 0);
204 fprintf(stderr,
205 "pid[%d]: do_shmat_shmadt(): shmat_addr = %#lx\n",
206 getpid(), (long)shmat_addr);
207 perror("do_shmat_shmadt(): shmat()");
208 PTHREAD_EXIT(-1);
209 }
210 dprt("pid[%d]: do_shmat_shmadt(): content of memory shmat_addr = %s\n", getpid(), shmat_addr);
211
212 fprintf(stdout,
213 "pid[%d]: do_shmat_shmadt(): got shmat address = %#lx\n",
214 getpid(), (long)shmat_addr);
215
216 if (!reader) {
217 /* write character 'Y' to that memory area */
218 index = 0;
219 write_to_mem = shmat_addr;
220 while (index < (int)locargs[2]) {
221 dprt("pid[%d]: do_shmat_shmatd(): write_to_mem = %#x\n", getpid(), write_to_mem);
222 *write_to_mem = 'Y';
223 index++;
224 write_to_mem++;
225 sched_yield();
226 }
227 } else {
228 /* read from the memory area */
229 index = 0;
230 read_from_mem = shmat_addr;
231 while (index < (int)locargs[2]) {
232 buff = *read_from_mem;
233 index++;
234 read_from_mem++;
235 sched_yield();
236 }
237 }
238
239 sched_yield();
240
241 /* remove the shared memory */
242 if (rm_shared_mem(shm_id, shmat_addr, 1) == -1) {
243 fprintf(stderr,
244 "pid[%d]: do_shmat_shmatd(): rm_shared_mem(): faild to rm id\n",
245 getpid());
246 PTHREAD_EXIT(-1);
247 }
248 }
249
250 PTHREAD_EXIT(0);
251 }
252
253 /******************************************************************************/
254 /* */
255 /* Function: main */
256 /* */
257 /* Description: This is the entry point to the program. This function will */
258 /* parse the input arguments and set the values accordingly. If */
259 /* no arguments (or desired) are provided default values are used*/
260 /* refer the usage function for the arguments that this program */
261 /* takes. It also creates the threads which do most of the dirty */
262 /* work. If the threads exits with a value '0' the program exits */
263 /* with success '0' else it exits with failure '-1'. */
264 /* */
265 /* Return: -1 on failure */
266 /* 0 on success */
267 /* */
268 /******************************************************************************/
main(int argc,char ** argv)269 int main(int argc, /* number of input parameters */
270 char **argv)
271 { /* pointer to the command line arguments. */
272 int c; /* command line options */
273 int num_thrd = MAXT; /* number of threads to create */
274 int num_reps = MAXR; /* number of repatitions the test is run */
275 int thrd_ndx; /* index into the array of thread ids */
276 void *th_status; /* exit status of LWP's */
277 int map_size; /* size of the file mapped. */
278 int shmkey = 1969; /* key used to generate shmid by shmget() */
279 pthread_t thrdid[30]; /* maxinum of 30 threads allowed */
280 long chld_args[4]; /* arguments to the thread function */
281 char *map_address = NULL;
282 /* address in memory of the mapped file */
283 extern int optopt; /* options to the program */
284
285 while ((c = getopt(argc, argv, "hl:t:")) != -1) {
286 switch (c) {
287 case 'h':
288 usage(argv[0]);
289 break;
290 case 'l': /* how many repetitions of the test to exec */
291 if ((num_reps = atoi(optarg)) == 0)
292 OPT_MISSING(argv[0], optopt);
293 else if (num_reps < 0) {
294 fprintf(stdout,
295 "WARNING: bad argument. Using default\n");
296 num_reps = MAXR;
297 }
298 break;
299 case 't':
300 if ((num_thrd = atoi(optarg)) == 0)
301 OPT_MISSING(argv[0], optopt);
302 else if (num_thrd < 0) {
303 fprintf(stdout,
304 "WARNING: bad argument. Using default\n");
305 num_thrd = MAXT;
306 }
307 break;
308 default:
309 usage(argv[0]);
310 break;
311 }
312 }
313
314 chld_args[0] = num_reps;
315
316 for (thrd_ndx = 0; thrd_ndx < num_thrd; thrd_ndx += 2) {
317 srand(time(NULL) % 100);
318 map_size =
319 (1 + (int)(1000.0 * rand() / (RAND_MAX + 1.0))) * 4096;
320
321 chld_args[1] = shmkey++;
322 chld_args[2] = map_size;
323
324 dprt("main(): thrd_ndx = %d map_address = %#x map_size = %d\n",
325 thrd_ndx, map_address, map_size);
326
327 chld_args[3] = WRITER;
328
329 if (pthread_create
330 (&thrdid[thrd_ndx], NULL, shmat_rd_wr, chld_args)) {
331 perror("shmat_rd_wr(): pthread_create()");
332 exit(-1);
333 }
334
335 chld_args[3] = READER;
336
337 if (pthread_create
338 (&thrdid[thrd_ndx + 1], NULL, shmat_rd_wr, chld_args)) {
339 perror("shmat_rd_wr(): pthread_create()");
340 exit(-1);
341 }
342 }
343
344 sync();
345
346 for (thrd_ndx = 0; thrd_ndx < num_thrd; thrd_ndx++) {
347 if (pthread_join(thrdid[thrd_ndx], &th_status) != 0) {
348 perror("shmat_rd_wr(): pthread_join()");
349 exit(-1);
350 } else {
351 dprt("WE ARE HERE %d\n", __LINE__);
352 if (th_status == (void *)-1) {
353 fprintf(stderr,
354 "thread [%ld] - process exited with errors\n",
355 (long)thrdid[thrd_ndx]);
356 exit(-1);
357 }
358 }
359 }
360 exit(0);
361 }
362