Back to index

lightning-sunbird  0.9+nobinonly
ranfile.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is the Netscape Portable Runtime (NSPR).
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998-2000
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 /***********************************************************************
00039 **
00040 ** Contact:     AOF<mailto:freier@netscape.com>
00041 **
00042 ** Name: ranfile.c
00043 **
00044 ** Description: Test to hammer on various components of NSPR
00045 ** Modification History:
00046 ** 20-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
00047 **              The debug mode will print all of the printfs associated with this test.
00048 **                    The regress mode will be the default mode. Since the regress tool limits
00049 **           the output to a one line status:PASS or FAIL,all of the printf statements
00050 **                    have been handled with an if (debug_mode) statement.
00051 ** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
00052 **                   recognize the return code from tha main program.
00053 ***********************************************************************/
00054 
00055 
00056 /***********************************************************************
00057 ** Includes
00058 ***********************************************************************/
00059 /* Used to get the command line option */
00060 #include <plgetopt.h>
00061 #include <prprf.h>
00062 #include <prio.h>
00063 
00064 #include "rccv.h"
00065 #include "rcthread.h"
00066 #include "rcfileio.h"
00067 #include "rclock.h"
00068 
00069 #include <string.h>
00070 #include <stdio.h>
00071 #include <stdlib.h>
00072 
00073 static PRFileDesc *output;
00074 static PRIntn debug_mode = 0;
00075 static PRIntn failed_already = 0;
00076 
00077 class HammerData
00078 {
00079 public:
00080     typedef enum {
00081         sg_go, sg_stop, sg_done} Action;
00082     typedef enum {
00083         sg_okay, sg_open, sg_close, sg_delete, sg_write, sg_seek} Problem;
00084 
00085        virtual ~HammerData();
00086        HammerData(RCLock* lock, RCCondition *cond, PRUint32 clip);
00087     virtual PRUint32 Random();
00088 
00089     Action action;
00090     Problem problem;
00091     PRUint32 writes;
00092     RCInterval timein;
00093 friend class Hammer;
00094 private:
00095     RCLock *ml;
00096     RCCondition *cv;
00097     PRUint32 limit;
00098 
00099     PRFloat64 seed;
00100 };  /* HammerData */
00101 
00102 class Hammer: public HammerData, public RCThread
00103 {
00104 public:
00105     virtual ~Hammer();
00106     Hammer(RCThread::Scope scope, RCLock* lock, RCCondition *cond, PRUint32 clip);
00107 
00108 private:
00109     void RootFunction();
00110 
00111 };
00112 
00113 static PRInt32 pageSize = 1024;
00114 static const char* baseName = "./";
00115 static const char *programName = "Random File";
00116 
00117 /***********************************************************************
00118 ** PRIVATE FUNCTION:    Random
00119 ** DESCRIPTION:
00120 **   Generate a pseudo-random number
00121 ** INPUTS:      None
00122 ** OUTPUTS:     None
00123 ** RETURN:      A pseudo-random unsigned number, 32-bits wide
00124 ** SIDE EFFECTS:
00125 **      Updates random seed (a static)
00126 ** RESTRICTIONS:
00127 **      None
00128 ** MEMORY:      NA
00129 ** ALGORITHM:
00130 **      Uses the current interval timer value, promoted to a 64 bit
00131 **      float as a multiplier for a static residue (which begins
00132 **      as an uninitialized variable). The result is bits [16..48)
00133 **      of the product. Seed is then updated with the return value
00134 **      promoted to a float-64.
00135 ***********************************************************************/
00136 PRUint32 HammerData::Random()
00137 {
00138     PRUint32 rv;
00139     PRUint64 shift;
00140     RCInterval now = RCInterval(RCInterval::now);
00141     PRFloat64 random = seed * (PRFloat64)((PRIntervalTime)now);
00142     LL_USHR(shift, *((PRUint64*)&random), 16);
00143     LL_L2UI(rv, shift);
00144     seed = (PRFloat64)rv;
00145     return rv;
00146 }  /* HammerData::Random */
00147 
00148 Hammer::~Hammer() { }
00149 
00150 Hammer::Hammer(
00151     RCThread::Scope scope, RCLock* lock, RCCondition *cond, PRUint32 clip):
00152        HammerData(lock, cond, clip), RCThread(scope, RCThread::joinable, 0) { }
00153 
00154 HammerData::~HammerData() { }
00155 
00156 HammerData::HammerData(RCLock* lock, RCCondition *cond, PRUint32 clip)
00157 {
00158     ml = lock;
00159     cv = cond;
00160     writes = 0;
00161     limit = clip;
00162     seed = 0x58a9382;
00163     action = HammerData::sg_go;
00164     problem = HammerData::sg_okay;
00165     timein = RCInterval(RCInterval::now);
00166 }  /* HammerData::HammerData */
00167 
00168 
00169 /***********************************************************************
00170 ** PRIVATE FUNCTION:    Hammer::RootFunction
00171 ** DESCRIPTION:
00172 **   Hammer on the file I/O system
00173 ** INPUTS:      A pointer to the thread's private data
00174 ** OUTPUTS:     None
00175 ** RETURN:      None
00176 ** SIDE EFFECTS:
00177 **      Creates, accesses and deletes a file
00178 ** RESTRICTIONS:
00179 **      (Currently) must have file create permission in "/usr/tmp".
00180 ** MEMORY:      NA
00181 ** ALGORITHM:
00182 **      This function is a root of a thread
00183 **      1) Creates a (hopefully) unique file in /usr/tmp/
00184 **      2) Writes a zero to a random number of sequential pages
00185 **      3) Closes the file
00186 **      4) Reopens the file
00187 **      5) Seeks to a random page within the file
00188 **      6) Writes a one byte on that page
00189 **      7) Repeat steps [5..6] for each page in the file
00190 **      8) Close and delete the file
00191 **      9) Repeat steps [1..8] until told to stop
00192 **     10) Notify complete and return
00193 ***********************************************************************/
00194 void Hammer::RootFunction()
00195 {
00196     PRUint32 index;
00197     RCFileIO file;
00198     char filename[30];
00199     const char zero = 0;
00200     PRStatus rv = PR_SUCCESS;
00201 
00202     limit = (Random() % limit) + 1;
00203 
00204     (void)sprintf(filename, "%ssg%04p.dat", baseName, this);
00205 
00206     if (debug_mode) PR_fprintf(output, "Starting work on %s\n", filename);
00207 
00208     while (PR_TRUE)
00209     {
00210         PRUint64 bytes;
00211         PRUint32 minor = (Random() % limit) + 1;
00212         PRUint32 random = (Random() % limit) + 1;
00213         PRUint32 pages = (Random() % limit) + 10;
00214         while (minor-- > 0)
00215         {
00216             problem = sg_okay;
00217             if (action != sg_go) goto finished;
00218             problem = sg_open;
00219             rv = file.Open(filename, PR_RDWR|PR_CREATE_FILE, 0666);
00220             if (PR_FAILURE == rv) goto finished;
00221             for (index = 0; index < pages; index++)
00222             {
00223                 problem = sg_okay;
00224                 if (action != sg_go) goto close;
00225                 problem = sg_seek;
00226                 bytes = file.Seek(pageSize * index, RCFileIO::set);
00227                 if (bytes != pageSize * index) goto close;
00228                 problem = sg_write;
00229                 bytes = file.Write(&zero, sizeof(zero));
00230                 if (bytes <= 0) goto close;
00231                 writes += 1;
00232             }
00233             problem = sg_close;
00234             rv = file.Close();
00235             if (rv != PR_SUCCESS) goto purge;
00236 
00237             problem = sg_okay;
00238             if (action != sg_go) goto purge;
00239 
00240             problem = sg_open;
00241             rv = file.Open(filename, PR_RDWR, 0666);
00242             if (PR_FAILURE == rv) goto finished;
00243             for (index = 0; index < pages; index++)
00244             {
00245                 problem = sg_okay;
00246                 if (action != sg_go) goto close;
00247                 problem = sg_seek;
00248                 bytes = file.Seek(pageSize * index, RCFileIO::set);
00249                 if (bytes != pageSize * index) goto close;
00250                 problem = sg_write;
00251                 bytes = file.Write(&zero, sizeof(zero));
00252                 if (bytes <= 0) goto close;
00253                 writes += 1;
00254                 random = (random + 511) % pages;
00255             }
00256             problem = sg_close;
00257             rv = file.Close();
00258             if (rv != PR_SUCCESS) goto purge;
00259             problem = sg_delete;
00260             rv = file.Delete(filename);
00261             if (rv != PR_SUCCESS) goto finished;
00262        }
00263     }
00264 
00265 close:
00266     (void)file.Close();
00267 purge:
00268     (void)file.Delete(filename);
00269 finished:
00270     RCEnter scope(ml);
00271     action = HammerData::sg_done;
00272     cv->Notify();
00273 
00274     if (debug_mode) PR_fprintf(output, "Ending work on %s\n", filename);
00275 
00276     return;
00277 }  /* Hammer::RootFunction */
00278 
00279 static Hammer* hammer[100];
00280 /***********************************************************************
00281 ** PRIVATE FUNCTION:    main
00282 ** DESCRIPTION:
00283 **   Hammer on the file I/O system
00284 ** INPUTS:      The usual argc and argv
00285 **              argv[0] - program name (not used)
00286 **              argv[1] - the number of virtual_procs to execute the major loop
00287 **              argv[2] - the number of threads to toss into the batch
00288 **              argv[3] - the clipping number applied to randoms
00289 **              default values: max_virtual_procs = 2, threads = 10, limit = 57
00290 ** OUTPUTS:     None
00291 ** RETURN:      None
00292 ** SIDE EFFECTS:
00293 **      Creates, accesses and deletes lots of files
00294 ** RESTRICTIONS:
00295 **      (Currently) must have file create permission in "/usr/tmp".
00296 ** MEMORY:      NA
00297 ** ALGORITHM:
00298 **      1) Fork a "Thread()"
00299 **      2) Wait for 'interleave' seconds
00300 **      3) For [0..'threads') repeat [1..2]
00301 **      4) Mark all objects to stop
00302 **      5) Collect the threads, accumulating the results
00303 **      6) For [0..'max_virtual_procs') repeat [1..5]
00304 **      7) Print accumulated results and exit
00305 **
00306 **      Characteristic output (from IRIX)
00307 **          Random File: Using max_virtual_procs = 2, threads = 10, limit = 57
00308 **          Random File: [min [avg] max] writes/sec average
00309 ***********************************************************************/
00310 PRIntn main (PRIntn argc, char *argv[])
00311 {
00312     RCLock ml;
00313        PLOptStatus os;
00314     RCCondition cv(&ml);
00315     PRUint32 writesMax = 0, durationTot = 0;
00316     RCThread::Scope thread_scope = RCThread::local;
00317     PRUint32 writes, writesMin = 0x7fffffff, writesTot = 0;
00318     PRIntn active, poll, limit = 0, max_virtual_procs = 0, threads = 0, virtual_procs;
00319     RCInterval interleave(RCInterval::FromMilliseconds(10000)), duration(0);
00320 
00321     const char *where[] = {"okay", "open", "close", "delete", "write", "seek"};
00322 
00323        PLOptState *opt = PL_CreateOptState(argc, argv, "Gdl:t:i:");
00324        while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
00325     {
00326               if (PL_OPT_BAD == os) continue;
00327         switch (opt->option)
00328         {
00329        case 0:
00330               baseName = opt->value;
00331               break;
00332         case 'G':  /* global threads */
00333               thread_scope = RCThread::global;
00334             break;
00335         case 'd':  /* debug mode */
00336                      debug_mode = 1;
00337             break;
00338         case 'l':  /* limiting number */
00339                      limit = atoi(opt->value);
00340             break;
00341         case 't':  /* number of threads */
00342                      threads = atoi(opt->value);
00343             break;
00344         case 'i':  /* iteration counter */
00345                      max_virtual_procs = atoi(opt->value);
00346             break;
00347          default:
00348             break;
00349         }
00350     }
00351        PL_DestroyOptState(opt);
00352     output = PR_GetSpecialFD(PR_StandardOutput);
00353 
00354  /* main test */
00355  
00356     cv.SetTimeout(interleave);
00357        
00358     if (max_virtual_procs == 0) max_virtual_procs = 2;
00359     if (limit == 0) limit = 57;
00360     if (threads == 0) threads = 10;
00361 
00362     if (debug_mode) PR_fprintf(output,
00363         "%s: Using %d virtual processors, %d threads, limit = %d and %s threads\n",
00364         programName, max_virtual_procs, threads, limit,
00365         (thread_scope == RCThread::local) ? "LOCAL" : "GLOBAL");
00366 
00367     for (virtual_procs = 0; virtual_procs < max_virtual_procs; ++virtual_procs)
00368     {
00369         if (debug_mode)
00370                      PR_fprintf(output,
00371                             "%s: Setting number of virtual processors to %d\n",
00372                             programName, virtual_procs + 1);
00373               RCPrimordialThread::SetVirtualProcessors(virtual_procs + 1);
00374         for (active = 0; active < threads; active++)
00375         {
00376             hammer[active] = new Hammer(thread_scope, &ml, &cv, limit);
00377             hammer[active]->Start();  /* then make it roll */
00378             RCThread::Sleep(interleave);  /* start them slowly */
00379         }
00380 
00381         /*
00382          * The last thread started has had the opportunity to run for
00383          * 'interleave' seconds. Now gather them all back in.
00384          */
00385         {
00386             RCEnter scope(&ml);
00387             for (poll = 0; poll < threads; poll++)
00388             {
00389                 if (hammer[poll]->action == HammerData::sg_go)  /* don't overwrite done */
00390                     hammer[poll]->action = HammerData::sg_stop;  /* ask him to stop */
00391             }
00392         }
00393 
00394         while (active > 0)
00395         {
00396             for (poll = 0; poll < threads; poll++)
00397             {
00398                 ml.Acquire();
00399                 while (hammer[poll]->action < HammerData::sg_done) cv.Wait();
00400                 ml.Release();
00401 
00402                 if (hammer[poll]->problem == HammerData::sg_okay)
00403                 {
00404                     duration = RCInterval(RCInterval::now) - hammer[poll]->timein;
00405                     writes = hammer[poll]->writes * 1000 / duration;
00406                     if (writes < writesMin)  writesMin = writes;
00407                     if (writes > writesMax) writesMax = writes;
00408                     writesTot += hammer[poll]->writes;
00409                     durationTot += duration;
00410                 }
00411                 else
00412                 {
00413                     if (debug_mode) PR_fprintf(output,
00414                         "%s: test failed %s after %ld seconds\n",
00415                         programName, where[hammer[poll]->problem], duration);
00416                                    else failed_already=1;
00417                 }
00418                 active -= 1;  /* this is another one down */
00419                 (void)hammer[poll]->Join();
00420                 hammer[poll] = NULL;
00421             }
00422         }
00423         if (debug_mode) PR_fprintf(output,
00424             "%s: [%ld [%ld] %ld] writes/sec average\n",
00425             programName, writesMin,
00426             writesTot * 1000 / durationTot, writesMax);
00427     }
00428 
00429         failed_already |= (PR_FAILURE == RCPrimordialThread::Cleanup());
00430            PR_fprintf(output, "%s\n", (failed_already) ? "FAIL\n" : "PASS\n");
00431               return failed_already;
00432 }  /* main */