1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/nsprpub/pr/src/cplus/tests/ranfile.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,400 @@ 1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +/*********************************************************************** 1.10 +** 1.11 +** Contact: AOF<mailto:freier@netscape.com> 1.12 +** 1.13 +** Name: ranfile.c 1.14 +** 1.15 +** Description: Test to hammer on various components of NSPR 1.16 +** Modification History: 1.17 +** 20-May-97 AGarcia- Converted the test to accomodate the debug_mode flag. 1.18 +** The debug mode will print all of the printfs associated with this test. 1.19 +** The regress mode will be the default mode. Since the regress tool limits 1.20 +** the output to a one line status:PASS or FAIL,all of the printf statements 1.21 +** have been handled with an if (debug_mode) statement. 1.22 +** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to 1.23 +** recognize the return code from tha main program. 1.24 +***********************************************************************/ 1.25 + 1.26 + 1.27 +/*********************************************************************** 1.28 +** Includes 1.29 +***********************************************************************/ 1.30 +/* Used to get the command line option */ 1.31 +#include <plgetopt.h> 1.32 +#include <prprf.h> 1.33 +#include <prio.h> 1.34 + 1.35 +#include "rccv.h" 1.36 +#include "rcthread.h" 1.37 +#include "rcfileio.h" 1.38 +#include "rclock.h" 1.39 + 1.40 +#include <string.h> 1.41 +#include <stdio.h> 1.42 +#include <stdlib.h> 1.43 + 1.44 +static PRFileDesc *output; 1.45 +static PRIntn debug_mode = 0; 1.46 +static PRIntn failed_already = 0; 1.47 + 1.48 +class HammerData 1.49 +{ 1.50 +public: 1.51 + typedef enum { 1.52 + sg_go, sg_stop, sg_done} Action; 1.53 + typedef enum { 1.54 + sg_okay, sg_open, sg_close, sg_delete, sg_write, sg_seek} Problem; 1.55 + 1.56 + virtual ~HammerData(); 1.57 + HammerData(RCLock* lock, RCCondition *cond, PRUint32 clip); 1.58 + virtual PRUint32 Random(); 1.59 + 1.60 + Action action; 1.61 + Problem problem; 1.62 + PRUint32 writes; 1.63 + RCInterval timein; 1.64 +friend class Hammer; 1.65 +private: 1.66 + RCLock *ml; 1.67 + RCCondition *cv; 1.68 + PRUint32 limit; 1.69 + 1.70 + PRFloat64 seed; 1.71 +}; /* HammerData */ 1.72 + 1.73 +class Hammer: public HammerData, public RCThread 1.74 +{ 1.75 +public: 1.76 + virtual ~Hammer(); 1.77 + Hammer(RCThread::Scope scope, RCLock* lock, RCCondition *cond, PRUint32 clip); 1.78 + 1.79 +private: 1.80 + void RootFunction(); 1.81 + 1.82 +}; 1.83 + 1.84 +static PRInt32 pageSize = 1024; 1.85 +static const char* baseName = "./"; 1.86 +static const char *programName = "Random File"; 1.87 + 1.88 +/*********************************************************************** 1.89 +** PRIVATE FUNCTION: Random 1.90 +** DESCRIPTION: 1.91 +** Generate a pseudo-random number 1.92 +** INPUTS: None 1.93 +** OUTPUTS: None 1.94 +** RETURN: A pseudo-random unsigned number, 32-bits wide 1.95 +** SIDE EFFECTS: 1.96 +** Updates random seed (a static) 1.97 +** RESTRICTIONS: 1.98 +** None 1.99 +** MEMORY: NA 1.100 +** ALGORITHM: 1.101 +** Uses the current interval timer value, promoted to a 64 bit 1.102 +** float as a multiplier for a static residue (which begins 1.103 +** as an uninitialized variable). The result is bits [16..48) 1.104 +** of the product. Seed is then updated with the return value 1.105 +** promoted to a float-64. 1.106 +***********************************************************************/ 1.107 +PRUint32 HammerData::Random() 1.108 +{ 1.109 + PRUint32 rv; 1.110 + PRUint64 shift; 1.111 + RCInterval now = RCInterval(RCInterval::now); 1.112 + PRFloat64 random = seed * (PRFloat64)((PRIntervalTime)now); 1.113 + LL_USHR(shift, *((PRUint64*)&random), 16); 1.114 + LL_L2UI(rv, shift); 1.115 + seed = (PRFloat64)rv; 1.116 + return rv; 1.117 +} /* HammerData::Random */ 1.118 + 1.119 +Hammer::~Hammer() { } 1.120 + 1.121 +Hammer::Hammer( 1.122 + RCThread::Scope scope, RCLock* lock, RCCondition *cond, PRUint32 clip): 1.123 + HammerData(lock, cond, clip), RCThread(scope, RCThread::joinable, 0) { } 1.124 + 1.125 +HammerData::~HammerData() { } 1.126 + 1.127 +HammerData::HammerData(RCLock* lock, RCCondition *cond, PRUint32 clip) 1.128 +{ 1.129 + ml = lock; 1.130 + cv = cond; 1.131 + writes = 0; 1.132 + limit = clip; 1.133 + seed = 0x58a9382; 1.134 + action = HammerData::sg_go; 1.135 + problem = HammerData::sg_okay; 1.136 + timein = RCInterval(RCInterval::now); 1.137 +} /* HammerData::HammerData */ 1.138 + 1.139 + 1.140 +/*********************************************************************** 1.141 +** PRIVATE FUNCTION: Hammer::RootFunction 1.142 +** DESCRIPTION: 1.143 +** Hammer on the file I/O system 1.144 +** INPUTS: A pointer to the thread's private data 1.145 +** OUTPUTS: None 1.146 +** RETURN: None 1.147 +** SIDE EFFECTS: 1.148 +** Creates, accesses and deletes a file 1.149 +** RESTRICTIONS: 1.150 +** (Currently) must have file create permission in "/usr/tmp". 1.151 +** MEMORY: NA 1.152 +** ALGORITHM: 1.153 +** This function is a root of a thread 1.154 +** 1) Creates a (hopefully) unique file in /usr/tmp/ 1.155 +** 2) Writes a zero to a random number of sequential pages 1.156 +** 3) Closes the file 1.157 +** 4) Reopens the file 1.158 +** 5) Seeks to a random page within the file 1.159 +** 6) Writes a one byte on that page 1.160 +** 7) Repeat steps [5..6] for each page in the file 1.161 +** 8) Close and delete the file 1.162 +** 9) Repeat steps [1..8] until told to stop 1.163 +** 10) Notify complete and return 1.164 +***********************************************************************/ 1.165 +void Hammer::RootFunction() 1.166 +{ 1.167 + PRUint32 index; 1.168 + RCFileIO file; 1.169 + char filename[30]; 1.170 + const char zero = 0; 1.171 + PRStatus rv = PR_SUCCESS; 1.172 + 1.173 + limit = (Random() % limit) + 1; 1.174 + 1.175 + (void)sprintf(filename, "%ssg%04p.dat", baseName, this); 1.176 + 1.177 + if (debug_mode) PR_fprintf(output, "Starting work on %s\n", filename); 1.178 + 1.179 + while (PR_TRUE) 1.180 + { 1.181 + PRUint64 bytes; 1.182 + PRUint32 minor = (Random() % limit) + 1; 1.183 + PRUint32 random = (Random() % limit) + 1; 1.184 + PRUint32 pages = (Random() % limit) + 10; 1.185 + while (minor-- > 0) 1.186 + { 1.187 + problem = sg_okay; 1.188 + if (action != sg_go) goto finished; 1.189 + problem = sg_open; 1.190 + rv = file.Open(filename, PR_RDWR|PR_CREATE_FILE, 0666); 1.191 + if (PR_FAILURE == rv) goto finished; 1.192 + for (index = 0; index < pages; index++) 1.193 + { 1.194 + problem = sg_okay; 1.195 + if (action != sg_go) goto close; 1.196 + problem = sg_seek; 1.197 + bytes = file.Seek(pageSize * index, RCFileIO::set); 1.198 + if (bytes != pageSize * index) goto close; 1.199 + problem = sg_write; 1.200 + bytes = file.Write(&zero, sizeof(zero)); 1.201 + if (bytes <= 0) goto close; 1.202 + writes += 1; 1.203 + } 1.204 + problem = sg_close; 1.205 + rv = file.Close(); 1.206 + if (rv != PR_SUCCESS) goto purge; 1.207 + 1.208 + problem = sg_okay; 1.209 + if (action != sg_go) goto purge; 1.210 + 1.211 + problem = sg_open; 1.212 + rv = file.Open(filename, PR_RDWR, 0666); 1.213 + if (PR_FAILURE == rv) goto finished; 1.214 + for (index = 0; index < pages; index++) 1.215 + { 1.216 + problem = sg_okay; 1.217 + if (action != sg_go) goto close; 1.218 + problem = sg_seek; 1.219 + bytes = file.Seek(pageSize * index, RCFileIO::set); 1.220 + if (bytes != pageSize * index) goto close; 1.221 + problem = sg_write; 1.222 + bytes = file.Write(&zero, sizeof(zero)); 1.223 + if (bytes <= 0) goto close; 1.224 + writes += 1; 1.225 + random = (random + 511) % pages; 1.226 + } 1.227 + problem = sg_close; 1.228 + rv = file.Close(); 1.229 + if (rv != PR_SUCCESS) goto purge; 1.230 + problem = sg_delete; 1.231 + rv = file.Delete(filename); 1.232 + if (rv != PR_SUCCESS) goto finished; 1.233 + } 1.234 + } 1.235 + 1.236 +close: 1.237 + (void)file.Close(); 1.238 +purge: 1.239 + (void)file.Delete(filename); 1.240 +finished: 1.241 + RCEnter scope(ml); 1.242 + action = HammerData::sg_done; 1.243 + cv->Notify(); 1.244 + 1.245 + if (debug_mode) PR_fprintf(output, "Ending work on %s\n", filename); 1.246 + 1.247 + return; 1.248 +} /* Hammer::RootFunction */ 1.249 + 1.250 +static Hammer* hammer[100]; 1.251 +/*********************************************************************** 1.252 +** PRIVATE FUNCTION: main 1.253 +** DESCRIPTION: 1.254 +** Hammer on the file I/O system 1.255 +** INPUTS: The usual argc and argv 1.256 +** argv[0] - program name (not used) 1.257 +** argv[1] - the number of virtual_procs to execute the major loop 1.258 +** argv[2] - the number of threads to toss into the batch 1.259 +** argv[3] - the clipping number applied to randoms 1.260 +** default values: max_virtual_procs = 2, threads = 10, limit = 57 1.261 +** OUTPUTS: None 1.262 +** RETURN: None 1.263 +** SIDE EFFECTS: 1.264 +** Creates, accesses and deletes lots of files 1.265 +** RESTRICTIONS: 1.266 +** (Currently) must have file create permission in "/usr/tmp". 1.267 +** MEMORY: NA 1.268 +** ALGORITHM: 1.269 +** 1) Fork a "Thread()" 1.270 +** 2) Wait for 'interleave' seconds 1.271 +** 3) For [0..'threads') repeat [1..2] 1.272 +** 4) Mark all objects to stop 1.273 +** 5) Collect the threads, accumulating the results 1.274 +** 6) For [0..'max_virtual_procs') repeat [1..5] 1.275 +** 7) Print accumulated results and exit 1.276 +** 1.277 +** Characteristic output (from IRIX) 1.278 +** Random File: Using max_virtual_procs = 2, threads = 10, limit = 57 1.279 +** Random File: [min [avg] max] writes/sec average 1.280 +***********************************************************************/ 1.281 +PRIntn main (PRIntn argc, char *argv[]) 1.282 +{ 1.283 + RCLock ml; 1.284 + PLOptStatus os; 1.285 + RCCondition cv(&ml); 1.286 + PRUint32 writesMax = 0, durationTot = 0; 1.287 + RCThread::Scope thread_scope = RCThread::local; 1.288 + PRUint32 writes, writesMin = 0x7fffffff, writesTot = 0; 1.289 + PRIntn active, poll, limit = 0, max_virtual_procs = 0, threads = 0, virtual_procs; 1.290 + RCInterval interleave(RCInterval::FromMilliseconds(10000)), duration(0); 1.291 + 1.292 + const char *where[] = {"okay", "open", "close", "delete", "write", "seek"}; 1.293 + 1.294 + PLOptState *opt = PL_CreateOptState(argc, argv, "Gdl:t:i:"); 1.295 + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) 1.296 + { 1.297 + if (PL_OPT_BAD == os) continue; 1.298 + switch (opt->option) 1.299 + { 1.300 + case 0: 1.301 + baseName = opt->value; 1.302 + break; 1.303 + case 'G': /* global threads */ 1.304 + thread_scope = RCThread::global; 1.305 + break; 1.306 + case 'd': /* debug mode */ 1.307 + debug_mode = 1; 1.308 + break; 1.309 + case 'l': /* limiting number */ 1.310 + limit = atoi(opt->value); 1.311 + break; 1.312 + case 't': /* number of threads */ 1.313 + threads = atoi(opt->value); 1.314 + break; 1.315 + case 'i': /* iteration counter */ 1.316 + max_virtual_procs = atoi(opt->value); 1.317 + break; 1.318 + default: 1.319 + break; 1.320 + } 1.321 + } 1.322 + PL_DestroyOptState(opt); 1.323 + output = PR_GetSpecialFD(PR_StandardOutput); 1.324 + 1.325 + /* main test */ 1.326 + 1.327 + cv.SetTimeout(interleave); 1.328 + 1.329 + if (max_virtual_procs == 0) max_virtual_procs = 2; 1.330 + if (limit == 0) limit = 57; 1.331 + if (threads == 0) threads = 10; 1.332 + 1.333 + if (debug_mode) PR_fprintf(output, 1.334 + "%s: Using %d virtual processors, %d threads, limit = %d and %s threads\n", 1.335 + programName, max_virtual_procs, threads, limit, 1.336 + (thread_scope == RCThread::local) ? "LOCAL" : "GLOBAL"); 1.337 + 1.338 + for (virtual_procs = 0; virtual_procs < max_virtual_procs; ++virtual_procs) 1.339 + { 1.340 + if (debug_mode) 1.341 + PR_fprintf(output, 1.342 + "%s: Setting number of virtual processors to %d\n", 1.343 + programName, virtual_procs + 1); 1.344 + RCPrimordialThread::SetVirtualProcessors(virtual_procs + 1); 1.345 + for (active = 0; active < threads; active++) 1.346 + { 1.347 + hammer[active] = new Hammer(thread_scope, &ml, &cv, limit); 1.348 + hammer[active]->Start(); /* then make it roll */ 1.349 + RCThread::Sleep(interleave); /* start them slowly */ 1.350 + } 1.351 + 1.352 + /* 1.353 + * The last thread started has had the opportunity to run for 1.354 + * 'interleave' seconds. Now gather them all back in. 1.355 + */ 1.356 + { 1.357 + RCEnter scope(&ml); 1.358 + for (poll = 0; poll < threads; poll++) 1.359 + { 1.360 + if (hammer[poll]->action == HammerData::sg_go) /* don't overwrite done */ 1.361 + hammer[poll]->action = HammerData::sg_stop; /* ask him to stop */ 1.362 + } 1.363 + } 1.364 + 1.365 + while (active > 0) 1.366 + { 1.367 + for (poll = 0; poll < threads; poll++) 1.368 + { 1.369 + ml.Acquire(); 1.370 + while (hammer[poll]->action < HammerData::sg_done) cv.Wait(); 1.371 + ml.Release(); 1.372 + 1.373 + if (hammer[poll]->problem == HammerData::sg_okay) 1.374 + { 1.375 + duration = RCInterval(RCInterval::now) - hammer[poll]->timein; 1.376 + writes = hammer[poll]->writes * 1000 / duration; 1.377 + if (writes < writesMin) writesMin = writes; 1.378 + if (writes > writesMax) writesMax = writes; 1.379 + writesTot += hammer[poll]->writes; 1.380 + durationTot += duration; 1.381 + } 1.382 + else 1.383 + { 1.384 + if (debug_mode) PR_fprintf(output, 1.385 + "%s: test failed %s after %ld seconds\n", 1.386 + programName, where[hammer[poll]->problem], duration); 1.387 + else failed_already=1; 1.388 + } 1.389 + active -= 1; /* this is another one down */ 1.390 + (void)hammer[poll]->Join(); 1.391 + hammer[poll] = NULL; 1.392 + } 1.393 + } 1.394 + if (debug_mode) PR_fprintf(output, 1.395 + "%s: [%ld [%ld] %ld] writes/sec average\n", 1.396 + programName, writesMin, 1.397 + writesTot * 1000 / durationTot, writesMax); 1.398 + } 1.399 + 1.400 + failed_already |= (PR_FAILURE == RCPrimordialThread::Cleanup()); 1.401 + PR_fprintf(output, "%s\n", (failed_already) ? "FAIL\n" : "PASS\n"); 1.402 + return failed_already; 1.403 +} /* main */