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