/*********************************************************************** * * Copyright: Daniel Measurement and Control, Inc. * 9753 Pine Lake Drive * Houston, TX 77055 * * Created by: Vipin Malik and Gail Murray * Released under GPL by permission of Daniel Industries. * * This software is licensed under the GPL version 2. Plese see the file * COPYING for details on the license. * * NO WARRANTY: Absolutely no claims of warranty or fitness of purpose * are made in this software. Please use at your own risk. * * Filename: JitterTest.c * * Description: Program to be used to measure wake jitter. * See README file for more info. * * * Revision History: * $Id: JitterTest.c,v 1.13 2005/11/07 11:15:20 gleixner Exp $ * $Log: JitterTest.c,v $ * Revision 1.13 2005/11/07 11:15:20 gleixner * [MTD / JFFS2] Clean up trailing white spaces * * Revision 1.12 2001/08/10 19:23:11 vipin * Ready to be released under GPL! Added proper headers etc. * * Revision 1.11 2001/07/09 15:35:50 vipin * Couple of new features:1. The program runs by default as a "regular" * (SCHED_OTHER) task by default, unless the -p n cmd line parameter is * specified. It then runs as SCHED_RR at that priority. * 2. Added ability to send SIGSTOP/SIGCONT to a specified PID. This * would presumably be the PID of the JFFS2 GC task. SIGSTOP is sent * before writing to the fs, and a SIGCONT after the write is done. * 3. The "pad" data now written to the file on the "fs under test" is * random, not sequential as previously. * * Revision 1.10 2001/06/27 19:14:24 vipin * Now console file to log at can be specified from cmd line. This can enable * you to run two instances of the program- one logging to the /dev/console * and another to a regular file (if you want the data) or /dev/null if you don't. * * Revision 1.9 2001/06/25 20:21:31 vipin * This is the latest version, NOT the one last checked in! * * Revision 1.7 2001/06/18 22:36:19 vipin * Fix minor typo that excluded '\n' from msg on console. * * Revision 1.6 2001/06/18 21:17:50 vipin * Added option to specify the amount of data written to outfile each period. * The regular info is written, but is then padded to the requested size. * This will enable testing of different sized writes to JFFS fs. * * Revision 1.5 2001/06/08 19:36:23 vipin * All write() are now checked for return err code. * * Revision 1.4 2001/06/06 23:10:31 vipin * Added README file. * In JitterTest.c: Changed operation of periodic timer to one shot. The timer is now * reset when the task wakes. This way every "jitter" is from the last time and * jitters from previous times are not accumulated and screw aroud with our display. * * All jitter is now +ve. (as it should be). Also added a "read file" functionality * to test for jitter in task if we have to read from JFFS fs. * The program now also prints data to console- where it can be logged, interspersed with * other "interesting" printk's from the kernel and drivers (flash sector erases etc.) * * Revision 1.3 2001/03/01 19:20:39 gmurray * Add priority scheduling. Shortened name of default output file. * Changed default interrupt period. Output delta time and jitter * instead of time of day. * * Revision 1.2 2001/02/28 16:20:19 vipin * Added version control Id and log fields. * ***********************************************************************/ /*************************** Included Files ***************************/ #include <stdio.h> /* fopen, printf, fprintf, fclose */ #include <string.h> /* strcpy, strcmp */ #include <stdlib.h> /* exit, atol, atoi */ #include <sys/time.h> /* setitimer, settimeofday, gettimeofday */ #include <time.h> /* time */ #include <signal.h> /* signal */ #include <sched.h> /* sched_setscheduler, sched_get_priority_min,*/ /* sched_get_priority_max */ #include <unistd.h> /* gettimeofday, sleep */ #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/mman.h> #define PROGRAM_NAME "JitterTest" #include "common.h" /**************************** Enumerations ****************************/ enum timerActions { ONESHOT, AUTORESETTING }; /****************************** Constants *****************************/ /* Exit error codes */ #define EXIT_FILE_OPEN_ERR (1) /* error opening output file*/ #define EXIT_REG_SIGALRM_ERR (2) /* error registering SIGALRM*/ #define EXIT_REG_SIGINT_ERR (3) /* error registering SIGINT */ #define EXIT_INV_INT_PERIOD (4) /* error invalid int. period*/ #define EXIT_MIN_PRIORITY_ERR (5) /* error, minimum priority */ #define EXIT_MAX_PRIORITY_ERR (6) /* error, maximum priority */ #define EXIT_INV_SCHED_PRIORITY (7) /* error, invalid priority */ #define EXIT_SET_SCHEDULER_ERR (8) /* error, set scheduler */ #define EXIT_PREV_TIME_OF_DAY_ERR (9) /* error, init. prev. */ /* time of day */ #define MAX_FILE_NAME_LEN (32) /* maximum file name length */ #define STRINGS_EQUAL ((int) 0) /* strcmp value if equal */ #define MIN_INT_PERIOD_MILLISEC ( 5L) /* minimum interrupt period */ #define MAX_INT_PERIOD_MILLISEC (5000L) /* maximum interrupt period */ #define DEF_INT_PERIOD_MILLISEC ( 10L) /* default interrupt period */ #define READ_FILE_MESSAGE "This is a junk file. Must contain at least 1 byte though!\n" /* The user can specify that the program pad out the write file to a given number of bytes. But a minimum number needs to be written. This will contain the jitter info. */ #define MIN_WRITE_BYTES 30 #define DEFAULT_WRITE_BYTES 30 #define MAX_WRITE_BYTES 4096 /* used for gen a printable ascii random # between spc and '~' */ #define MIN_ASCII 32 /* <SPACE> can be char*/ #define MAX_ASCII 126.0 /* needs to be float. See man rand() */ /*---------------------------------------------------------------------- * It appears that the preprocessor can't handle multi-line #if * statements. Thus, the check on the default is divided into two * #if statements. *---------------------------------------------------------------------*/ #if (DEF_INT_PERIOD_MILLISEC < MIN_INT_PERIOD_MILLISEC) #error *** Invalid default interrupt period. *** #endif #if (DEF_INT_PERIOD_MILLISEC > MAX_INT_PERIOD_MILLISEC) #error *** Invalid default interrupt period. *** #endif #define TRUE 1 /* Boolean true value */ #define FALSE 0 /* Time conversion constants. */ #define MILLISEC_PER_SEC (1000L) /* milliseconds per second */ #define MICROSEC_PER_MILLISEC (1000L) /* microsecs per millisec */ #define MICROSEC_PER_SEC (1000000L) /* microsecs per second */ #define PRIORITY_POLICY ((int) SCHED_RR) /* If specified iwth "-p" */ /************************** Module Variables **************************/ static char OutFileName[MAX_FILE_NAME_LEN+1]; /* output file name */ static char LogFile[MAX_FILE_NAME_LEN+1] = "/dev/console"; /* default */ static char ReadFile[MAX_FILE_NAME_LEN+1]; /* This file is read. Should contain at least 1 byte */ static int WriteBytes = DEFAULT_WRITE_BYTES; /* pad out file to these many bytes. */ static int Fd1; /* fd where the above buffer if o/p */ static int Cfd; /* fd to console (or file specified) */ static int Fd2; /* fd for the ReadFile */ static int DoRead = FALSE; /* should we attempt to ReadFile?*/ static long InterruptPeriodMilliSec; /* interrupt period, millisec */ static int MinPriority; /* minimum scheduler priority */ static int MaxPriority; /* maximum scheduler priority */ static int RequestedPriority; /* requested priority */ static struct itimerval ITimer; /* interrupt timer structure */ static struct timeval PrevTimeVal; /* previous time structure */ static struct timeval CurrTimeVal; /* current time structure */ static long LastMaxDiff = 0; /* keeps track of worst jitter encountered */ static int GrabKProfile = FALSE; /* To help determine system bottle necks this parameter can be set. This causes the /proc/profile file to be read and stored in unique filenames in current dir, and indication to be o/p on the /dev/console also. */ static long ProfileTriggerMSecs = 15000l; /* Jitter time in seconds that triggers a snapshot of the profile to be taken */ static int SignalGCTask = FALSE; /* should be signal SIGSTOP/SIGCONT to gc task?*/ static int GCTaskPID; static int RunAsRTTask = FALSE; /* default action unless priority is specified on cmd line */ /********************* Local Function Prototypes **********************/ void HandleCmdLineArgs(int argc, char *argv[]); void SetFileName(char * pFileName); void SetInterruptPeriod(char * pASCIIInterruptPeriodMilliSec); void SetSchedulerPriority(char * pASCIISchedulerPriority); void PrintVersionInfo(void); void PrintHelpInfo(void); int Write(int fd, void *buf, size_t bytes, int lineNo); void InitITimer(struct itimerval * pITimer, int action); /* For catching timer interrupts (SIGALRM). */ void AlarmHandler(int sigNum); /* For catching Ctrl+C SIGINT. */ void SignalHandler(int sigNum); /*********************************************************************** * main function * return: exit code ***********************************************************************/ int main( int argc, char *argv[]) { struct sched_param schedParam; int mypri; char tmpBuf[200]; strcpy(OutFileName,"jitter.dat"); InterruptPeriodMilliSec = MIN_INT_PERIOD_MILLISEC; /* Get the minimum and maximum priorities. */ MinPriority = sched_get_priority_min(PRIORITY_POLICY); MaxPriority = sched_get_priority_max(PRIORITY_POLICY); if (MinPriority == -1) { printf("\n*** Unable to get minimum priority. ***\n"); exit(EXIT_MIN_PRIORITY_ERR); } if (MaxPriority == -1) { printf("\n*** Unable to get maximum priority. ***\n"); exit(EXIT_MAX_PRIORITY_ERR); } /* Set requested priority to minimum value as default. */ RequestedPriority = MinPriority; HandleCmdLineArgs(argc, argv); if(mlockall(MCL_CURRENT|MCL_FUTURE) < 0){ printf("Could not lock task into memory. Bye\n"); perror("Error"); } if(RunAsRTTask){ /* Set the priority. */ schedParam.sched_priority = RequestedPriority; if (sched_setscheduler( 0, PRIORITY_POLICY, &schedParam) != (int) 0) { printf("Exiting: Unsuccessful sched_setscheduler.\n"); close(Fd1); exit(EXIT_SET_SCHEDULER_ERR); } /* Double check as I have some doubts that it's really running at realtime priority! */ if((mypri = sched_getscheduler(0)) != RequestedPriority) { printf("Not running with request priority %i. running with priority %i instead!\n", RequestedPriority, mypri); }else { printf("Running with %i priority. Good!\n", mypri); } } /*------------------------- Initializations --------------------------*/ if((Fd1 = open(OutFileName, O_RDWR|O_CREAT|O_SYNC, S_IRWXU)) <= 0) { perror("Cannot open outfile for write:"); exit(1); } /* If a request is made to read from a specified file, then create that file and fill with junk data so that there is something there to read. */ if(DoRead) { if((Fd2 = open(ReadFile, O_RDWR|O_CREAT|O_SYNC|O_TRUNC, S_IRWXU)) <= 0) { perror("cannot open read file:"); exit(1); }else { /* Don't really care how much we write here */ if(write(Fd2, READ_FILE_MESSAGE, strlen(READ_FILE_MESSAGE)) < 0) { perror("Problems writing to readfile:"); exit(1); } lseek(Fd2, 0, SEEK_SET); /* position back to byte #0 */ } } /* Also log output to console. This way we can capture it on a serial console to a log file. */ if((Cfd = open(LogFile, O_WRONLY|O_SYNC)) <= 0) { perror("cannot open o/p logfile:"); exit(1); } /* Set-up handler for periodic interrupt. */ if (signal(SIGALRM, &AlarmHandler) == SIG_ERR) { printf("Couldn't register signal handler for SIGALRM.\n"); sprintf(tmpBuf, "Couldn't register signal handler for SIGALRM.\n"); Write(Fd1, tmpBuf, strlen(tmpBuf), __LINE__); close(Fd1); exit(EXIT_REG_SIGALRM_ERR); } /* Set-up handler for Ctrl+C to exit the program. */ if (signal(SIGINT, &SignalHandler) == SIG_ERR) { printf("Couldn't register signal handler for SIGINT.\n"); sprintf(tmpBuf, "Couldn't register signal handler for SIGINT.\n"); Write(Fd1, tmpBuf, strlen(tmpBuf), __LINE__); close(Fd1); exit(EXIT_REG_SIGINT_ERR); } printf("Press Ctrl+C to exit the program.\n"); printf("Output File: %s\n", OutFileName); printf("Scheduler priority: %d\n", RequestedPriority); sprintf(tmpBuf, "\nScheduler priority: %d\n", RequestedPriority); Write(Fd1, tmpBuf, strlen(tmpBuf), __LINE__); Write(Cfd, tmpBuf, strlen(tmpBuf), __LINE__); printf("Interrupt period: %ld milliseconds\n", InterruptPeriodMilliSec); sprintf(tmpBuf, "Interrupt period: %ld milliseconds\n", InterruptPeriodMilliSec); Write(Fd1, tmpBuf, strlen(tmpBuf), __LINE__); Write(Cfd, tmpBuf, strlen(tmpBuf), __LINE__); fflush(0); /* Initialize the periodic timer. */ InitITimer(&ITimer, ONESHOT); /* Initialize "previous" time. */ if (gettimeofday(&PrevTimeVal, NULL) != (int) 0) { printf("Exiting - "); printf("Unable to initialize previous time of day.\n"); sprintf(tmpBuf, "Exiting - "); Write(Fd1, tmpBuf, strlen(tmpBuf), __LINE__); sprintf(tmpBuf, "Unable to initialize previous time of day.\n"); Write(Fd1, tmpBuf, strlen(tmpBuf), __LINE__); } /* Start the periodic timer. */ setitimer(ITIMER_REAL, &ITimer, NULL); while(TRUE) { /* Intentional infinite loop. */ /* Sleep for one second. */ sleep((unsigned int) 1); } /* Just in case. File should be closed in SignalHandler. */ close(Fd1); close(Cfd); return 0; } /*********************************************************************** * SignalHandler * This is a handler for the SIGINT signal. It is assumed that the * SIGINT is due to the user pressing Ctrl+C to halt the program. * output: N/A ***********************************************************************/ void SignalHandler( int sigNum) { char tmpBuf[200]; /* Note sigNum not used. */ (void)sigNum; printf("Ctrl+C detected. Worst Jitter time was:%fms.\nJitterTest exiting.\n", (float)LastMaxDiff/1000.0); sprintf(tmpBuf, "\nCtrl+C detected. Worst Jitter time was:%fms\nJitterTest exiting.\n", (float)LastMaxDiff/1000.0); Write(Fd1, tmpBuf, strlen(tmpBuf), __LINE__); Write(Cfd, tmpBuf, strlen(tmpBuf), __LINE__); close(Fd1); close(Cfd); exit(0); } /* A snapshot of the /proc/profile needs to be taken. This is stored as a new file every time, and the stats reset by doing a (any) write to the /proc/profile file. */ static void doGrabKProfile(int jitterusec, char *fileName) { int fdSnapshot; int fdProfile; int readBytes; char readBuf[4096]; (void)jitterusec; if((fdSnapshot = open(fileName, O_WRONLY | O_CREAT, S_IRWXU)) <= 0) { fprintf(stderr, "Could not open file %s.\n", fileName); perror("Error:"); return; } if((fdProfile = open("/proc/profile", O_RDWR)) <= 0) { fprintf(stderr, "Could not open file /proc/profile. Make sure you booted with profile=2\n"); close(fdSnapshot); return; } while((readBytes = read(fdProfile, readBuf, sizeof(readBuf))) > 0) { int writeBytes = write(fdSnapshot, readBuf, readBytes); if (writeBytes != readBytes) { perror("write error"); break; } } close(fdSnapshot); if(write(fdProfile, readBuf, 1) != 1) { perror("Could Not clear profile by writing to /proc/profile:"); } close(fdProfile); }/* end doGrabKProfile()*/ /* Call this routine to clear the kernel profiling buffer /proc/profile */ static void clearProfileBuf(void){ int fdProfile; char readBuf[10]; if((fdProfile = open("/proc/profile", O_RDWR)) <= 0) { fprintf(stderr, "Could not open file /proc/profile. Make sure you booted with profile=2\n"); return; } if(write(fdProfile, readBuf, 1) != 1) { perror("Could Not clear profile by writing to /proc/profile:"); } close(fdProfile); }/* end clearProfileBuf() */ /*********************************************************************** * AlarmHandler * This is a handler for the SIGALRM signal (due to the periodic * timer interrupt). It prints the time (seconds) to * the output file. * output: N/A ***********************************************************************/ void AlarmHandler( int sigNum) /* signal number (not used) */ { long timeDiffusec; /* diff time in micro seconds */ long intervalusec; char tmpBuf[MAX_WRITE_BYTES]; int cntr; char padChar; static int profileFileNo = 0; char profileFileName[150]; static int seedStarter = 0; /* carries over rand info to next time where time() will be the same as this time if invoked < 1sec apart. */ (void)sigNum; if (gettimeofday(&CurrTimeVal, NULL) == (int) 0) { /*---------------------------------------------------------------- * Compute the elapsed time between the current and previous * time of day values and store the result. * * Print the elapsed time to the output file. *---------------------------------------------------------------*/ timeDiffusec = (long)(((((long long)CurrTimeVal.tv_sec) * 1000000L) + CurrTimeVal.tv_usec) - (((long long)PrevTimeVal.tv_sec * 1000000L) + PrevTimeVal.tv_usec)); sprintf(tmpBuf," %f ms ", (float)timeDiffusec/1000.0); intervalusec = InterruptPeriodMilliSec * 1000L; timeDiffusec = timeDiffusec - intervalusec; sprintf(&tmpBuf[strlen(tmpBuf)]," %f ms", (float)timeDiffusec/1000.0); /* should we send a SIGSTOP/SIGCONT to the specified PID? */ if(SignalGCTask){ if(kill(GCTaskPID, SIGSTOP) < 0){ perror("error:"); } } /* Store some historical #'s */ if(labs(timeDiffusec) > LastMaxDiff) { LastMaxDiff = labs(timeDiffusec); sprintf(&tmpBuf[strlen(tmpBuf)],"!"); if((GrabKProfile == TRUE) && (ProfileTriggerMSecs < (labs(timeDiffusec)/1000))) { sprintf(profileFileName, "JitterTest.profilesnap-%i", profileFileNo); /* go and grab the kernel performance profile. */ doGrabKProfile(timeDiffusec, profileFileName); profileFileNo++; /* unique name next time */ /* Say so on the console so that a marker gets put in the console log */ sprintf(&tmpBuf[strlen(tmpBuf)],"<Profile saved in file:%s>", profileFileName); } } sprintf(&tmpBuf[strlen(tmpBuf)],"\n"); /* CR for the data going out of the console */ Write(Cfd, tmpBuf, strlen(tmpBuf), __LINE__); /* The "-1" below takes out the '\n' at the end that we appended for the msg printed on the console.*/ sprintf(&tmpBuf[strlen(tmpBuf)-1]," PadBytes:"); /* Now pad the output file if requested by user. */ if(WriteBytes > MIN_WRITE_BYTES) { /* start from a new place every time */ srand(time(NULL) + seedStarter); /* already written MIN_WRITE_BYTES by now */ for(cntr = strlen(tmpBuf); cntr < WriteBytes - 1 ; cntr++) /* "-1" adj for '\n' at end */ { /* don't accept any # < 'SPACE' */ padChar = (char)(MIN_ASCII+(int)((MAX_ASCII-(float)MIN_ASCII)*rand()/(RAND_MAX+1.0))); /* padChar = (cntr % (126-33)) + 33; */ tmpBuf[cntr] = padChar; } seedStarter = tmpBuf[cntr-1]; /* save for next time */ tmpBuf[cntr] = '\n'; /* CR for the data going into the outfile. */ tmpBuf[cntr+1] = '\0'; /* NULL terminate the string */ } /* write out the entire line to the output file. */ Write(Fd1, tmpBuf, strlen(tmpBuf), __LINE__); /* Read a byte from the specified file */ if(DoRead) { cntr = read(Fd2, tmpBuf, 1); if (cntr < 0) perror("read error"); lseek(Fd2, 0, SEEK_SET); /* back to start */ } /* Start the periodic timer again. */ setitimer(ITIMER_REAL, &ITimer, NULL); /* Update previous time with current time. */ PrevTimeVal.tv_sec = CurrTimeVal.tv_sec; PrevTimeVal.tv_usec = CurrTimeVal.tv_usec; } else { sprintf(tmpBuf, "gettimeofday error \n"); Write(Fd1, tmpBuf, strlen(tmpBuf), __LINE__); printf("gettimeofday error \n"); } /* now clear the profiling buffer */ if(GrabKProfile == TRUE){ clearProfileBuf(); } /* should we send a SIGSTOP/SIGCONT to the specified PID? */ if(SignalGCTask){ if(kill(GCTaskPID, SIGCONT) < 0){ perror("error:"); } } return; } /*********************************************************************** * InitITimer * This function initializes the 'struct itimerval' structure whose * address is passed to interrupt every InterruptPeriodMilliSec. * output: N/A ***********************************************************************/ void InitITimer( struct itimerval * pITimer, /* pointer to interrupt timer struct*/ int action) /* ONESHOT or autosetting? */ { long seconds; /* seconds portion of int. period */ long microSeconds; /* microsec. portion of int. period */ /*-------------------------------------------------------------------- * Divide the desired interrupt period into its seconds and * microseconds components. *-------------------------------------------------------------------*/ if (InterruptPeriodMilliSec < MILLISEC_PER_SEC) { seconds = 0L; microSeconds = InterruptPeriodMilliSec * MICROSEC_PER_MILLISEC; } else { seconds = InterruptPeriodMilliSec / MILLISEC_PER_SEC; microSeconds = (InterruptPeriodMilliSec - (seconds * MILLISEC_PER_SEC)) * MICROSEC_PER_MILLISEC; } /* Initialize the interrupt period structure. */ pITimer->it_value.tv_sec = seconds; pITimer->it_value.tv_usec = microSeconds; if(action == ONESHOT) { /* This will (should) prevent the timer from restarting itself */ pITimer->it_interval.tv_sec = 0; pITimer->it_interval.tv_usec = 0; }else { pITimer->it_interval.tv_sec = seconds; pITimer->it_interval.tv_usec = microSeconds; } return; } /*********************************************************************** * HandleCmdLineArgs * This function handles the command line arguments. * output: stack size ***********************************************************************/ void HandleCmdLineArgs( int argc, /* number of command-line arguments */ char *argv[]) /* ptrs to command-line arguments */ { int argNum; /* argument number */ if (argc > (int) 1) { for (argNum = (int) 1; argNum < argc; argNum++) { /* The command line contains an argument. */ if ((strcmp(argv[argNum],"--version") == STRINGS_EQUAL) || (strcmp(argv[argNum],"-v") == STRINGS_EQUAL)) { /* Print version information and exit. */ PrintVersionInfo(); exit(0); } else if ((strcmp(argv[argNum],"--help") == STRINGS_EQUAL) || (strcmp(argv[argNum],"-h") == STRINGS_EQUAL) || (strcmp(argv[argNum],"-?") == STRINGS_EQUAL)) { /* Print help information and exit. */ PrintHelpInfo(); exit(0); } else if ((strcmp(argv[argNum],"--file") == STRINGS_EQUAL) || (strcmp(argv[argNum],"-f") == STRINGS_EQUAL)) { /* Set the name of the output file. */ ++argNum; if (argNum < argc) { SetFileName(argv[argNum]); } else { printf("*** Output file name not specified. ***\n"); printf("Default output file name will be used.\n"); } } else if ((strcmp(argv[argNum],"--time") == STRINGS_EQUAL) || (strcmp(argv[argNum],"-t") == STRINGS_EQUAL)) { /* Set the interrupt period. */ ++argNum; if (argNum < argc) { SetInterruptPeriod(argv[argNum]); } else { printf("*** Interrupt period not specified. ***\n"); printf("Default interrupt period will be used.\n"); } } else if ((strcmp(argv[argNum],"--priority") == STRINGS_EQUAL) || (strcmp(argv[argNum],"-p") == STRINGS_EQUAL)) { /* Set the scheduler priority. */ ++argNum; if (argNum < argc) { SetSchedulerPriority(argv[argNum]); } else { printf("*** Scheduler priority not specified. ***\n"); printf("Default scheduler priority will be used.\n"); } } else if ((strcmp(argv[argNum],"--readfile") == STRINGS_EQUAL) || (strcmp(argv[argNum],"-r") == STRINGS_EQUAL)) { /* Set the file to read*/ ++argNum; strncpy(ReadFile, argv[argNum], sizeof(ReadFile)); DoRead = TRUE; } else if ((strcmp(argv[argNum],"--write_bytes") == STRINGS_EQUAL) || (strcmp(argv[argNum],"-w") == STRINGS_EQUAL)) { /* Set the file to read*/ ++argNum; WriteBytes = atoi(argv[argNum]); if(WriteBytes < MIN_WRITE_BYTES) { printf("Writing less than %i bytes is not allowed. Bye.\n", MIN_WRITE_BYTES); exit(0); } } else if ((strcmp(argv[argNum],"--consolefile") == STRINGS_EQUAL) || (strcmp(argv[argNum],"-c") == STRINGS_EQUAL)) { /* Set the file to log console log on. */ ++argNum; strncpy(LogFile, argv[argNum], sizeof(LogFile)); } else if ((strcmp(argv[argNum],"--grab_kprofile") == STRINGS_EQUAL)) { /* We will read the /proc/profile file on configurable timeout */ GrabKProfile = TRUE; ++argNum; /* If the jittter is > this #, then the profile is grabbed. */ ProfileTriggerMSecs = (long) atoi(argv[argNum]); if(ProfileTriggerMSecs <= 0){ printf("Illegal value for profile trigger threshold.\n"); exit(0); } } else if ((strcmp(argv[argNum],"--siggc") == STRINGS_EQUAL)) { /* We will SIGSTOP/SIGCONT the specified pid */ SignalGCTask = TRUE; ++argNum; GCTaskPID = atoi(argv[argNum]); if(ProfileTriggerMSecs <= 0){ printf("Illegal value for JFFS(2) GC task pid.\n"); exit(0); } } else { /* Unknown argument. Print help information and exit. */ printf("Invalid option %s\n", argv[argNum]); printf("Try 'JitterTest --help' for more information.\n"); exit(0); } } } return; } /*********************************************************************** * SetFileName * This function sets the output file name. * output: N/A ***********************************************************************/ void SetFileName( char * pFileName) /* ptr to desired output file name */ { size_t fileNameLen; /* file name length (bytes) */ /* Check file name length. */ fileNameLen = strlen(pFileName); if (fileNameLen > (size_t) MAX_FILE_NAME_LEN) { printf("File name %s exceeds maximum length %d.\n", pFileName, MAX_FILE_NAME_LEN); exit(0); } /* File name length is OK so save the file name. */ strcpy(OutFileName, pFileName); return; } /*********************************************************************** * SetInterruptPeriod * This function sets the interrupt period. * output: N/A ***********************************************************************/ void SetInterruptPeriod( char * /* ptr to desired interrupt period */ pASCIIInterruptPeriodMilliSec) { long period; /* interrupt period */ period = atol(pASCIIInterruptPeriodMilliSec); if ((period < MIN_INT_PERIOD_MILLISEC) || (period > MAX_INT_PERIOD_MILLISEC)) { printf("Invalid interrupt period: %ld ms.\n", period); exit(EXIT_INV_INT_PERIOD); } else { InterruptPeriodMilliSec = period; } return; } /*********************************************************************** * SetSchedulerPriority * This function sets the desired scheduler priority. * output: N/A ***********************************************************************/ void SetSchedulerPriority( char * pASCIISchedulerPriority) /* ptr to desired scheduler priority*/ { int priority; /* desired scheduler priority value */ priority = atoi(pASCIISchedulerPriority); if ((priority < MinPriority) || (priority > MaxPriority)) { printf("Scheduler priority %d outside of range [%d, %d]\n", priority, MinPriority, MaxPriority); exit(EXIT_INV_SCHED_PRIORITY); } else { RequestedPriority = priority; RunAsRTTask = TRUE; /* We shall run as a POSIX real time task */ } return; } /*********************************************************************** * PrintVersionInfo * This function prints version information. * output: N/A ***********************************************************************/ void PrintVersionInfo(void) { common_print_version(); printf("Copyright (c) 2001, Daniel Industries, Inc.\n"); return; } /*********************************************************************** * PrintHelpInfo * This function prints help information. * output: N/A ***********************************************************************/ void PrintHelpInfo(void) { printf("Usage: JitterTest [options]\n"); printf(" *** Requires root privileges. ***\n"); printf("Option:\n"); printf(" [-h, --help, -?] Print this message and exit.\n"); printf(" [-v, --version] "); printf( "Print the version number of JitterTest and exit.\n"); printf(" [-f FILE, --file FILE] Set output file name to FILE. Typically you would put this on the fs under test\n"); printf(" [-c FILE, --consolefile] Set device or file to write the console log to.\n\tTypically you would set this to /dev/console and save it on another computer.\n"); printf(" [-w BYTES, --write_bytes BYTES Write BYTES to FILE each period.\n"); printf(" [-r FILE, --readfile FILE] Also read 1 byte every cycle from FILE. FILE will be created and filled with data.\n"); printf(" [-t <n>, --time <n>] "); printf( "Set interrupt period to <n> milliseconds.\n"); printf(" "); printf( "Range: [%ld, %ld] milliseconds.\n", MIN_INT_PERIOD_MILLISEC, MAX_INT_PERIOD_MILLISEC); printf(" [-p <n>, --priority <n>] "); printf( "Set scheduler priority to <n>.\n"); printf(" "); printf( "Range: [%d, %d] (higher number = higher priority)\n", MinPriority, MaxPriority); printf(" [--grab_kprofile <THRESHOLD in ms>] Read the /proc/profile if jitter is > THRESHOLD and store in file.\n"); printf(" [--siggc <PID>] Before writing to fs send SIGSTOP to PID. After write send SIGCONT\n"); return; } /* A common write that checks for write errors and exits. Pass it __LINE__ for lineNo */ int Write(int fd, void *buf, size_t bytes, int lineNo) { int err; err = write(fd, buf, bytes); if(err < bytes) { printf("Write Error at line %i! Wanted to write %zu bytes, but wrote only %i bytes.\n", lineNo, bytes, err); perror("Write did not complete. Error. Bye:"); /* show error from errno. */ exit(1); } return err; }/* end Write*/