thruput.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  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. ** File: thruput.c
  7. ** Description: Test server's throughput capability comparing various
  8. ** implmentation strategies.
  9. **
  10. ** Note: Requires a server machine and an aribitrary number of
  11. ** clients to bang on it. Trust the numbers on the server
  12. ** more than those being displayed by the various clients.
  13. */
  14. #include "prerror.h"
  15. #include "prinrval.h"
  16. #include "prinit.h"
  17. #include "prio.h"
  18. #include "prlock.h"
  19. #include "prmem.h"
  20. #include "prnetdb.h"
  21. #include "prprf.h"
  22. #include "prthread.h"
  23. #include "pprio.h"
  24. #include "plerror.h"
  25. #include "plgetopt.h"
  26. #define ADDR_BUFFER 100
  27. #ifdef DEBUG
  28. #define PORT_INC_DO +100
  29. #else
  30. #define PORT_INC_DO
  31. #endif
  32. #ifdef IS_64
  33. #define PORT_INC_3264 +200
  34. #else
  35. #define PORT_INC_3264
  36. #endif
  37. #define PORT_NUMBER 51877 PORT_INC_DO PORT_INC_3264
  38. #define SAMPLING_INTERVAL 10
  39. #define BUFFER_SIZE (32 * 1024)
  40. static PRInt32 domain = PR_AF_INET;
  41. static PRInt32 protocol = 6; /* TCP */
  42. static PRFileDesc *err = NULL;
  43. static PRIntn concurrency = 1;
  44. static PRInt32 xport_buffer = -1;
  45. static PRUint32 initial_streams = 1;
  46. static PRInt32 buffer_size = BUFFER_SIZE;
  47. static PRThreadScope thread_scope = PR_LOCAL_THREAD;
  48. typedef struct Shared
  49. {
  50. PRLock *ml;
  51. PRUint32 sampled;
  52. PRUint32 threads;
  53. PRIntervalTime timein;
  54. PRNetAddr server_address;
  55. } Shared;
  56. static Shared *shared = NULL;
  57. static PRStatus PrintAddress(const PRNetAddr* address)
  58. {
  59. char buffer[ADDR_BUFFER];
  60. PRStatus rv = PR_NetAddrToString(address, buffer, sizeof(buffer));
  61. if (PR_SUCCESS == rv) {
  62. PR_fprintf(err, "%s:%u\n", buffer, PR_ntohs(address->inet.port));
  63. }
  64. else {
  65. PL_FPrintError(err, "PR_NetAddrToString");
  66. }
  67. return rv;
  68. } /* PrintAddress */
  69. static void PR_CALLBACK Clientel(void *arg)
  70. {
  71. PRStatus rv;
  72. PRFileDesc *xport;
  73. PRInt32 bytes, sampled;
  74. PRIntervalTime now, interval;
  75. PRBool do_display = PR_FALSE;
  76. Shared *shared = (Shared*)arg;
  77. char *buffer = (char*)PR_Malloc(buffer_size);
  78. PRNetAddr *server_address = &shared->server_address;
  79. PRIntervalTime connect_timeout = PR_SecondsToInterval(5);
  80. PRIntervalTime sampling_interval = PR_SecondsToInterval(SAMPLING_INTERVAL);
  81. PR_fprintf(err, "Client connecting to ");
  82. (void)PrintAddress(server_address);
  83. do
  84. {
  85. xport = PR_Socket(domain, PR_SOCK_STREAM, protocol);
  86. if (NULL == xport)
  87. {
  88. PL_FPrintError(err, "PR_Socket");
  89. return;
  90. }
  91. if (xport_buffer != -1)
  92. {
  93. PRSocketOptionData data;
  94. data.option = PR_SockOpt_RecvBufferSize;
  95. data.value.recv_buffer_size = (PRSize)xport_buffer;
  96. rv = PR_SetSocketOption(xport, &data);
  97. if (PR_FAILURE == rv) {
  98. PL_FPrintError(err, "PR_SetSocketOption - ignored");
  99. }
  100. data.option = PR_SockOpt_SendBufferSize;
  101. data.value.send_buffer_size = (PRSize)xport_buffer;
  102. rv = PR_SetSocketOption(xport, &data);
  103. if (PR_FAILURE == rv) {
  104. PL_FPrintError(err, "PR_SetSocketOption - ignored");
  105. }
  106. }
  107. rv = PR_Connect(xport, server_address, connect_timeout);
  108. if (PR_FAILURE == rv)
  109. {
  110. PL_FPrintError(err, "PR_Connect");
  111. if (PR_IO_TIMEOUT_ERROR != PR_GetError()) {
  112. PR_Sleep(connect_timeout);
  113. }
  114. PR_Close(xport); /* delete it and start over */
  115. }
  116. } while (PR_FAILURE == rv);
  117. do
  118. {
  119. bytes = PR_Recv(
  120. xport, buffer, buffer_size, 0, PR_INTERVAL_NO_TIMEOUT);
  121. PR_Lock(shared->ml);
  122. now = PR_IntervalNow();
  123. shared->sampled += bytes;
  124. interval = now - shared->timein;
  125. if (interval > sampling_interval)
  126. {
  127. sampled = shared->sampled;
  128. shared->timein = now;
  129. shared->sampled = 0;
  130. do_display = PR_TRUE;
  131. }
  132. PR_Unlock(shared->ml);
  133. if (do_display)
  134. {
  135. PRUint32 rate = sampled / PR_IntervalToMilliseconds(interval);
  136. PR_fprintf(err, "%u streams @ %u Kbytes/sec\n", shared->threads, rate);
  137. do_display = PR_FALSE;
  138. }
  139. } while (bytes > 0);
  140. } /* Clientel */
  141. static void Client(const char *server_name)
  142. {
  143. PRStatus rv;
  144. PRHostEnt host;
  145. char buffer[PR_NETDB_BUF_SIZE];
  146. PRIntervalTime dally = PR_SecondsToInterval(60);
  147. PR_fprintf(err, "Translating the name %s\n", server_name);
  148. rv = PR_GetHostByName(server_name, buffer, sizeof(buffer), &host);
  149. if (PR_FAILURE == rv) {
  150. PL_FPrintError(err, "PR_GetHostByName");
  151. }
  152. else
  153. {
  154. if (PR_EnumerateHostEnt(
  155. 0, &host, PORT_NUMBER, &shared->server_address) < 0) {
  156. PL_FPrintError(err, "PR_EnumerateHostEnt");
  157. }
  158. else
  159. {
  160. do
  161. {
  162. shared->threads += 1;
  163. (void)PR_CreateThread(
  164. PR_USER_THREAD, Clientel, shared,
  165. PR_PRIORITY_NORMAL, thread_scope,
  166. PR_UNJOINABLE_THREAD, 8 * 1024);
  167. if (shared->threads == initial_streams)
  168. {
  169. PR_Sleep(dally);
  170. initial_streams += 1;
  171. }
  172. } while (PR_TRUE);
  173. }
  174. }
  175. }
  176. static void PR_CALLBACK Servette(void *arg)
  177. {
  178. PRInt32 bytes, sampled;
  179. PRIntervalTime now, interval;
  180. PRBool do_display = PR_FALSE;
  181. PRFileDesc *client = (PRFileDesc*)arg;
  182. char *buffer = (char*)PR_Malloc(buffer_size);
  183. PRIntervalTime sampling_interval = PR_SecondsToInterval(SAMPLING_INTERVAL);
  184. if (xport_buffer != -1)
  185. {
  186. PRStatus rv;
  187. PRSocketOptionData data;
  188. data.option = PR_SockOpt_RecvBufferSize;
  189. data.value.recv_buffer_size = (PRSize)xport_buffer;
  190. rv = PR_SetSocketOption(client, &data);
  191. if (PR_FAILURE == rv) {
  192. PL_FPrintError(err, "PR_SetSocketOption - ignored");
  193. }
  194. data.option = PR_SockOpt_SendBufferSize;
  195. data.value.send_buffer_size = (PRSize)xport_buffer;
  196. rv = PR_SetSocketOption(client, &data);
  197. if (PR_FAILURE == rv) {
  198. PL_FPrintError(err, "PR_SetSocketOption - ignored");
  199. }
  200. }
  201. do
  202. {
  203. bytes = PR_Send(
  204. client, buffer, buffer_size, 0, PR_INTERVAL_NO_TIMEOUT);
  205. PR_Lock(shared->ml);
  206. now = PR_IntervalNow();
  207. shared->sampled += bytes;
  208. interval = now - shared->timein;
  209. if (interval > sampling_interval)
  210. {
  211. sampled = shared->sampled;
  212. shared->timein = now;
  213. shared->sampled = 0;
  214. do_display = PR_TRUE;
  215. }
  216. PR_Unlock(shared->ml);
  217. if (do_display)
  218. {
  219. PRUint32 rate = sampled / PR_IntervalToMilliseconds(interval);
  220. PR_fprintf(err, "%u streams @ %u Kbytes/sec\n", shared->threads, rate);
  221. do_display = PR_FALSE;
  222. }
  223. } while (bytes > 0);
  224. } /* Servette */
  225. static void Server(void)
  226. {
  227. PRStatus rv;
  228. PRNetAddr server_address, client_address;
  229. PRFileDesc *xport = PR_Socket(domain, PR_SOCK_STREAM, protocol);
  230. if (NULL == xport)
  231. {
  232. PL_FPrintError(err, "PR_Socket");
  233. return;
  234. }
  235. rv = PR_InitializeNetAddr(PR_IpAddrAny, PORT_NUMBER, &server_address);
  236. if (PR_FAILURE == rv) {
  237. PL_FPrintError(err, "PR_InitializeNetAddr");
  238. }
  239. else
  240. {
  241. rv = PR_Bind(xport, &server_address);
  242. if (PR_FAILURE == rv) {
  243. PL_FPrintError(err, "PR_Bind");
  244. }
  245. else
  246. {
  247. PRFileDesc *client;
  248. rv = PR_Listen(xport, 10);
  249. PR_fprintf(err, "Server listening on ");
  250. (void)PrintAddress(&server_address);
  251. do
  252. {
  253. client = PR_Accept(
  254. xport, &client_address, PR_INTERVAL_NO_TIMEOUT);
  255. if (NULL == client) {
  256. PL_FPrintError(err, "PR_Accept");
  257. }
  258. else
  259. {
  260. PR_fprintf(err, "Server accepting from ");
  261. (void)PrintAddress(&client_address);
  262. shared->threads += 1;
  263. (void)PR_CreateThread(
  264. PR_USER_THREAD, Servette, client,
  265. PR_PRIORITY_NORMAL, thread_scope,
  266. PR_UNJOINABLE_THREAD, 8 * 1024);
  267. }
  268. } while (PR_TRUE);
  269. }
  270. }
  271. } /* Server */
  272. static void Help(void)
  273. {
  274. PR_fprintf(err, "Usage: [-h] [<server>]\n");
  275. PR_fprintf(err, "\t-s <n> Initial # of connections (default: 1)\n");
  276. PR_fprintf(err, "\t-C <n> Set 'concurrency' (default: 1)\n");
  277. PR_fprintf(err, "\t-b <nK> Client buffer size (default: 32k)\n");
  278. PR_fprintf(err, "\t-B <nK> Transport recv/send buffer size (default: sys)\n");
  279. PR_fprintf(err, "\t-G Use GLOBAL threads (default: LOCAL)\n");
  280. PR_fprintf(err, "\t-X Use XTP transport (default: TCP)\n");
  281. PR_fprintf(err, "\t-6 Use IPv6 (default: IPv4)\n");
  282. PR_fprintf(err, "\t-h This message and nothing else\n");
  283. PR_fprintf(err, "\t<server> DNS name of server\n");
  284. PR_fprintf(err, "\t\tIf <server> is not specified, this host will be\n");
  285. PR_fprintf(err, "\t\tthe server and not act as a client.\n");
  286. } /* Help */
  287. int main(int argc, char **argv)
  288. {
  289. PLOptStatus os;
  290. const char *server_name = NULL;
  291. PLOptState *opt = PL_CreateOptState(argc, argv, "hGX6C:b:s:B:");
  292. err = PR_GetSpecialFD(PR_StandardError);
  293. while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
  294. {
  295. if (PL_OPT_BAD == os) {
  296. continue;
  297. }
  298. switch (opt->option)
  299. {
  300. case 0: /* Name of server */
  301. server_name = opt->value;
  302. break;
  303. case 'G': /* Globular threads */
  304. thread_scope = PR_GLOBAL_THREAD;
  305. break;
  306. case 'X': /* Use XTP as the transport */
  307. protocol = 36;
  308. break;
  309. case '6': /* Use IPv6 */
  310. domain = PR_AF_INET6;
  311. break;
  312. case 's': /* initial_streams */
  313. initial_streams = atoi(opt->value);
  314. break;
  315. case 'C': /* concurrency */
  316. concurrency = atoi(opt->value);
  317. break;
  318. case 'b': /* buffer size */
  319. buffer_size = 1024 * atoi(opt->value);
  320. break;
  321. case 'B': /* buffer size */
  322. xport_buffer = 1024 * atoi(opt->value);
  323. break;
  324. case 'h': /* user wants some guidance */
  325. default:
  326. Help(); /* so give him an earful */
  327. return 2; /* but not a lot else */
  328. }
  329. }
  330. PL_DestroyOptState(opt);
  331. shared = PR_NEWZAP(Shared);
  332. shared->ml = PR_NewLock();
  333. PR_fprintf(err,
  334. "This machine is %s\n",
  335. (NULL == server_name) ? "the SERVER" : "a CLIENT");
  336. PR_fprintf(err,
  337. "Transport being used is %s\n",
  338. (6 == protocol) ? "TCP" : "XTP");
  339. if (PR_GLOBAL_THREAD == thread_scope)
  340. {
  341. if (1 != concurrency)
  342. {
  343. PR_fprintf(err, " **Concurrency > 1 and GLOBAL threads!?!?\n");
  344. PR_fprintf(err, " **Ignoring concurrency\n");
  345. concurrency = 1;
  346. }
  347. }
  348. if (1 != concurrency)
  349. {
  350. PR_SetConcurrency(concurrency);
  351. PR_fprintf(err, "Concurrency set to %u\n", concurrency);
  352. }
  353. PR_fprintf(err,
  354. "All threads will be %s\n",
  355. (PR_GLOBAL_THREAD == thread_scope) ? "GLOBAL" : "LOCAL");
  356. PR_fprintf(err, "Client buffer size will be %u\n", buffer_size);
  357. if (-1 != xport_buffer)
  358. PR_fprintf(
  359. err, "Transport send & receive buffer size will be %u\n", xport_buffer);
  360. if (NULL == server_name) {
  361. Server();
  362. }
  363. else {
  364. Client(server_name);
  365. }
  366. return 0;
  367. } /* main */
  368. /* thruput.c */