Home | History | Annotate | Download | only in tool
      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