Back to index

lightning-sunbird  0.9+nobinonly
ranfile.c
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<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 
00062 #include "prinit.h"
00063 #include "prthread.h"
00064 #include "prlock.h"
00065 #include "prcvar.h"
00066 #include "prmem.h"
00067 #include "prinrval.h"
00068 #include "prio.h"
00069 
00070 #include <string.h>
00071 #include <stdio.h>
00072 
00073 static PRIntn debug_mode = 0;
00074 static PRIntn failed_already=0;
00075 static PRThreadScope thread_scope = PR_LOCAL_THREAD;
00076 
00077 typedef enum {sg_go, sg_stop, sg_done} Action;
00078 typedef enum {sg_okay, sg_open, sg_close, sg_delete, sg_write, sg_seek} Problem;
00079 
00080 typedef struct Hammer_s {
00081     PRLock *ml;
00082     PRCondVar *cv;
00083     PRUint32 id;
00084     PRUint32 limit;
00085     PRUint32 writes;
00086     PRThread *thread;
00087     PRIntervalTime timein;
00088     Action action;
00089     Problem problem;
00090 } Hammer_t;
00091 
00092 #define DEFAULT_LIMIT              10
00093 #define DEFAULT_THREADS            2
00094 #define DEFAULT_LOOPS              1
00095 
00096 static PRInt32 pageSize = 1024;
00097 static const char* baseName = "./";
00098 static const char *programName = "Random File";
00099 
00100 #ifdef XP_MAC
00101 #include "prlog.h"
00102 #define printf PR_LogPrint
00103 extern void SetupMacPrintfLog(char *logFile);
00104 #endif
00105 
00106 /***********************************************************************
00107 ** PRIVATE FUNCTION:    Random
00108 ** DESCRIPTION:
00109 **   Generate a pseudo-random number
00110 ** INPUTS:      None
00111 ** OUTPUTS:     None
00112 ** RETURN:      A pseudo-random unsigned number, 32-bits wide
00113 ** SIDE EFFECTS:
00114 **      Updates random seed (a static)
00115 ** RESTRICTIONS:
00116 **      None
00117 ** MEMORY:      NA
00118 ** ALGORITHM:
00119 **      Uses the current interval timer value, promoted to a 64 bit
00120 **      float as a multiplier for a static residue (which begins
00121 **      as an uninitialized variable). The result is bits [16..48)
00122 **      of the product. Seed is then updated with the return value
00123 **      promoted to a float-64.
00124 ***********************************************************************/
00125 static PRUint32 Random(void)
00126 {
00127     PRUint32 rv;
00128     PRUint64 shift;
00129     static PRFloat64 seed = 0x58a9382;  /* Just make sure it isn't 0! */
00130     PRFloat64 random = seed * (PRFloat64)PR_IntervalNow();
00131     LL_USHR(shift, *((PRUint64*)&random), 16);
00132     LL_L2UI(rv, shift);
00133     seed = (PRFloat64)rv;
00134     return rv;
00135 }  /* Random */
00136 
00137 /***********************************************************************
00138 ** PRIVATE FUNCTION:    Thread
00139 ** DESCRIPTION:
00140 **   Hammer on the file I/O system
00141 ** INPUTS:      A pointer to the thread's private data
00142 ** OUTPUTS:     None
00143 ** RETURN:      None
00144 ** SIDE EFFECTS:
00145 **      Creates, accesses and deletes a file
00146 ** RESTRICTIONS:
00147 **      (Currently) must have file create permission in "/usr/tmp".
00148 ** MEMORY:      NA
00149 ** ALGORITHM:
00150 **      This function is a root of a thread
00151 **      1) Creates a (hopefully) unique file in /usr/tmp/
00152 **      2) Writes a zero to a random number of sequential pages
00153 **      3) Closes the file
00154 **      4) Reopens the file
00155 **      5) Seeks to a random page within the file
00156 **      6) Writes a one byte on that page
00157 **      7) Repeat steps [5..6] for each page in the file
00158 **      8) Close and delete the file
00159 **      9) Repeat steps [1..8] until told to stop
00160 **     10) Notify complete and return
00161 ***********************************************************************/
00162 static void PR_CALLBACK Thread(void *arg)
00163 {
00164     PRUint32 index;
00165     char filename[30];
00166     const char zero = 0;
00167     PRFileDesc *file = NULL;
00168     PRStatus rv = PR_SUCCESS;
00169     Hammer_t *cd = (Hammer_t*)arg;
00170 
00171     (void)sprintf(filename, "%ssg%04ld.dat", baseName, cd->id);
00172 
00173     if (debug_mode) printf("Starting work on %s\n", filename);
00174 
00175     while (PR_TRUE)
00176     {
00177         PRUint32 bytes;
00178         PRUint32 minor = (Random() % cd->limit) + 1;
00179         PRUint32 random = (Random() % cd->limit) + 1;
00180         PRUint32 pages = (Random() % cd->limit) + 10;
00181         while (minor-- > 0)
00182         {
00183             cd->problem = sg_okay;
00184             if (cd->action != sg_go) goto finished;
00185             cd->problem = sg_open;
00186             file = PR_Open(filename, PR_RDWR|PR_CREATE_FILE, 0666);
00187             if (file == NULL) goto finished;
00188             for (index = 0; index < pages; index++)
00189             {
00190                 cd->problem = sg_okay;
00191                 if (cd->action != sg_go) goto close;
00192                 cd->problem = sg_seek;
00193                 bytes = PR_Seek(file, pageSize * index, PR_SEEK_SET);
00194                 if (bytes != pageSize * index) goto close;
00195                 cd->problem = sg_write;
00196                 bytes = PR_Write(file, &zero, sizeof(zero));
00197                 if (bytes <= 0) goto close;
00198                 cd->writes += 1;
00199             }
00200             cd->problem = sg_close;
00201             rv = PR_Close(file);
00202             if (rv != PR_SUCCESS) goto purge;
00203 
00204             cd->problem = sg_okay;
00205             if (cd->action != sg_go) goto purge;
00206 
00207             cd->problem = sg_open;
00208             file = PR_Open(filename, PR_RDWR, 0666);
00209             for (index = 0; index < pages; index++)
00210             {
00211                 cd->problem = sg_okay;
00212                 if (cd->action != sg_go) goto close;
00213                 cd->problem = sg_seek;
00214                 bytes = PR_Seek(file, pageSize * index, PR_SEEK_SET);
00215                 if (bytes != pageSize * index) goto close;
00216                 cd->problem = sg_write;
00217                 bytes = PR_Write(file, &zero, sizeof(zero));
00218                 if (bytes <= 0) goto close;
00219                 cd->writes += 1;
00220                 random = (random + 511) % pages;
00221             }
00222             cd->problem = sg_close;
00223             rv = PR_Close(file);
00224             if (rv != PR_SUCCESS) goto purge;
00225             cd->problem = sg_delete;
00226             rv = PR_Delete(filename);
00227             if (rv != PR_SUCCESS) goto finished;
00228        }
00229     }
00230 
00231 close:
00232     (void)PR_Close(file);
00233 purge:
00234     (void)PR_Delete(filename);
00235 finished:
00236     PR_Lock(cd->ml);
00237     cd->action = sg_done;
00238     PR_NotifyCondVar(cd->cv);
00239     PR_Unlock(cd->ml);
00240 
00241     if (debug_mode) printf("Ending work on %s\n", filename);
00242 
00243     return;
00244 }  /* Thread */
00245 
00246 static Hammer_t hammer[100];
00247 static PRCondVar *cv;
00248 /***********************************************************************
00249 ** PRIVATE FUNCTION:    main
00250 ** DESCRIPTION:
00251 **   Hammer on the file I/O system
00252 ** INPUTS:      The usual argc and argv
00253 **              argv[0] - program name (not used)
00254 **              argv[1] - the number of times to execute the major loop
00255 **              argv[2] - the number of threads to toss into the batch
00256 **              argv[3] - the clipping number applied to randoms
00257 **              default values: loops = 2, threads = 10, limit = 57
00258 ** OUTPUTS:     None
00259 ** RETURN:      None
00260 ** SIDE EFFECTS:
00261 **      Creates, accesses and deletes lots of files
00262 ** RESTRICTIONS:
00263 **      (Currently) must have file create permission in "/usr/tmp".
00264 ** MEMORY:      NA
00265 ** ALGORITHM:
00266 **      1) Fork a "Thread()"
00267 **      2) Wait for 'interleave' seconds
00268 **      3) For [0..'threads') repeat [1..2]
00269 **      4) Mark all objects to stop
00270 **      5) Collect the threads, accumulating the results
00271 **      6) For [0..'loops') repeat [1..5]
00272 **      7) Print accumulated results and exit
00273 **
00274 **      Characteristic output (from IRIX)
00275 **          Random File: Using loops = 2, threads = 10, limit = 57
00276 **          Random File: [min [avg] max] writes/sec average
00277 ***********************************************************************/
00278 int main (int argc,      char   *argv[])
00279 {
00280     PRLock *ml;
00281     PRUint32 id = 0;
00282     int active, poll;
00283     PRIntervalTime interleave;
00284     PRIntervalTime duration = 0;
00285     int limit = 0, loops = 0, threads = 0, times;
00286     PRUint32 writes, writesMin = 0x7fffffff, writesTot = 0, durationTot = 0, writesMax = 0;
00287 
00288     const char *where[] = {"okay", "open", "close", "delete", "write", "seek"};
00289 
00290        /* The command line argument: -d is used to determine if the test is being run
00291        in debug mode. The regress tool requires only one line output:PASS or FAIL.
00292        All of the printfs associated with this test has been handled with a if (debug_mode)
00293        test.
00294        Usage: test_name -d
00295        */
00296        PLOptStatus os;
00297        PLOptState *opt = PL_CreateOptState(argc, argv, "Gdl:t:i:");
00298        while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
00299     {
00300               if (PL_OPT_BAD == os) continue;
00301         switch (opt->option)
00302         {
00303         case 'G':  /* global threads */
00304                      thread_scope = PR_GLOBAL_THREAD;
00305             break;
00306         case 'd':  /* debug mode */
00307                      debug_mode = 1;
00308             break;
00309         case 'l':  /* limiting number */
00310                      limit = atoi(opt->value);
00311             break;
00312         case 't':  /* number of threads */
00313                      threads = atoi(opt->value);
00314             break;
00315         case 'i':  /* iteration counter */
00316                      loops = atoi(opt->value);
00317             break;
00318          default:
00319             break;
00320         }
00321     }
00322        PL_DestroyOptState(opt);
00323 
00324  /* main test */
00325        
00326     PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
00327     PR_STDIO_INIT();
00328 
00329     interleave = PR_SecondsToInterval(10);
00330 
00331 #ifdef XP_MAC
00332        SetupMacPrintfLog("ranfile.log");
00333        debug_mode = 1;
00334 #endif
00335 
00336     ml = PR_NewLock();
00337     cv = PR_NewCondVar(ml);
00338 
00339     if (loops == 0) loops = DEFAULT_LOOPS;
00340     if (limit == 0) limit = DEFAULT_LIMIT;
00341     if (threads == 0) threads = DEFAULT_THREADS;
00342 
00343     if (debug_mode) printf(
00344         "%s: Using loops = %d, threads = %d, limit = %d and %s threads\n",
00345         programName, loops, threads, limit,
00346         (thread_scope == PR_LOCAL_THREAD) ? "LOCAL" : "GLOBAL");
00347 
00348     for (times = 0; times < loops; ++times)
00349     {
00350         if (debug_mode) printf("%s: Setting concurrency level to %d\n", programName, times + 1);
00351         PR_SetConcurrency(times + 1);
00352         for (active = 0; active < threads; active++)
00353         {
00354             hammer[active].ml = ml;
00355             hammer[active].cv = cv;
00356             hammer[active].id = id++;
00357             hammer[active].writes = 0;
00358             hammer[active].action = sg_go;
00359             hammer[active].problem = sg_okay;
00360             hammer[active].limit = (Random() % limit) + 1;
00361             hammer[active].timein = PR_IntervalNow();
00362             hammer[active].thread = PR_CreateThread(
00363                 PR_USER_THREAD, Thread, &hammer[active],
00364                 PR_GetThreadPriority(PR_GetCurrentThread()),
00365                 thread_scope, PR_JOINABLE_THREAD, 0);
00366 
00367             PR_Lock(ml);
00368             PR_WaitCondVar(cv, interleave);  /* start new ones slowly */
00369             PR_Unlock(ml);
00370         }
00371 
00372         /*
00373          * The last thread started has had the opportunity to run for
00374          * 'interleave' seconds. Now gather them all back in.
00375          */
00376         PR_Lock(ml);
00377         for (poll = 0; poll < threads; poll++)
00378         {
00379             if (hammer[poll].action == sg_go)  /* don't overwrite done */
00380                 hammer[poll].action = sg_stop;  /* ask him to stop */
00381         }
00382         PR_Unlock(ml);
00383 
00384         while (active > 0)
00385         {
00386             for (poll = 0; poll < threads; poll++)
00387             {
00388                 PR_Lock(ml);
00389                 while (hammer[poll].action < sg_done)
00390                     PR_WaitCondVar(cv, PR_INTERVAL_NO_TIMEOUT);
00391                 PR_Unlock(ml);
00392 
00393                 active -= 1;  /* this is another one down */
00394                 (void)PR_JoinThread(hammer[poll].thread);
00395                 hammer[poll].thread = NULL;
00396                 if (hammer[poll].problem == sg_okay)
00397                 {
00398                     duration = PR_IntervalToMilliseconds(
00399                         PR_IntervalNow() - hammer[poll].timein);
00400                     writes = hammer[poll].writes * 1000 / duration;
00401                     if (writes < writesMin) 
00402                         writesMin = writes;
00403                     if (writes > writesMax) 
00404                         writesMax = writes;
00405                     writesTot += hammer[poll].writes;
00406                     durationTot += duration;
00407                 }
00408                 else
00409                     if (debug_mode) printf(
00410                         "%s: test failed %s after %ld seconds\n",
00411                         programName, where[hammer[poll].problem], duration);
00412                                    else failed_already=1;
00413             }
00414         }
00415     }
00416     if (debug_mode) printf(
00417         "%s: [%ld [%ld] %ld] writes/sec average\n",
00418         programName, writesMin, writesTot * 1000 / durationTot, writesMax);
00419 
00420     PR_DestroyCondVar(cv);
00421     PR_DestroyLock(ml);
00422 
00423        if (failed_already) 
00424        {
00425            printf("FAIL\n");
00426               return 1;
00427        }
00428     else
00429     {
00430         printf("PASS\n");
00431               return 0;
00432     }
00433 }  /* main */