1/********************************************************************
2 * COPYRIGHT:
3 * Copyright (c) 2002-2011, 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
17static const char delim = '/';
18static int32_t execCount = 0;
19UPerfTest* UPerfTest::gTest = NULL;
20static const int MAXLINES = 40000;
21const 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
42enum
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
61static 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
77UPerfTest::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
90UPerfTest::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
106void 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
205ULine* UPerfTest::getLines(UErrorCode& status){
206    if (U_FAILURE(status)) {
207        return NULL;
208    }
209    if (lines != NULL) {
210        return lines;  // don't do it again
211    }
212    lines     = new ULine[MAXLINES];
213    int maxLines = MAXLINES;
214    numLines=0;
215    const UChar* line=NULL;
216    int32_t len =0;
217    for (;;) {
218        line = ucbuf_readline(ucharBuf,&len,&status);
219        if(line == NULL || U_FAILURE(status)){
220            break;
221        }
222        lines[numLines].name  = new UChar[len];
223        lines[numLines].len   = len;
224        memcpy(lines[numLines].name, line, len * U_SIZEOF_UCHAR);
225
226        numLines++;
227        len = 0;
228        if (numLines >= maxLines) {
229            maxLines += MAXLINES;
230            ULine *newLines = new ULine[maxLines];
231            if(newLines == NULL) {
232                fprintf(stderr, "Out of memory reading line %d.\n", (int)numLines);
233                status= U_MEMORY_ALLOCATION_ERROR;
234                delete []lines;
235                return NULL;
236            }
237
238            memcpy(newLines, lines, numLines*sizeof(ULine));
239            delete []lines;
240            lines = newLines;
241        }
242    }
243    return lines;
244}
245const UChar* UPerfTest::getBuffer(int32_t& len, UErrorCode& status){
246    if (U_FAILURE(status)) {
247        return NULL;
248    }
249    len = ucbuf_size(ucharBuf);
250    buffer =  (UChar*) uprv_malloc(U_SIZEOF_UCHAR * (len+1));
251    u_strncpy(buffer,ucbuf_getBuffer(ucharBuf,&bufferLen,&status),len);
252    buffer[len]=0;
253    len = bufferLen;
254    return buffer;
255}
256UBool UPerfTest::run(){
257    if(_remainingArgc==1){
258        // Testing all methods
259        return runTest();
260    }
261    UBool res=FALSE;
262    // Test only the specified fucntion
263    for (int i = 1; i < _remainingArgc; ++i) {
264        if (_argv[i][0] != '-') {
265            char* name = (char*) _argv[i];
266            if(verbose==TRUE){
267                //fprintf(stdout, "\n=== Handling test: %s: ===\n", name);
268                //fprintf(stdout, "\n%s:\n", name);
269            }
270            char* parameter = strchr( name, '@' );
271            if (parameter) {
272                *parameter = 0;
273                parameter += 1;
274            }
275            execCount = 0;
276            res = runTest( name, parameter );
277            if (!res || (execCount <= 0)) {
278                fprintf(stdout, "\n---ERROR: Test doesn't exist: %s!\n", name);
279                return FALSE;
280            }
281        }
282    }
283    return res;
284}
285UBool UPerfTest::runTest(char* name, char* par ){
286    UBool rval;
287    char* pos = NULL;
288
289    if (name)
290        pos = strchr( name, delim ); // check if name contains path (by looking for '/')
291    if (pos) {
292        path = pos+1;   // store subpath for calling subtest
293        *pos = 0;       // split into two strings
294    }else{
295        path = NULL;
296    }
297
298    if (!name || (name[0] == 0) || (strcmp(name, "*") == 0)) {
299        rval = runTestLoop( NULL, NULL );
300
301    }else if (strcmp( name, "LIST" ) == 0) {
302        this->usage();
303        rval = TRUE;
304
305    }else{
306        rval = runTestLoop( name, par );
307    }
308
309    if (pos)
310        *pos = delim;  // restore original value at pos
311    return rval;
312}
313
314
315void UPerfTest::setPath( char* pathVal )
316{
317    this->path = pathVal;
318}
319
320// call individual tests, to be overriden to call implementations
321UPerfFunction* UPerfTest::runIndexedTest( int32_t index, UBool exec, const char* &name, char* par )
322{
323    // to be overriden by a method like:
324    /*
325    switch (index) {
326        case 0: name = "First Test"; if (exec) FirstTest( par ); break;
327        case 1: name = "Second Test"; if (exec) SecondTest( par ); break;
328        default: name = ""; break;
329    }
330    */
331    fprintf(stderr,"*** runIndexedTest needs to be overriden! ***");
332    name = ""; exec = exec; index = index; par = par;
333    return NULL;
334}
335
336
337UBool UPerfTest::runTestLoop( char* testname, char* par )
338{
339    int32_t    index = 0;
340    const char*   name;
341    UBool  run_this_test;
342    UBool  rval = FALSE;
343    UErrorCode status = U_ZERO_ERROR;
344    UPerfTest* saveTest = gTest;
345    gTest = this;
346    int32_t loops = 0;
347    double t=0;
348    int32_t n = 1;
349    long ops;
350    do {
351        this->runIndexedTest( index, FALSE, name );
352        if (!name || (name[0] == 0))
353            break;
354        if (!testname) {
355            run_this_test = TRUE;
356        }else{
357            run_this_test = (UBool) (strcmp( name, testname ) == 0);
358        }
359        if (run_this_test) {
360            UPerfFunction* testFunction = this->runIndexedTest( index, TRUE, name, par );
361            execCount++;
362            rval=TRUE;
363            if(testFunction==NULL){
364                fprintf(stderr,"%s function returned NULL", name);
365                return FALSE;
366            }
367            ops = testFunction->getOperationsPerIteration();
368            if (ops < 1) {
369                fprintf(stderr, "%s returned an illegal operations/iteration()\n", name);
370                return FALSE;
371            }
372            if(iterations == 0) {
373                n = time;
374                // Run for specified duration in seconds
375                if(verbose==TRUE){
376                    fprintf(stdout,"= %s calibrating %i seconds \n", name, (int)n);
377                }
378
379                //n *=  1000; // s => ms
380                //System.out.println("# " + meth.getName() + " " + n + " sec");
381                int32_t failsafe = 1; // last resort for very fast methods
382                t = 0;
383                while (t < (int)(n * 0.9)) { // 90% is close enough
384                    if (loops == 0 || t == 0) {
385                        loops = failsafe;
386                        failsafe *= 10;
387                    } else {
388                        //System.out.println("# " + meth.getName() + " x " + loops + " = " + t);
389                        loops = (int)((double)n / t * loops + 0.5);
390                        if (loops == 0) {
391                            fprintf(stderr,"Unable to converge on desired duration");
392                            return FALSE;
393                        }
394                    }
395                    //System.out.println("# " + meth.getName() + " x " + loops);
396                    t = testFunction->time(loops,&status);
397                    if(U_FAILURE(status)){
398                        printf("Performance test failed with error: %s \n", u_errorName(status));
399                        break;
400                    }
401                }
402            } else {
403                loops = iterations;
404            }
405
406            double min_t=1000000.0, sum_t=0.0;
407            long events = -1;
408
409            for(int32_t ps =0; ps < passes; ps++){
410                fprintf(stdout,"= %s begin " ,name);
411                if(verbose==TRUE){
412                    if(iterations > 0) {
413                        fprintf(stdout, "%i\n", (int)loops);
414                    } else {
415                        fprintf(stdout, "%i\n", (int)n);
416                    }
417                } else {
418                    fprintf(stdout, "\n");
419                }
420                t = testFunction->time(loops, &status);
421                if(U_FAILURE(status)){
422                    printf("Performance test failed with error: %s \n", u_errorName(status));
423                    break;
424                }
425                sum_t+=t;
426                if(t<min_t) {
427                    min_t=t;
428                }
429                events = testFunction->getEventsPerIteration();
430                //print info only in verbose mode
431                if(verbose==TRUE){
432                    if(events == -1){
433                        fprintf(stdout, "= %s end: %f loops: %i operations: %li \n", name, t, (int)loops, ops);
434                    }else{
435                        fprintf(stdout, "= %s end: %f loops: %i operations: %li events: %li\n", name, t, (int)loops, ops, events);
436                    }
437                }else{
438                    if(events == -1){
439                        fprintf(stdout,"= %s end %f %i %li\n", name, t, (int)loops, ops);
440                    }else{
441                        fprintf(stdout,"= %s end %f %i %li %li\n", name, t, (int)loops, ops, events);
442                    }
443                }
444            }
445            if(verbose && U_SUCCESS(status)) {
446                double avg_t = sum_t/passes;
447                if (loops == 0 || ops == 0) {
448                    fprintf(stderr, "%s did not run\n", name);
449                }
450                else if(events == -1) {
451                    fprintf(stdout, "%%= %s avg: %.4g loops: %i avg/op: %.4g ns\n",
452                            name, avg_t, (int)loops, (avg_t*1E9)/(loops*ops));
453                    fprintf(stdout, "_= %s min: %.4g loops: %i min/op: %.4g ns\n",
454                            name, min_t, (int)loops, (min_t*1E9)/(loops*ops));
455                }
456                else {
457                    fprintf(stdout, "%%= %s avg: %.4g loops: %i avg/op: %.4g ns avg/event: %.4g ns\n",
458                            name, avg_t, (int)loops, (avg_t*1E9)/(loops*ops), (avg_t*1E9)/(loops*events));
459                    fprintf(stdout, "_= %s min: %.4g loops: %i min/op: %.4g ns min/event: %.4g ns\n",
460                            name, min_t, (int)loops, (min_t*1E9)/(loops*ops), (min_t*1E9)/(loops*events));
461                }
462            }
463            delete testFunction;
464        }
465        index++;
466    }while(name);
467
468    gTest = saveTest;
469    return rval;
470}
471
472/**
473* Print a usage message for this test class.
474*/
475void UPerfTest::usage( void )
476{
477    puts(gUsageString);
478    if (_addUsage != NULL) {
479        puts(_addUsage);
480    }
481
482    UBool save_verbose = verbose;
483    verbose = TRUE;
484    fprintf(stdout,"Test names:\n");
485    fprintf(stdout,"-----------\n");
486
487    int32_t index = 0;
488    const char* name = NULL;
489    do{
490        this->runIndexedTest( index, FALSE, name );
491        if (!name)
492            break;
493        fprintf(stdout, "%s\n", name);
494        index++;
495    }while (name && (name[0] != 0));
496    verbose = save_verbose;
497}
498
499
500
501
502void UPerfTest::setCaller( UPerfTest* callingTest )
503{
504    caller = callingTest;
505    if (caller) {
506        verbose = caller->verbose;
507    }
508}
509
510UBool UPerfTest::callTest( UPerfTest& testToBeCalled, char* par )
511{
512    execCount--; // correct a previously assumed test-exec, as this only calls a subtest
513    testToBeCalled.setCaller( this );
514    return testToBeCalled.runTest( path, par );
515}
516
517UPerfTest::~UPerfTest(){
518    if(lines!=NULL){
519        delete[] lines;
520    }
521    if(buffer!=NULL){
522        uprv_free(buffer);
523    }
524    if(resolvedFileName!=NULL){
525        uprv_free(resolvedFileName);
526    }
527    ucbuf_close(ucharBuf);
528}
529
530#endif
531