ranfile.cpp (14680B)
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<mailto: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 test. 16 ** The regress mode will be the default mode. Since the regress tool limits 17 ** the output to a one line status:PASS or FAIL,all of the printf statements 18 ** have been handled with an if (debug_mode) statement. 19 ** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to 20 ** recognize the return code from tha main program. 21 ***********************************************************************/ 22 23 24 /*********************************************************************** 25 ** Includes 26 ***********************************************************************/ 27 /* Used to get the command line option */ 28 #include <plgetopt.h> 29 #include <prprf.h> 30 #include <prio.h> 31 32 #include "rccv.h" 33 #include "rcthread.h" 34 #include "rcfileio.h" 35 #include "rclock.h" 36 37 #include <string.h> 38 #include <stdio.h> 39 #include <stdlib.h> 40 41 static PRFileDesc *output; 42 static PRIntn debug_mode = 0; 43 static PRIntn failed_already = 0; 44 45 class HammerData 46 { 47 public: 48 typedef enum { 49 sg_go, sg_stop, sg_done 50 } Action; 51 typedef enum { 52 sg_okay, sg_open, sg_close, sg_delete, sg_write, sg_seek 53 } Problem; 54 55 virtual ~HammerData(); 56 HammerData(RCLock* lock, RCCondition *cond, PRUint32 clip); 57 virtual PRUint32 Random(); 58 59 Action action; 60 Problem problem; 61 PRUint32 writes; 62 RCInterval timein; 63 friend class Hammer; 64 private: 65 RCLock *ml; 66 RCCondition *cv; 67 PRUint32 limit; 68 69 PRFloat64 seed; 70 }; /* HammerData */ 71 72 class Hammer: public HammerData, public RCThread 73 { 74 public: 75 virtual ~Hammer(); 76 Hammer(RCThread::Scope scope, RCLock* lock, RCCondition *cond, PRUint32 clip); 77 78 private: 79 void RootFunction(); 80 81 }; 82 83 static PRInt32 pageSize = 1024; 84 static const char* baseName = "./"; 85 static const char *programName = "Random File"; 86 87 /*********************************************************************** 88 ** PRIVATE FUNCTION: Random 89 ** DESCRIPTION: 90 ** Generate a pseudo-random number 91 ** INPUTS: None 92 ** OUTPUTS: None 93 ** RETURN: A pseudo-random unsigned number, 32-bits wide 94 ** SIDE EFFECTS: 95 ** Updates random seed (a static) 96 ** RESTRICTIONS: 97 ** None 98 ** MEMORY: NA 99 ** ALGORITHM: 100 ** Uses the current interval timer value, promoted to a 64 bit 101 ** float as a multiplier for a static residue (which begins 102 ** as an uninitialized variable). The result is bits [16..48) 103 ** of the product. Seed is then updated with the return value 104 ** promoted to a float-64. 105 ***********************************************************************/ 106 PRUint32 HammerData::Random() 107 { 108 PRUint32 rv; 109 PRUint64 shift; 110 RCInterval now = RCInterval(RCInterval::now); 111 PRFloat64 random = seed * (PRFloat64)((PRIntervalTime)now); 112 LL_USHR(shift, *((PRUint64*)&random), 16); 113 LL_L2UI(rv, shift); 114 seed = (PRFloat64)rv; 115 return rv; 116 } /* HammerData::Random */ 117 118 Hammer::~Hammer() { } 119 120 Hammer::Hammer( 121 RCThread::Scope scope, RCLock* lock, RCCondition *cond, PRUint32 clip): 122 HammerData(lock, cond, clip), RCThread(scope, RCThread::joinable, 0) { } 123 124 HammerData::~HammerData() { } 125 126 HammerData::HammerData(RCLock* lock, RCCondition *cond, PRUint32 clip) 127 { 128 ml = lock; 129 cv = cond; 130 writes = 0; 131 limit = clip; 132 seed = 0x58a9382; 133 action = HammerData::sg_go; 134 problem = HammerData::sg_okay; 135 timein = RCInterval(RCInterval::now); 136 } /* HammerData::HammerData */ 137 138 139 /*********************************************************************** 140 ** PRIVATE FUNCTION: Hammer::RootFunction 141 ** DESCRIPTION: 142 ** Hammer on the file I/O system 143 ** INPUTS: A pointer to the thread's private data 144 ** OUTPUTS: None 145 ** RETURN: None 146 ** SIDE EFFECTS: 147 ** Creates, accesses and deletes a file 148 ** RESTRICTIONS: 149 ** (Currently) must have file create permission in "/usr/tmp". 150 ** MEMORY: NA 151 ** ALGORITHM: 152 ** This function is a root of a thread 153 ** 1) Creates a (hopefully) unique file in /usr/tmp/ 154 ** 2) Writes a zero to a random number of sequential pages 155 ** 3) Closes the file 156 ** 4) Reopens the file 157 ** 5) Seeks to a random page within the file 158 ** 6) Writes a one byte on that page 159 ** 7) Repeat steps [5..6] for each page in the file 160 ** 8) Close and delete the file 161 ** 9) Repeat steps [1..8] until told to stop 162 ** 10) Notify complete and return 163 ***********************************************************************/ 164 void Hammer::RootFunction() 165 { 166 PRUint32 index; 167 RCFileIO file; 168 char filename[30]; 169 const char zero = 0; 170 PRStatus rv = PR_SUCCESS; 171 172 limit = (Random() % limit) + 1; 173 174 (void)sprintf(filename, "%ssg%04p.dat", baseName, this); 175 176 if (debug_mode) { 177 PR_fprintf(output, "Starting work on %s\n", filename); 178 } 179 180 while (PR_TRUE) 181 { 182 PRUint64 bytes; 183 PRUint32 minor = (Random() % limit) + 1; 184 PRUint32 random = (Random() % limit) + 1; 185 PRUint32 pages = (Random() % limit) + 10; 186 while (minor-- > 0) 187 { 188 problem = sg_okay; 189 if (action != sg_go) { 190 goto finished; 191 } 192 problem = sg_open; 193 rv = file.Open(filename, PR_RDWR|PR_CREATE_FILE, 0666); 194 if (PR_FAILURE == rv) { 195 goto finished; 196 } 197 for (index = 0; index < pages; index++) 198 { 199 problem = sg_okay; 200 if (action != sg_go) { 201 goto close; 202 } 203 problem = sg_seek; 204 bytes = file.Seek(pageSize * index, RCFileIO::set); 205 if (bytes != pageSize * index) { 206 goto close; 207 } 208 problem = sg_write; 209 bytes = file.Write(&zero, sizeof(zero)); 210 if (bytes <= 0) { 211 goto close; 212 } 213 writes += 1; 214 } 215 problem = sg_close; 216 rv = file.Close(); 217 if (rv != PR_SUCCESS) { 218 goto purge; 219 } 220 221 problem = sg_okay; 222 if (action != sg_go) { 223 goto purge; 224 } 225 226 problem = sg_open; 227 rv = file.Open(filename, PR_RDWR, 0666); 228 if (PR_FAILURE == rv) { 229 goto finished; 230 } 231 for (index = 0; index < pages; index++) 232 { 233 problem = sg_okay; 234 if (action != sg_go) { 235 goto close; 236 } 237 problem = sg_seek; 238 bytes = file.Seek(pageSize * index, RCFileIO::set); 239 if (bytes != pageSize * index) { 240 goto close; 241 } 242 problem = sg_write; 243 bytes = file.Write(&zero, sizeof(zero)); 244 if (bytes <= 0) { 245 goto close; 246 } 247 writes += 1; 248 random = (random + 511) % pages; 249 } 250 problem = sg_close; 251 rv = file.Close(); 252 if (rv != PR_SUCCESS) { 253 goto purge; 254 } 255 problem = sg_delete; 256 rv = file.Delete(filename); 257 if (rv != PR_SUCCESS) { 258 goto finished; 259 } 260 } 261 } 262 263 close: 264 (void)file.Close(); 265 purge: 266 (void)file.Delete(filename); 267 finished: 268 RCEnter scope(ml); 269 action = HammerData::sg_done; 270 cv->Notify(); 271 272 if (debug_mode) { 273 PR_fprintf(output, "Ending work on %s\n", filename); 274 } 275 276 return; 277 } /* Hammer::RootFunction */ 278 279 static Hammer* hammer[100]; 280 /*********************************************************************** 281 ** PRIVATE FUNCTION: main 282 ** DESCRIPTION: 283 ** Hammer on the file I/O system 284 ** INPUTS: The usual argc and argv 285 ** argv[0] - program name (not used) 286 ** argv[1] - the number of virtual_procs to execute the major loop 287 ** argv[2] - the number of threads to toss into the batch 288 ** argv[3] - the clipping number applied to randoms 289 ** default values: max_virtual_procs = 2, threads = 10, limit = 57 290 ** OUTPUTS: None 291 ** RETURN: None 292 ** SIDE EFFECTS: 293 ** Creates, accesses and deletes lots of files 294 ** RESTRICTIONS: 295 ** (Currently) must have file create permission in "/usr/tmp". 296 ** MEMORY: NA 297 ** ALGORITHM: 298 ** 1) Fork a "Thread()" 299 ** 2) Wait for 'interleave' seconds 300 ** 3) For [0..'threads') repeat [1..2] 301 ** 4) Mark all objects to stop 302 ** 5) Collect the threads, accumulating the results 303 ** 6) For [0..'max_virtual_procs') repeat [1..5] 304 ** 7) Print accumulated results and exit 305 ** 306 ** Characteristic output (from IRIX) 307 ** Random File: Using max_virtual_procs = 2, threads = 10, limit = 57 308 ** Random File: [min [avg] max] writes/sec average 309 ***********************************************************************/ 310 PRIntn main (PRIntn argc, char *argv[]) 311 { 312 RCLock ml; 313 PLOptStatus os; 314 RCCondition cv(&ml); 315 PRUint32 writesMax = 0, durationTot = 0; 316 RCThread::Scope thread_scope = RCThread::local; 317 PRUint32 writes, writesMin = 0x7fffffff, writesTot = 0; 318 PRIntn active, poll, limit = 0, max_virtual_procs = 0, threads = 0, virtual_procs; 319 RCInterval interleave(RCInterval::FromMilliseconds(10000)), duration(0); 320 321 const char *where[] = {"okay", "open", "close", "delete", "write", "seek"}; 322 323 PLOptState *opt = PL_CreateOptState(argc, argv, "Gdl:t:i:"); 324 while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) 325 { 326 if (PL_OPT_BAD == os) { 327 continue; 328 } 329 switch (opt->option) 330 { 331 case 0: 332 baseName = opt->value; 333 break; 334 case 'G': /* global threads */ 335 thread_scope = RCThread::global; 336 break; 337 case 'd': /* debug mode */ 338 debug_mode = 1; 339 break; 340 case 'l': /* limiting number */ 341 limit = atoi(opt->value); 342 break; 343 case 't': /* number of threads */ 344 threads = atoi(opt->value); 345 break; 346 case 'i': /* iteration counter */ 347 max_virtual_procs = atoi(opt->value); 348 break; 349 default: 350 break; 351 } 352 } 353 PL_DestroyOptState(opt); 354 output = PR_GetSpecialFD(PR_StandardOutput); 355 356 /* main test */ 357 358 cv.SetTimeout(interleave); 359 360 if (max_virtual_procs == 0) { 361 max_virtual_procs = 2; 362 } 363 if (limit == 0) { 364 limit = 57; 365 } 366 if (threads == 0) { 367 threads = 10; 368 } 369 370 if (debug_mode) PR_fprintf(output, 371 "%s: Using %d virtual processors, %d threads, limit = %d and %s threads\n", 372 programName, max_virtual_procs, threads, limit, 373 (thread_scope == RCThread::local) ? "LOCAL" : "GLOBAL"); 374 375 for (virtual_procs = 0; virtual_procs < max_virtual_procs; ++virtual_procs) 376 { 377 if (debug_mode) 378 PR_fprintf(output, 379 "%s: Setting number of virtual processors to %d\n", 380 programName, virtual_procs + 1); 381 RCPrimordialThread::SetVirtualProcessors(virtual_procs + 1); 382 for (active = 0; active < threads; active++) 383 { 384 hammer[active] = new Hammer(thread_scope, &ml, &cv, limit); 385 hammer[active]->Start(); /* then make it roll */ 386 RCThread::Sleep(interleave); /* start them slowly */ 387 } 388 389 /* 390 * The last thread started has had the opportunity to run for 391 * 'interleave' seconds. Now gather them all back in. 392 */ 393 { 394 RCEnter scope(&ml); 395 for (poll = 0; poll < threads; poll++) 396 { 397 if (hammer[poll]->action == HammerData::sg_go) { /* don't overwrite done */ 398 hammer[poll]->action = HammerData::sg_stop; /* ask him to stop */ 399 } 400 } 401 } 402 403 while (active > 0) 404 { 405 for (poll = 0; poll < threads; poll++) 406 { 407 ml.Acquire(); 408 while (hammer[poll]->action < HammerData::sg_done) { 409 cv.Wait(); 410 } 411 ml.Release(); 412 413 if (hammer[poll]->problem == HammerData::sg_okay) 414 { 415 duration = RCInterval(RCInterval::now) - hammer[poll]->timein; 416 writes = hammer[poll]->writes * 1000 / duration; 417 if (writes < writesMin) { 418 writesMin = writes; 419 } 420 if (writes > writesMax) { 421 writesMax = writes; 422 } 423 writesTot += hammer[poll]->writes; 424 durationTot += duration; 425 } 426 else 427 { 428 if (debug_mode) PR_fprintf(output, 429 "%s: test failed %s after %ld seconds\n", 430 programName, where[hammer[poll]->problem], duration); 431 else { 432 failed_already=1; 433 } 434 } 435 active -= 1; /* this is another one down */ 436 (void)hammer[poll]->Join(); 437 hammer[poll] = NULL; 438 } 439 } 440 if (debug_mode) PR_fprintf(output, 441 "%s: [%ld [%ld] %ld] writes/sec average\n", 442 programName, writesMin, 443 writesTot * 1000 / durationTot, writesMax); 444 } 445 446 failed_already |= (PR_FAILURE == RCPrimordialThread::Cleanup()); 447 PR_fprintf(output, "%s\n", (failed_already) ? "FAIL\n" : "PASS\n"); 448 return failed_already; 449 } /* main */