]> git.saurik.com Git - apple/xnu.git/blob - tools/tests/MMTest/MPMMtest.c
xnu-1228.tar.gz
[apple/xnu.git] / tools / tests / MMTest / MPMMtest.c
1 #include <AvailabilityMacros.h>
2 #ifdef AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER
3 #include </System/Library/Frameworks/System.framework/PrivateHeaders/mach/thread_policy.h>
4 #endif
5
6 #include <pthread.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <err.h>
11
12 #include <pthread.h>
13 #include <mach/mach.h>
14 #include <mach/mach_error.h>
15 #include <mach/notify.h>
16 #include <servers/bootstrap.h>
17 #include <sys/types.h>
18 #include <sys/time.h>
19 #include <sys/signal.h>
20
21 #define MAX(A, B) ((A) < (B) ? (B) : (A))
22
23 typedef struct {
24 unsigned int msgt_name : 8,
25 msgt_size : 8,
26 msgt_number : 12,
27 msgt_inline : 1,
28 msgt_longform : 1,
29 msgt_deallocate : 1,
30 msgt_unused : 1;
31 } mach_msg_type_t;
32
33 typedef struct {
34 mach_msg_type_t msgtl_header;
35 unsigned short msgtl_name;
36 unsigned short msgtl_size;
37 natural_t msgtl_number;
38 } mach_msg_type_long_t;
39 #define MACH_MSG_TYPE_INTEGER_32 0
40
41
42 typedef struct {
43 mach_msg_header_t header;
44 mach_msg_trailer_t trailer; // subtract this when sending
45 } ipc_trivial_message;
46
47 typedef struct {
48 mach_msg_header_t header;
49 mach_msg_type_t type;
50 u_int32_t numbers[0];
51 mach_msg_trailer_t trailer; // subtract this when sending
52 } ipc_inline_message;
53
54 typedef struct {
55 mach_msg_header_t header;
56 mach_msg_body_t body;
57 mach_msg_ool_descriptor_t descriptor;
58 mach_msg_trailer_t trailer; // subtract this when sending
59 } ipc_complex_message;
60
61 enum {
62 msg_type_trivial = 0,
63 msg_type_inline = 1,
64 msg_type_complex = 2
65 };
66
67 struct port_args {
68 int server_num;
69 int req_size;
70 mach_msg_header_t *req_msg;
71 int reply_size;
72 mach_msg_header_t *reply_msg;
73 mach_port_t port;
74 };
75
76 typedef union {
77 pid_t pid;
78 pthread_t tid;
79 } thread_id_t;
80
81 /* Global options */
82 static boolean_t verbose = FALSE;
83 static boolean_t affinity = FALSE;
84 static boolean_t timeshare = FALSE;
85 static boolean_t threaded = FALSE;
86 static boolean_t oneway = FALSE;
87 int msg_type;
88 int num_ints;
89 int num_msgs;
90 int num_clients;
91 int num_servers;
92 int client_delay;
93 int client_spin;
94 int client_pages;
95 char **server_port_name;
96
97 void signal_handler(int sig) {
98 }
99
100 void usage(const char *progname) {
101 fprintf(stderr, "usage: %s [options]\n", progname);
102 fprintf(stderr, "where options are:\n");
103 fprintf(stderr, " -affinity\t\tthreads use affinity\n");
104 fprintf(stderr, " -timeshare\t\tthreads use timeshare\n");
105 fprintf(stderr, " -threaded\t\tuse (p)threads\n");
106 fprintf(stderr, " -verbose\t\tbe verbose\n");
107 fprintf(stderr, " -oneway\t\tdo not request return reply\n");
108 fprintf(stderr, " -count num\t\tnumber of messages to send\n");
109 fprintf(stderr, " -type trivial|inline|complex\ttype of messages to send\n");
110 fprintf(stderr, " -numints num\tnumber of 32-bit ints to send in messages\n");
111 fprintf(stderr, " -servers num\tnumber of servers threads to run\n");
112 fprintf(stderr, " -clients num\tnumber of clients per server\n");
113 fprintf(stderr, " -delay num\t\tmicroseconds to sleep clients between messages\n");
114 fprintf(stderr, " -work num\t\tmicroseconds of client work\n");
115 fprintf(stderr, " -pages num\t\tpages of memory touched by client work\n");
116 fprintf(stderr, "default values are:\n");
117 fprintf(stderr, " . no affinity\n");
118 fprintf(stderr, " . not timeshare\n");
119 fprintf(stderr, " . not verbose\n");
120 fprintf(stderr, " . not oneway\n");
121 fprintf(stderr, " . client sends 100000 messages\n");
122 fprintf(stderr, " . inline message type\n");
123 fprintf(stderr, " . 64 32-bit integers in inline/complex messages\n");
124 fprintf(stderr, " . (num_available_processors+1)%%2 servers\n");
125 fprintf(stderr, " . 4 clients per server\n");
126 fprintf(stderr, " . no delay\n");
127 exit(1);
128 }
129
130 void parse_args(int argc, char *argv[]) {
131 host_basic_info_data_t info;
132 mach_msg_type_number_t count;
133 kern_return_t result;
134
135 /* Initialize defaults */
136 msg_type = msg_type_trivial;
137 num_ints = 64;
138 num_msgs = 100000;
139 client_delay = 0;
140 num_clients = 4;
141
142 count = HOST_BASIC_INFO_COUNT;
143 result = host_info(mach_host_self(), HOST_BASIC_INFO,
144 (host_info_t)&info, &count);
145 if (result == KERN_SUCCESS && info.avail_cpus > 1)
146 num_servers = info.avail_cpus / 2;
147 else
148 num_servers = 1;
149
150 const char *progname = argv[0];
151 argc--; argv++;
152 while (0 < argc) {
153 if (0 == strcmp("-verbose", argv[0])) {
154 verbose = TRUE;
155 argc--; argv++;
156 } else if (0 == strcmp("-affinity", argv[0])) {
157 affinity = TRUE;
158 argc--; argv++;
159 } else if (0 == strcmp("-timeshare", argv[0])) {
160 timeshare = TRUE;
161 argc--; argv++;
162 } else if (0 == strcmp("-threaded", argv[0])) {
163 threaded = TRUE;
164 argc--; argv++;
165 } else if (0 == strcmp("-oneway", argv[0])) {
166 oneway = TRUE;
167 argc--; argv++;
168 } else if (0 == strcmp("-type", argv[0])) {
169 if (argc < 2)
170 usage(progname);
171 if (0 == strcmp("trivial", argv[1])) {
172 msg_type = msg_type_trivial;
173 } else if (0 == strcmp("inline", argv[1])) {
174 msg_type = msg_type_inline;
175 } else if (0 == strcmp("complex", argv[1])) {
176 msg_type = msg_type_complex;
177 } else
178 usage(progname);
179 argc -= 2; argv += 2;
180 } else if (0 == strcmp("-numints", argv[0])) {
181 if (argc < 2)
182 usage(progname);
183 num_ints = strtoul(argv[1], NULL, 0);
184 argc -= 2; argv += 2;
185 } else if (0 == strcmp("-count", argv[0])) {
186 if (argc < 2)
187 usage(progname);
188 num_msgs = strtoul(argv[1], NULL, 0);
189 argc -= 2; argv += 2;
190 } else if (0 == strcmp("-clients", argv[0])) {
191 if (argc < 2)
192 usage(progname);
193 num_clients = strtoul(argv[1], NULL, 0);
194 argc -= 2; argv += 2;
195 } else if (0 == strcmp("-servers", argv[0])) {
196 if (argc < 2)
197 usage(progname);
198 num_servers = strtoul(argv[1], NULL, 0);
199 argc -= 2; argv += 2;
200 } else if (0 == strcmp("-delay", argv[0])) {
201 if (argc < 2)
202 usage(progname);
203 client_delay = strtoul(argv[1], NULL, 0);
204 argc -= 2; argv += 2;
205 } else if (0 == strcmp("-spin", argv[0])) {
206 if (argc < 2)
207 usage(progname);
208 client_spin = strtoul(argv[1], NULL, 0);
209 argc -= 2; argv += 2;
210 } else if (0 == strcmp("-pages", argv[0])) {
211 if (argc < 2)
212 usage(progname);
213 client_pages = strtoul(argv[1], NULL, 0);
214 argc -= 2; argv += 2;
215 } else
216 usage(progname);
217 }
218 }
219
220 void setup_server_ports(struct port_args *ports)
221 {
222 kern_return_t ret = 0;
223 mach_port_t bsport;
224
225 ports->req_size = MAX(sizeof(ipc_inline_message) +
226 sizeof(u_int32_t) * num_ints,
227 sizeof(ipc_complex_message));
228 ports->reply_size = sizeof(ipc_trivial_message) -
229 sizeof(mach_msg_trailer_t);
230 ports->req_msg = malloc(ports->req_size);
231 ports->reply_msg = malloc(ports->reply_size);
232
233 ret = mach_port_allocate(mach_task_self(),
234 MACH_PORT_RIGHT_RECEIVE,
235 &(ports->port));
236 if (KERN_SUCCESS != ret) {
237 mach_error("mach_port_allocate(): ", ret);
238 exit(1);
239 }
240
241 ret = mach_port_insert_right(mach_task_self(),
242 ports->port,
243 ports->port,
244 MACH_MSG_TYPE_MAKE_SEND);
245 if (KERN_SUCCESS != ret) {
246 mach_error("mach_port_insert_right(): ", ret);
247 exit(1);
248 }
249
250 ret = task_get_bootstrap_port(mach_task_self(), &bsport);
251 if (KERN_SUCCESS != ret) {
252 mach_error("task_get_bootstrap_port(): ", ret);
253 exit(1);
254 }
255
256 if (verbose) {
257 printf("server waiting for IPC messages from client on port '%s'.\n",
258 server_port_name[ports->server_num]);
259 }
260 ret = bootstrap_register(bsport,
261 server_port_name[ports->server_num],
262 ports->port);
263 if (KERN_SUCCESS != ret) {
264 mach_error("bootstrap_register(): ", ret);
265 exit(1);
266 }
267 }
268
269 void setup_client_ports(struct port_args *ports)
270 {
271 kern_return_t ret = 0;
272 switch(msg_type) {
273 case msg_type_trivial:
274 ports->req_size = sizeof(ipc_trivial_message);
275 break;
276 case msg_type_inline:
277 ports->req_size = sizeof(ipc_inline_message) +
278 sizeof(u_int32_t) * num_ints;
279 break;
280 case msg_type_complex:
281 ports->req_size = sizeof(ipc_complex_message);
282 break;
283 }
284 ports->req_size -= sizeof(mach_msg_trailer_t);
285 ports->reply_size = sizeof(ipc_trivial_message);
286 ports->req_msg = malloc(ports->req_size);
287 ports->reply_msg = malloc(ports->reply_size);
288
289 ret = mach_port_allocate(mach_task_self(),
290 MACH_PORT_RIGHT_RECEIVE,
291 &(ports->port));
292 if (KERN_SUCCESS != ret) {
293 mach_error("mach_port_allocate(): ", ret);
294 exit(1);
295 }
296 if (verbose) {
297 printf("Client sending %d %s IPC messages to port '%s' in %s mode.\n",
298 num_msgs, (msg_type == msg_type_inline) ?
299 "inline" : ((msg_type == msg_type_complex) ?
300 "complex" : "trivial"),
301 server_port_name[ports->server_num],
302 (oneway ? "oneway" : "rpc"));
303 }
304
305 }
306
307
308 static void
309 thread_setup(int tag) {
310 #ifdef AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER
311 kern_return_t ret;
312 thread_extended_policy_data_t epolicy;
313 thread_affinity_policy_data_t policy;
314
315 if (!timeshare) {
316 epolicy.timeshare = FALSE;
317 ret = thread_policy_set(
318 mach_thread_self(), THREAD_EXTENDED_POLICY,
319 (thread_policy_t) &epolicy,
320 THREAD_EXTENDED_POLICY_COUNT);
321 if (ret != KERN_SUCCESS)
322 printf("thread_policy_set(THREAD_EXTENDED_POLICY) returned %d\n", ret);
323 }
324
325 if (affinity) {
326 policy.affinity_tag = tag;
327 ret = thread_policy_set(
328 mach_thread_self(), THREAD_AFFINITY_POLICY,
329 (thread_policy_t) &policy,
330 THREAD_AFFINITY_POLICY_COUNT);
331 if (ret != KERN_SUCCESS)
332 printf("thread_policy_set(THREAD_AFFINITY_POLICY) returned %d\n", ret);
333 }
334 #endif
335 }
336
337 void *
338 server(void *serverarg)
339 {
340 struct port_args args;
341 int idx;
342 kern_return_t ret;
343 int totalmsg = num_msgs * num_clients;
344
345 args.server_num = (int) (long) serverarg;
346 setup_server_ports(&args);
347
348 thread_setup(args.server_num + 1);
349
350 for (idx = 0; idx < totalmsg; idx++) {
351 if (verbose)
352 printf("server awaiting message %d\n", idx);
353 args.req_msg->msgh_bits = 0;
354 args.req_msg->msgh_size = args.req_size;
355 args.req_msg->msgh_local_port = args.port;
356 ret = mach_msg(args.req_msg,
357 MACH_RCV_MSG|MACH_RCV_INTERRUPT|MACH_RCV_LARGE,
358 0,
359 args.req_size,
360 args.port,
361 MACH_MSG_TIMEOUT_NONE,
362 MACH_PORT_NULL);
363 if (MACH_RCV_INTERRUPTED == ret)
364 break;
365 if (MACH_MSG_SUCCESS != ret) {
366 if (verbose)
367 printf("mach_msg() ret=%d", ret);
368 mach_error("mach_msg (receive): ", ret);
369 exit(1);
370 }
371 if (verbose)
372 printf("server received message %d\n", idx);
373 if (args.req_msg->msgh_bits & MACH_MSGH_BITS_COMPLEX) {
374 ret = vm_deallocate(mach_task_self(),
375 (vm_address_t)((ipc_complex_message *)args.req_msg)->descriptor.address,
376 ((ipc_complex_message *)args.req_msg)->descriptor.size);
377 }
378
379 if (1 == args.req_msg->msgh_id) {
380 if (verbose)
381 printf("server sending reply %d\n", idx);
382 args.reply_msg->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND,
383 MACH_MSG_TYPE_MAKE_SEND);
384 args.reply_msg->msgh_size = args.reply_size;
385 args.reply_msg->msgh_remote_port = args.req_msg->msgh_remote_port;
386 args.reply_msg->msgh_local_port = args.req_msg->msgh_local_port;
387 args.reply_msg->msgh_id = 2;
388 ret = mach_msg(args.reply_msg,
389 MACH_SEND_MSG,
390 args.reply_size,
391 0,
392 MACH_PORT_NULL,
393 MACH_MSG_TIMEOUT_NONE,
394 MACH_PORT_NULL);
395 if (MACH_MSG_SUCCESS != ret) {
396 mach_error("mach_msg (send): ", ret);
397 exit(1);
398 }
399 }
400 }
401 }
402
403 static inline void
404 client_spin_loop(unsigned count, void (fn)(void))
405 {
406 while (count--)
407 fn();
408 }
409
410 static long dummy_memory;
411 static long *client_memory = &dummy_memory;
412 static void
413 client_work_atom(void)
414 {
415 static int i;
416
417 if (++i > client_pages * PAGE_SIZE / sizeof(long))
418 i = 0;
419 client_memory[i] = 0;
420 }
421
422 static int calibration_count = 10000;
423 static int calibration_usec;
424 static void *
425 calibrate_client_work(void)
426 {
427 long dummy;
428 struct timeval nowtv;
429 struct timeval warmuptv = { 0, 100 * 1000 }; /* 100ms */
430 struct timeval starttv;
431 struct timeval endtv;
432
433 if (client_spin) {
434 /* Warm-up the stepper first... */
435 gettimeofday(&nowtv, NULL);
436 timeradd(&nowtv, &warmuptv, &endtv);
437 do {
438 client_spin_loop(calibration_count, client_work_atom);
439 gettimeofday(&nowtv, NULL);
440 } while (timercmp(&nowtv, &endtv, < ));
441
442 /* Now do the calibration */
443 while (TRUE) {
444 gettimeofday(&starttv, NULL);
445 client_spin_loop(calibration_count, client_work_atom);
446 gettimeofday(&endtv, NULL);
447 if (endtv.tv_sec - starttv.tv_sec > 1) {
448 calibration_count /= 10;
449 continue;
450 }
451 calibration_usec = endtv.tv_usec - starttv.tv_usec;
452 if (endtv.tv_usec < starttv.tv_usec) {
453 calibration_usec += 1000000;
454 }
455 if (calibration_usec < 1000) {
456 calibration_count *= 10;
457 continue;
458 }
459 calibration_count /= calibration_usec;
460 break;
461 }
462 if (verbose)
463 printf("calibration_count=%d calibration_usec=%d\n",
464 calibration_count, calibration_usec);
465 }
466 }
467
468 static void *
469 client_work(void)
470 {
471
472 if (client_spin) {
473 client_spin_loop(calibration_count*client_spin,
474 client_work_atom);
475 }
476
477 if (client_delay) {
478 usleep(client_delay);
479 }
480 }
481
482 void *client(void *threadarg)
483 {
484 struct port_args args;
485 int idx;
486 mach_msg_header_t *req, *reply;
487 mach_port_t bsport, servport;
488 kern_return_t ret;
489 long server_num = (long) threadarg;
490 void *ints = malloc(sizeof(u_int32_t) * num_ints);
491
492 if (verbose)
493 printf("client(%d) started, server port name %s\n",
494 server_num, server_port_name[server_num]);
495
496 args.server_num = server_num;
497 thread_setup(server_num + 1);
498
499 /* find server port */
500 ret = task_get_bootstrap_port(mach_task_self(), &bsport);
501 if (KERN_SUCCESS != ret) {
502 mach_error("task_get_bootstrap_port(): ", ret);
503 exit(1);
504 }
505 ret = bootstrap_look_up(bsport,
506 server_port_name[server_num],
507 &servport);
508 if (KERN_SUCCESS != ret) {
509 mach_error("bootstrap_look_up(): ", ret);
510 exit(1);
511 }
512
513 setup_client_ports(&args);
514
515 /* Allocate and touch memory */
516 if (client_pages) {
517 unsigned i;
518 client_memory = (long *) malloc(client_pages * PAGE_SIZE);
519 for (i = 0; i < client_pages; i++)
520 client_memory[i * PAGE_SIZE / sizeof(long)] = 0;
521 }
522
523 /* start message loop */
524 for (idx = 0; idx < num_msgs; idx++) {
525 req = args.req_msg;
526 reply = args.reply_msg;
527
528 req->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND,
529 MACH_MSG_TYPE_MAKE_SEND);
530 req->msgh_size = args.req_size;
531 req->msgh_remote_port = servport;
532 req->msgh_local_port = args.port;
533 req->msgh_id = oneway ? 0 : 1;
534 switch (msg_type) {
535 case msg_type_trivial:
536 break;
537 case msg_type_inline:
538 ((ipc_inline_message *)req)->type.msgt_name = MACH_MSG_TYPE_INTEGER_32;
539 ((ipc_inline_message *)req)->type.msgt_size = 32;
540 ((ipc_inline_message *)req)->type.msgt_number = num_ints;
541 ((ipc_inline_message *)req)->type.msgt_inline = TRUE;
542 ((ipc_inline_message *)req)->type.msgt_longform = FALSE;
543 ((ipc_inline_message *)req)->type.msgt_deallocate = FALSE;
544 ((ipc_inline_message *)req)->type.msgt_unused = 0;
545 break;
546 case msg_type_complex:
547 (req)->msgh_bits |= MACH_MSGH_BITS_COMPLEX;
548 ((ipc_complex_message *)req)->body.msgh_descriptor_count = 1;
549 ((ipc_complex_message *)req)->descriptor.address = ints;
550 ((ipc_complex_message *)req)->descriptor.size =
551 num_ints * sizeof(u_int32_t);
552 ((ipc_complex_message *)req)->descriptor.deallocate = FALSE;
553 ((ipc_complex_message *)req)->descriptor.copy = MACH_MSG_VIRTUAL_COPY;
554 ((ipc_complex_message *)req)->descriptor.type = MACH_MSG_OOL_DESCRIPTOR;
555 break;
556 }
557 if (verbose)
558 printf("client sending message %d\n", idx);
559 ret = mach_msg(req,
560 MACH_SEND_MSG,
561 args.req_size,
562 0,
563 MACH_PORT_NULL,
564 MACH_MSG_TIMEOUT_NONE,
565 MACH_PORT_NULL);
566 if (MACH_MSG_SUCCESS != ret) {
567 mach_error("mach_msg (send): ", ret);
568 fprintf(stderr, "bailing after %u iterations\n", idx);
569 exit(1);
570 break;
571 }
572 if (!oneway) {
573 if (verbose)
574 printf("client awaiting reply %d\n", idx);
575 reply->msgh_bits = 0;
576 reply->msgh_size = args.reply_size;
577 reply->msgh_local_port = args.port;
578 ret = mach_msg(args.reply_msg,
579 MACH_RCV_MSG|MACH_RCV_INTERRUPT,
580 0,
581 args.reply_size,
582 args.port,
583 MACH_MSG_TIMEOUT_NONE,
584 MACH_PORT_NULL);
585 if (MACH_MSG_SUCCESS != ret) {
586 mach_error("mach_msg (receive): ", ret);
587 fprintf(stderr, "bailing after %u iterations\n",
588 idx);
589 exit(1);
590 }
591 if (verbose)
592 printf("client received reply %d\n", idx);
593 }
594
595 client_work();
596 }
597
598 free(ints);
599 return;
600 }
601
602 static void
603 thread_spawn(thread_id_t *thread, void *(fn)(void *), void *arg) {
604 if (threaded) {
605 kern_return_t ret;
606 ret = pthread_create(
607 &thread->tid,
608 NULL,
609 fn,
610 arg);
611 if (ret != 0)
612 err(1, "pthread_create()");
613 if (verbose)
614 printf("created pthread 0x%x\n", thread->tid);
615 } else {
616 thread->pid = fork();
617 if (thread->pid == 0) {
618 if (verbose)
619 printf("calling 0x%x(0x%x)\n", fn, arg);
620 fn(arg);
621 exit(0);
622 }
623 if (verbose)
624 printf("forked pid %d\n", thread->pid);
625 }
626 }
627
628 static void
629 thread_join(thread_id_t *thread) {
630 if (threaded) {
631 kern_return_t ret;
632 if (verbose)
633 printf("joining thread 0x%x\n", thread->tid);
634 ret = pthread_join(thread->tid, NULL);
635 if (ret != KERN_SUCCESS)
636 err(1, "pthread_join(0x%x)", thread->tid);
637 } else {
638 int stat;
639 if (verbose)
640 printf("waiting for pid %d\n", thread->pid);
641 waitpid(thread->pid, &stat, 0);
642 }
643 }
644
645 static void
646 wait_for_servers(void)
647 {
648 int i;
649 int retry_count = 10;
650 mach_port_t bsport, servport;
651 kern_return_t ret;
652
653 /* find server port */
654 ret = task_get_bootstrap_port(mach_task_self(), &bsport);
655 if (KERN_SUCCESS != ret) {
656 mach_error("task_get_bootstrap_port(): ", ret);
657 exit(1);
658 }
659
660 while (retry_count-- > 0) {
661 for (i = 0; i < num_servers; i++) {
662 ret = bootstrap_look_up(bsport,
663 server_port_name[i],
664 &servport);
665 if (ret != KERN_SUCCESS) {
666 break;
667 }
668 }
669 if (ret == KERN_SUCCESS)
670 return;
671 usleep(100 * 1000); /* 100ms */
672 }
673 fprintf(stderr, "Server(s) failed to register\n");
674 exit(1);
675 }
676
677 int main(int argc, char *argv[])
678 {
679 int i;
680 int j;
681 thread_id_t *client_id;
682 thread_id_t *server_id;
683
684 signal(SIGINT, signal_handler);
685 parse_args(argc, argv);
686
687 calibrate_client_work();
688
689 /*
690 * If we're using affinity create an empty namespace now
691 * so this is shared by all our offspring.
692 */
693 if (affinity)
694 thread_setup(0);
695
696 server_id = (thread_id_t *) malloc(num_servers * sizeof(thread_id_t));
697 server_port_name = (char **) malloc(num_servers * sizeof(char *));
698 if (verbose)
699 printf("creating %d servers\n", num_servers);
700 for (i = 0; i < num_servers; i++) {
701 server_port_name[i] = (char *) malloc(sizeof("PORT.pppppp.xx"));
702 /* PORT names include pid of main process for disambiguation */
703 sprintf(server_port_name[i], "PORT.%06d.%02d", getpid(), i);
704 thread_spawn(&server_id[i], server, (void *) (long) i);
705 }
706
707 int totalclients = num_servers * num_clients;
708 int totalmsg = num_msgs * totalclients;
709 struct timeval starttv, endtv, deltatv;
710
711 /*
712 * Wait for all servers to have registered all ports before starting
713 * the clients and the clock.
714 */
715 wait_for_servers();
716
717 printf("%d server%s, %d client%s per server (%d total) %u messages...",
718 num_servers, (num_servers > 1)? "s" : "",
719 num_clients, (num_clients > 1)? "s" : "",
720 totalclients,
721 totalmsg);
722 fflush(stdout);
723
724 /* Call gettimeofday() once and throw away result; some implementations
725 * (like Mach's) cache some time zone info on first call.
726 */
727 gettimeofday(&starttv, NULL);
728 gettimeofday(&starttv, NULL);
729
730 client_id = (thread_id_t *) malloc(totalclients * sizeof(thread_id_t));
731 if (verbose)
732 printf("creating %d clients\n", totalclients);
733 for (i = 0; i < num_servers; i++) {
734 for (j = 0; j < num_clients; j++) {
735 thread_spawn(
736 &client_id[(i*num_clients) + j],
737 client,
738 (void *) (long) i);
739 }
740 }
741
742 /* Wait for servers to complete */
743 for (i = 0; i < num_servers; i++) {
744 thread_join(&server_id[i]);
745 }
746
747 gettimeofday(&endtv, NULL);
748
749 for (i = 0; i < totalclients; i++) {
750 thread_join(&client_id[i]);
751 }
752
753 /* report results */
754 deltatv.tv_sec = endtv.tv_sec - starttv.tv_sec;
755 deltatv.tv_usec = endtv.tv_usec - starttv.tv_usec;
756 if (endtv.tv_usec < starttv.tv_usec) {
757 deltatv.tv_sec--;
758 deltatv.tv_usec += 1000000;
759 }
760
761 double dsecs = (double) deltatv.tv_sec +
762 1.0E-6 * (double) deltatv.tv_usec;
763
764 printf(" in %u.%03u seconds\n",
765 deltatv.tv_sec, deltatv.tv_usec/1000);
766 printf(" throughput in messages/sec: %g\n",
767 (double)totalmsg / dsecs);
768 printf(" average message latency (usec): %2.3g\n",
769 dsecs * 1.0E6 / (double) totalmsg);
770
771 return (0);
772
773 }