|
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/. */ |
|
5 |
|
6 /*********************************************************************** |
|
7 ** |
|
8 ** Contact: AOF<mailto:freier@netscape.com> |
|
9 ** |
|
10 ** Name: ranfile.c |
|
11 ** |
|
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 ***********************************************************************/ |
|
22 |
|
23 |
|
24 /*********************************************************************** |
|
25 ** Includes |
|
26 ***********************************************************************/ |
|
27 /* Used to get the command line option */ |
|
28 #include <plgetopt.h> |
|
29 #include <prprf.h> |
|
30 #include <prio.h> |
|
31 |
|
32 #include "rccv.h" |
|
33 #include "rcthread.h" |
|
34 #include "rcfileio.h" |
|
35 #include "rclock.h" |
|
36 |
|
37 #include <string.h> |
|
38 #include <stdio.h> |
|
39 #include <stdlib.h> |
|
40 |
|
41 static PRFileDesc *output; |
|
42 static PRIntn debug_mode = 0; |
|
43 static PRIntn failed_already = 0; |
|
44 |
|
45 class HammerData |
|
46 { |
|
47 public: |
|
48 typedef enum { |
|
49 sg_go, sg_stop, sg_done} Action; |
|
50 typedef enum { |
|
51 sg_okay, sg_open, sg_close, sg_delete, sg_write, sg_seek} Problem; |
|
52 |
|
53 virtual ~HammerData(); |
|
54 HammerData(RCLock* lock, RCCondition *cond, PRUint32 clip); |
|
55 virtual PRUint32 Random(); |
|
56 |
|
57 Action action; |
|
58 Problem problem; |
|
59 PRUint32 writes; |
|
60 RCInterval timein; |
|
61 friend class Hammer; |
|
62 private: |
|
63 RCLock *ml; |
|
64 RCCondition *cv; |
|
65 PRUint32 limit; |
|
66 |
|
67 PRFloat64 seed; |
|
68 }; /* HammerData */ |
|
69 |
|
70 class Hammer: public HammerData, public RCThread |
|
71 { |
|
72 public: |
|
73 virtual ~Hammer(); |
|
74 Hammer(RCThread::Scope scope, RCLock* lock, RCCondition *cond, PRUint32 clip); |
|
75 |
|
76 private: |
|
77 void RootFunction(); |
|
78 |
|
79 }; |
|
80 |
|
81 static PRInt32 pageSize = 1024; |
|
82 static const char* baseName = "./"; |
|
83 static const char *programName = "Random File"; |
|
84 |
|
85 /*********************************************************************** |
|
86 ** PRIVATE FUNCTION: Random |
|
87 ** DESCRIPTION: |
|
88 ** Generate a pseudo-random number |
|
89 ** INPUTS: None |
|
90 ** OUTPUTS: None |
|
91 ** RETURN: A pseudo-random unsigned number, 32-bits wide |
|
92 ** SIDE EFFECTS: |
|
93 ** Updates random seed (a static) |
|
94 ** RESTRICTIONS: |
|
95 ** None |
|
96 ** MEMORY: NA |
|
97 ** ALGORITHM: |
|
98 ** Uses the current interval timer value, promoted to a 64 bit |
|
99 ** float as a multiplier for a static residue (which begins |
|
100 ** as an uninitialized variable). The result is bits [16..48) |
|
101 ** of the product. Seed is then updated with the return value |
|
102 ** promoted to a float-64. |
|
103 ***********************************************************************/ |
|
104 PRUint32 HammerData::Random() |
|
105 { |
|
106 PRUint32 rv; |
|
107 PRUint64 shift; |
|
108 RCInterval now = RCInterval(RCInterval::now); |
|
109 PRFloat64 random = seed * (PRFloat64)((PRIntervalTime)now); |
|
110 LL_USHR(shift, *((PRUint64*)&random), 16); |
|
111 LL_L2UI(rv, shift); |
|
112 seed = (PRFloat64)rv; |
|
113 return rv; |
|
114 } /* HammerData::Random */ |
|
115 |
|
116 Hammer::~Hammer() { } |
|
117 |
|
118 Hammer::Hammer( |
|
119 RCThread::Scope scope, RCLock* lock, RCCondition *cond, PRUint32 clip): |
|
120 HammerData(lock, cond, clip), RCThread(scope, RCThread::joinable, 0) { } |
|
121 |
|
122 HammerData::~HammerData() { } |
|
123 |
|
124 HammerData::HammerData(RCLock* lock, RCCondition *cond, PRUint32 clip) |
|
125 { |
|
126 ml = lock; |
|
127 cv = cond; |
|
128 writes = 0; |
|
129 limit = clip; |
|
130 seed = 0x58a9382; |
|
131 action = HammerData::sg_go; |
|
132 problem = HammerData::sg_okay; |
|
133 timein = RCInterval(RCInterval::now); |
|
134 } /* HammerData::HammerData */ |
|
135 |
|
136 |
|
137 /*********************************************************************** |
|
138 ** PRIVATE FUNCTION: Hammer::RootFunction |
|
139 ** DESCRIPTION: |
|
140 ** Hammer on the file I/O system |
|
141 ** INPUTS: A pointer to the thread's private data |
|
142 ** OUTPUTS: None |
|
143 ** RETURN: None |
|
144 ** SIDE EFFECTS: |
|
145 ** Creates, accesses and deletes a file |
|
146 ** RESTRICTIONS: |
|
147 ** (Currently) must have file create permission in "/usr/tmp". |
|
148 ** MEMORY: NA |
|
149 ** ALGORITHM: |
|
150 ** This function is a root of a thread |
|
151 ** 1) Creates a (hopefully) unique file in /usr/tmp/ |
|
152 ** 2) Writes a zero to a random number of sequential pages |
|
153 ** 3) Closes the file |
|
154 ** 4) Reopens the file |
|
155 ** 5) Seeks to a random page within the file |
|
156 ** 6) Writes a one byte on that page |
|
157 ** 7) Repeat steps [5..6] for each page in the file |
|
158 ** 8) Close and delete the file |
|
159 ** 9) Repeat steps [1..8] until told to stop |
|
160 ** 10) Notify complete and return |
|
161 ***********************************************************************/ |
|
162 void Hammer::RootFunction() |
|
163 { |
|
164 PRUint32 index; |
|
165 RCFileIO file; |
|
166 char filename[30]; |
|
167 const char zero = 0; |
|
168 PRStatus rv = PR_SUCCESS; |
|
169 |
|
170 limit = (Random() % limit) + 1; |
|
171 |
|
172 (void)sprintf(filename, "%ssg%04p.dat", baseName, this); |
|
173 |
|
174 if (debug_mode) PR_fprintf(output, "Starting work on %s\n", filename); |
|
175 |
|
176 while (PR_TRUE) |
|
177 { |
|
178 PRUint64 bytes; |
|
179 PRUint32 minor = (Random() % limit) + 1; |
|
180 PRUint32 random = (Random() % limit) + 1; |
|
181 PRUint32 pages = (Random() % limit) + 10; |
|
182 while (minor-- > 0) |
|
183 { |
|
184 problem = sg_okay; |
|
185 if (action != sg_go) goto finished; |
|
186 problem = sg_open; |
|
187 rv = file.Open(filename, PR_RDWR|PR_CREATE_FILE, 0666); |
|
188 if (PR_FAILURE == rv) goto finished; |
|
189 for (index = 0; index < pages; index++) |
|
190 { |
|
191 problem = sg_okay; |
|
192 if (action != sg_go) goto close; |
|
193 problem = sg_seek; |
|
194 bytes = file.Seek(pageSize * index, RCFileIO::set); |
|
195 if (bytes != pageSize * index) goto close; |
|
196 problem = sg_write; |
|
197 bytes = file.Write(&zero, sizeof(zero)); |
|
198 if (bytes <= 0) goto close; |
|
199 writes += 1; |
|
200 } |
|
201 problem = sg_close; |
|
202 rv = file.Close(); |
|
203 if (rv != PR_SUCCESS) goto purge; |
|
204 |
|
205 problem = sg_okay; |
|
206 if (action != sg_go) goto purge; |
|
207 |
|
208 problem = sg_open; |
|
209 rv = file.Open(filename, PR_RDWR, 0666); |
|
210 if (PR_FAILURE == rv) goto finished; |
|
211 for (index = 0; index < pages; index++) |
|
212 { |
|
213 problem = sg_okay; |
|
214 if (action != sg_go) goto close; |
|
215 problem = sg_seek; |
|
216 bytes = file.Seek(pageSize * index, RCFileIO::set); |
|
217 if (bytes != pageSize * index) goto close; |
|
218 problem = sg_write; |
|
219 bytes = file.Write(&zero, sizeof(zero)); |
|
220 if (bytes <= 0) goto close; |
|
221 writes += 1; |
|
222 random = (random + 511) % pages; |
|
223 } |
|
224 problem = sg_close; |
|
225 rv = file.Close(); |
|
226 if (rv != PR_SUCCESS) goto purge; |
|
227 problem = sg_delete; |
|
228 rv = file.Delete(filename); |
|
229 if (rv != PR_SUCCESS) goto finished; |
|
230 } |
|
231 } |
|
232 |
|
233 close: |
|
234 (void)file.Close(); |
|
235 purge: |
|
236 (void)file.Delete(filename); |
|
237 finished: |
|
238 RCEnter scope(ml); |
|
239 action = HammerData::sg_done; |
|
240 cv->Notify(); |
|
241 |
|
242 if (debug_mode) PR_fprintf(output, "Ending work on %s\n", filename); |
|
243 |
|
244 return; |
|
245 } /* Hammer::RootFunction */ |
|
246 |
|
247 static Hammer* hammer[100]; |
|
248 /*********************************************************************** |
|
249 ** PRIVATE FUNCTION: main |
|
250 ** DESCRIPTION: |
|
251 ** Hammer on the file I/O system |
|
252 ** INPUTS: The usual argc and argv |
|
253 ** argv[0] - program name (not used) |
|
254 ** argv[1] - the number of virtual_procs to execute the major loop |
|
255 ** argv[2] - the number of threads to toss into the batch |
|
256 ** argv[3] - the clipping number applied to randoms |
|
257 ** default values: max_virtual_procs = 2, threads = 10, limit = 57 |
|
258 ** OUTPUTS: None |
|
259 ** RETURN: None |
|
260 ** SIDE EFFECTS: |
|
261 ** Creates, accesses and deletes lots of files |
|
262 ** RESTRICTIONS: |
|
263 ** (Currently) must have file create permission in "/usr/tmp". |
|
264 ** MEMORY: NA |
|
265 ** ALGORITHM: |
|
266 ** 1) Fork a "Thread()" |
|
267 ** 2) Wait for 'interleave' seconds |
|
268 ** 3) For [0..'threads') repeat [1..2] |
|
269 ** 4) Mark all objects to stop |
|
270 ** 5) Collect the threads, accumulating the results |
|
271 ** 6) For [0..'max_virtual_procs') repeat [1..5] |
|
272 ** 7) Print accumulated results and exit |
|
273 ** |
|
274 ** Characteristic output (from IRIX) |
|
275 ** Random File: Using max_virtual_procs = 2, threads = 10, limit = 57 |
|
276 ** Random File: [min [avg] max] writes/sec average |
|
277 ***********************************************************************/ |
|
278 PRIntn main (PRIntn argc, char *argv[]) |
|
279 { |
|
280 RCLock ml; |
|
281 PLOptStatus os; |
|
282 RCCondition cv(&ml); |
|
283 PRUint32 writesMax = 0, durationTot = 0; |
|
284 RCThread::Scope thread_scope = RCThread::local; |
|
285 PRUint32 writes, writesMin = 0x7fffffff, writesTot = 0; |
|
286 PRIntn active, poll, limit = 0, max_virtual_procs = 0, threads = 0, virtual_procs; |
|
287 RCInterval interleave(RCInterval::FromMilliseconds(10000)), duration(0); |
|
288 |
|
289 const char *where[] = {"okay", "open", "close", "delete", "write", "seek"}; |
|
290 |
|
291 PLOptState *opt = PL_CreateOptState(argc, argv, "Gdl:t:i:"); |
|
292 while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) |
|
293 { |
|
294 if (PL_OPT_BAD == os) continue; |
|
295 switch (opt->option) |
|
296 { |
|
297 case 0: |
|
298 baseName = opt->value; |
|
299 break; |
|
300 case 'G': /* global threads */ |
|
301 thread_scope = RCThread::global; |
|
302 break; |
|
303 case 'd': /* debug mode */ |
|
304 debug_mode = 1; |
|
305 break; |
|
306 case 'l': /* limiting number */ |
|
307 limit = atoi(opt->value); |
|
308 break; |
|
309 case 't': /* number of threads */ |
|
310 threads = atoi(opt->value); |
|
311 break; |
|
312 case 'i': /* iteration counter */ |
|
313 max_virtual_procs = atoi(opt->value); |
|
314 break; |
|
315 default: |
|
316 break; |
|
317 } |
|
318 } |
|
319 PL_DestroyOptState(opt); |
|
320 output = PR_GetSpecialFD(PR_StandardOutput); |
|
321 |
|
322 /* main test */ |
|
323 |
|
324 cv.SetTimeout(interleave); |
|
325 |
|
326 if (max_virtual_procs == 0) max_virtual_procs = 2; |
|
327 if (limit == 0) limit = 57; |
|
328 if (threads == 0) threads = 10; |
|
329 |
|
330 if (debug_mode) PR_fprintf(output, |
|
331 "%s: Using %d virtual processors, %d threads, limit = %d and %s threads\n", |
|
332 programName, max_virtual_procs, threads, limit, |
|
333 (thread_scope == RCThread::local) ? "LOCAL" : "GLOBAL"); |
|
334 |
|
335 for (virtual_procs = 0; virtual_procs < max_virtual_procs; ++virtual_procs) |
|
336 { |
|
337 if (debug_mode) |
|
338 PR_fprintf(output, |
|
339 "%s: Setting number of virtual processors to %d\n", |
|
340 programName, virtual_procs + 1); |
|
341 RCPrimordialThread::SetVirtualProcessors(virtual_procs + 1); |
|
342 for (active = 0; active < threads; active++) |
|
343 { |
|
344 hammer[active] = new Hammer(thread_scope, &ml, &cv, limit); |
|
345 hammer[active]->Start(); /* then make it roll */ |
|
346 RCThread::Sleep(interleave); /* start them slowly */ |
|
347 } |
|
348 |
|
349 /* |
|
350 * The last thread started has had the opportunity to run for |
|
351 * 'interleave' seconds. Now gather them all back in. |
|
352 */ |
|
353 { |
|
354 RCEnter scope(&ml); |
|
355 for (poll = 0; poll < threads; poll++) |
|
356 { |
|
357 if (hammer[poll]->action == HammerData::sg_go) /* don't overwrite done */ |
|
358 hammer[poll]->action = HammerData::sg_stop; /* ask him to stop */ |
|
359 } |
|
360 } |
|
361 |
|
362 while (active > 0) |
|
363 { |
|
364 for (poll = 0; poll < threads; poll++) |
|
365 { |
|
366 ml.Acquire(); |
|
367 while (hammer[poll]->action < HammerData::sg_done) cv.Wait(); |
|
368 ml.Release(); |
|
369 |
|
370 if (hammer[poll]->problem == HammerData::sg_okay) |
|
371 { |
|
372 duration = RCInterval(RCInterval::now) - hammer[poll]->timein; |
|
373 writes = hammer[poll]->writes * 1000 / duration; |
|
374 if (writes < writesMin) writesMin = writes; |
|
375 if (writes > writesMax) writesMax = writes; |
|
376 writesTot += hammer[poll]->writes; |
|
377 durationTot += duration; |
|
378 } |
|
379 else |
|
380 { |
|
381 if (debug_mode) PR_fprintf(output, |
|
382 "%s: test failed %s after %ld seconds\n", |
|
383 programName, where[hammer[poll]->problem], duration); |
|
384 else failed_already=1; |
|
385 } |
|
386 active -= 1; /* this is another one down */ |
|
387 (void)hammer[poll]->Join(); |
|
388 hammer[poll] = NULL; |
|
389 } |
|
390 } |
|
391 if (debug_mode) PR_fprintf(output, |
|
392 "%s: [%ld [%ld] %ld] writes/sec average\n", |
|
393 programName, writesMin, |
|
394 writesTot * 1000 / durationTot, writesMax); |
|
395 } |
|
396 |
|
397 failed_already |= (PR_FAILURE == RCPrimordialThread::Cleanup()); |
|
398 PR_fprintf(output, "%s\n", (failed_already) ? "FAIL\n" : "PASS\n"); |
|
399 return failed_already; |
|
400 } /* main */ |