Home | History | Annotate | Download | only in ctestfw
      1 /********************************************************************
      2  * COPYRIGHT:
      3  * Copyright (c) 2002-2009, International Business Machines Corporation and
      4  * others. All Rights Reserved.
      5  ********************************************************************/
      6 
      7 /* z/OS needs this definition for timeval */
      8 #include "platform_xopen_source_extended.h"
      9 
     10 #include "unicode/uperf.h"
     11 #include "uoptions.h"
     12 #include "cmemory.h"
     13 #include <stdio.h>
     14 #include <stdlib.h>
     15 
     16 #if !UCONFIG_NO_CONVERSION
     17 static const char delim = '/';
     18 static int32_t execCount = 0;
     19 UPerfTest* UPerfTest::gTest = NULL;
     20 static const int MAXLINES = 40000;
     21 const char UPerfTest::gUsageString[] =
     22     "Usage: %s [OPTIONS] [FILES]\n"
     23     "\tReads the input file and prints out time taken in seconds\n"
     24     "Options:\n"
     25     "\t-h or -? or --help   this usage text\n"
     26     "\t-v or --verbose      print extra information when processing files\n"
     27     "\t-s or --sourcedir    source directory for files followed by path\n"
     28     "\t                     followed by path\n"
     29     "\t-e or --encoding     encoding of source files\n"
     30     "\t-u or --uselen       perform timing analysis on non-null terminated buffer using length\n"
     31     "\t-f or --file-name    file to be used as input data\n"
     32     "\t-p or --passes       Number of passes to be performed. Requires Numeric argument.\n"
     33     "\t                     Cannot be used with --time\n"
     34     "\t-i or --iterations   Number of iterations to be performed. Requires Numeric argument\n"
     35     "\t-t or --time         Threshold time for looping until in seconds. Requires Numeric argument.\n"
     36     "\t                     Cannot be used with --iterations\n"
     37     "\t-l or --line-mode    The data file should be processed in line mode\n"
     38     "\t-b or --bulk-mode    The data file should be processed in file based.\n"
     39     "\t                     Cannot be used with --line-mode\n"
     40     "\t-L or --locale       Locale for the test\n";
     41 
     42 enum
     43 {
     44     HELP1,
     45     HELP2,
     46     VERBOSE,
     47     SOURCEDIR,
     48     ENCODING,
     49     USELEN,
     50     FILE_NAME,
     51     PASSES,
     52     ITERATIONS,
     53     TIME,
     54     LINE_MODE,
     55     BULK_MODE,
     56     LOCALE,
     57     OPTIONS_COUNT
     58 };
     59 
     60 
     61 static UOption options[OPTIONS_COUNT+20]={
     62     UOPTION_HELP_H,
     63     UOPTION_HELP_QUESTION_MARK,
     64     UOPTION_VERBOSE,
     65     UOPTION_SOURCEDIR,
     66     UOPTION_ENCODING,
     67     UOPTION_DEF( "uselen",        'u', UOPT_NO_ARG),
     68     UOPTION_DEF( "file-name",     'f', UOPT_REQUIRES_ARG),
     69     UOPTION_DEF( "passes",        'p', UOPT_REQUIRES_ARG),
     70     UOPTION_DEF( "iterations",    'i', UOPT_REQUIRES_ARG),
     71     UOPTION_DEF( "time",          't', UOPT_REQUIRES_ARG),
     72     UOPTION_DEF( "line-mode",     'l', UOPT_NO_ARG),
     73     UOPTION_DEF( "bulk-mode",     'b', UOPT_NO_ARG),
     74     UOPTION_DEF( "locale",        'L', UOPT_REQUIRES_ARG)
     75 };
     76 
     77 UPerfTest::UPerfTest(int32_t argc, const char* argv[], UErrorCode& status)
     78         : _argc(argc), _argv(argv), _addUsage(NULL),
     79           ucharBuf(NULL), encoding(""),
     80           uselen(FALSE),
     81           fileName(NULL), sourceDir("."),
     82           lines(NULL), numLines(0), line_mode(TRUE),
     83           buffer(NULL), bufferLen(0),
     84           verbose(FALSE), bulk_mode(FALSE),
     85           passes(1), iterations(0), time(0),
     86           locale(NULL) {
     87     init(NULL, 0, status);
     88 }
     89 
     90 UPerfTest::UPerfTest(int32_t argc, const char* argv[],
     91                      UOption addOptions[], int32_t addOptionsCount,
     92                      const char *addUsage,
     93                      UErrorCode& status)
     94         : _argc(argc), _argv(argv), _addUsage(addUsage),
     95           ucharBuf(NULL), encoding(""),
     96           uselen(FALSE),
     97           fileName(NULL), sourceDir("."),
     98           lines(NULL), numLines(0), line_mode(TRUE),
     99           buffer(NULL), bufferLen(0),
    100           verbose(FALSE), bulk_mode(FALSE),
    101           passes(1), iterations(0), time(0),
    102           locale(NULL) {
    103     init(addOptions, addOptionsCount, status);
    104 }
    105 
    106 void UPerfTest::init(UOption addOptions[], int32_t addOptionsCount,
    107                      UErrorCode& status) {
    108     //initialize the argument list
    109     U_MAIN_INIT_ARGS(_argc, _argv);
    110 
    111     resolvedFileName = NULL;
    112 
    113     // add specific options
    114     int32_t optionsCount = OPTIONS_COUNT;
    115     if (addOptionsCount > 0) {
    116         memcpy(options+optionsCount, addOptions, addOptionsCount*sizeof(UOption));
    117         optionsCount += addOptionsCount;
    118     }
    119 
    120     //parse the arguments
    121     _remainingArgc = u_parseArgs(_argc, (char**)_argv, optionsCount, options);
    122 
    123     // copy back values for additional options
    124     if (addOptionsCount > 0) {
    125         memcpy(addOptions, options+OPTIONS_COUNT, addOptionsCount*sizeof(UOption));
    126     }
    127 
    128     // Now setup the arguments
    129     if(_argc==1 || options[HELP1].doesOccur || options[HELP2].doesOccur) {
    130         status = U_ILLEGAL_ARGUMENT_ERROR;
    131         return;
    132     }
    133 
    134     if(options[VERBOSE].doesOccur) {
    135         verbose = TRUE;
    136     }
    137 
    138     if(options[SOURCEDIR].doesOccur) {
    139         sourceDir = options[SOURCEDIR].value;
    140     }
    141 
    142     if(options[ENCODING].doesOccur) {
    143         encoding = options[ENCODING].value;
    144     }
    145 
    146     if(options[USELEN].doesOccur) {
    147         uselen = TRUE;
    148     }
    149 
    150     if(options[FILE_NAME].doesOccur){
    151         fileName = options[FILE_NAME].value;
    152     }
    153 
    154     if(options[PASSES].doesOccur) {
    155         passes = atoi(options[PASSES].value);
    156     }
    157     if(options[ITERATIONS].doesOccur) {
    158         iterations = atoi(options[ITERATIONS].value);
    159         if(options[TIME].doesOccur) {
    160             status = U_ILLEGAL_ARGUMENT_ERROR;
    161             return;
    162         }
    163     } else if(options[TIME].doesOccur) {
    164         time = atoi(options[TIME].value);
    165     } else {
    166         iterations = 1000; // some default
    167     }
    168 
    169     if(options[LINE_MODE].doesOccur) {
    170         line_mode = TRUE;
    171         bulk_mode = FALSE;
    172     }
    173 
    174     if(options[BULK_MODE].doesOccur) {
    175         bulk_mode = TRUE;
    176         line_mode = FALSE;
    177     }
    178 
    179     if(options[LOCALE].doesOccur) {
    180         locale = options[LOCALE].value;
    181     }
    182 
    183     int32_t len = 0;
    184     if(fileName!=NULL){
    185         //pre-flight
    186         ucbuf_resolveFileName(sourceDir, fileName, NULL, &len, &status);
    187         resolvedFileName = (char*) uprv_malloc(len);
    188         if(resolvedFileName==NULL){
    189             status= U_MEMORY_ALLOCATION_ERROR;
    190             return;
    191         }
    192         if(status == U_BUFFER_OVERFLOW_ERROR){
    193             status = U_ZERO_ERROR;
    194         }
    195         ucbuf_resolveFileName(sourceDir, fileName, resolvedFileName, &len, &status);
    196         ucharBuf = ucbuf_open(resolvedFileName,&encoding,TRUE,FALSE,&status);
    197 
    198         if(U_FAILURE(status)){
    199             printf("Could not open the input file %s. Error: %s\n", fileName, u_errorName(status));
    200             return;
    201         }
    202     }
    203 }
    204 
    205 ULine* UPerfTest::getLines(UErrorCode& status){
    206     lines     = new ULine[MAXLINES];
    207     int maxLines = MAXLINES;
    208     numLines=0;
    209     const UChar* line=NULL;
    210     int32_t len =0;
    211     for (;;) {
    212         line = ucbuf_readline(ucharBuf,&len,&status);
    213         if(line == NULL || U_FAILURE(status)){
    214             break;
    215         }
    216         lines[numLines].name  = new UChar[len];
    217         lines[numLines].len   = len;
    218         memcpy(lines[numLines].name, line, len * U_SIZEOF_UCHAR);
    219 
    220         numLines++;
    221         len = 0;
    222         if (numLines >= maxLines) {
    223             maxLines += MAXLINES;
    224             ULine *newLines = new ULine[maxLines];
    225             if(newLines == NULL) {
    226                 fprintf(stderr, "Out of memory reading line %d.\n", (int)numLines);
    227                 status= U_MEMORY_ALLOCATION_ERROR;
    228                 delete []lines;
    229                 return NULL;
    230             }
    231 
    232             memcpy(newLines, lines, numLines*sizeof(ULine));
    233             delete []lines;
    234             lines = newLines;
    235         }
    236     }
    237     return lines;
    238 }
    239 const UChar* UPerfTest::getBuffer(int32_t& len, UErrorCode& status){
    240     if (U_FAILURE(status)) {
    241         return NULL;
    242     }
    243     len = ucbuf_size(ucharBuf);
    244     buffer =  (UChar*) uprv_malloc(U_SIZEOF_UCHAR * (len+1));
    245     u_strncpy(buffer,ucbuf_getBuffer(ucharBuf,&bufferLen,&status),len);
    246     buffer[len]=0;
    247     len = bufferLen;
    248     return buffer;
    249 }
    250 UBool UPerfTest::run(){
    251     if(_remainingArgc==1){
    252         // Testing all methods
    253         return runTest();
    254     }
    255     UBool res=FALSE;
    256     // Test only the specified fucntion
    257     for (int i = 1; i < _remainingArgc; ++i) {
    258         if (_argv[i][0] != '-') {
    259             char* name = (char*) _argv[i];
    260             if(verbose==TRUE){
    261                 //fprintf(stdout, "\n=== Handling test: %s: ===\n", name);
    262                 //fprintf(stdout, "\n%s:\n", name);
    263             }
    264             char* parameter = strchr( name, '@' );
    265             if (parameter) {
    266                 *parameter = 0;
    267                 parameter += 1;
    268             }
    269             execCount = 0;
    270             res = runTest( name, parameter );
    271             if (!res || (execCount <= 0)) {
    272                 fprintf(stdout, "\n---ERROR: Test doesn't exist: %s!\n", name);
    273                 return FALSE;
    274             }
    275         }
    276     }
    277     return res;
    278 }
    279 UBool UPerfTest::runTest(char* name, char* par ){
    280     UBool rval;
    281     char* pos = NULL;
    282 
    283     if (name)
    284         pos = strchr( name, delim ); // check if name contains path (by looking for '/')
    285     if (pos) {
    286         path = pos+1;   // store subpath for calling subtest
    287         *pos = 0;       // split into two strings
    288     }else{
    289         path = NULL;
    290     }
    291 
    292     if (!name || (name[0] == 0) || (strcmp(name, "*") == 0)) {
    293         rval = runTestLoop( NULL, NULL );
    294 
    295     }else if (strcmp( name, "LIST" ) == 0) {
    296         this->usage();
    297         rval = TRUE;
    298 
    299     }else{
    300         rval = runTestLoop( name, par );
    301     }
    302 
    303     if (pos)
    304         *pos = delim;  // restore original value at pos
    305     return rval;
    306 }
    307 
    308 
    309 void UPerfTest::setPath( char* pathVal )
    310 {
    311     this->path = pathVal;
    312 }
    313 
    314 // call individual tests, to be overriden to call implementations
    315 UPerfFunction* UPerfTest::runIndexedTest( int32_t index, UBool exec, const char* &name, char* par )
    316 {
    317     // to be overriden by a method like:
    318     /*
    319     switch (index) {
    320         case 0: name = "First Test"; if (exec) FirstTest( par ); break;
    321         case 1: name = "Second Test"; if (exec) SecondTest( par ); break;
    322         default: name = ""; break;
    323     }
    324     */
    325     fprintf(stderr,"*** runIndexedTest needs to be overriden! ***");
    326     name = ""; exec = exec; index = index; par = par;
    327     return NULL;
    328 }
    329 
    330 
    331 UBool UPerfTest::runTestLoop( char* testname, char* par )
    332 {
    333     int32_t    index = 0;
    334     const char*   name;
    335     UBool  run_this_test;
    336     UBool  rval = FALSE;
    337     UErrorCode status = U_ZERO_ERROR;
    338     UPerfTest* saveTest = gTest;
    339     gTest = this;
    340     int32_t loops = 0;
    341     double t=0;
    342     int32_t n = 1;
    343     long ops;
    344     do {
    345         this->runIndexedTest( index, FALSE, name );
    346         if (!name || (name[0] == 0))
    347             break;
    348         if (!testname) {
    349             run_this_test = TRUE;
    350         }else{
    351             run_this_test = (UBool) (strcmp( name, testname ) == 0);
    352         }
    353         if (run_this_test) {
    354             UPerfFunction* testFunction = this->runIndexedTest( index, TRUE, name, par );
    355             execCount++;
    356             rval=TRUE;
    357             if(testFunction==NULL){
    358                 fprintf(stderr,"%s function returned NULL", name);
    359                 return FALSE;
    360             }
    361             ops = testFunction->getOperationsPerIteration();
    362             if (ops < 1) {
    363                 fprintf(stderr, "%s returned an illegal operations/iteration()\n", name);
    364                 return FALSE;
    365             }
    366             if(iterations == 0) {
    367                 n = time;
    368                 // Run for specified duration in seconds
    369                 if(verbose==TRUE){
    370                     fprintf(stdout,"= %s calibrating %i seconds \n", name, (int)n);
    371                 }
    372 
    373                 //n *=  1000; // s => ms
    374                 //System.out.println("# " + meth.getName() + " " + n + " sec");
    375                 int32_t failsafe = 1; // last resort for very fast methods
    376                 t = 0;
    377                 while (t < (int)(n * 0.9)) { // 90% is close enough
    378                     if (loops == 0 || t == 0) {
    379                         loops = failsafe;
    380                         failsafe *= 10;
    381                     } else {
    382                         //System.out.println("# " + meth.getName() + " x " + loops + " = " + t);
    383                         loops = (int)((double)n / t * loops + 0.5);
    384                         if (loops == 0) {
    385                             fprintf(stderr,"Unable to converge on desired duration");
    386                             return FALSE;
    387                         }
    388                     }
    389                     //System.out.println("# " + meth.getName() + " x " + loops);
    390                     t = testFunction->time(loops,&status);
    391                     if(U_FAILURE(status)){
    392                         printf("Performance test failed with error: %s \n", u_errorName(status));
    393                         break;
    394                     }
    395                 }
    396             } else {
    397                 loops = iterations;
    398             }
    399 
    400             double min_t=1000000.0, sum_t=0.0;
    401             long events = -1;
    402 
    403             for(int32_t ps =0; ps < passes; ps++){
    404                 fprintf(stdout,"= %s begin " ,name);
    405                 if(verbose==TRUE){
    406                     if(iterations > 0) {
    407                         fprintf(stdout, "%i\n", (int)loops);
    408                     } else {
    409                         fprintf(stdout, "%i\n", (int)n);
    410                     }
    411                 } else {
    412                     fprintf(stdout, "\n");
    413                 }
    414                 t = testFunction->time(loops, &status);
    415                 if(U_FAILURE(status)){
    416                     printf("Performance test failed with error: %s \n", u_errorName(status));
    417                     break;
    418                 }
    419                 sum_t+=t;
    420                 if(t<min_t) {
    421                     min_t=t;
    422                 }
    423                 events = testFunction->getEventsPerIteration();
    424                 //print info only in verbose mode
    425                 if(verbose==TRUE){
    426                     if(events == -1){
    427                         fprintf(stdout, "= %s end: %f loops: %i operations: %li \n", name, t, (int)loops, ops);
    428                     }else{
    429                         fprintf(stdout, "= %s end: %f loops: %i operations: %li events: %li\n", name, t, (int)loops, ops, events);
    430                     }
    431                 }else{
    432                     if(events == -1){
    433                         fprintf(stdout,"= %s end %f %i %li\n", name, t, (int)loops, ops);
    434                     }else{
    435                         fprintf(stdout,"= %s end %f %i %li %li\n", name, t, (int)loops, ops, events);
    436                     }
    437                 }
    438             }
    439             if(verbose && U_SUCCESS(status)) {
    440                 double avg_t = sum_t/passes;
    441                 if (loops == 0 || ops == 0) {
    442                     fprintf(stderr, "%s did not run\n", name);
    443                 }
    444                 else if(events == -1) {
    445                     fprintf(stdout, "%%= %s avg: %.4g loops: %i avg/op: %.4g ns\n",
    446                             name, avg_t, (int)loops, (avg_t*1E9)/(loops*ops));
    447                     fprintf(stdout, "_= %s min: %.4g loops: %i min/op: %.4g ns\n",
    448                             name, min_t, (int)loops, (min_t*1E9)/(loops*ops));
    449                 }
    450                 else {
    451                     fprintf(stdout, "%%= %s avg: %.4g loops: %i avg/op: %.4g ns avg/event: %.4g ns\n",
    452                             name, avg_t, (int)loops, (avg_t*1E9)/(loops*ops), (avg_t*1E9)/(loops*events));
    453                     fprintf(stdout, "_= %s min: %.4g loops: %i min/op: %.4g ns min/event: %.4g ns\n",
    454                             name, min_t, (int)loops, (min_t*1E9)/(loops*ops), (min_t*1E9)/(loops*events));
    455                 }
    456             }
    457             delete testFunction;
    458         }
    459         index++;
    460     }while(name);
    461 
    462     gTest = saveTest;
    463     return rval;
    464 }
    465 
    466 /**
    467 * Print a usage message for this test class.
    468 */
    469 void UPerfTest::usage( void )
    470 {
    471     puts(gUsageString);
    472     if (_addUsage != NULL) {
    473         puts(_addUsage);
    474     }
    475 
    476     UBool save_verbose = verbose;
    477     verbose = TRUE;
    478     fprintf(stdout,"Test names:\n");
    479     fprintf(stdout,"-----------\n");
    480 
    481     int32_t index = 0;
    482     const char* name = NULL;
    483     do{
    484         this->runIndexedTest( index, FALSE, name );
    485         if (!name)
    486             break;
    487         fprintf(stdout,name);
    488         fprintf(stdout,"\n");
    489         index++;
    490     }while (name && (name[0] != 0));
    491     verbose = save_verbose;
    492 }
    493 
    494 
    495 
    496 
    497 void UPerfTest::setCaller( UPerfTest* callingTest )
    498 {
    499     caller = callingTest;
    500     if (caller) {
    501         verbose = caller->verbose;
    502     }
    503 }
    504 
    505 UBool UPerfTest::callTest( UPerfTest& testToBeCalled, char* par )
    506 {
    507     execCount--; // correct a previously assumed test-exec, as this only calls a subtest
    508     testToBeCalled.setCaller( this );
    509     return testToBeCalled.runTest( path, par );
    510 }
    511 
    512 UPerfTest::~UPerfTest(){
    513     if(lines!=NULL){
    514         delete[] lines;
    515     }
    516     if(buffer!=NULL){
    517         uprv_free(buffer);
    518     }
    519     if(resolvedFileName!=NULL){
    520         uprv_free(resolvedFileName);
    521     }
    522     ucbuf_close(ucharBuf);
    523 }
    524 
    525 #endif
    526