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
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/
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
15 * The Original Code is the Netscape Portable Runtime (NSPR).
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.
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.
36 * ***** END LICENSE BLOCK ***** */
38 /***********************************************************************
40 ** Contact: AOF<mailto:freier@netscape.com>
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 ***********************************************************************/
56 /***********************************************************************
58 ***********************************************************************/
59 /* Used to get the command line option */
73 static PRFileDesc *output;
74 static PRIntn debug_mode = 0;
75 static PRIntn failed_already = 0;
81 sg_go, sg_stop, sg_done} Action;
83 sg_okay, sg_open, sg_close, sg_delete, sg_write, sg_seek} Problem;
85 virtual ~HammerData();
86 HammerData(RCLock* lock, RCCondition *cond, PRUint32 clip);
87 virtual PRUint32 Random();
102 class Hammer: public HammerData, public RCThread
106 Hammer(RCThread::Scope scope, RCLock* lock, RCCondition *cond, PRUint32 clip);
113 static PRInt32 pageSize = 1024;
114 static const char* baseName = "./";
115 static const char *programName = "Random File";
117 /***********************************************************************
118 ** PRIVATE FUNCTION: Random
120 ** Generate a pseudo-random number
123 ** RETURN: A pseudo-random unsigned number, 32-bits wide
125 ** Updates random seed (a static)
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()
140 RCInterval now = RCInterval(RCInterval::now);
141 PRFloat64 random = seed * (PRFloat64)((PRIntervalTime)now);
142 LL_USHR(shift, *((PRUint64*)&random), 16);
144 seed = (PRFloat64)rv;
146 } /* HammerData::Random */
148 Hammer::~Hammer() { }
151 RCThread::Scope scope, RCLock* lock, RCCondition *cond, PRUint32 clip):
152 HammerData(lock, cond, clip), RCThread(scope, RCThread::joinable, 0) { }
154 HammerData::~HammerData() { }
156 HammerData::HammerData(RCLock* lock, RCCondition *cond, PRUint32 clip)
163 action = HammerData::sg_go;
164 problem = HammerData::sg_okay;
165 timein = RCInterval(RCInterval::now);
166 } /* HammerData::HammerData */
169 /***********************************************************************
170 ** PRIVATE FUNCTION: Hammer::RootFunction
172 ** Hammer on the file I/O system
173 ** INPUTS: A pointer to the thread's private data
177 ** Creates, accesses and deletes a file
179 ** (Currently) must have file create permission in "/usr/tmp".
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()
200 PRStatus rv = PR_SUCCESS;
202 limit = (Random() % limit) + 1;
204 (void)sprintf(filename, "%ssg%04p.dat", baseName, this);
206 if (debug_mode) PR_fprintf(output, "Starting work on %s\n", filename);
211 PRUint32 minor = (Random() % limit) + 1;
212 PRUint32 random = (Random() % limit) + 1;
213 PRUint32 pages = (Random() % limit) + 10;
217 if (action != sg_go) goto finished;
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++)
224 if (action != sg_go) goto close;
226 bytes = file.Seek(pageSize * index, RCFileIO::set);
227 if (bytes != pageSize * index) goto close;
229 bytes = file.Write(&zero, sizeof(zero));
230 if (bytes <= 0) goto close;
235 if (rv != PR_SUCCESS) goto purge;
238 if (action != sg_go) goto purge;
241 rv = file.Open(filename, PR_RDWR, 0666);
242 if (PR_FAILURE == rv) goto finished;
243 for (index = 0; index < pages; index++)
246 if (action != sg_go) goto close;
248 bytes = file.Seek(pageSize * index, RCFileIO::set);
249 if (bytes != pageSize * index) goto close;
251 bytes = file.Write(&zero, sizeof(zero));
252 if (bytes <= 0) goto close;
254 random = (random + 511) % pages;
258 if (rv != PR_SUCCESS) goto purge;
260 rv = file.Delete(filename);
261 if (rv != PR_SUCCESS) goto finished;
268 (void)file.Delete(filename);
271 action = HammerData::sg_done;
274 if (debug_mode) PR_fprintf(output, "Ending work on %s\n", filename);
277 } /* Hammer::RootFunction */
279 static Hammer* hammer[100];
280 /***********************************************************************
281 ** PRIVATE FUNCTION: main
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
293 ** Creates, accesses and deletes lots of files
295 ** (Currently) must have file create permission in "/usr/tmp".
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
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[])
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);
321 const char *where[] = {"okay", "open", "close", "delete", "write", "seek"};
323 PLOptState *opt = PL_CreateOptState(argc, argv, "Gdl:t:i:");
324 while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
326 if (PL_OPT_BAD == os) continue;
330 baseName = opt->value;
332 case 'G': /* global threads */
333 thread_scope = RCThread::global;
335 case 'd': /* debug mode */
338 case 'l': /* limiting number */
339 limit = atoi(opt->value);
341 case 't': /* number of threads */
342 threads = atoi(opt->value);
344 case 'i': /* iteration counter */
345 max_virtual_procs = atoi(opt->value);
351 PL_DestroyOptState(opt);
352 output = PR_GetSpecialFD(PR_StandardOutput);
356 cv.SetTimeout(interleave);
358 if (max_virtual_procs == 0) max_virtual_procs = 2;
359 if (limit == 0) limit = 57;
360 if (threads == 0) threads = 10;
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");
367 for (virtual_procs = 0; virtual_procs < max_virtual_procs; ++virtual_procs)
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++)
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 */
382 * The last thread started has had the opportunity to run for
383 * 'interleave' seconds. Now gather them all back in.
387 for (poll = 0; poll < threads; poll++)
389 if (hammer[poll]->action == HammerData::sg_go) /* don't overwrite done */
390 hammer[poll]->action = HammerData::sg_stop; /* ask him to stop */
396 for (poll = 0; poll < threads; poll++)
399 while (hammer[poll]->action < HammerData::sg_done) cv.Wait();
402 if (hammer[poll]->problem == HammerData::sg_okay)
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;
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;
418 active -= 1; /* this is another one down */
419 (void)hammer[poll]->Join();
423 if (debug_mode) PR_fprintf(output,
424 "%s: [%ld [%ld] %ld] writes/sec average\n",
425 programName, writesMin,
426 writesTot * 1000 / durationTot, writesMax);
429 failed_already |= (PR_FAILURE == RCPrimordialThread::Cleanup());
430 PR_fprintf(output, "%s\n", (failed_already) ? "FAIL\n" : "PASS\n");
431 return failed_already;