tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 */