ranfile.c 14 KB

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