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: Feb - 21 - 2002 Created - Manoj Iyer, IBM Austin TX. */
24 /* email: manjo@austin.ibm.com. */
25 /* */
26 /* Feb - 25 - 2002 Modified - Manoj Iyer, IBM Austin TX. */
27 /* - Added structure thread_sched_t. */
28 /* - Added logic to specify scheduling policy. */
29 /* */
30 /* Feb - 25 - 2002 Modified - Manoj Iyer, IBM Austin TX. */
31 /* - Added header file string.h. */
32 /* - Removed dead variable ppid from thread_func.*/
33 /* - Fixed date from 2001 to 2002 in History. */
34 /* */
35 /* File: trace_sched.c */
36 /* */
37 /* Description: This utility spawns N tasks, each task sets its priority by */
38 /* making a system call to the scheduler. The thread function */
39 /* reads the priority that tbe schedular sets for this task and */
40 /* also reads from /proc the processor this task last executed on*/
41 /* the information that is gathered by the thread function may */
42 /* be in real-time. Its only an approximation. */
43 /* */
44 /******************************************************************************/
45
46 #include <fcntl.h>
47 #include <limits.h>
48 #include <pthread.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <sched.h>
52 #include <sys/types.h>
53 #include <sys/stat.h>
54 #include <sys/wait.h>
55 #include <sys/time.h>
56 #include <unistd.h>
57 #include <string.h>
58
noprintf(char * string,...)59 void noprintf(char *string, ...)
60 {
61 }
62
63 #ifdef DEBUG /* compile with this flag for debug, use dprt in code */
64 #define dprt printf
65 #else
66 #define dprt noprintf
67 #endif
68
69 #ifndef PID_MAX
70 #define PID_MAX 0x8000
71 #endif
72
73 #define MAXT 100
74
75 #ifdef PTHREAD_THREADS_MAX
76 #define PIDS PTHREAD_THREADS_MAX /* maximum thread allowed. */
77 #elif defined(PID_MAX_DEFAULT)
78 #define PIDS PID_MAX_DEFAULT /* maximum pids allowed. */
79 #else
80 #define PIDS PID_MAX /* alternative way maximum pids may be defined */
81 #endif
82
83 #define UP 1 /* assume UP if no SMP value is specified. */
84
85 #define OPT_MISSING(prog, opt) do{\
86 fprintf(stderr, "%s: option -%c ", prog, opt); \
87 fprintf(stderr, "requires an argument\n"); \
88 usage(prog); \
89 } while (0)
90
91 #define SAFE_FREE(p) { if (p) { free(p); (p)=NULL; } }
92
93 typedef struct { /* contains priority and CPU info of the task. */
94 int exp_prio; /* priority that we wish to set. */
95 int act_prio; /* priority set by the scheduler. */
96 int proc_num; /* last processor on which this task executed. */
97 int procs_id; /* pid of this task. */
98 int s_policy; /* scheduling policy for the task. */
99 } thread_sched_t;
100
101 int verbose = 0; /* set verbose printing, makes output look ugly! */
102
103 /******************************************************************************/
104 /* */
105 /* Function: usage */
106 /* */
107 /* Description: Print the usage message. */
108 /* */
109 /* Return: exits with -1 */
110 /* */
111 /******************************************************************************/
usage(char * progname)112 void usage(char *progname)
113 { /* name of this program */
114 fprintf(stderr,
115 "Usage: %s -c NCPU -h -p [fifo:rr:other] -t THREADS -v\n"
116 "\t -c Number of CUPS in the machine. User MUST provide\n"
117 "\t -h Help!\n"
118 "\t -p Scheduling policy, choice: fifo, rr, other. Default: fifo\n"
119 "\t -t Number of threads to create. Default: %d\n"
120 "\t -v Verbose out put, print ugly!. Default: OFF\n",
121 progname, MAXT);
122 exit(-1);
123 }
124
125 /******************************************************************************/
126 /* */
127 /* Function: get_proc_num */
128 /* */
129 /* Description: Function reads the proc filesystem file /proc/<PID>/stat */
130 /* gets the CPU number this process last executed on and returns */
131 /* Some hard assumptions were made regarding buffer sizes. */
132 /* */
133 /* Return: exits with -1 - on error */
134 /* CPU number - on success */
135 /* */
136 /******************************************************************************/
get_proc_num(void)137 static int get_proc_num(void)
138 {
139 int fd = -1; /* file descriptor of the /proc/<pid>/stat file. */
140 int fsize = -1; /* size of the /proc/<pid>/stat file. */
141 char filename[256]; /* buffer to hold the string /proc/<pid>/stat. */
142 char fbuff[512]; /* contains the contents of the stat file. */
143
144 /* get the name of the stat file for this process */
145 sprintf(filename, "/proc/%d/stat", getpid());
146
147 /* open the stat file and read the contents to a buffer */
148 if ((fd = open(filename, O_RDONLY)) == -1) {
149 perror("get_proc_num(): open()");
150 return -1;
151 }
152
153 usleep(6);
154 sched_yield();
155
156 if ((fsize = read(fd, fbuff, 512)) == -1) {
157 perror("main(): read()");
158 return -1;
159 }
160
161 close(fd);
162 /* return the processor number last executed on. */
163 return atoi(&fbuff[fsize - 2]);
164 }
165
166 /******************************************************************************/
167 /* */
168 /* Function: thread_func */
169 /* */
170 /* Description: This function is executed in the context of the new task that */
171 /* pthread_createi() will spawn. The (thread) task will get the */
172 /* minimum and maximum static priority for this system, set the */
173 /* priority of the current task to a random priority value if */
174 /* the policy set if SCHED_FIFO or SCHED_RR. The priority if this*/
175 /* task that was assigned by the scheduler is got from making the*/
176 /* system call to sched_getscheduler(). The CPU number on which */
177 /* the task was last seen is also recorded. All the above data is*/
178 /* returned to the calling routine in a structure thread_sched_t.*/
179 /* */
180 /* Input: thread_sched_t */
181 /* s_policy - scheduling policy for the task. */
182 /* */
183 /* Return: thread_sched_t - on success. */
184 /* exp_prio - random priority value to set. */
185 /* act_prio - priority set by the scheduler. */
186 /* proc_num - CPU number on which this task last executed. */
187 /* procs_id - pid of this task. */
188 /* */
189 /* -1 - on error. */
190 /* */
191 /******************************************************************************/
thread_func(void * args)192 void *thread_func(void *args)
193 { /* arguments to the thread function */
194 static int max_priority; /* max possible priority for a process. */
195 static int min_priority; /* min possible priority for a process. */
196 static int set_priority; /* set the priority of the proc by this value. */
197 static int get_priority; /* get the priority that is set for this proc. */
198 static int procnum; /* processor number last executed on. */
199 static int sched_policy; /* scheduling policy as set by user/default */
200 struct sched_param ssp; /* set schedule priority. */
201 struct sched_param gsp; /* gsp schedule priority. */
202 struct timeval tptr; /* tptr.tv_usec will be used to seed srand. */
203 thread_sched_t *locargptr = /* local ptr to the arguments. */
204 (thread_sched_t *) args;
205
206 /* Get the system max and min static priority for a process. */
207 if (((max_priority = sched_get_priority_max(SCHED_FIFO)) == -1) ||
208 ((min_priority = sched_get_priority_min(SCHED_FIFO)) == -1)) {
209 fprintf(stderr, "failed to get static priority range\n");
210 dprt("pid[%d]: exiting with -1\n", getpid());
211 pthread_exit((void *)-1);
212 }
213
214 if ((sched_policy = locargptr->s_policy) == SCHED_OTHER)
215 ssp.sched_priority = 0;
216 else {
217 /* Set a random value between max_priority and min_priority */
218 gettimeofday(&tptr, NULL);
219 srand((unsigned int)tptr.tv_usec);
220 set_priority = (min_priority + (int)((float)max_priority
221 * rand() / (RAND_MAX +
222 1.0)));
223 ssp.sched_priority = set_priority;
224 }
225
226 /* give other threads a chance */
227 usleep(8);
228
229 /* set a random priority value and check if this value was honoured. */
230 if ((sched_setscheduler(getpid(), sched_policy, &ssp)) == -1) {
231 perror("main(): sched_setscheduler()");
232 dprt("pid[%d]: exiting with -1\n", getpid());
233 pthread_exit((void *)-1);
234 }
235
236 /* processor number this process last executed on */
237 if ((procnum = get_proc_num()) == -1) {
238 fprintf(stderr, "main(): get_proc_num() failed\n");
239 dprt("pid[%d]: exiting with -1\n", getpid());
240 pthread_exit((void *)-1);
241 }
242
243 if ((get_priority = sched_getparam(getpid(), &gsp)) == -1) {
244 perror("main(): sched_setscheduler()");
245 dprt("pid[%d]: exiting with -1\n", getpid());
246 pthread_exit((void *)-1);
247 }
248
249 /* processor number this process last executed on */
250 if ((procnum = get_proc_num()) == -1) {
251 fprintf(stderr, "main(): get_proc_num() failed\n");
252 dprt("pid[%d]: exiting with -1\n", getpid());
253 pthread_exit((void *)-1);
254 }
255
256 if (verbose) {
257 fprintf(stdout,
258 "PID of this task = %d\n"
259 "Max priority = %d\n"
260 "Min priority = %d\n"
261 "Expected priority = %d\n"
262 "Actual assigned priority = %d\n"
263 "Processor last execed on = %d\n\n", getpid(),
264 max_priority, min_priority, set_priority,
265 gsp.sched_priority, procnum);
266 }
267
268 locargptr->exp_prio = set_priority;
269 locargptr->act_prio = gsp.sched_priority;
270 locargptr->proc_num = procnum;
271 locargptr->procs_id = getpid();
272
273 dprt("pid[%d]: exiting with %ld\n", getpid(), locargptr);
274 pthread_exit((void *)locargptr);
275 }
276
277 /******************************************************************************/
278 /* */
279 /* Function: main */
280 /* */
281 /* Description: Entry point of the program, parse options, check for their */
282 /* validity, spawn N tasks, wait for them to return, in the end */
283 /* print all the data that the thiread function collected. */
284 /* */
285 /* Return: exits with -1 - on error. */
286 /* exits with 0 - on success. */
287 /* */
288 /******************************************************************************/
main(int argc,char ** argv)289 int main(int argc, /* number of input parameters. */
290 char **argv)
291 { /* pointer to the command line arguments. */
292 int c; /* command line options. */
293 int proc_ndx; /* number of time to repete the loop. */
294 int pid_ndx; /* number of time to repete the loop. */
295 int num_cpus = UP; /* assume machine is an UP machine. */
296 int num_thrd = MAXT; /* number of threads to create. */
297 int thrd_ndx; /* index into the array of threads. */
298 int exp_prio[PIDS]; /* desired priority, random value. */
299 int act_prio[PIDS]; /* priority actually set. */
300 int gen_pid[PIDS]; /* pid of the processes on this processor. */
301 int proc_id[PIDS]; /* id of the processor last execed on. */
302 int spcy = SCHED_FIFO; /* scheduling policy for the tasks. */
303 pthread_t thid[PIDS]; /* pids of process or threads spawned */
304 thread_sched_t *chld_args; /* arguments to funcs execed by child process. */
305 thread_sched_t *status; /* exit status for light weight process. */
306 extern char *optarg; /* arguments passed to each option. */
307 thread_sched_t **args_table; /* pointer table of arguments address */
308 thread_sched_t **status_table; /*pointer table of status address */
309
310 if (getuid() != 0) {
311 fprintf(stderr,
312 "ERROR: Only root user can run this program.\n");
313 usage(argv[0]);
314 }
315
316 if (argc < 2) {
317 fprintf(stderr,
318 "ERROR: Enter a value for the number of CPUS\n");
319 usage(argv[0]);
320 }
321
322 while ((c = getopt(argc, argv, "c:hp:t:v")) != -1) {
323 switch (c) {
324 case 'c': /* number of processors. no default. */
325 if ((num_cpus = atoi(optarg)) == 0)
326 OPT_MISSING(argv[0], optopt);
327 else if (num_cpus < 0) {
328 fprintf(stdout,
329 "WARNING: Bad argument -p %d. Using default\n",
330 num_cpus);
331 num_cpus = UP;
332 }
333 /* MAXT threads per cpu. */
334 num_thrd = num_thrd * num_cpus;
335 break;
336 case 'h': /* usage message */
337 usage(argv[0]);
338 break;
339 case 'p': /* schedular policy. default SCHED_FIFO */
340 if (strncmp(optarg, "fifo", 4) == 0)
341 spcy = SCHED_FIFO;
342 else if (strncmp(optarg, "rr", 2) == 0)
343 spcy = SCHED_RR;
344 else if (strncmp(optarg, "other", 5) == 0)
345 spcy = SCHED_OTHER;
346 else {
347 fprintf(stderr,
348 "ERROR: Unrecognized scheduler policy, "
349 "using default\n");
350 usage(argv[0]);
351 }
352 break;
353 case 't': /* input how many threads to create */
354 if ((num_thrd = atoi(optarg)) == 0)
355 OPT_MISSING(argv[0], optopt);
356 else if (num_thrd < 0) {
357 fprintf(stderr,
358 "WARNING: Bad argument -t %d. Using default\n",
359 num_thrd);
360 num_thrd = MAXT;
361 } else if (num_thrd > PIDS) {
362 fprintf(stderr,
363 "WARNING: -t %d exceeds maximum number of allowed pids"
364 " %d\n Setting number of threads to %d\n",
365 num_thrd, PIDS, PIDS - 1000);
366 num_thrd = (PIDS - 1000);
367 }
368 break;
369 case 'v': /* verbose out put, make output look ugly! */
370 verbose = 1;
371 break;
372 default:
373 usage(argv[0]);
374 break;
375 }
376 }
377
378 /* create num_thrd number of threads. */
379 args_table = malloc(num_thrd * sizeof(thread_sched_t *));
380 if (!args_table) {
381 perror("main(): malloc failed");
382 exit(-1);
383 }
384 for (thrd_ndx = 0; thrd_ndx < num_thrd; thrd_ndx++) {
385 args_table[thrd_ndx] = malloc(sizeof(thread_sched_t));
386 if (!args_table[thrd_ndx]) {
387 perror("main(): malloc failed");
388 exit(-1);
389 }
390 chld_args = args_table[thrd_ndx];
391 chld_args->s_policy = spcy;
392 if (pthread_create(&thid[thrd_ndx], NULL, thread_func,
393 chld_args)) {
394 fprintf(stderr, "ERROR: creating task number: %d\n",
395 thrd_ndx);
396 perror("main(): pthread_create()");
397 exit(-1);
398 }
399 if (verbose)
400 fprintf(stdout, "Created thread[%d]\n", thrd_ndx);
401 usleep(9);
402 sched_yield();
403 }
404
405 /* wait for the children to terminate */
406 status_table = malloc(num_thrd * sizeof(thread_sched_t *));
407 if (!status_table) {
408 perror("main(): malloc failed");
409 exit(-1);
410 }
411 for (thrd_ndx = 0; thrd_ndx < num_thrd; thrd_ndx++) {
412 status_table[thrd_ndx] = malloc(sizeof(thread_sched_t));
413 if (!status_table[thrd_ndx]) {
414 perror("main(): malloc failed");
415 exit(-1);
416 }
417 status = status_table[thrd_ndx];
418 if (pthread_join(thid[thrd_ndx], (void **)&status)) {
419 perror("main(): pthread_join()");
420 exit(-1);
421 } else {
422 if (status == (thread_sched_t *) - 1) {
423 fprintf(stderr,
424 "thread [%d] - process exited with exit code -1\n",
425 thrd_ndx);
426 exit(-1);
427 } else {
428 exp_prio[thrd_ndx] = status->exp_prio;
429 act_prio[thrd_ndx] = status->act_prio;
430 proc_id[thrd_ndx] = status->proc_num;
431 gen_pid[thrd_ndx] = status->procs_id;
432 }
433 }
434 SAFE_FREE(args_table[thrd_ndx]);
435 SAFE_FREE(status_table[thrd_ndx]);
436 usleep(10);
437 }
438
439 if (verbose) {
440 fprintf(stdout,
441 "Number of tasks spawned: %d\n"
442 "Number of CPUs: %d\n"
443 "Scheduling policy: %d\n", num_thrd, num_cpus,
444 spcy);
445 }
446
447 SAFE_FREE(args_table);
448 SAFE_FREE(status_table);
449
450 for (proc_ndx = 0; proc_ndx < num_cpus; proc_ndx++) {
451 fprintf(stdout, "For processor number = %d\n", proc_ndx);
452 fprintf(stdout, "%s\n", "===========================");
453 for (pid_ndx = 0; pid_ndx < num_thrd; pid_ndx++) {
454 if (proc_id[pid_ndx] == proc_ndx)
455 fprintf(stdout,
456 "pid of task = %d priority requested = %d"
457 " priority assigned by scheduler = %d\n",
458 gen_pid[pid_ndx], exp_prio[pid_ndx],
459 act_prio[pid_ndx]);
460 }
461 }
462 exit(0);
463 }
464