ranfile.c (12762B)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 /*********************************************************************** 7 ** 8 ** Contact: AOF<freier@netscape.com> 9 ** 10 ** Name: ranfile.c 11 ** 12 ** Description: Test to hammer on various components of NSPR 13 ** Modification History: 14 ** 20-May-97 AGarcia- Converted the test to accomodate the debug_mode flag. 15 ** The debug mode will print all of the printfs associated with this 16 *test. 17 ** The regress mode will be the default mode. Since the regress tool 18 *limits 19 ** the output to a one line status:PASS or FAIL,all of the printf 20 *statements 21 ** have been handled with an if (debug_mode) statement. 22 ** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been 23 *updated to 24 ** recognize the return code from tha main program. 25 ***********************************************************************/ 26 27 /*********************************************************************** 28 ** Includes 29 ***********************************************************************/ 30 /* Used to get the command line option */ 31 #include "plgetopt.h" 32 33 #include "prinit.h" 34 #include "prthread.h" 35 #include "prlock.h" 36 #include "prcvar.h" 37 #include "prmem.h" 38 #include "prinrval.h" 39 #include "prio.h" 40 41 #include <string.h> 42 #include <stdio.h> 43 44 static PRIntn debug_mode = 0; 45 static PRIntn failed_already = 0; 46 static PRThreadScope thread_scope = PR_LOCAL_THREAD; 47 48 typedef enum { sg_go, sg_stop, sg_done } Action; 49 typedef enum { 50 sg_okay, 51 sg_open, 52 sg_close, 53 sg_delete, 54 sg_write, 55 sg_seek 56 } Problem; 57 58 typedef struct Hammer_s { 59 PRLock* ml; 60 PRCondVar* cv; 61 PRUint32 id; 62 PRUint32 limit; 63 PRUint32 writes; 64 PRThread* thread; 65 PRIntervalTime timein; 66 Action action; 67 Problem problem; 68 } Hammer_t; 69 70 #define DEFAULT_LIMIT 10 71 #define DEFAULT_THREADS 2 72 #define DEFAULT_LOOPS 1 73 74 static PRInt32 pageSize = 1024; 75 static const char* baseName = "./"; 76 static const char* programName = "Random File"; 77 78 /*********************************************************************** 79 ** PRIVATE FUNCTION: RandomNum 80 ** DESCRIPTION: 81 ** Generate a pseudo-random number 82 ** INPUTS: None 83 ** OUTPUTS: None 84 ** RETURN: A pseudo-random unsigned number, 32-bits wide 85 ** SIDE EFFECTS: 86 ** Updates random seed (a static) 87 ** RESTRICTIONS: 88 ** None 89 ** MEMORY: NA 90 ** ALGORITHM: 91 ** Uses the current interval timer value, promoted to a 64 bit 92 ** float as a multiplier for a static residue (which begins 93 ** as an uninitialized variable). The result is bits [16..48) 94 ** of the product. Seed is then updated with the return value 95 ** promoted to a float-64. 96 ***********************************************************************/ 97 static PRUint32 RandomNum(void) { 98 PRUint32 rv; 99 PRUint64 shift; 100 static PRFloat64 seed = 0x58a9382; /* Just make sure it isn't 0! */ 101 PRFloat64 random = seed * (PRFloat64)PR_IntervalNow(); 102 LL_USHR(shift, *((PRUint64*)&random), 16); 103 LL_L2UI(rv, shift); 104 seed = (PRFloat64)rv; 105 return rv; 106 } /* RandomNum */ 107 108 /*********************************************************************** 109 ** PRIVATE FUNCTION: Thread 110 ** DESCRIPTION: 111 ** Hammer on the file I/O system 112 ** INPUTS: A pointer to the thread's private data 113 ** OUTPUTS: None 114 ** RETURN: None 115 ** SIDE EFFECTS: 116 ** Creates, accesses and deletes a file 117 ** RESTRICTIONS: 118 ** (Currently) must have file create permission in "/usr/tmp". 119 ** MEMORY: NA 120 ** ALGORITHM: 121 ** This function is a root of a thread 122 ** 1) Creates a (hopefully) unique file in /usr/tmp/ 123 ** 2) Writes a zero to a random number of sequential pages 124 ** 3) Closes the file 125 ** 4) Reopens the file 126 ** 5) Seeks to a random page within the file 127 ** 6) Writes a one byte on that page 128 ** 7) Repeat steps [5..6] for each page in the file 129 ** 8) Close and delete the file 130 ** 9) Repeat steps [1..8] until told to stop 131 ** 10) Notify complete and return 132 ***********************************************************************/ 133 static void PR_CALLBACK Thread(void* arg) { 134 PRUint32 index; 135 char filename[30]; 136 const char zero = 0; 137 PRFileDesc* file = NULL; 138 PRStatus rv = PR_SUCCESS; 139 Hammer_t* cd = (Hammer_t*)arg; 140 141 (void)sprintf(filename, "%ssg%04ld.dat", baseName, cd->id); 142 143 if (debug_mode) { 144 printf("Starting work on %s\n", filename); 145 } 146 147 while (PR_TRUE) { 148 PRUint32 bytes; 149 PRUint32 minor = (RandomNum() % cd->limit) + 1; 150 PRUint32 random = (RandomNum() % cd->limit) + 1; 151 PRUint32 pages = (RandomNum() % cd->limit) + 10; 152 while (minor-- > 0) { 153 cd->problem = sg_okay; 154 if (cd->action != sg_go) { 155 goto finished; 156 } 157 cd->problem = sg_open; 158 file = PR_Open(filename, PR_RDWR | PR_CREATE_FILE, 0666); 159 if (file == NULL) { 160 goto finished; 161 } 162 for (index = 0; index < pages; index++) { 163 cd->problem = sg_okay; 164 if (cd->action != sg_go) { 165 goto close; 166 } 167 cd->problem = sg_seek; 168 bytes = PR_Seek(file, pageSize * index, PR_SEEK_SET); 169 if (bytes != pageSize * index) { 170 goto close; 171 } 172 cd->problem = sg_write; 173 bytes = PR_Write(file, &zero, sizeof(zero)); 174 if (bytes <= 0) { 175 goto close; 176 } 177 cd->writes += 1; 178 } 179 cd->problem = sg_close; 180 rv = PR_Close(file); 181 if (rv != PR_SUCCESS) { 182 goto purge; 183 } 184 185 cd->problem = sg_okay; 186 if (cd->action != sg_go) { 187 goto purge; 188 } 189 190 cd->problem = sg_open; 191 file = PR_Open(filename, PR_RDWR, 0666); 192 for (index = 0; index < pages; index++) { 193 cd->problem = sg_okay; 194 if (cd->action != sg_go) { 195 goto close; 196 } 197 cd->problem = sg_seek; 198 bytes = PR_Seek(file, pageSize * index, PR_SEEK_SET); 199 if (bytes != pageSize * index) { 200 goto close; 201 } 202 cd->problem = sg_write; 203 bytes = PR_Write(file, &zero, sizeof(zero)); 204 if (bytes <= 0) { 205 goto close; 206 } 207 cd->writes += 1; 208 random = (random + 511) % pages; 209 } 210 cd->problem = sg_close; 211 rv = PR_Close(file); 212 if (rv != PR_SUCCESS) { 213 goto purge; 214 } 215 cd->problem = sg_delete; 216 rv = PR_Delete(filename); 217 if (rv != PR_SUCCESS) { 218 goto finished; 219 } 220 } 221 } 222 223 close: 224 (void)PR_Close(file); 225 purge: 226 (void)PR_Delete(filename); 227 finished: 228 PR_Lock(cd->ml); 229 cd->action = sg_done; 230 PR_NotifyCondVar(cd->cv); 231 PR_Unlock(cd->ml); 232 233 if (debug_mode) { 234 printf("Ending work on %s\n", filename); 235 } 236 237 return; 238 } /* Thread */ 239 240 static Hammer_t hammer[100]; 241 static PRCondVar* cv; 242 /*********************************************************************** 243 ** PRIVATE FUNCTION: main 244 ** DESCRIPTION: 245 ** Hammer on the file I/O system 246 ** INPUTS: The usual argc and argv 247 ** argv[0] - program name (not used) 248 ** argv[1] - the number of times to execute the major loop 249 ** argv[2] - the number of threads to toss into the batch 250 ** argv[3] - the clipping number applied to randoms 251 ** default values: loops = 2, threads = 10, limit = 57 252 ** OUTPUTS: None 253 ** RETURN: None 254 ** SIDE EFFECTS: 255 ** Creates, accesses and deletes lots of files 256 ** RESTRICTIONS: 257 ** (Currently) must have file create permission in "/usr/tmp". 258 ** MEMORY: NA 259 ** ALGORITHM: 260 ** 1) Fork a "Thread()" 261 ** 2) Wait for 'interleave' seconds 262 ** 3) For [0..'threads') repeat [1..2] 263 ** 4) Mark all objects to stop 264 ** 5) Collect the threads, accumulating the results 265 ** 6) For [0..'loops') repeat [1..5] 266 ** 7) Print accumulated results and exit 267 ** 268 ** Characteristic output (from IRIX) 269 ** Random File: Using loops = 2, threads = 10, limit = 57 270 ** Random File: [min [avg] max] writes/sec average 271 ***********************************************************************/ 272 int main(int argc, char** argv) { 273 PRLock* ml; 274 PRUint32 id = 0; 275 int active, poll; 276 PRIntervalTime interleave; 277 PRIntervalTime duration = 0; 278 int limit = 0, loops = 0, threads = 0, times; 279 PRUint32 writes, writesMin = 0x7fffffff, writesTot = 0, durationTot = 0, 280 writesMax = 0; 281 282 const char* where[] = {"okay", "open", "close", "delete", "write", "seek"}; 283 284 /* The command line argument: -d is used to determine if the test is being run 285 in debug mode. The regress tool requires only one line output:PASS or FAIL. 286 All of the printfs associated with this test has been handled with a if 287 (debug_mode) test. Usage: test_name -d 288 */ 289 PLOptStatus os; 290 PLOptState* opt = PL_CreateOptState(argc, argv, "Gdl:t:i:"); 291 while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) { 292 if (PL_OPT_BAD == os) { 293 continue; 294 } 295 switch (opt->option) { 296 case 'G': /* global threads */ 297 thread_scope = PR_GLOBAL_THREAD; 298 break; 299 case 'd': /* debug mode */ 300 debug_mode = 1; 301 break; 302 case 'l': /* limiting number */ 303 limit = atoi(opt->value); 304 break; 305 case 't': /* number of threads */ 306 threads = atoi(opt->value); 307 break; 308 case 'i': /* iteration counter */ 309 loops = atoi(opt->value); 310 break; 311 default: 312 break; 313 } 314 } 315 PL_DestroyOptState(opt); 316 317 /* main test */ 318 319 PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); 320 321 interleave = PR_SecondsToInterval(10); 322 323 ml = PR_NewLock(); 324 cv = PR_NewCondVar(ml); 325 326 if (loops == 0) { 327 loops = DEFAULT_LOOPS; 328 } 329 if (limit == 0) { 330 limit = DEFAULT_LIMIT; 331 } 332 if (threads == 0) { 333 threads = DEFAULT_THREADS; 334 } 335 336 if (debug_mode) 337 printf("%s: Using loops = %d, threads = %d, limit = %d and %s threads\n", 338 programName, loops, threads, limit, 339 (thread_scope == PR_LOCAL_THREAD) ? "LOCAL" : "GLOBAL"); 340 341 for (times = 0; times < loops; ++times) { 342 if (debug_mode) { 343 printf("%s: Setting concurrency level to %d\n", programName, times + 1); 344 } 345 PR_SetConcurrency(times + 1); 346 for (active = 0; active < threads; active++) { 347 hammer[active].ml = ml; 348 hammer[active].cv = cv; 349 hammer[active].id = id++; 350 hammer[active].writes = 0; 351 hammer[active].action = sg_go; 352 hammer[active].problem = sg_okay; 353 hammer[active].limit = (RandomNum() % limit) + 1; 354 hammer[active].timein = PR_IntervalNow(); 355 hammer[active].thread = 356 PR_CreateThread(PR_USER_THREAD, Thread, &hammer[active], 357 PR_GetThreadPriority(PR_GetCurrentThread()), 358 thread_scope, PR_JOINABLE_THREAD, 0); 359 360 PR_Lock(ml); 361 PR_WaitCondVar(cv, interleave); /* start new ones slowly */ 362 PR_Unlock(ml); 363 } 364 365 /* 366 * The last thread started has had the opportunity to run for 367 * 'interleave' seconds. Now gather them all back in. 368 */ 369 PR_Lock(ml); 370 for (poll = 0; poll < threads; poll++) { 371 if (hammer[poll].action == sg_go) { /* don't overwrite done */ 372 hammer[poll].action = sg_stop; /* ask him to stop */ 373 } 374 } 375 PR_Unlock(ml); 376 377 while (active > 0) { 378 for (poll = 0; poll < threads; poll++) { 379 PR_Lock(ml); 380 while (hammer[poll].action < sg_done) { 381 PR_WaitCondVar(cv, PR_INTERVAL_NO_TIMEOUT); 382 } 383 PR_Unlock(ml); 384 385 active -= 1; /* this is another one down */ 386 (void)PR_JoinThread(hammer[poll].thread); 387 hammer[poll].thread = NULL; 388 if (hammer[poll].problem == sg_okay) { 389 duration = 390 PR_IntervalToMilliseconds(PR_IntervalNow() - hammer[poll].timein); 391 writes = hammer[poll].writes * 1000 / duration; 392 if (writes < writesMin) { 393 writesMin = writes; 394 } 395 if (writes > writesMax) { 396 writesMax = writes; 397 } 398 writesTot += hammer[poll].writes; 399 durationTot += duration; 400 } else if (debug_mode) 401 printf("%s: test failed %s after %ld seconds\n", programName, 402 where[hammer[poll].problem], duration); 403 else { 404 failed_already = 1; 405 } 406 } 407 } 408 } 409 if (debug_mode) 410 printf("%s: [%ld [%ld] %ld] writes/sec average\n", programName, writesMin, 411 writesTot * 1000 / durationTot, writesMax); 412 413 PR_DestroyCondVar(cv); 414 PR_DestroyLock(ml); 415 416 if (failed_already) { 417 printf("FAIL\n"); 418 return 1; 419 } else { 420 printf("PASS\n"); 421 return 0; 422 } 423 } /* main */