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 
~UPerfFunction()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 
UPerfTest(int32_t argc,const char * argv[],UErrorCode & status)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 
UPerfTest(int32_t argc,const char * argv[],UOption addOptions[],int32_t addOptionsCount,const char * addUsage,UErrorCode & status)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 
init(UOption addOptions[],int32_t addOptionsCount,UErrorCode & status)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 
getLines(UErrorCode & status)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 }
getBuffer(int32_t & len,UErrorCode & status)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 }
run()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 }
runTest(char * name,char * par)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 
setPath(char * pathVal)319 void UPerfTest::setPath( char* pathVal )
320 {
321     this->path = pathVal;
322 }
323 
324 // call individual tests, to be overriden to call implementations
runIndexedTest(int32_t,UBool,const char * &,char *)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 
runTestLoop(char * testname,char * par)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 */
usage(void)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 
setCaller(UPerfTest * callingTest)505 void UPerfTest::setCaller( UPerfTest* callingTest )
506 {
507     caller = callingTest;
508     if (caller) {
509         verbose = caller->verbose;
510     }
511 }
512 
callTest(UPerfTest & testToBeCalled,char * par)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 
~UPerfTest()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