| /*********************************************************************** |
| * |
| * 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 <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> |
| |
| |
| |
| /**************************** 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 **************************/ |
| /* version identifier (value supplied by CVS)*/ |
| static const char Version[] = "$Id: JitterTest.c,v 1.13 2005/11/07 11:15:20 gleixner Exp $"; |
| |
| 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)) <= 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)) <= 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. */ |
| 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. |
| */ |
| void doGrabKProfile(int jitterusec, char *fileName) |
| { |
| int fdSnapshot; |
| int fdProfile; |
| int readBytes; |
| char readBuf[4096]; |
| |
| if((fdSnapshot = open(fileName, O_WRONLY | O_CREAT)) <= 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) |
| { |
| write(fdSnapshot, readBuf, readBytes); |
| } |
| |
| 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 |
| */ |
| 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. |
| */ |
| |
| 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(abs(timeDiffusec) > LastMaxDiff) |
| { |
| LastMaxDiff = abs(timeDiffusec); |
| sprintf(&tmpBuf[strlen(tmpBuf)],"!"); |
| |
| if((GrabKProfile == TRUE) && (ProfileTriggerMSecs < (abs(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) |
| { |
| |
| read(Fd2, tmpBuf, 1); |
| 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) |
| { |
| printf("JitterTest version %s\n", 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 %i 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*/ |