USGS

Isis 3.0 Application Source Code Reference

Home

ProgramAnalyzer.cpp

Go to the documentation of this file.
00001 /**                                                                       
00002  * @file                                                                  
00003  * $Revision: 1.3 $                                                             
00004  * $Date: 2010/02/25 18:39:05 $                                                                 
00005  *                                                                        
00006  *   Unless noted otherwise, the portions of Isis written by the USGS are 
00007  *   public domain. See individual third-party library and package descriptions 
00008  *   for intellectual property information, user agreements, and related  
00009  *   information.                                                         
00010  *                                                                        
00011  *   Although Isis has been used by the USGS, no warranty, expressed or   
00012  *   implied, is made by the USGS as to the accuracy and functioning of such 
00013  *   software and related material nor shall the fact of distribution     
00014  *   constitute any such warranty, and no responsibility is assumed by the
00015  *   USGS in connection therewith.                                        
00016  *                                                                        
00017  *   For additional information, launch                                   
00018  *   $ISISROOT/doc//documents/Disclaimers/Disclaimers.html                
00019  *   in a browser or see the Privacy & Disclaimers page on the Isis website,
00020  *   http://isis.astrogeology.usgs.gov, and the USGS privacy and disclaimers on
00021  *   http://www.usgs.gov/privacy.html.                                    
00022  */                                                                       
00023 #include <memory>
00024 #include <cmath>
00025 #include <cstdlib>
00026 #include <ctime>
00027 #include <iostream>
00028 #include <iomanip>
00029 #include <sstream>
00030 
00031 #include "ProgramAnalyzer.h"
00032 #include "IString.h"
00033 #include "Pvl.h"
00034 #include "PvlObject.h"
00035 
00036 
00037 using namespace std;
00038 
00039 namespace Isis {
00040 
00041 /** Default constructor */
00042 ProgramAnalyzer::ProgramAnalyzer() {
00043   init();
00044 }
00045 
00046 /**
00047  * Analyzes the given log file on construction
00048  * 
00049  * @param logfile A print.prt file
00050  */
00051 ProgramAnalyzer::ProgramAnalyzer(const QString &logfile) {
00052   init();
00053   add(logfile);
00054 }
00055 
00056 /**
00057  * @brief Set the list of program exclusions 
00058  *  
00059  *  When provided, the QString should contain names of ISIS programs that will be
00060  *  excluded in the analysis.  If more than one program is desired, separate
00061  *  them by commas.
00062  *  
00063  *  The exclusion list takes precedence over any applications added in the
00064  *  inclusion list.  In other words, if the same program is included in both the
00065  *  inclusion and exclusion list, it will be excluded.
00066  *  
00067  *  Note this method can be called repeatedly to add names.
00068  *
00069  * @param name Comma separated list of names of programs to exclude
00070  */
00071 void ProgramAnalyzer::setExclude(const QString &name) {
00072   foreach (QString singleName, name.split(",")) {
00073     exclude(singleName);
00074   }
00075 }
00076 
00077 /**
00078  * @brief Loads a list of excluded program names from a file
00079  *  
00080  * The file is expected to contain the name of an ISIS program, one per line. 
00081  * These programs will be logged but not included in the analysis. 
00082  *  
00083  * The exclusion list takes precedence over any applications added in the
00084  * inclusion list.  In other words, if the same program is included in both the
00085  * inclusion and exclusion list, it will be excluded.
00086  *  
00087  * Note this method can be called repeatedly to add names.
00088  *  
00089  * @param name  Name of file containing program list to exclude
00090  */
00091 void ProgramAnalyzer::exclude(const QString &name) {
00092   QString prog = name.trimmed();
00093   if ( !_excludes.exists(prog) ) {
00094     _excludes.add(prog, 0);
00095   }
00096   return;
00097 }
00098 
00099 
00100 /**
00101  * @brief Set the list of program inclusions 
00102  *  
00103  * When provided, the QString should contain names of ISIS programs that will be
00104  * included in the analysis.  If more than one program is desired, separate
00105  * them by commas.
00106  *  
00107  * If this option is used, it will only included programs given in this list. 
00108  * It operates as both an inclusive and exclusive list, so there is no need to 
00109  * also utilize the exclude features of this class. 
00110  *  
00111  * However, if you do use the exclusion features of this clas, the exclusion 
00112  * list takes precedence over any applications added in the inclusion list.  In 
00113  * other words, if the same program is included in both the inclusion and 
00114  * exclusion list, it will be excluded. 
00115  *  
00116  * Note this method can be called repeatedly to add names.
00117  *  
00118  * @param name Comma separated list of names of programs to include
00119  */
00120 void ProgramAnalyzer::setInclude(const QString &name) {
00121   foreach (QString singleName, name.split(",")) {
00122     include(singleName);
00123   }
00124 }
00125 
00126 /**
00127  * @brief Loads a list of included program names from a file
00128  *  
00129  * The file is expected to contain the name of an ISIS program, one per line. 
00130  * These programs will be included in the analysis. 
00131  *  
00132  * If this option is used, it will only included programs given in this list. 
00133  * It operates as both an inclusive and exclusive list, so there is no need to 
00134  * also utilize the exclude features of this class. 
00135  *  
00136  * However, if you do use the exclusion feartures, the exclusion list takes 
00137  * precedence over any applications added in the inclusion list.  In other 
00138  * words, if the same program is included in both the inclusion and exclusion 
00139  * list, it will be excluded. 
00140  *  
00141  * Note this method can be called repeatedly to add names.
00142  *  
00143  * @param name  Name of file containing program list to include
00144  */
00145 void ProgramAnalyzer::include(const QString &name) {
00146   QString prog = name.trimmed();
00147   if ( !_includes.exists(prog) ) {
00148     _includes.add(prog, 0);
00149   }
00150   return;
00151 }
00152 
00153 /**
00154  * @brief Adds a print.prt file to the analysis 
00155  *  
00156  * The programs contained in the log file, assumed to be a print.prt file, will 
00157  * be added to the list of programs to be analyzed.  They are subject to the 
00158  * exclude and include program lists. 
00159  * 
00160  * @param logfile An ISIS3 print.prt file
00161  */
00162 void ProgramAnalyzer::add(const QString &logfile) {
00163   Pvl plog(logfile);
00164   for(int i = 0; i < plog.objects(); i++) {
00165     add(plog.object(i));
00166   }
00167 }
00168 
00169 /**
00170  * @brief Adds a program object originating from a print.prt file 
00171  *  
00172  * The PvlObject provided is assumed to orginate from an ISIS3 print.prt log 
00173  * file.  It contains information that will be extracted and analyzed according 
00174  * to the features of this class. 
00175  * 
00176  * 
00177  * @param program Pvl object containing the output log of an ISIS3 application
00178  */
00179 void ProgramAnalyzer::add(PvlObject &program) {
00180   _count++;
00181   QString prog(program.name());
00182   if ( _excludes.exists(prog) ) {
00183     _excludes.get(prog)++;
00184     return;
00185   }
00186 
00187   if ( _includes.size() > 0 ) {
00188     if ( !_includes.exists(prog)  ) {
00189       return;
00190     }
00191     _includes.get(prog)++;
00192   }
00193 
00194   ProgramData pdata;
00195   pdata.name = prog;
00196   pdata.runtime = getKey(program, "ExecutionDateTime");
00197   pdata.from = getKey(program, "From", "UserParameters");
00198   pdata.to   = getKey(program, "To", "UserParameters");
00199   accounting(program, pdata);
00200   analyze(pdata);
00201   return;
00202 }
00203  
00204 /**
00205  * @brief Reports program counters for the current state
00206  *  
00207  * This method reports counts of programs as they were added to the object.  It 
00208  * reports total programs, numbers for analyzed, included, excluded, unique, 
00209  * valid, errors, zero CPU/Connect times and incomplete or invalid (typcially 
00210  * negative times) for programs it evaluted. 
00211  * 
00212  * @param name Name of Pvl group to create
00213  * 
00214  * @return PvlGroup Pvl group containing program numbers/counts
00215  */
00216 PvlGroup ProgramAnalyzer::review(const QString &name) const {
00217   PvlGroup pvl(name);
00218 
00219   pvl += PvlKeyword("Programs", toString(size()));
00220   pvl += PvlKeyword("Unique", toString(Programs()));
00221   pvl += PvlKeyword("Included", toString(LimitTotals(_includes)));
00222   pvl += PvlKeyword("Excluded", toString(LimitTotals(_excludes)));
00223   pvl += PvlKeyword("Valid", toString(valid()));
00224   pvl += PvlKeyword("Errors", toString(errors()));
00225   pvl += PvlKeyword("ZeroTime", toString(zerotime()));
00226   pvl += PvlKeyword("NoData", toString(nodata()));
00227   pvl += PvlKeyword("BadData", toString(baddata()));
00228   pvl += PvlKeyword("Total", toString(count()));
00229   return (pvl);
00230 }
00231 
00232 
00233 /**
00234  * @brief Reports cumulative runtime performance statistics for programs 
00235  *  
00236  * This method formats the contents of the program analysis in a Pvl group that 
00237  * provides information for all programs regardin CPU, connect and I/O times. 
00238  * 
00239  * 
00240  * @param name Name of Pvl group to create
00241  * 
00242  * @return PvlGroup Pvl group containing cumulative program analysis
00243  */
00244 PvlGroup ProgramAnalyzer::cumulative(const QString &name) const {
00245   return (toPvl(_totals, name));
00246 }
00247 
00248 /**
00249  * @brief Reports analysis for a specific program 
00250  *  
00251  * This object maintains individual statistics for each unique program.  This 
00252  * method reports the analysis for a particular program. 
00253  * 
00254  * 
00255  * @param name Name of the program to analyze
00256  * 
00257  * @return PvlGroup Pvl group containing program analysis
00258  */
00259 PvlGroup ProgramAnalyzer::summarize(const QString &name) const {
00260   return (toPvl(_programs.get(name)));
00261 }
00262 
00263 /**
00264  * @brief Reports analysis for the nth occuring application in the list
00265  * 
00266  * This object maintains individual statistics for each unique program.  This 
00267  * method reports the analysis for a program that occurs in the list at the 
00268  * given index. 
00269  
00270  * @param index Index of the application to summerize
00271  * 
00272  * @return PvlGroup Pvl group containing the program analysis
00273  */
00274 PvlGroup ProgramAnalyzer::summarize(const int index) const {
00275   return (toPvl(_programs.getNth(index)));
00276 }
00277 
00278 /**
00279  * @brief Writes a header in CVS format to a ostream
00280  * 
00281  * @param out Output stream to write the header
00282  * 
00283  * @return ostream& Returns the stream
00284  */
00285 ostream &ProgramAnalyzer::header(ostream &out) const {
00286   out << "Program,From,To,ExecutionDateTime,ConnectTime,CpuTime,IOTime\n";
00287   return (out);
00288 }
00289 
00290 /**
00291  * @brief Writes the analysis to the stream in CSV format 
00292  *  
00293  * This method provides the analysis in CSV format for more traditional 
00294  * manipulation.  This format is well suited to be plotted for further analysis 
00295  * of the program/system performance. 
00296  *  
00297  * The columns provided are:  Program name, FROM file, TO file, runtime, connect 
00298  * time, CPU time, and I/O time (difference in runtime and CPU time). 
00299  * 
00300  * 
00301  * @param out Output stream to write data to
00302  * 
00303  * @return ostream& The output stream
00304  */
00305 ostream &ProgramAnalyzer::listify(ostream &out) const {
00306   vector<ProgramData>::const_iterator progs = _pdata.begin();
00307   while (progs != _pdata.end()) {
00308     if ( progs->status == VALID ) {
00309       out << format(progs->name) << ",";
00310       out << format(progs->from) << ",";
00311       out << format(progs->to) << ",";
00312       out << format(progs->runtime) << ",";
00313       out << DblToStr(progs->connectTime, 2) << ",";
00314       out << DblToStr(progs->cpuTime, 2) << ",";
00315       out << DblToStr(progs->connectTime-progs->cpuTime, 2) << "\n";
00316     }
00317    ++progs;
00318   }
00319   return (out);
00320 }
00321 
00322 
00323 /**
00324  * @brief Initializes the class
00325  * 
00326  * This init function is reintrant and will reset all internal parameters to the
00327  * empty state.
00328  */
00329 void ProgramAnalyzer::init() {
00330   _count = 0;
00331   _excludes = LogList();
00332   _includes = LogList();
00333   _programs = RunList();
00334   _totals = RunTimeStats("Cumulative");
00335   _pdata.clear();
00336 }
00337 
00338 
00339 /**
00340  * @brief Provides a count of analyzed programs
00341  *  
00342  * Iterates through all programs included in the analysis and provides a count 
00343  * fo the total.  It will search through for a given status and only includes 
00344  * those which have the indicated status.  The valid status to check are those 
00345  * defined in the Status enum list. 
00346  * 
00347  * @param status Status of the program to count
00348  * 
00349  * @return int 
00350  */
00351 int ProgramAnalyzer::getCount (ProgramAnalyzer::Status status) const {
00352   vector<ProgramData>::const_iterator progs = _pdata.begin();
00353   int n(0);
00354   while (progs != _pdata.end()) {
00355     if ( progs->status == status ) n++;
00356     ++progs;
00357   }
00358   return (n);
00359 }
00360  
00361  
00362 /**
00363  * @brief Extracts a keyword value from the Pvl object 
00364  * 
00365  * 
00366  * @param obj Pvl object to search for the keyword
00367  * @param key Name of keyword to find
00368  * @param grp Optional group within the object to find the keyword
00369  *  
00370  * @return QString Value of the keyword if the keyword exists, otherwise an empty 
00371  *                QString is returned.
00372  */
00373 QString ProgramAnalyzer::getKey(PvlObject &obj, const QString &key, 
00374                                const QString &grp) const {
00375 
00376   QString value("");
00377   if ( !grp.isEmpty() ) {
00378     PvlGroup &g = obj.findGroup(grp);
00379     value = findKey(g, key);
00380   }
00381   else {
00382     value = findKey(obj, key);
00383   }
00384   return (value);
00385 }
00386 
00387 /**
00388  * @brief Converts times represented in text to digital values 
00389  *  
00390  * The text QString, atime, is expected to be of the format "HH:MM:SS.sss" where 
00391  * "HH" is hours, "MM" is minutes and "SS.sss" is seconds.milliseconds.  The 
00392  * units returned are in seconds. 
00393  * 
00394  * @param atime Text QString containing time to convert
00395  * @param dtime Time in seconds
00396  * 
00397  * @return ProgramAnalyzer::Status Returns BADDATA if the text QString is empty 
00398  *                                 or malformed, or VALID if the conversion
00399  *                                 succeeds.
00400  */
00401 ProgramAnalyzer::Status ProgramAnalyzer::convertTime(const QString &atime, 
00402                                                      double &dtime) const {
00403   if ( atime.isEmpty() ) return (BADDATA);
00404   QStringList t = atime.split(":");
00405   if ( t.size() != 3 ) {
00406     return (BADDATA);
00407   }
00408 
00409   //  Convert to seconds
00410   double toSeconds(3600.0);
00411   dtime = 0.0;
00412   for ( unsigned int i = 0 ; i < 3 ; i++ ) {
00413     dtime += toDouble(t[i]) * toSeconds;
00414     toSeconds /= 60.0;
00415   }
00416 
00417   return (VALID);
00418 }
00419 
00420 /**
00421  * @brief Compute analysis of program entry 
00422  *  
00423  * This method accepts a Pvl object that is assumed to orignate from an ISIS3 
00424  * print.prt log file and conforms to the format in the log file. 
00425  *  
00426  * Data is extracted from certain keywords in the object.  Invalid objects or 
00427  * error conditions are detected and are indicated in the status of the program 
00428  * analysis structure, pdata.  Other conditions of no time for runtimes or CPU 
00429  * times is also detected and indicated. 
00430  * 
00431  * 
00432  * @param obj   Object containing program data
00433  * @param pdata Structure to return derived values from the program data
00434  * 
00435  * @return bool True if successful, false otherwise.
00436  */
00437 bool ProgramAnalyzer::accounting(PvlObject &obj, 
00438                                  ProgramAnalyzer::ProgramData &pdata) const {
00439 
00440   //  Assume an error occured if the Accounting group is missing
00441   if ( !obj.hasGroup("Accounting") ) {
00442     pdata.status = ERRORS;
00443     return (false);
00444   }
00445   
00446   PvlGroup &acc = obj.findGroup("Accounting");
00447   Status status = convertTime(findKey(acc,"ConnectTime"), pdata.connectTime);
00448   pdata.status = convertTime(findKey(acc, "CpuTime"), pdata.cpuTime);
00449 
00450   //  Test a few remaining times
00451   if ( status != VALID ) pdata.status = status;
00452   else if ( pdata.connectTime <= 0.0 ) pdata.status = ZEROTIME;
00453 
00454   return (pdata.status == VALID);
00455 }
00456 
00457 
00458 
00459 /**
00460  * @brief Performs the analysis of a program 
00461  *  
00462  * This method accepts a program data structure, determines validity for 
00463  * inclusion in the analysis and computes statistics from the data content. 
00464  * 
00465  * @param data Structure containing program data
00466  * 
00467  * @return bool True if valid and included, false otherwize
00468  */
00469 bool ProgramAnalyzer::analyze(const ProgramAnalyzer::ProgramData &data) {
00470 
00471   bool good(false);
00472   if ( data.status == VALID ) {
00473     if ( !_programs.exists(data.name) ) {
00474        _programs.add(data.name, RunTimeStats(data.name));
00475     }
00476     RunTimeStats &stats = _programs.get(data.name);
00477     stats.contime.AddData(data.connectTime);
00478     stats.cputime.AddData(data.cpuTime);
00479     stats.iotime.AddData(data.connectTime - data.cpuTime);
00480 
00481     _totals.contime.AddData(data.connectTime);
00482     _totals.cputime.AddData(data.cpuTime);
00483     _totals.iotime.AddData(data.connectTime - data.cpuTime);
00484 
00485     good = true;
00486   }
00487   _pdata.push_back(data);
00488   return (good);
00489 }
00490 
00491 /**
00492  * @brief Produces report of run time statistics for the given structure
00493  * 
00494  *  
00495  * The data contained with the RunTimeStats is externalized to a Pvl group with 
00496  * some resonable formatting. 
00497  *  
00498  * @param stats Run time stats for the given data
00499  * @param name  Optional name of the PvlGroup results
00500  * 
00501  * @return PvlGroup Pvl group of runtime statistics
00502  */
00503 PvlGroup ProgramAnalyzer::toPvl(const RunTimeStats &stats, 
00504                                 const QString &name) const {
00505   PvlGroup pvl((name.isEmpty() ? stats.pname : name));
00506 
00507   pvl += PvlKeyword("Hits", toString(stats.contime.TotalPixels()));
00508   pvl += PvlKeyword("ConnectTimeMinimum", DblToStr(stats.contime.Minimum(), 2));
00509   pvl += PvlKeyword("ConnectTimeMaximum", DblToStr(stats.contime.Maximum(), 2));
00510   pvl += PvlKeyword("ConnectTimeAverage", DblToStr(stats.contime.Average(), 2));
00511   pvl += PvlKeyword("ConnectTimeStdDev", DblToStr(stats.contime.StandardDeviation(), 4));
00512 
00513   pvl += PvlKeyword("CpuTimeMinimum", DblToStr(stats.cputime.Minimum(), 2));
00514   pvl += PvlKeyword("CpuTimeMaximum", DblToStr(stats.cputime.Maximum(), 2));
00515   pvl += PvlKeyword("CpuTimeAverage", DblToStr(stats.cputime.Average(), 2));
00516   pvl += PvlKeyword("CpuTimeStdDev", DblToStr(stats.cputime.StandardDeviation(), 4));
00517 
00518   pvl += PvlKeyword("IOTimeMinimum", DblToStr(stats.iotime.Minimum(), 2));
00519   pvl += PvlKeyword("IOTimeMaximum", DblToStr(stats.iotime.Maximum(), 2));
00520   pvl += PvlKeyword("IOTimeAverage", DblToStr(stats.iotime.Average(), 2));
00521   pvl += PvlKeyword("IOTimeStdDev", DblToStr(stats.iotime.StandardDeviation(), 4));
00522 
00523   return (pvl);
00524 }
00525 
00526 /**
00527  * @brief Returns NULL for empty QStrings to ensure meaningful content
00528  * 
00529  * @param s  String to test for content
00530  * 
00531  * @return QString Returns existing content if present, NULL if empty
00532  */
00533 QString ProgramAnalyzer::format(const QString &s) const {
00534  if ( s.isEmpty() )  return (QString("NULL"));
00535  return (s);
00536 }
00537 
00538 
00539   /**
00540    * @brief Convert a double value to a QString subject to precision specs
00541    *
00542    * This method converts a double value to a QString that has a prefined digitis
00543    * of precision.  Fixed float form is used with the specified number of digits
00544    * of precision.
00545    *
00546    * @param value Double value to convert to QString
00547    *
00548    * @return QString Returns the converted QString
00549    */
00550   QString ProgramAnalyzer::DblToStr(const double &value, const int precision) 
00551                                     const {
00552     if(IsSpecial(value)) {
00553       return (QString("0.0"));
00554     }
00555 
00556     //  Format the QString to specs
00557     ostringstream strcnv;
00558     strcnv.setf(std::ios::fixed);
00559     strcnv << setprecision(precision) << value;
00560     return (QString(strcnv.str().c_str()));
00561   }
00562 
00563   /**
00564    * @brief Returns the count of all programs in the log list 
00565    *  
00566    * This method computes a count of all programs that exist in the list of 
00567    * applications that encurred a limit in the analysis.  It is not enough to 
00568    * just report the number of entries in the list - each list contains a count 
00569    * of occurances.  These occurances are summed and return to the caller. 
00570    * 
00571    * @param limit List of applications incurring a limit
00572    * 
00573    * @return int Total number of occurances all programs in the list
00574    */
00575   int ProgramAnalyzer::LimitTotals(const ProgramAnalyzer::LogList &limit) 
00576                                    const {
00577     int total(0);
00578     for ( int i = 0 ; i < limit.size() ; i++ ) {
00579       total += limit.getNth(i);
00580     }
00581     return (total);
00582   }
00583 
00584 } // namespace Isis
00585 
00586