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/. */
6 /***********************************************************************
8 ** Contact: AOF<freier@netscape.com>
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 test.
16 ** The regress mode will be the default mode. Since the regress tool limits
17 ** the output to a one line status:PASS or FAIL,all of the printf statements
18 ** have been handled with an if (debug_mode) statement.
19 ** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
20 ** recognize the return code from tha main program.
21 ***********************************************************************/
24 /***********************************************************************
26 ***********************************************************************/
27 /* Used to get the command line option */
41 static PRIntn debug_mode
= 0;
42 static PRIntn failed_already
=0;
43 static PRThreadScope thread_scope
= PR_LOCAL_THREAD
;
45 typedef enum {sg_go
, sg_stop
, sg_done
} Action
;
46 typedef enum {sg_okay
, sg_open
, sg_close
, sg_delete
, sg_write
, sg_seek
} Problem
;
48 typedef struct Hammer_s
{
55 PRIntervalTime timein
;
60 #define DEFAULT_LIMIT 10
61 #define DEFAULT_THREADS 2
62 #define DEFAULT_LOOPS 1
64 static PRInt32 pageSize
= 1024;
65 static const char* baseName
= "./";
66 static const char *programName
= "Random File";
68 /***********************************************************************
69 ** PRIVATE FUNCTION: RandomNum
71 ** Generate a pseudo-random number
74 ** RETURN: A pseudo-random unsigned number, 32-bits wide
76 ** Updates random seed (a static)
81 ** Uses the current interval timer value, promoted to a 64 bit
82 ** float as a multiplier for a static residue (which begins
83 ** as an uninitialized variable). The result is bits [16..48)
84 ** of the product. Seed is then updated with the return value
85 ** promoted to a float-64.
86 ***********************************************************************/
87 static PRUint32
RandomNum(void)
91 static PRFloat64 seed
= 0x58a9382; /* Just make sure it isn't 0! */
92 PRFloat64 random
= seed
* (PRFloat64
)PR_IntervalNow();
93 LL_USHR(shift
, *((PRUint64
*)&random
), 16);
99 /***********************************************************************
100 ** PRIVATE FUNCTION: Thread
102 ** Hammer on the file I/O system
103 ** INPUTS: A pointer to the thread's private data
107 ** Creates, accesses and deletes a file
109 ** (Currently) must have file create permission in "/usr/tmp".
112 ** This function is a root of a thread
113 ** 1) Creates a (hopefully) unique file in /usr/tmp/
114 ** 2) Writes a zero to a random number of sequential pages
115 ** 3) Closes the file
116 ** 4) Reopens the file
117 ** 5) Seeks to a random page within the file
118 ** 6) Writes a one byte on that page
119 ** 7) Repeat steps [5..6] for each page in the file
120 ** 8) Close and delete the file
121 ** 9) Repeat steps [1..8] until told to stop
122 ** 10) Notify complete and return
123 ***********************************************************************/
124 static void PR_CALLBACK
Thread(void *arg
)
129 PRFileDesc
*file
= NULL
;
130 PRStatus rv
= PR_SUCCESS
;
131 Hammer_t
*cd
= (Hammer_t
*)arg
;
133 (void)sprintf(filename
, "%ssg%04ld.dat", baseName
, cd
->id
);
135 if (debug_mode
) printf("Starting work on %s\n", filename
);
140 PRUint32 minor
= (RandomNum() % cd
->limit
) + 1;
141 PRUint32 random
= (RandomNum() % cd
->limit
) + 1;
142 PRUint32 pages
= (RandomNum() % cd
->limit
) + 10;
145 cd
->problem
= sg_okay
;
146 if (cd
->action
!= sg_go
) goto finished
;
147 cd
->problem
= sg_open
;
148 file
= PR_Open(filename
, PR_RDWR
|PR_CREATE_FILE
, 0666);
149 if (file
== NULL
) goto finished
;
150 for (index
= 0; index
< pages
; index
++)
152 cd
->problem
= sg_okay
;
153 if (cd
->action
!= sg_go
) goto close
;
154 cd
->problem
= sg_seek
;
155 bytes
= PR_Seek(file
, pageSize
* index
, PR_SEEK_SET
);
156 if (bytes
!= pageSize
* index
) goto close
;
157 cd
->problem
= sg_write
;
158 bytes
= PR_Write(file
, &zero
, sizeof(zero
));
159 if (bytes
<= 0) goto close
;
162 cd
->problem
= sg_close
;
164 if (rv
!= PR_SUCCESS
) goto purge
;
166 cd
->problem
= sg_okay
;
167 if (cd
->action
!= sg_go
) goto purge
;
169 cd
->problem
= sg_open
;
170 file
= PR_Open(filename
, PR_RDWR
, 0666);
171 for (index
= 0; index
< pages
; index
++)
173 cd
->problem
= sg_okay
;
174 if (cd
->action
!= sg_go
) goto close
;
175 cd
->problem
= sg_seek
;
176 bytes
= PR_Seek(file
, pageSize
* index
, PR_SEEK_SET
);
177 if (bytes
!= pageSize
* index
) goto close
;
178 cd
->problem
= sg_write
;
179 bytes
= PR_Write(file
, &zero
, sizeof(zero
));
180 if (bytes
<= 0) goto close
;
182 random
= (random
+ 511) % pages
;
184 cd
->problem
= sg_close
;
186 if (rv
!= PR_SUCCESS
) goto purge
;
187 cd
->problem
= sg_delete
;
188 rv
= PR_Delete(filename
);
189 if (rv
!= PR_SUCCESS
) goto finished
;
194 (void)PR_Close(file
);
196 (void)PR_Delete(filename
);
199 cd
->action
= sg_done
;
200 PR_NotifyCondVar(cd
->cv
);
203 if (debug_mode
) printf("Ending work on %s\n", filename
);
208 static Hammer_t hammer
[100];
209 static PRCondVar
*cv
;
210 /***********************************************************************
211 ** PRIVATE FUNCTION: main
213 ** Hammer on the file I/O system
214 ** INPUTS: The usual argc and argv
215 ** argv[0] - program name (not used)
216 ** argv[1] - the number of times to execute the major loop
217 ** argv[2] - the number of threads to toss into the batch
218 ** argv[3] - the clipping number applied to randoms
219 ** default values: loops = 2, threads = 10, limit = 57
223 ** Creates, accesses and deletes lots of files
225 ** (Currently) must have file create permission in "/usr/tmp".
228 ** 1) Fork a "Thread()"
229 ** 2) Wait for 'interleave' seconds
230 ** 3) For [0..'threads') repeat [1..2]
231 ** 4) Mark all objects to stop
232 ** 5) Collect the threads, accumulating the results
233 ** 6) For [0..'loops') repeat [1..5]
234 ** 7) Print accumulated results and exit
236 ** Characteristic output (from IRIX)
237 ** Random File: Using loops = 2, threads = 10, limit = 57
238 ** Random File: [min [avg] max] writes/sec average
239 ***********************************************************************/
240 int main(int argc
, char **argv
)
245 PRIntervalTime interleave
;
246 PRIntervalTime duration
= 0;
247 int limit
= 0, loops
= 0, threads
= 0, times
;
248 PRUint32 writes
, writesMin
= 0x7fffffff, writesTot
= 0, durationTot
= 0, writesMax
= 0;
250 const char *where
[] = {"okay", "open", "close", "delete", "write", "seek"};
252 /* The command line argument: -d is used to determine if the test is being run
253 in debug mode. The regress tool requires only one line output:PASS or FAIL.
254 All of the printfs associated with this test has been handled with a if (debug_mode)
259 PLOptState
*opt
= PL_CreateOptState(argc
, argv
, "Gdl:t:i:");
260 while (PL_OPT_EOL
!= (os
= PL_GetNextOpt(opt
)))
262 if (PL_OPT_BAD
== os
) continue;
265 case 'G': /* global threads */
266 thread_scope
= PR_GLOBAL_THREAD
;
268 case 'd': /* debug mode */
271 case 'l': /* limiting number */
272 limit
= atoi(opt
->value
);
274 case 't': /* number of threads */
275 threads
= atoi(opt
->value
);
277 case 'i': /* iteration counter */
278 loops
= atoi(opt
->value
);
284 PL_DestroyOptState(opt
);
288 PR_Init(PR_USER_THREAD
, PR_PRIORITY_NORMAL
, 0);
291 interleave
= PR_SecondsToInterval(10);
294 cv
= PR_NewCondVar(ml
);
296 if (loops
== 0) loops
= DEFAULT_LOOPS
;
297 if (limit
== 0) limit
= DEFAULT_LIMIT
;
298 if (threads
== 0) threads
= DEFAULT_THREADS
;
300 if (debug_mode
) printf(
301 "%s: Using loops = %d, threads = %d, limit = %d and %s threads\n",
302 programName
, loops
, threads
, limit
,
303 (thread_scope
== PR_LOCAL_THREAD
) ? "LOCAL" : "GLOBAL");
305 for (times
= 0; times
< loops
; ++times
)
307 if (debug_mode
) printf("%s: Setting concurrency level to %d\n", programName
, times
+ 1);
308 PR_SetConcurrency(times
+ 1);
309 for (active
= 0; active
< threads
; active
++)
311 hammer
[active
].ml
= ml
;
312 hammer
[active
].cv
= cv
;
313 hammer
[active
].id
= id
++;
314 hammer
[active
].writes
= 0;
315 hammer
[active
].action
= sg_go
;
316 hammer
[active
].problem
= sg_okay
;
317 hammer
[active
].limit
= (RandomNum() % limit
) + 1;
318 hammer
[active
].timein
= PR_IntervalNow();
319 hammer
[active
].thread
= PR_CreateThread(
320 PR_USER_THREAD
, Thread
, &hammer
[active
],
321 PR_GetThreadPriority(PR_GetCurrentThread()),
322 thread_scope
, PR_JOINABLE_THREAD
, 0);
325 PR_WaitCondVar(cv
, interleave
); /* start new ones slowly */
330 * The last thread started has had the opportunity to run for
331 * 'interleave' seconds. Now gather them all back in.
334 for (poll
= 0; poll
< threads
; poll
++)
336 if (hammer
[poll
].action
== sg_go
) /* don't overwrite done */
337 hammer
[poll
].action
= sg_stop
; /* ask him to stop */
343 for (poll
= 0; poll
< threads
; poll
++)
346 while (hammer
[poll
].action
< sg_done
)
347 PR_WaitCondVar(cv
, PR_INTERVAL_NO_TIMEOUT
);
350 active
-= 1; /* this is another one down */
351 (void)PR_JoinThread(hammer
[poll
].thread
);
352 hammer
[poll
].thread
= NULL
;
353 if (hammer
[poll
].problem
== sg_okay
)
355 duration
= PR_IntervalToMilliseconds(
356 PR_IntervalNow() - hammer
[poll
].timein
);
357 writes
= hammer
[poll
].writes
* 1000 / duration
;
358 if (writes
< writesMin
)
360 if (writes
> writesMax
)
362 writesTot
+= hammer
[poll
].writes
;
363 durationTot
+= duration
;
366 if (debug_mode
) printf(
367 "%s: test failed %s after %ld seconds\n",
368 programName
, where
[hammer
[poll
].problem
], duration
);
369 else failed_already
=1;
373 if (debug_mode
) printf(
374 "%s: [%ld [%ld] %ld] writes/sec average\n",
375 programName
, writesMin
, writesTot
* 1000 / durationTot
, writesMax
);
377 PR_DestroyCondVar(cv
);