blob: d04e2bbb878d79eed31496b1c531a130c72ce713 [file] [log] [blame]
/*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 <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 ((long *) 0);
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")) == (FILE *) 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((void *) 0) != saved_pid) ;
/*
* Open the temporary file to get the returned number of seconds
*/
if ((datafile = fopen ("sch.measure", "r")) == (FILE *) 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 ((long *) 0);
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);
}
}