Upload Tizen:Base source
[toolchains/nspr.git] / mozilla / nsprpub / pr / src / cplus / tests / ranfile.cpp
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4  *
5  * The contents of this file are subject to the Mozilla Public License Version
6  * 1.1 (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  * http://www.mozilla.org/MPL/
9  *
10  * Software distributed under the License is distributed on an "AS IS" basis,
11  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12  * for the specific language governing rights and limitations under the
13  * License.
14  *
15  * The Original Code is the Netscape Portable Runtime (NSPR).
16  *
17  * The Initial Developer of the Original Code is
18  * Netscape Communications Corporation.
19  * Portions created by the Initial Developer are Copyright (C) 1998-2000
20  * the Initial Developer. All Rights Reserved.
21  *
22  * Contributor(s):
23  *
24  * Alternatively, the contents of this file may be used under the terms of
25  * either the GNU General Public License Version 2 or later (the "GPL"), or
26  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27  * in which case the provisions of the GPL or the LGPL are applicable instead
28  * of those above. If you wish to allow use of your version of this file only
29  * under the terms of either the GPL or the LGPL, and not to allow others to
30  * use your version of this file under the terms of the MPL, indicate your
31  * decision by deleting the provisions above and replace them with the notice
32  * and other provisions required by the GPL or the LGPL. If you do not delete
33  * the provisions above, a recipient may use your version of this file under
34  * the terms of any one of the MPL, the GPL or the LGPL.
35  *
36  * ***** END LICENSE BLOCK ***** */
37
38 /***********************************************************************
39 **
40 ** Contact:     AOF<mailto:freier@netscape.com>
41 **
42 ** Name: ranfile.c
43 **
44 ** Description: Test to hammer on various components of NSPR
45 ** Modification History:
46 ** 20-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
47 **               The debug mode will print all of the printfs associated with this test.
48 **                       The regress mode will be the default mode. Since the regress tool limits
49 **           the output to a one line status:PASS or FAIL,all of the printf statements
50 **                       have been handled with an if (debug_mode) statement.
51 ** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
52 **                      recognize the return code from tha main program.
53 ***********************************************************************/
54
55
56 /***********************************************************************
57 ** Includes
58 ***********************************************************************/
59 /* Used to get the command line option */
60 #include <plgetopt.h>
61 #include <prprf.h>
62 #include <prio.h>
63
64 #include "rccv.h"
65 #include "rcthread.h"
66 #include "rcfileio.h"
67 #include "rclock.h"
68
69 #include <string.h>
70 #include <stdio.h>
71 #include <stdlib.h>
72
73 static PRFileDesc *output;
74 static PRIntn debug_mode = 0;
75 static PRIntn failed_already = 0;
76
77 class HammerData
78 {
79 public:
80     typedef enum {
81         sg_go, sg_stop, sg_done} Action;
82     typedef enum {
83         sg_okay, sg_open, sg_close, sg_delete, sg_write, sg_seek} Problem;
84
85         virtual ~HammerData();
86         HammerData(RCLock* lock, RCCondition *cond, PRUint32 clip);
87     virtual PRUint32 Random();
88
89     Action action;
90     Problem problem;
91     PRUint32 writes;
92     RCInterval timein;
93 friend class Hammer;
94 private:
95     RCLock *ml;
96     RCCondition *cv;
97     PRUint32 limit;
98
99     PRFloat64 seed;
100 };  /* HammerData */
101
102 class Hammer: public HammerData, public RCThread
103 {
104 public:
105     virtual ~Hammer();
106     Hammer(RCThread::Scope scope, RCLock* lock, RCCondition *cond, PRUint32 clip);
107
108 private:
109     void RootFunction();
110
111 };
112
113 static PRInt32 pageSize = 1024;
114 static const char* baseName = "./";
115 static const char *programName = "Random File";
116
117 /***********************************************************************
118 ** PRIVATE FUNCTION:    Random
119 ** DESCRIPTION:
120 **   Generate a pseudo-random number
121 ** INPUTS:      None
122 ** OUTPUTS:     None
123 ** RETURN:      A pseudo-random unsigned number, 32-bits wide
124 ** SIDE EFFECTS:
125 **      Updates random seed (a static)
126 ** RESTRICTIONS:
127 **      None
128 ** MEMORY:      NA
129 ** ALGORITHM:
130 **      Uses the current interval timer value, promoted to a 64 bit
131 **      float as a multiplier for a static residue (which begins
132 **      as an uninitialized variable). The result is bits [16..48)
133 **      of the product. Seed is then updated with the return value
134 **      promoted to a float-64.
135 ***********************************************************************/
136 PRUint32 HammerData::Random()
137 {
138     PRUint32 rv;
139     PRUint64 shift;
140     RCInterval now = RCInterval(RCInterval::now);
141     PRFloat64 random = seed * (PRFloat64)((PRIntervalTime)now);
142     LL_USHR(shift, *((PRUint64*)&random), 16);
143     LL_L2UI(rv, shift);
144     seed = (PRFloat64)rv;
145     return rv;
146 }  /* HammerData::Random */
147
148 Hammer::~Hammer() { }
149
150 Hammer::Hammer(
151     RCThread::Scope scope, RCLock* lock, RCCondition *cond, PRUint32 clip):
152         HammerData(lock, cond, clip), RCThread(scope, RCThread::joinable, 0) { }
153
154 HammerData::~HammerData() { }
155
156 HammerData::HammerData(RCLock* lock, RCCondition *cond, PRUint32 clip)
157 {
158     ml = lock;
159     cv = cond;
160     writes = 0;
161     limit = clip;
162     seed = 0x58a9382;
163     action = HammerData::sg_go;
164     problem = HammerData::sg_okay;
165     timein = RCInterval(RCInterval::now);
166 }  /* HammerData::HammerData */
167
168
169 /***********************************************************************
170 ** PRIVATE FUNCTION:    Hammer::RootFunction
171 ** DESCRIPTION:
172 **   Hammer on the file I/O system
173 ** INPUTS:      A pointer to the thread's private data
174 ** OUTPUTS:     None
175 ** RETURN:      None
176 ** SIDE EFFECTS:
177 **      Creates, accesses and deletes a file
178 ** RESTRICTIONS:
179 **      (Currently) must have file create permission in "/usr/tmp".
180 ** MEMORY:      NA
181 ** ALGORITHM:
182 **      This function is a root of a thread
183 **      1) Creates a (hopefully) unique file in /usr/tmp/
184 **      2) Writes a zero to a random number of sequential pages
185 **      3) Closes the file
186 **      4) Reopens the file
187 **      5) Seeks to a random page within the file
188 **      6) Writes a one byte on that page
189 **      7) Repeat steps [5..6] for each page in the file
190 **      8) Close and delete the file
191 **      9) Repeat steps [1..8] until told to stop
192 **     10) Notify complete and return
193 ***********************************************************************/
194 void Hammer::RootFunction()
195 {
196     PRUint32 index;
197     RCFileIO file;
198     char filename[30];
199     const char zero = 0;
200     PRStatus rv = PR_SUCCESS;
201
202     limit = (Random() % limit) + 1;
203
204     (void)sprintf(filename, "%ssg%04p.dat", baseName, this);
205
206     if (debug_mode) PR_fprintf(output, "Starting work on %s\n", filename);
207
208     while (PR_TRUE)
209     {
210         PRUint64 bytes;
211         PRUint32 minor = (Random() % limit) + 1;
212         PRUint32 random = (Random() % limit) + 1;
213         PRUint32 pages = (Random() % limit) + 10;
214         while (minor-- > 0)
215         {
216             problem = sg_okay;
217             if (action != sg_go) goto finished;
218             problem = sg_open;
219             rv = file.Open(filename, PR_RDWR|PR_CREATE_FILE, 0666);
220             if (PR_FAILURE == rv) goto finished;
221             for (index = 0; index < pages; index++)
222             {
223                 problem = sg_okay;
224                 if (action != sg_go) goto close;
225                 problem = sg_seek;
226                 bytes = file.Seek(pageSize * index, RCFileIO::set);
227                 if (bytes != pageSize * index) goto close;
228                 problem = sg_write;
229                 bytes = file.Write(&zero, sizeof(zero));
230                 if (bytes <= 0) goto close;
231                 writes += 1;
232             }
233             problem = sg_close;
234             rv = file.Close();
235             if (rv != PR_SUCCESS) goto purge;
236
237             problem = sg_okay;
238             if (action != sg_go) goto purge;
239
240             problem = sg_open;
241             rv = file.Open(filename, PR_RDWR, 0666);
242             if (PR_FAILURE == rv) goto finished;
243             for (index = 0; index < pages; index++)
244             {
245                 problem = sg_okay;
246                 if (action != sg_go) goto close;
247                 problem = sg_seek;
248                 bytes = file.Seek(pageSize * index, RCFileIO::set);
249                 if (bytes != pageSize * index) goto close;
250                 problem = sg_write;
251                 bytes = file.Write(&zero, sizeof(zero));
252                 if (bytes <= 0) goto close;
253                 writes += 1;
254                 random = (random + 511) % pages;
255             }
256             problem = sg_close;
257             rv = file.Close();
258             if (rv != PR_SUCCESS) goto purge;
259             problem = sg_delete;
260             rv = file.Delete(filename);
261             if (rv != PR_SUCCESS) goto finished;
262        }
263     }
264
265 close:
266     (void)file.Close();
267 purge:
268     (void)file.Delete(filename);
269 finished:
270     RCEnter scope(ml);
271     action = HammerData::sg_done;
272     cv->Notify();
273
274     if (debug_mode) PR_fprintf(output, "Ending work on %s\n", filename);
275
276     return;
277 }  /* Hammer::RootFunction */
278
279 static Hammer* hammer[100];
280 /***********************************************************************
281 ** PRIVATE FUNCTION:    main
282 ** DESCRIPTION:
283 **   Hammer on the file I/O system
284 ** INPUTS:      The usual argc and argv
285 **              argv[0] - program name (not used)
286 **              argv[1] - the number of virtual_procs to execute the major loop
287 **              argv[2] - the number of threads to toss into the batch
288 **              argv[3] - the clipping number applied to randoms
289 **              default values: max_virtual_procs = 2, threads = 10, limit = 57
290 ** OUTPUTS:     None
291 ** RETURN:      None
292 ** SIDE EFFECTS:
293 **      Creates, accesses and deletes lots of files
294 ** RESTRICTIONS:
295 **      (Currently) must have file create permission in "/usr/tmp".
296 ** MEMORY:      NA
297 ** ALGORITHM:
298 **      1) Fork a "Thread()"
299 **      2) Wait for 'interleave' seconds
300 **      3) For [0..'threads') repeat [1..2]
301 **      4) Mark all objects to stop
302 **      5) Collect the threads, accumulating the results
303 **      6) For [0..'max_virtual_procs') repeat [1..5]
304 **      7) Print accumulated results and exit
305 **
306 **      Characteristic output (from IRIX)
307 **          Random File: Using max_virtual_procs = 2, threads = 10, limit = 57
308 **          Random File: [min [avg] max] writes/sec average
309 ***********************************************************************/
310 PRIntn main (PRIntn argc, char *argv[])
311 {
312     RCLock ml;
313         PLOptStatus os;
314     RCCondition cv(&ml);
315     PRUint32 writesMax = 0, durationTot = 0;
316     RCThread::Scope thread_scope = RCThread::local;
317     PRUint32 writes, writesMin = 0x7fffffff, writesTot = 0;
318     PRIntn active, poll, limit = 0, max_virtual_procs = 0, threads = 0, virtual_procs;
319     RCInterval interleave(RCInterval::FromMilliseconds(10000)), duration(0);
320
321     const char *where[] = {"okay", "open", "close", "delete", "write", "seek"};
322
323         PLOptState *opt = PL_CreateOptState(argc, argv, "Gdl:t:i:");
324         while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
325     {
326                 if (PL_OPT_BAD == os) continue;
327         switch (opt->option)
328         {
329         case 0:
330                 baseName = opt->value;
331                 break;
332         case 'G':  /* global threads */
333                 thread_scope = RCThread::global;
334             break;
335         case 'd':  /* debug mode */
336                         debug_mode = 1;
337             break;
338         case 'l':  /* limiting number */
339                         limit = atoi(opt->value);
340             break;
341         case 't':  /* number of threads */
342                         threads = atoi(opt->value);
343             break;
344         case 'i':  /* iteration counter */
345                         max_virtual_procs = atoi(opt->value);
346             break;
347          default:
348             break;
349         }
350     }
351         PL_DestroyOptState(opt);
352     output = PR_GetSpecialFD(PR_StandardOutput);
353
354  /* main test */
355  
356     cv.SetTimeout(interleave);
357         
358     if (max_virtual_procs == 0) max_virtual_procs = 2;
359     if (limit == 0) limit = 57;
360     if (threads == 0) threads = 10;
361
362     if (debug_mode) PR_fprintf(output,
363         "%s: Using %d virtual processors, %d threads, limit = %d and %s threads\n",
364         programName, max_virtual_procs, threads, limit,
365         (thread_scope == RCThread::local) ? "LOCAL" : "GLOBAL");
366
367     for (virtual_procs = 0; virtual_procs < max_virtual_procs; ++virtual_procs)
368     {
369         if (debug_mode)
370                         PR_fprintf(output,
371                                 "%s: Setting number of virtual processors to %d\n",
372                                 programName, virtual_procs + 1);
373                 RCPrimordialThread::SetVirtualProcessors(virtual_procs + 1);
374         for (active = 0; active < threads; active++)
375         {
376             hammer[active] = new Hammer(thread_scope, &ml, &cv, limit);
377             hammer[active]->Start();  /* then make it roll */
378             RCThread::Sleep(interleave);  /* start them slowly */
379         }
380
381         /*
382          * The last thread started has had the opportunity to run for
383          * 'interleave' seconds. Now gather them all back in.
384          */
385         {
386             RCEnter scope(&ml);
387             for (poll = 0; poll < threads; poll++)
388             {
389                 if (hammer[poll]->action == HammerData::sg_go)  /* don't overwrite done */
390                     hammer[poll]->action = HammerData::sg_stop;  /* ask him to stop */
391             }
392         }
393
394         while (active > 0)
395         {
396             for (poll = 0; poll < threads; poll++)
397             {
398                 ml.Acquire();
399                 while (hammer[poll]->action < HammerData::sg_done) cv.Wait();
400                 ml.Release();
401
402                 if (hammer[poll]->problem == HammerData::sg_okay)
403                 {
404                     duration = RCInterval(RCInterval::now) - hammer[poll]->timein;
405                     writes = hammer[poll]->writes * 1000 / duration;
406                     if (writes < writesMin)  writesMin = writes;
407                     if (writes > writesMax) writesMax = writes;
408                     writesTot += hammer[poll]->writes;
409                     durationTot += duration;
410                 }
411                 else
412                 {
413                     if (debug_mode) PR_fprintf(output,
414                         "%s: test failed %s after %ld seconds\n",
415                         programName, where[hammer[poll]->problem], duration);
416                                         else failed_already=1;
417                 }
418                 active -= 1;  /* this is another one down */
419                 (void)hammer[poll]->Join();
420                 hammer[poll] = NULL;
421             }
422         }
423         if (debug_mode) PR_fprintf(output,
424             "%s: [%ld [%ld] %ld] writes/sec average\n",
425             programName, writesMin,
426             writesTot * 1000 / durationTot, writesMax);
427     }
428
429         failed_already |= (PR_FAILURE == RCPrimordialThread::Cleanup());
430             PR_fprintf(output, "%s\n", (failed_already) ? "FAIL\n" : "PASS\n");
431                 return failed_already;
432 }  /* main */