nsprpub/pr/tests/ranfile.c

changeset 0
6474c204b198
     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 */

mercurial