/******************************************************************************/
/* */
/* Copyright (c) International Business Machines Corp., 2001 */
/* */
/* This program is free software; you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation; either version 2 of the License, or */
/* (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See */
/* the GNU General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program; if not, write to the Free Software */
/* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
/* */
/******************************************************************************/
/******************************************************************************/
/* */
/* History: Feb - 21 - 2002 Created - Manoj Iyer, IBM Austin TX. */
/* email: manjo@austin.ibm.com. */
/* */
/* Feb - 25 - 2002 Modified - Manoj Iyer, IBM Austin TX. */
/* - Added structure thread_sched_t. */
/* - Added logic to specify scheduling policy. */
/* */
/* Feb - 25 - 2002 Modified - Manoj Iyer, IBM Austin TX. */
/* - Added header file string.h. */
/* - Removed dead variable ppid from thread_func.*/
/* - Fixed date from 2001 to 2002 in History. */
/* */
/* File: trace_sched.c */
/* */
/* Description: This utility spawns N tasks, each task sets its priority by */
/* making a system call to the scheduler. The thread function */
/* reads the priority that tbe schedular sets for this task and */
/* also reads from /proc the processor this task last executed on*/
/* the information that is gathered by the thread function may */
/* be in real-time. Its only an approximation. */
/* */
/******************************************************************************/
#include <fcntl.h>
#include <limits.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <sched.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/timeb.h>
#include <unistd.h>
#include <string.h>
void noprintf(char *string, ...)
{
}
#ifdef DEBUG /* compile with this flag for debug, use dprt in code */
#define dprt printf
#else
#define dprt noprintf
#endif
#ifndef PID_MAX
#define PID_MAX 0x8000
#endif
#define MAXT 100
#ifdef PTHREAD_THREADS_MAX
#define PIDS PTHREAD_THREADS_MAX /* maximum thread allowed. */
#elif defined(PID_MAX_DEFAULT)
#define PIDS PID_MAX_DEFAULT /* maximum pids allowed. */
#else
#define PIDS PID_MAX /* alternative way maximum pids may be defined */
#endif
#define UP 1 /* assume UP if no SMP value is specified. */
#define OPT_MISSING(prog, opt) do{\
fprintf(stderr, "%s: option -%c ", prog, opt); \
fprintf(stderr, "requires an argument\n"); \
usage(prog); \
} while (0)
#define SAFE_FREE(p) { if (p) { free(p); (p)=NULL; } }
typedef struct { /* contains priority and CPU info of the task. */
int exp_prio; /* priority that we wish to set. */
int act_prio; /* priority set by the scheduler. */
int proc_num; /* last processor on which this task executed. */
int procs_id; /* pid of this task. */
int s_policy; /* scheduling policy for the task. */
} thread_sched_t;
int verbose = 0; /* set verbose printing, makes output look ugly! */
/******************************************************************************/
/* */
/* Function: usage */
/* */
/* Description: Print the usage message. */
/* */
/* Return: exits with -1 */
/* */
/******************************************************************************/
void usage(char *progname)
{ /* name of this program */
fprintf(stderr,
"Usage: %s -c NCPU -h -p [fifo:rr:other] -t THREADS -v\n"
"\t -c Number of CUPS in the machine. User MUST provide\n"
"\t -h Help!\n"
"\t -p Scheduling policy, choice: fifo, rr, other. Default: fifo\n"
"\t -t Number of threads to create. Default: %d\n"
"\t -v Verbose out put, print ugly!. Default: OFF\n",
progname, MAXT);
exit(-1);
}
/******************************************************************************/
/* */
/* Function: get_proc_num */
/* */
/* Description: Function reads the proc filesystem file /proc/<PID>/stat */
/* gets the CPU number this process last executed on and returns */
/* Some hard assumptions were made regarding buffer sizes. */
/* */
/* Return: exits with -1 - on error */
/* CPU number - on success */
/* */
/******************************************************************************/
static int get_proc_num(void)
{
int fd = -1; /* file descriptor of the /proc/<pid>/stat file. */
int fsize = -1; /* size of the /proc/<pid>/stat file. */
char filename[256]; /* buffer to hold the string /proc/<pid>/stat. */
char fbuff[512]; /* contains the contents of the stat file. */
/* get the name of the stat file for this process */
sprintf(filename, "/proc/%d/stat", getpid());
/* open the stat file and read the contents to a buffer */
if ((fd = open(filename, O_RDONLY)) == -1) {
perror("get_proc_num(): open()");
return -1;
}
usleep(6);
sched_yield();
if ((fsize = read(fd, fbuff, 512)) == -1) {
perror("main(): read()");
return -1;
}
close(fd);
/* return the processor number last executed on. */
return atoi(&fbuff[fsize - 2]);
}
/******************************************************************************/
/* */
/* Function: thread_func */
/* */
/* Description: This function is executed in the context of the new task that */
/* pthread_createi() will spawn. The (thread) task will get the */
/* minimum and maximum static priority for this system, set the */
/* priority of the current task to a random priority value if */
/* the policy set if SCHED_FIFO or SCHED_RR. The priority if this*/
/* task that was assigned by the scheduler is got from making the*/
/* system call to sched_getscheduler(). The CPU number on which */
/* the task was last seen is also recorded. All the above data is*/
/* returned to the calling routine in a structure thread_sched_t.*/
/* */
/* Input: thread_sched_t */
/* s_policy - scheduling policy for the task. */
/* */
/* Return: thread_sched_t - on success. */
/* exp_prio - random priority value to set. */
/* act_prio - priority set by the scheduler. */
/* proc_num - CPU number on which this task last executed. */
/* procs_id - pid of this task. */
/* */
/* -1 - on error. */
/* */
/******************************************************************************/
void *thread_func(void *args)
{ /* arguments to the thread function */
static int max_priority; /* max possible priority for a process. */
static int min_priority; /* min possible priority for a process. */
static int set_priority; /* set the priority of the proc by this value. */
static int get_priority; /* get the priority that is set for this proc. */
static int procnum; /* processor number last executed on. */
static int sched_policy; /* scheduling policy as set by user/default */
struct sched_param ssp; /* set schedule priority. */
struct sched_param gsp; /* gsp schedule priority. */
struct timeb tptr; /* tptr.millitm will be used to seed srand. */
thread_sched_t *locargptr = /* local ptr to the arguments. */
(thread_sched_t *) args;
/* Get the system max and min static priority for a process. */
if (((max_priority = sched_get_priority_max(SCHED_FIFO)) == -1) ||
((min_priority = sched_get_priority_min(SCHED_FIFO)) == -1)) {
fprintf(stderr, "failed to get static priority range\n");
dprt("pid[%d]: exiting with -1\n", getpid());
pthread_exit((void *)-1);
}
if ((sched_policy = locargptr->s_policy) == SCHED_OTHER)
ssp.sched_priority = 0;
else {
/* Set a random value between max_priority and min_priority */
ftime(&tptr);
srand((tptr.millitm) % 1000);
set_priority = (min_priority + (int)((float)max_priority
* rand() / (RAND_MAX +
1.0)));
ssp.sched_priority = set_priority;
}
/* give other threads a chance */
usleep(8);
/* set a random priority value and check if this value was honoured. */
if ((sched_setscheduler(getpid(), sched_policy, &ssp)) == -1) {
perror("main(): sched_setscheduler()");
dprt("pid[%d]: exiting with -1\n", getpid());
pthread_exit((void *)-1);
}
/* processor number this process last executed on */
if ((procnum = get_proc_num()) == -1) {
fprintf(stderr, "main(): get_proc_num() failed\n");
dprt("pid[%d]: exiting with -1\n", getpid());
pthread_exit((void *)-1);
}
if ((get_priority = sched_getparam(getpid(), &gsp)) == -1) {
perror("main(): sched_setscheduler()");
dprt("pid[%d]: exiting with -1\n", getpid());
pthread_exit((void *)-1);
}
/* processor number this process last executed on */
if ((procnum = get_proc_num()) == -1) {
fprintf(stderr, "main(): get_proc_num() failed\n");
dprt("pid[%d]: exiting with -1\n", getpid());
pthread_exit((void *)-1);
}
if (verbose) {
fprintf(stdout,
"PID of this task = %d\n"
"Max priority = %d\n"
"Min priority = %d\n"
"Expected priority = %d\n"
"Actual assigned priority = %d\n"
"Processor last execed on = %d\n\n", getpid(),
max_priority, min_priority, set_priority,
gsp.sched_priority, procnum);
}
locargptr->exp_prio = set_priority;
locargptr->act_prio = gsp.sched_priority;
locargptr->proc_num = procnum;
locargptr->procs_id = getpid();
dprt("pid[%d]: exiting with %ld\n", getpid(), locargptr);
pthread_exit((void *)locargptr);
}
/******************************************************************************/
/* */
/* Function: main */
/* */
/* Description: Entry point of the program, parse options, check for their */
/* validity, spawn N tasks, wait for them to return, in the end */
/* print all the data that the thiread function collected. */
/* */
/* Return: exits with -1 - on error. */
/* exits with 0 - on success. */
/* */
/******************************************************************************/
int main(int argc, /* number of input parameters. */
char **argv)
{ /* pointer to the command line arguments. */
int c; /* command line options. */
int proc_ndx; /* number of time to repete the loop. */
int pid_ndx; /* number of time to repete the loop. */
int num_cpus = UP; /* assume machine is an UP machine. */
int num_thrd = MAXT; /* number of threads to create. */
int thrd_ndx; /* index into the array of threads. */
int exp_prio[PIDS]; /* desired priority, random value. */
int act_prio[PIDS]; /* priority actually set. */
int gen_pid[PIDS]; /* pid of the processes on this processor. */
int proc_id[PIDS]; /* id of the processor last execed on. */
int spcy = SCHED_FIFO; /* scheduling policy for the tasks. */
pthread_t thid[PIDS]; /* pids of process or threads spawned */
thread_sched_t *chld_args; /* arguments to funcs execed by child process. */
thread_sched_t *status; /* exit status for light weight process. */
extern char *optarg; /* arguments passed to each option. */
thread_sched_t **args_table; /* pointer table of arguments address */
thread_sched_t **status_table; /*pointer table of status address */
if (getuid() != 0) {
fprintf(stderr,
"ERROR: Only root user can run this program.\n");
usage(argv[0]);
}
if (argc < 2) {
fprintf(stderr,
"ERROR: Enter a value for the number of CPUS\n");
usage(argv[0]);
}
while ((c = getopt(argc, argv, "c:hp:t:v")) != -1) {
switch (c) {
case 'c': /* number of processors. no default. */
if ((num_cpus = atoi(optarg)) == 0)
OPT_MISSING(argv[0], optopt);
else if (num_cpus < 0) {
fprintf(stdout,
"WARNING: Bad argument -p %d. Using default\n",
num_cpus);
num_cpus = UP;
}
/* MAXT threads per cpu. */
num_thrd = num_thrd * num_cpus;
break;
case 'h': /* usage message */
usage(argv[0]);
break;
case 'p': /* schedular policy. default SCHED_FIFO */
if (strncmp(optarg, "fifo", 4) == 0)
spcy = SCHED_FIFO;
else if (strncmp(optarg, "rr", 2) == 0)
spcy = SCHED_RR;
else if (strncmp(optarg, "other", 5) == 0)
spcy = SCHED_OTHER;
else {
fprintf(stderr,
"ERROR: Unrecognized scheduler policy,"
"using default\n");
usage(argv[0]);
}
break;
case 't': /* input how many threads to create */
if ((num_thrd = atoi(optarg)) == 0)
OPT_MISSING(argv[0], optopt);
else if (num_thrd < 0) {
fprintf(stderr,
"WARNING: Bad argument -t %d. Using default\n",
num_thrd);
num_thrd = MAXT;
} else if (num_thrd > PIDS) {
fprintf(stderr,
"WARNING: -t %d exceeds maximum number of allowed pids"
" %d\n Setting number of threads to %d\n",
num_thrd, PIDS, PIDS - 1000);
num_thrd = (PIDS - 1000);
}
break;
case 'v': /* verbose out put, make output look ugly! */
verbose = 1;
break;
default:
usage(argv[0]);
break;
}
}
/* create num_thrd number of threads. */
args_table = malloc(num_thrd * sizeof(thread_sched_t *));
if (!args_table) {
perror("main(): malloc failed");
exit(-1);
}
for (thrd_ndx = 0; thrd_ndx < num_thrd; thrd_ndx++) {
args_table[thrd_ndx] = malloc(sizeof(thread_sched_t));
if (!args_table[thrd_ndx]) {
perror("main(): malloc failed");
exit(-1);
}
chld_args = args_table[thrd_ndx];
chld_args->s_policy = spcy;
if (pthread_create(&thid[thrd_ndx], NULL, thread_func,
chld_args)) {
fprintf(stderr, "ERROR: creating task number: %d\n",
thrd_ndx);
perror("main(): pthread_create()");
exit(-1);
}
if (verbose)
fprintf(stdout, "Created thread[%d]\n", thrd_ndx);
usleep(9);
sched_yield();
}
/* wait for the children to terminate */
status_table = malloc(num_thrd * sizeof(thread_sched_t *));
if (!status_table) {
perror("main(): malloc failed");
exit(-1);
}
for (thrd_ndx = 0; thrd_ndx < num_thrd; thrd_ndx++) {
status_table[thrd_ndx] = malloc(sizeof(thread_sched_t));
if (!status_table[thrd_ndx]) {
perror("main(): malloc failed");
exit(-1);
}
status = status_table[thrd_ndx];
if (pthread_join(thid[thrd_ndx], (void **)&status)) {
perror("main(): pthread_join()");
exit(-1);
} else {
if (status == (thread_sched_t *) - 1) {
fprintf(stderr,
"thread [%d] - process exited with exit code -1\n",
thrd_ndx);
exit(-1);
} else {
exp_prio[thrd_ndx] = status->exp_prio;
act_prio[thrd_ndx] = status->act_prio;
proc_id[thrd_ndx] = status->proc_num;
gen_pid[thrd_ndx] = status->procs_id;
}
}
SAFE_FREE(args_table[thrd_ndx]);
SAFE_FREE(status_table[thrd_ndx]);
usleep(10);
}
if (verbose) {
fprintf(stdout,
"Number of tasks spawned: %d\n"
"Number of CPUs: %d\n"
"Scheduling policy: %d\n", num_thrd, num_cpus,
spcy);
}
SAFE_FREE(args_table);
SAFE_FREE(status_table);
for (proc_ndx = 0; proc_ndx < num_cpus; proc_ndx++) {
fprintf(stdout, "For processor number = %d\n", proc_ndx);
fprintf(stdout, "%s\n", "===========================");
for (pid_ndx = 0; pid_ndx < num_thrd; pid_ndx++) {
if (proc_id[pid_ndx] == proc_ndx)
fprintf(stdout,
"pid of task = %d priority requested = %d"
" priority assigned by scheduler = %d\n",
gen_pid[pid_ndx], exp_prio[pid_ndx],
act_prio[pid_ndx]);
}
}
exit(0);
}