1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/nsprpub/pr/tests/ranfile.c Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,390 @@ 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<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 + 1.33 +#include "prinit.h" 1.34 +#include "prthread.h" 1.35 +#include "prlock.h" 1.36 +#include "prcvar.h" 1.37 +#include "prmem.h" 1.38 +#include "prinrval.h" 1.39 +#include "prio.h" 1.40 + 1.41 +#include <string.h> 1.42 +#include <stdio.h> 1.43 + 1.44 +static PRIntn debug_mode = 0; 1.45 +static PRIntn failed_already=0; 1.46 +static PRThreadScope thread_scope = PR_LOCAL_THREAD; 1.47 + 1.48 +typedef enum {sg_go, sg_stop, sg_done} Action; 1.49 +typedef enum {sg_okay, sg_open, sg_close, sg_delete, sg_write, sg_seek} Problem; 1.50 + 1.51 +typedef struct Hammer_s { 1.52 + PRLock *ml; 1.53 + PRCondVar *cv; 1.54 + PRUint32 id; 1.55 + PRUint32 limit; 1.56 + PRUint32 writes; 1.57 + PRThread *thread; 1.58 + PRIntervalTime timein; 1.59 + Action action; 1.60 + Problem problem; 1.61 +} Hammer_t; 1.62 + 1.63 +#define DEFAULT_LIMIT 10 1.64 +#define DEFAULT_THREADS 2 1.65 +#define DEFAULT_LOOPS 1 1.66 + 1.67 +static PRInt32 pageSize = 1024; 1.68 +static const char* baseName = "./"; 1.69 +static const char *programName = "Random File"; 1.70 + 1.71 +/*********************************************************************** 1.72 +** PRIVATE FUNCTION: RandomNum 1.73 +** DESCRIPTION: 1.74 +** Generate a pseudo-random number 1.75 +** INPUTS: None 1.76 +** OUTPUTS: None 1.77 +** RETURN: A pseudo-random unsigned number, 32-bits wide 1.78 +** SIDE EFFECTS: 1.79 +** Updates random seed (a static) 1.80 +** RESTRICTIONS: 1.81 +** None 1.82 +** MEMORY: NA 1.83 +** ALGORITHM: 1.84 +** Uses the current interval timer value, promoted to a 64 bit 1.85 +** float as a multiplier for a static residue (which begins 1.86 +** as an uninitialized variable). The result is bits [16..48) 1.87 +** of the product. Seed is then updated with the return value 1.88 +** promoted to a float-64. 1.89 +***********************************************************************/ 1.90 +static PRUint32 RandomNum(void) 1.91 +{ 1.92 + PRUint32 rv; 1.93 + PRUint64 shift; 1.94 + static PRFloat64 seed = 0x58a9382; /* Just make sure it isn't 0! */ 1.95 + PRFloat64 random = seed * (PRFloat64)PR_IntervalNow(); 1.96 + LL_USHR(shift, *((PRUint64*)&random), 16); 1.97 + LL_L2UI(rv, shift); 1.98 + seed = (PRFloat64)rv; 1.99 + return rv; 1.100 +} /* RandomNum */ 1.101 + 1.102 +/*********************************************************************** 1.103 +** PRIVATE FUNCTION: Thread 1.104 +** DESCRIPTION: 1.105 +** Hammer on the file I/O system 1.106 +** INPUTS: A pointer to the thread's private data 1.107 +** OUTPUTS: None 1.108 +** RETURN: None 1.109 +** SIDE EFFECTS: 1.110 +** Creates, accesses and deletes a file 1.111 +** RESTRICTIONS: 1.112 +** (Currently) must have file create permission in "/usr/tmp". 1.113 +** MEMORY: NA 1.114 +** ALGORITHM: 1.115 +** This function is a root of a thread 1.116 +** 1) Creates a (hopefully) unique file in /usr/tmp/ 1.117 +** 2) Writes a zero to a random number of sequential pages 1.118 +** 3) Closes the file 1.119 +** 4) Reopens the file 1.120 +** 5) Seeks to a random page within the file 1.121 +** 6) Writes a one byte on that page 1.122 +** 7) Repeat steps [5..6] for each page in the file 1.123 +** 8) Close and delete the file 1.124 +** 9) Repeat steps [1..8] until told to stop 1.125 +** 10) Notify complete and return 1.126 +***********************************************************************/ 1.127 +static void PR_CALLBACK Thread(void *arg) 1.128 +{ 1.129 + PRUint32 index; 1.130 + char filename[30]; 1.131 + const char zero = 0; 1.132 + PRFileDesc *file = NULL; 1.133 + PRStatus rv = PR_SUCCESS; 1.134 + Hammer_t *cd = (Hammer_t*)arg; 1.135 + 1.136 + (void)sprintf(filename, "%ssg%04ld.dat", baseName, cd->id); 1.137 + 1.138 + if (debug_mode) printf("Starting work on %s\n", filename); 1.139 + 1.140 + while (PR_TRUE) 1.141 + { 1.142 + PRUint32 bytes; 1.143 + PRUint32 minor = (RandomNum() % cd->limit) + 1; 1.144 + PRUint32 random = (RandomNum() % cd->limit) + 1; 1.145 + PRUint32 pages = (RandomNum() % cd->limit) + 10; 1.146 + while (minor-- > 0) 1.147 + { 1.148 + cd->problem = sg_okay; 1.149 + if (cd->action != sg_go) goto finished; 1.150 + cd->problem = sg_open; 1.151 + file = PR_Open(filename, PR_RDWR|PR_CREATE_FILE, 0666); 1.152 + if (file == NULL) goto finished; 1.153 + for (index = 0; index < pages; index++) 1.154 + { 1.155 + cd->problem = sg_okay; 1.156 + if (cd->action != sg_go) goto close; 1.157 + cd->problem = sg_seek; 1.158 + bytes = PR_Seek(file, pageSize * index, PR_SEEK_SET); 1.159 + if (bytes != pageSize * index) goto close; 1.160 + cd->problem = sg_write; 1.161 + bytes = PR_Write(file, &zero, sizeof(zero)); 1.162 + if (bytes <= 0) goto close; 1.163 + cd->writes += 1; 1.164 + } 1.165 + cd->problem = sg_close; 1.166 + rv = PR_Close(file); 1.167 + if (rv != PR_SUCCESS) goto purge; 1.168 + 1.169 + cd->problem = sg_okay; 1.170 + if (cd->action != sg_go) goto purge; 1.171 + 1.172 + cd->problem = sg_open; 1.173 + file = PR_Open(filename, PR_RDWR, 0666); 1.174 + for (index = 0; index < pages; index++) 1.175 + { 1.176 + cd->problem = sg_okay; 1.177 + if (cd->action != sg_go) goto close; 1.178 + cd->problem = sg_seek; 1.179 + bytes = PR_Seek(file, pageSize * index, PR_SEEK_SET); 1.180 + if (bytes != pageSize * index) goto close; 1.181 + cd->problem = sg_write; 1.182 + bytes = PR_Write(file, &zero, sizeof(zero)); 1.183 + if (bytes <= 0) goto close; 1.184 + cd->writes += 1; 1.185 + random = (random + 511) % pages; 1.186 + } 1.187 + cd->problem = sg_close; 1.188 + rv = PR_Close(file); 1.189 + if (rv != PR_SUCCESS) goto purge; 1.190 + cd->problem = sg_delete; 1.191 + rv = PR_Delete(filename); 1.192 + if (rv != PR_SUCCESS) goto finished; 1.193 + } 1.194 + } 1.195 + 1.196 +close: 1.197 + (void)PR_Close(file); 1.198 +purge: 1.199 + (void)PR_Delete(filename); 1.200 +finished: 1.201 + PR_Lock(cd->ml); 1.202 + cd->action = sg_done; 1.203 + PR_NotifyCondVar(cd->cv); 1.204 + PR_Unlock(cd->ml); 1.205 + 1.206 + if (debug_mode) printf("Ending work on %s\n", filename); 1.207 + 1.208 + return; 1.209 +} /* Thread */ 1.210 + 1.211 +static Hammer_t hammer[100]; 1.212 +static PRCondVar *cv; 1.213 +/*********************************************************************** 1.214 +** PRIVATE FUNCTION: main 1.215 +** DESCRIPTION: 1.216 +** Hammer on the file I/O system 1.217 +** INPUTS: The usual argc and argv 1.218 +** argv[0] - program name (not used) 1.219 +** argv[1] - the number of times to execute the major loop 1.220 +** argv[2] - the number of threads to toss into the batch 1.221 +** argv[3] - the clipping number applied to randoms 1.222 +** default values: loops = 2, threads = 10, limit = 57 1.223 +** OUTPUTS: None 1.224 +** RETURN: None 1.225 +** SIDE EFFECTS: 1.226 +** Creates, accesses and deletes lots of files 1.227 +** RESTRICTIONS: 1.228 +** (Currently) must have file create permission in "/usr/tmp". 1.229 +** MEMORY: NA 1.230 +** ALGORITHM: 1.231 +** 1) Fork a "Thread()" 1.232 +** 2) Wait for 'interleave' seconds 1.233 +** 3) For [0..'threads') repeat [1..2] 1.234 +** 4) Mark all objects to stop 1.235 +** 5) Collect the threads, accumulating the results 1.236 +** 6) For [0..'loops') repeat [1..5] 1.237 +** 7) Print accumulated results and exit 1.238 +** 1.239 +** Characteristic output (from IRIX) 1.240 +** Random File: Using loops = 2, threads = 10, limit = 57 1.241 +** Random File: [min [avg] max] writes/sec average 1.242 +***********************************************************************/ 1.243 +int main(int argc, char **argv) 1.244 +{ 1.245 + PRLock *ml; 1.246 + PRUint32 id = 0; 1.247 + int active, poll; 1.248 + PRIntervalTime interleave; 1.249 + PRIntervalTime duration = 0; 1.250 + int limit = 0, loops = 0, threads = 0, times; 1.251 + PRUint32 writes, writesMin = 0x7fffffff, writesTot = 0, durationTot = 0, writesMax = 0; 1.252 + 1.253 + const char *where[] = {"okay", "open", "close", "delete", "write", "seek"}; 1.254 + 1.255 + /* The command line argument: -d is used to determine if the test is being run 1.256 + in debug mode. The regress tool requires only one line output:PASS or FAIL. 1.257 + All of the printfs associated with this test has been handled with a if (debug_mode) 1.258 + test. 1.259 + Usage: test_name -d 1.260 + */ 1.261 + PLOptStatus os; 1.262 + PLOptState *opt = PL_CreateOptState(argc, argv, "Gdl:t:i:"); 1.263 + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) 1.264 + { 1.265 + if (PL_OPT_BAD == os) continue; 1.266 + switch (opt->option) 1.267 + { 1.268 + case 'G': /* global threads */ 1.269 + thread_scope = PR_GLOBAL_THREAD; 1.270 + break; 1.271 + case 'd': /* debug mode */ 1.272 + debug_mode = 1; 1.273 + break; 1.274 + case 'l': /* limiting number */ 1.275 + limit = atoi(opt->value); 1.276 + break; 1.277 + case 't': /* number of threads */ 1.278 + threads = atoi(opt->value); 1.279 + break; 1.280 + case 'i': /* iteration counter */ 1.281 + loops = atoi(opt->value); 1.282 + break; 1.283 + default: 1.284 + break; 1.285 + } 1.286 + } 1.287 + PL_DestroyOptState(opt); 1.288 + 1.289 + /* main test */ 1.290 + 1.291 + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); 1.292 + PR_STDIO_INIT(); 1.293 + 1.294 + interleave = PR_SecondsToInterval(10); 1.295 + 1.296 + ml = PR_NewLock(); 1.297 + cv = PR_NewCondVar(ml); 1.298 + 1.299 + if (loops == 0) loops = DEFAULT_LOOPS; 1.300 + if (limit == 0) limit = DEFAULT_LIMIT; 1.301 + if (threads == 0) threads = DEFAULT_THREADS; 1.302 + 1.303 + if (debug_mode) printf( 1.304 + "%s: Using loops = %d, threads = %d, limit = %d and %s threads\n", 1.305 + programName, loops, threads, limit, 1.306 + (thread_scope == PR_LOCAL_THREAD) ? "LOCAL" : "GLOBAL"); 1.307 + 1.308 + for (times = 0; times < loops; ++times) 1.309 + { 1.310 + if (debug_mode) printf("%s: Setting concurrency level to %d\n", programName, times + 1); 1.311 + PR_SetConcurrency(times + 1); 1.312 + for (active = 0; active < threads; active++) 1.313 + { 1.314 + hammer[active].ml = ml; 1.315 + hammer[active].cv = cv; 1.316 + hammer[active].id = id++; 1.317 + hammer[active].writes = 0; 1.318 + hammer[active].action = sg_go; 1.319 + hammer[active].problem = sg_okay; 1.320 + hammer[active].limit = (RandomNum() % limit) + 1; 1.321 + hammer[active].timein = PR_IntervalNow(); 1.322 + hammer[active].thread = PR_CreateThread( 1.323 + PR_USER_THREAD, Thread, &hammer[active], 1.324 + PR_GetThreadPriority(PR_GetCurrentThread()), 1.325 + thread_scope, PR_JOINABLE_THREAD, 0); 1.326 + 1.327 + PR_Lock(ml); 1.328 + PR_WaitCondVar(cv, interleave); /* start new ones slowly */ 1.329 + PR_Unlock(ml); 1.330 + } 1.331 + 1.332 + /* 1.333 + * The last thread started has had the opportunity to run for 1.334 + * 'interleave' seconds. Now gather them all back in. 1.335 + */ 1.336 + PR_Lock(ml); 1.337 + for (poll = 0; poll < threads; poll++) 1.338 + { 1.339 + if (hammer[poll].action == sg_go) /* don't overwrite done */ 1.340 + hammer[poll].action = sg_stop; /* ask him to stop */ 1.341 + } 1.342 + PR_Unlock(ml); 1.343 + 1.344 + while (active > 0) 1.345 + { 1.346 + for (poll = 0; poll < threads; poll++) 1.347 + { 1.348 + PR_Lock(ml); 1.349 + while (hammer[poll].action < sg_done) 1.350 + PR_WaitCondVar(cv, PR_INTERVAL_NO_TIMEOUT); 1.351 + PR_Unlock(ml); 1.352 + 1.353 + active -= 1; /* this is another one down */ 1.354 + (void)PR_JoinThread(hammer[poll].thread); 1.355 + hammer[poll].thread = NULL; 1.356 + if (hammer[poll].problem == sg_okay) 1.357 + { 1.358 + duration = PR_IntervalToMilliseconds( 1.359 + PR_IntervalNow() - hammer[poll].timein); 1.360 + writes = hammer[poll].writes * 1000 / duration; 1.361 + if (writes < writesMin) 1.362 + writesMin = writes; 1.363 + if (writes > writesMax) 1.364 + writesMax = writes; 1.365 + writesTot += hammer[poll].writes; 1.366 + durationTot += duration; 1.367 + } 1.368 + else 1.369 + if (debug_mode) printf( 1.370 + "%s: test failed %s after %ld seconds\n", 1.371 + programName, where[hammer[poll].problem], duration); 1.372 + else failed_already=1; 1.373 + } 1.374 + } 1.375 + } 1.376 + if (debug_mode) printf( 1.377 + "%s: [%ld [%ld] %ld] writes/sec average\n", 1.378 + programName, writesMin, writesTot * 1000 / durationTot, writesMax); 1.379 + 1.380 + PR_DestroyCondVar(cv); 1.381 + PR_DestroyLock(ml); 1.382 + 1.383 + if (failed_already) 1.384 + { 1.385 + printf("FAIL\n"); 1.386 + return 1; 1.387 + } 1.388 + else 1.389 + { 1.390 + printf("PASS\n"); 1.391 + return 0; 1.392 + } 1.393 +} /* main */