/*
* 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
*/
/*---------------------------------------------------------------------+
| sched_driver |
| ==================================================================== |
| |
| Description: This program uses system calls to change the |
| priorities of the throughput measurement testcases. |
| When real-time is in effect, priorities 50 through 64 |
| are used. (MAX_PRI and MIN_PRI) When user-time |
| (normal) is in effect, 0-14 (corresponding to nice() |
| calls) is used. The driver only keeps track of |
| values from 50 to 64, and the testcases will scale |
| them down to 0 to 14 when needed, to change the |
| priority of a user-time process. |
| |
| Algorithm: o Parse command line arguments |
| o Set current priority |
| o Calcucations (process slots, short/long term slots) |
| o Perform throughput tests with high priority |
| o Start long-term testcases |
| o While time remains |
| - Start short-term tests |
| - Perform throughput tests with new priority |
| - Kill short-term tests |
| - Increase priority |
| |
| Usage: sched_driver [-s n] [-p n] [-t n] [-d] [-v] |
| |
| where: |
| -s n stress percentage |
| -p n process slots |
| -t n execution time in hours |
| -d enable debugging messages |
| -v Turn on verbose mode |
| |
| Last update: Ver. 1.15, 4/10/94 23:04:23 |
| |
| Change Activity |
| |
| Version Date Name Reason |
| 0.1 072889 GEB Initial draft |
| 1.2 120793 JAT Changes for AIX 4.1 |
| 1.3 041094 DJK Rewrote protions... |
| 1.4 010402 Manoj Iyer Ported to Linux |
| |
+---------------------------------------------------------------------*/
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <pwd.h>
#include <time.h>
#include <limits.h>
#include "sched.h"
/*
* Defines:
*
* MAXPROCS: maximum number of processes
*
* PRIINC: priority step value
*
* MAX_PRI: highest priority to use
*
* MIN_PRI: lowest priority to use
*
* DEFAULT_STRESS_PERCENTAGE: stress percentage (process slot multiplier)
*
* DEFAULT_PROCESS_SLOTS: number of processes test driver will try and create
*
* DEFAULT_TIME: time (hours) for which this test driver will run
*
* USAGE: usage statement
*/
#define MAXPROCS 100
#define PRIINC 2
#define MAX_PRI 55 /* was 50 */
#define MIN_PRI 75 /* was 64 */
#define DEFAULT_STRESS_PERCENTAGE 0.5
#define DEFAULT_PROCESS_SLOTS 16
#define DEFAULT_TIME 1.00
#define USAGE "Usage: %s [-s n] [-p n] [-t n] [-d] [-v] \n" \
" -s n stress percentage [0.0<n<1.0] (default 0.5) \n" \
" -p n process slots (default 16) \n" \
" -t n execution time in hours (default 1.0 hrs) \n" \
" -d enable debugging messages \n" \
" -v Turn on verbose mode \n"
/*
* Global variables:
*
* stress_percent: stress percentage
*
* :
*
* execution_time: execution time in hours
*
* debug: (option flag) enables debugging messages
*/
int numprocs, /* number of process id's in table */
procs[MAXPROCS], /* array of process id's for killing */
long_running, /* number of long term testcases running */
short_running; /* number of short term testcases running */
float e4user, /* previous elapsed seconds for tc 4-user */
e4real, /* previous elapsed seconds for tc 4-real */
e5user, /* previous elapsed seconds for tc 5-user */
e5real, /* previous elapsed seconds for tc 5-real */
e6user0, /* previous elapsed seconds for tc 6-user,nf */
e6real0, /* previous elapsed seconds for tc 6-real,nf */
e6user1, /* previous elapsed seconds for tc 6-user,f */
e6child; /* previous elapsed seconds for tc 6-child */
double stress_percent = DEFAULT_STRESS_PERCENTAGE;
double execution_time = DEFAULT_TIME;
int process_slots = DEFAULT_PROCESS_SLOTS;
int debug = 0;
/*
* Function prototypes
*/
void startup(long);
int start_testcase(char *, char *, char *, char *, char *, char *);
int process_slots_in_use();
int available_user_process_slots();
float measure_test(char *, char *, char *, char *, float *);
void display_line(char *, int, int, float, float *, int);
void perform_throughput_tests(int);
void start_long_term_testcases(int, char *);
void kill_short_term_testcases();
void start_short_term_testcases(int, double, int);
void finishup(long);
void parse_args(int, char **);
/*---------------------------------------------------------------------+
| main () |
| ==================================================================== |
| |
| Function: Main program |
| |
+---------------------------------------------------------------------*/
int main(int argc, char **argv)
{
long runseconds, /* number of seconds to run */
start_time; /* time at start of driver */
int current_priority, /* current priority level for nice */
workslots, /* number of free workslots */
long_term_slot_total, /* workslots for long-term processes */
short_term_slot_total; /* workslots for short-term */
/*
* Parse command line arguments & printer program header...
*/
parse_args(argc, argv);
printf("Scheduler Testsuite Program\n\n");
fflush(stdout);
/*
* Calculate number of seconds to run, then print out start info
*/
runseconds = (long)(execution_time * 60.0 * 60.0);
start_time = time(NULL);
startup(start_time);
/*
* Calculate available workslots, long-term, and short-term slots
*/
workslots = available_user_process_slots() * stress_percent;
long_term_slot_total = workslots / 2;
if (debug) {
printf("available slots: %d\n",
available_user_process_slots());
printf("workslots available: %d\n", workslots);
printf("stress_percent: %f\n", stress_percent);
printf("run-hours: %f (hrs)\n", execution_time);
printf("runseconds: %ld (sec)\n", runseconds);
}
/*
* Run the first set of tests with an average priority
*/
perform_throughput_tests((MAX_PRI + MIN_PRI) / 2);
fflush(stdout);
/*
* Start the long-term testcases running
*/
start_long_term_testcases(long_term_slot_total, argv[2]);
short_term_slot_total = workslots / 2;
fflush(stdout);
/*
* Loop while there is still time
*/
current_priority = MAX_PRI;
while ((time(0) - start_time) < runseconds) {
if (debug)
printf("current priority: %d\n", current_priority);
if (debug)
printf("starting short term tests\n");
start_short_term_testcases(short_term_slot_total,
stress_percent, current_priority);
fflush(stdout);
perform_throughput_tests(current_priority);
fflush(stdout);
if (debug)
printf("killing short term tests\n");
kill_short_term_testcases();
fflush(stdout);
if (current_priority + PRIINC > MIN_PRI)
current_priority = MAX_PRI;
else
current_priority += PRIINC;
}
/*
* Exit with success...
*/
finishup(start_time);
printf("\nsuccessful!\n");
fflush(stdout);
return (0);
}
/*------------------------------ startup() ------------------------------*/
/* This procedure opens the , and then outputs some starting *
* information to the screen and . It also initializes the *
* process id list and other global variables. *
*-----------------------------------------------------------------------*/
void startup(long start_time)
{
char tempbuffer[50]; /* temporary buffer to hold names */
/*
* Now output some diagnostic information
*/
printf("start time = %s\n", ctime(&start_time));
gethostname(tempbuffer, 40);
printf("host name = %s\n", tempbuffer);
printf("user name = %s\n", getpwuid(geteuid())->pw_name);
printf("test duration = %4.2f (hours)\n", execution_time);
printf("test stress = %4.2f%%%%\n\n", 100 * stress_percent);
/*
* Initialize the global variables
*/
numprocs = 0;
long_running = 0;
short_running = 0;
e4user = 0.0;
e4real = 0.0;
e5user = 0.0;
e5real = 0.0;
e6user0 = 0.0;
e6real0 = 0.0;
e6user1 = 0.0;
e6child = 0.0;
}
/*--------------------------- start_testcase() --------------------------*/
/* This procedure will run a specified testcase by forking a process, and*
* then running the testcase with it. It will also store the process id *
* number in the process id table. The process id of the child process *
* is returned to the calling program. *
* name1 pathname of testcase to run *
* name2 filename of testcase to run *
* param1 parameters to pass to the testcase *
* param2 *
* param3 *
* param4 if sched_tc6: fork flag: 0=false, 1=true *
*-----------------------------------------------------------------------*/
int start_testcase(char *name1, char *name2, char *param1, char *param2,
char *param3, char *param4)
{
int pid, /* pid of currently running process */
pid_save; /* saved pid of process */
/*
* Fork a process that will run testcase and save the pid
*/
if (debug)
printf("test: %s %s p1[%s] p2[%s] p3[%s] p4[%s]\n",
name1, name2, param1, param2, param3, param4);
pid_save = pid = fork();
/*
* If the pid returned is -1, fork failed. If the pid returned is
* 0, then the process running is the child process, and we need
* to do an 'execl' to run the testcase. If the pid returned is
* anything else, then the parent is running, and we return.
*/
switch (pid) {
case -1:
exit(-1);
case 0:
execl(name1, name2, param1, param2, param3, param4, NULL);
printf("ERROR: start_testcase(): execl failed.\n");
exit(-1);
default:
break;
}
if (debug)
printf("testcase %s started -- pid is %d\n", name2, pid_save);
/*
* If the process just forked is for a short-term testcase, then
* add the process id to the table.
*/
if (debug)
printf("new process: %s ", name2);
if (strstr(name2, "tc1") || strstr(name2, "tc3")) {
procs[numprocs] = pid_save;
numprocs++;
short_running++;
if (debug)
printf("(%d short term)", short_running);
}
if (strstr(name2, "tc0") || strstr(name2, "tc2")) {
long_running++;
if (debug)
printf("(%d long term)", long_running);
}
if (debug)
printf("\n");
return (pid_save);
}
/*------------------------- process_slots_in_use() ----------------------*/
/* This function will return the number of process slots currently in use*
* by executing the 'ps' command. *
*-----------------------------------------------------------------------*/
int process_slots_in_use()
{
FILE *psfile; /* temporary file to hold output of 'ps' command */
int usedslots; /* holds the number of used process slots */
/*
* Call the 'ps' command and write the number of process slots to a file
*/
if (system("ps -e | wc -l > ps.out") < 0)
sys_error("system failed", __FILE__, __LINE__);
/*
* Open the output file
*/
if ((psfile = fopen("ps.out", "r")) == NULL) {
exit(-1);
}
/*
* Read the number of process slots in use from the file
*/
fscanf(psfile, "%d", &usedslots);
/*
* Close the output file
*/
if (fclose(psfile) == -1) {
exit(-1);
}
/*
* Remove the output file
*/
if (system("/bin/rm ps.out") < 0)
sys_error("system failed", __FILE__, __LINE__);
return (usedslots - 1);
}
/*----------------------- available_user_process_slots() ----------------*/
/* This function returns the total number of available user process slots*
* by subtracting the process slots currently in use from the maximum *
* possible process slots. *
*-----------------------------------------------------------------------*/
int available_user_process_slots()
{
int num = process_slots_in_use();
return ((process_slots < num) ? process_slots : process_slots - num);
}
/*---------------------------- measure_test() ---------------------------*/
/* This function executes a throughput measurement process and waits for *
* that process to finish. When finished, it reads the result from a *
* file and returns that result to the caller. The file is then deleted.*
* If sched_tc6 is called, then the second time is also read from the *
* results file and returned to the caller. *
*-----------------------------------------------------------------------*/
float measure_test(name, param1, param2, param3, t2)
char *name, /* filename of testcase to run */
*param1, /* user flag: 0=user, 1=real time */
*param2, /* priority to run the throughput test at */
*param3; /* if sched_tc6: fork flag, 0=false, 1=true */
float *t2; /* if sched_tc6: second time returned from testcase */
{
char temp[PATH_MAX], /* holds pathname and returned floating number */
t2asc[50]; /* holds second returned floating number */
int saved_pid; /* process id of forked process */
FILE *datafile; /* file pointer for temporary file */
/*
* Create the path name to be passed to the start_testcase() function
*/
sprintf(temp, "./%s", name);
/*
* Send all the parameters, and start the testcase
*/
saved_pid = start_testcase(temp, name, param1,
"-lsch.measure", param2, param3);
/*
* Wait for the testcase to finish
*/
if (debug)
printf("waiting on child %d\n", saved_pid);
while (wait(NULL) != saved_pid) ;
/*
* Open the temporary file to get the returned number of seconds
*/
if ((datafile = fopen("sch.measure", "r")) == NULL) {
sys_error("cannot open sch.measure", __FILE__, __LINE__);
}
/*
* Read the number of seconds
*/
fgets(temp, 50, datafile);
/*added by mpt
printf("sched+driver: measure_test: number of seconds=%s\n",temp)
*********** */
/*
* If this is sched_tc6, then there is another number we must return
*/
if (strcmp(name, "sched_tc6") == 0) {
fgets(t2asc, 50, datafile);
*t2 = atof(t2asc);
}
/*
* Close the temporary file
*/
if (fclose(datafile) != 0) {
exit(-1);
}
/*
* Now try to remove the temporary file
*/
/*added by MPT
printf("measure_test: REMOVING sch.measure\n");
fflush(stdout);
if (system ("rm sch.measure") < 0)
sys_error ("system failed", __FILE__, __LINE__);
*/
return (atof(temp));
}
/*------------------------- display_line() ------------------------------*/
/* This procedure displays a line of output given the results of a *
* throughput test. It displays the testcase name, the current priority *
* level, the user/real time flag, and the elapsed time in seconds, as *
* well as the percent change between the current and previous times. *
* It then updates the previous elapsed time to be the current one. *
*-----------------------------------------------------------------------*/
void display_line(char *tcname, int pri, int f, float et, float *pet, int ff)
{
static int display_header = 0;
float pc; /* holds percent change */
/*
* Print header every eight lines...
*/
if (display_header-- == 0) {
printf("\n Test Processes "
" Time Notes\n"
"--------- --------------------------- "
"--------------- -------\n"
"name long short priority mode "
"elapsed %%%%delta\n\n");
display_header = 6;
}
/*
* Calculate the percent change in time
*/
pc = (*pet == 0.0) ? 0.0 : 100.0 * ((et - *pet) / *pet) + 0.05;
printf("%-12s %2d %2d %2d %4s %06.4f %+06.4f %s\n",
tcname, long_running, short_running, pri,
(f == 0) ? "user" : "real", et, pc, (ff) ? "forked child" : " ");
fflush(stdout);
*pet = et;
}
/*------------------------- perform_throughput_tests() ------------------*/
/* This procedure is called each time throughput tests are to be *
* performed. This procedure executes each of the throughput tests, and *
* records the results of each to the . *
*-----------------------------------------------------------------------*/
void perform_throughput_tests(int current_priority)
{
float esecs, /* elapsed seconds returned from each testcase */
esecs2, /* elapsed seconds (second part) for sched_tc6 */
pc; /* percent change for sched_tc6 */
char pristr[10]; /* holds ascii value of priority as parameter */
sprintf(pristr, "-p%d", current_priority);
#if defined(_IA64) && !defined(__64BIT__)
esecs =
measure_test("sched_tc4.32", "-tvariable", pristr, NULL, &esecs2);
display_line("sched_tc4.32", current_priority, 0, esecs, &e4user, 2);
esecs = measure_test("sched_tc4.32", "-tfixed", pristr, NULL, &esecs2);
display_line("sched_tc4.32", current_priority, 1, esecs, &e4real, 2);
esecs =
measure_test("sched_tc5.32", "-tvariable", pristr, NULL, &esecs2);
display_line("sched_tc5.32", current_priority, 0, esecs, &e5user, 2);
esecs = measure_test("sched_tc5.32", "-tfixed", pristr, NULL, &esecs2);
display_line("sched_tc5.32", current_priority, 1, esecs, &e5real, 2);
esecs =
measure_test("sched_tc6.32", "-tvariable", pristr, " -d ", &esecs2);
display_line("sched_tc6.32", current_priority, 0, esecs, &e6user0, 0);
esecs =
measure_test("sched_tc6.32", "-tfixed", pristr, " -d ", &esecs2);
display_line("sched_tc6.32", current_priority, 1, esecs, &e6real0, 0);
esecs =
measure_test("sched_tc6.32", "-tvariable", pristr, " -df ",
&esecs2);
display_line("sched_tc6.32", current_priority, 0, esecs, &e6user1, 1);
#else
esecs = measure_test("sched_tc4", "-tvariable", pristr, NULL, &esecs2);
display_line("sched_tc4", current_priority, 0, esecs, &e4user, 2);
esecs = measure_test("sched_tc4", "-tfixed", pristr, NULL, &esecs2);
display_line("sched_tc4", current_priority, 1, esecs, &e4real, 2);
esecs = measure_test("sched_tc5", "-tvariable", pristr, NULL, &esecs2);
display_line("sched_tc5", current_priority, 0, esecs, &e5user, 2);
esecs = measure_test("sched_tc5", "-tfixed", pristr, NULL, &esecs2);
display_line("sched_tc5", current_priority, 1, esecs, &e5real, 2);
esecs =
measure_test("sched_tc6", "-tvariable", pristr, " -d ", &esecs2);
display_line("sched_tc6", current_priority, 0, esecs, &e6user0, 0);
esecs = measure_test("sched_tc6", "-tfixed", pristr, " -d ", &esecs2);
display_line("sched_tc6", current_priority, 1, esecs, &e6real0, 0);
esecs =
measure_test("sched_tc6", "-tvariable", pristr, " -df ", &esecs2);
display_line("sched_tc6", current_priority, 0, esecs, &e6user1, 1);
#endif
/*
* Manually build the display line for the second part of sched_tc6
*/
/*
* Calculate the percent change in time
*/
pc = (e6child ==
0.0) ? 0.0 : 100 * ((esecs2 - e6child) / e6child) + 0.05;
printf("%-12s forked child %4s %06.4f %+06.4f\n",
"sched_tc6", "real", esecs2, pc);
e6child = esecs2;
}
/*------------------------ start_long_term_testcases() ------------------*/
/* This procedure takes the number of long-term process slots available, *
* and executes the long term testcases. *
*-----------------------------------------------------------------------*/
void start_long_term_testcases(long_term_slot_total, execution_time)
int long_term_slot_total; /* total number of long-term slots */
char *execution_time; /* runtime hours to pass to each testcase */
{
int i;
/*
* Now use up the long_term_slot_total by starting testcases call
* half with real-time flag '1' set, other half user flag '0'
*/
if (debug)
printf("long-term slots available: %d\n",
long_term_slot_total);
for (i = 0; i < (long_term_slot_total / 4); i++) {
#if defined(_IA64) && !defined(__64BIT__)
start_testcase("./sched_tc0.32", "sched_tc0 -t", execution_time,
" -p1", NULL, NULL);
start_testcase("./sched_tc2.32", "sched_tc2", execution_time,
"1", NULL, NULL);
start_testcase("./sched_tc0.32", "sched_tc0 -t", execution_time,
" -p0", NULL, NULL);
start_testcase("./sched_tc2.32", "sched_tc2", execution_time,
"0", NULL, NULL);
#else
start_testcase("./sched_tc0", "sched_tc0 -t", execution_time,
" -p1", NULL, NULL);
start_testcase("./sched_tc2", "sched_tc2", execution_time, "1",
NULL, NULL);
start_testcase("./sched_tc0", "sched_tc0 -t", execution_time,
" -p0", NULL, NULL);
start_testcase("./sched_tc2", "sched_tc2", execution_time, "0",
NULL, NULL);
#endif
}
}
/*---------------------------------------------------------------------+
| start_short_term_testcases () |
| ==================================================================== |
| |
| Function: Starts short term testcases (one for each process slot) |
| |
+---------------------------------------------------------------------*/
void start_short_term_testcases(int short_term_slot_total,
double stress_percent, int pri)
{
int i;
int short_term_slots; /* number of slots to use */
/*
* Set up the short_term_slot_total by starting testcases call
* half with real-time flag '1' set, other half user flag '0'
*/
if (available_user_process_slots() < short_term_slot_total)
short_term_slots =
available_user_process_slots() * stress_percent / 2;
else
short_term_slots = short_term_slot_total;
printf("\n<< Starting %d short-term testcases>> \n\n",
short_term_slots);
if (debug)
printf("short-term slots available: %d\n", short_term_slots);
for (i = 0; i < (short_term_slots / 4); i++) {
#if defined(_IA64) && !defined(__64BIT__)
start_testcase("./sched_tc1.32", "sched_tc1", "1", NULL, NULL,
NULL);
start_testcase("./sched_tc3.32", "sched_tc3", "1", NULL, NULL,
NULL);
start_testcase("./sched_tc1.32", "sched_tc1", "0", NULL, NULL,
NULL);
start_testcase("./sched_tc3.32", "sched_tc3", "0", NULL, NULL,
NULL);
#else
start_testcase("./sched_tc1", "sched_tc1", "1", NULL, NULL,
NULL);
start_testcase("./sched_tc3", "sched_tc3", "1", NULL, NULL,
NULL);
start_testcase("./sched_tc1", "sched_tc1", "0", NULL, NULL,
NULL);
start_testcase("./sched_tc3", "sched_tc3", "0", NULL, NULL,
NULL);
#endif
#if 0
perform_throughput_tests(pri);
#endif
}
}
/*------------------------ kill_short_term_testcases() ------------------*/
/* This procedure goes through the process id table, and sends each *
* process id number found in the table a signal in order to terminate *
* it. The signal sent is SIGUSR1. It also re-initializes the table. *
*-----------------------------------------------------------------------*/
void kill_short_term_testcases()
{
int i; /* loop counter to step through the list of process id's */
/*
* Loop through the array of process id's one at a time, and
* attempt to kill each one. If kill fails, report error and
* continue.
*/
if (debug)
printf("killing short-term processes...\n");
for (i = 0; i < numprocs; i++) {
if (debug)
printf("killing process [%d]\n", procs[i]);
kill(procs[i], SIGUSR1);
}
/*
* Adjust the number of short_term_testcases
*/
short_running -= numprocs;
/*
* Clear the table by setting number of entries to zero
*/
numprocs = 0;
}
/*----------------------------- finishup() ------------------------------*/
/* This procedure closing information to the about ending *
* times, elapsed times, etc. This procedure then closes the file*
*-----------------------------------------------------------------------*/
void finishup(start_time)
long start_time; /* starting time to calculate elapsed time */
{
long end_time; /* time when program finished */
/*
* Get the end time and calculate elapsed time; write all this out
*/
end_time = time(NULL);
printf("\nend time = %s\n", ctime(&end_time));
printf("elapsed time = %4.2f hours\n",
((end_time - start_time) / 3600.0));
}
/*---------------------------------------------------------------------+
| parse_args () |
| ==================================================================== |
| |
| Function: Parse the command line arguments & initialize global |
| variables. |
| |
| Updates: (command line options) |
| |
| [-s] size: shared memory segment size |
| |
+---------------------------------------------------------------------*/
void parse_args(int argc, char **argv)
{
int opt;
int sflg = 0, pflg = 0, tflg = 0;
int errflag = 0;
char *program_name = *argv;
extern char *optarg; /* Command line option */
/*
* Parse command line options.
*/
while ((opt = getopt(argc, argv, "vs:p:t:l:d")) != EOF) {
switch (opt) {
case 's': /* stress percentage */
sflg++;
stress_percent = atof(optarg);
break;
case 'p': /* process slots */
pflg++;
process_slots = atof(optarg);
break;
case 't': /* time (hours) */
tflg++;
execution_time = atof(optarg);
break;
case 'd': /* Enable debugging messages */
debug++;
break;
case 'v': /* Enable verbose mode=debug mode */
debug++;
break;
default:
errflag++;
break;
}
}
/*
* Check percentage, execution time and process slots...
*/
if (sflg) {
if (stress_percent < 0.0 || stress_percent > 1.0)
errflag++;
}
if (pflg) {
if (process_slots < 0 || process_slots > MAXPROCS)
errflag++;
}
if (tflg) {
if (execution_time < 0.0 || execution_time > 100.0)
errflag++;
}
if (debug)
printf("\n(debugging messages enabled)\n\n");
if (errflag) {
fprintf(stderr, USAGE, program_name);
exit(2);
}
}