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