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