464 lines
18 KiB
C
464 lines
18 KiB
C
/******************************************************************************/
|
|
/* */
|
|
/* 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/time.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 timeval tptr; /* tptr.tv_usec 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 */
|
|
gettimeofday(&tptr, NULL);
|
|
srand((unsigned int)tptr.tv_usec);
|
|
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);
|
|
}
|