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 (at) 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/timeb.h> 56 #include <unistd.h> 57 #include <string.h> 58 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 /******************************************************************************/ 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 /******************************************************************************/ 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 /******************************************************************************/ 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 timeb tptr; /* tptr.millitm 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 ftime(&tptr); 219 srand((tptr.millitm) % 1000); 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 /******************************************************************************/ 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 errors %d\n", 425 thrd_ndx, WEXITSTATUS(status)); 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