1#include <pthread.h> 2#include <stdio.h> 3#include <stdlib.h> 4#include <string.h> 5 6#include <mach/mach.h> 7#include <mach/mach_error.h> 8#include <mach/notify.h> 9#include <servers/bootstrap.h> 10#include <sys/types.h> 11#include <sys/time.h> 12#include <sys/signal.h> 13 14#define MAX(A, B) ((A) < (B) ? (B) : (A)) 15 16static __inline__ unsigned long long ReadTSR() { 17 union { 18 unsigned long long time64; 19 unsigned long word[2]; 20 } now; 21#if defined(__i386__) 22 /* Read from Pentium and Pentium Pro 64-bit timestamp counter. 23 * The counter is set to 0 at processor reset and increments on 24 * every clock cycle. */ 25 __asm__ volatile("rdtsc" : : : "eax", "edx"); 26 __asm__ volatile("movl %%eax,%0" : "=m" (now.word[0]) : : "eax"); 27 __asm__ volatile("movl %%edx,%0" : "=m" (now.word[1]) : : "edx"); 28#elif defined(__ppc__) 29 /* Read from PowerPC 64-bit time base register. The increment 30 * rate of the time base is implementation-dependent, but is 31 * 1/4th the bus clock cycle on 603/604 processors. */ 32 unsigned long t3; 33 do { 34 __asm__ volatile("mftbu %0" : "=r" (now.word[0])); 35 __asm__ volatile("mftb %0" : "=r" (now.word[1])); 36 __asm__ volatile("mftbu %0" : "=r" (t3)); 37 } while (now.word[0] != t3); 38#else 39#warning Do not know how to read a time stamp register on this architecture 40 now.time64 = 0ULL; 41#endif 42 return now.time64; 43} 44 45typedef struct { 46 unsigned int msgt_name : 8, 47 msgt_size : 8, 48 msgt_number : 12, 49 msgt_inline : 1, 50 msgt_longform : 1, 51 msgt_deallocate : 1, 52 msgt_unused : 1; 53} mach_msg_type_t; 54 55typedef struct { 56 mach_msg_type_t msgtl_header; 57 unsigned short msgtl_name; 58 unsigned short msgtl_size; 59 natural_t msgtl_number; 60} mach_msg_type_long_t; 61#define MACH_MSG_TYPE_INTEGER_32 0 62 63 64typedef struct { 65 mach_msg_header_t header; 66 mach_msg_trailer_t trailer; // subtract this when sending 67} ipc_trivial_message; 68 69typedef struct { 70 mach_msg_header_t header; 71 mach_msg_type_t type; 72 u_int32_t numbers[0]; 73 mach_msg_trailer_t trailer; // subtract this when sending 74} ipc_inline_message; 75 76typedef struct { 77 mach_msg_header_t header; 78 mach_msg_body_t body; 79 mach_msg_ool_descriptor_t descriptor; 80 mach_msg_trailer_t trailer; // subtract this when sending 81} ipc_complex_message; 82 83enum { 84 msg_type_trivial = 0, 85 msg_type_inline = 1, 86 msg_type_complex = 2 87}; 88 89struct port_args { 90 int req_size; 91 mach_msg_header_t *req_msg; 92 int reply_size; 93 mach_msg_header_t *reply_msg; 94 mach_port_t port; 95}; 96 97/* Global options */ 98static int verbose; 99int oneway; 100int msg_type; 101int num_ints; 102int num_msgs; 103int num_clients; 104int client_delay; 105char *server_port_name; 106 107void signal_handler(int sig) { 108} 109 110void usage(const char *progname) { 111 fprintf(stderr, "usage: %s [options]\n", progname); 112 fprintf(stderr, "where options are:\n"); 113 fprintf(stderr, " -verbose\t\tbe verbose\n"); 114 fprintf(stderr, " -oneway\t\tdo not request return reply\n"); 115 fprintf(stderr, " -count num\t\tnumber of messages to send\n"); 116 fprintf(stderr, " -type trivial|inline|complex\ttype of messages to send\n"); 117 fprintf(stderr, " -numints num\tnumber of 32-bit ints to send in messages\n"); 118 fprintf(stderr, " -clients num\tnumber of client threads to run\n"); 119 fprintf(stderr, " -delay num\t\tmicroseconds to sleep clients between messages\n"); 120 fprintf(stderr, " -name portname\tname of port on which to communicate\n"); 121 fprintf(stderr, "default values are:\n"); 122 fprintf(stderr, " . not verbose\n"); 123 fprintf(stderr, " . not oneway\n"); 124 fprintf(stderr, " . client sends 10000 messages\n"); 125 fprintf(stderr, " . inline message type\n"); 126 fprintf(stderr, " . 64 32-bit integers in inline/complex messages\n"); 127 fprintf(stderr, " . avail_cpus - 1 clients\n"); 128 fprintf(stderr, " . no delay\n"); 129 fprintf(stderr, " . port name 'TEST'\n"); 130 exit(1); 131} 132 133void parse_args(int argc, char *argv[]) { 134 host_basic_info_data_t info; 135 mach_msg_type_number_t count; 136 kern_return_t result; 137 138 /* Initialize defaults */ 139 verbose = 0; 140 oneway = 0; 141 msg_type = msg_type_trivial; 142 num_ints = 64; 143 num_msgs = 10000; 144 client_delay = 0; 145 server_port_name = "TEST"; 146 147 count = HOST_BASIC_INFO_COUNT; 148 result = host_info(mach_host_self(), HOST_BASIC_INFO, 149 (host_info_t)&info, &count); 150 if (result == KERN_SUCCESS) 151 num_clients = MAX(1, info.avail_cpus - 1); 152 else 153 num_clients = 1; 154 155 const char *progname = argv[0]; 156 argc--; argv++; 157 while (0 < argc) { 158 if (0 == strcmp("-verbose", argv[0])) { 159 verbose = 1; 160 argc--; argv++; 161 } else if (0 == strcmp("-oneway", argv[0])) { 162 oneway = 1; 163 argc--; argv++; 164 } else if (0 == strcmp("-type", argv[0])) { 165 if (argc < 2) 166 usage(progname); 167 if (0 == strcmp("trivial", argv[1])) { 168 msg_type = msg_type_trivial; 169 } else if (0 == strcmp("inline", argv[1])) { 170 msg_type = msg_type_inline; 171 } else if (0 == strcmp("complex", argv[1])) { 172 msg_type = msg_type_complex; 173 } else 174 usage(progname); 175 argc -= 2; argv += 2; 176 } else if (0 == strcmp("-name", argv[0])) { 177 if (argc < 2) 178 usage(progname); 179 server_port_name = argv[1]; 180 argc -= 2; argv += 2; 181 } else if (0 == strcmp("-numints", argv[0])) { 182 if (argc < 2) 183 usage(progname); 184 num_ints = strtoul(argv[1], NULL, 0); 185 argc -= 2; argv += 2; 186 } else if (0 == strcmp("-count", argv[0])) { 187 if (argc < 2) 188 usage(progname); 189 num_msgs = strtoul(argv[1], NULL, 0); 190 argc -= 2; argv += 2; 191 } else if (0 == strcmp("-clients", argv[0])) { 192 if (argc < 2) 193 usage(progname); 194 num_clients = strtoul(argv[1], NULL, 0); 195 argc -= 2; argv += 2; 196 } else if (0 == strcmp("-delay", argv[0])) { 197 if (argc < 2) 198 usage(progname); 199 client_delay = strtoul(argv[1], NULL, 0); 200 argc -= 2; argv += 2; 201 } else 202 usage(progname); 203 } 204} 205 206void setup_server_ports(struct port_args *ports) 207{ 208 kern_return_t ret = 0; 209 mach_port_t bsport; 210 211 ports->req_size = MAX(sizeof(ipc_inline_message) + 212 sizeof(u_int32_t) * num_ints, 213 sizeof(ipc_complex_message)); 214 ports->reply_size = sizeof(ipc_trivial_message) - 215 sizeof(mach_msg_trailer_t); 216 ports->req_msg = malloc(ports->req_size); 217 ports->reply_msg = malloc(ports->reply_size); 218 219 ret = mach_port_allocate(mach_task_self(), 220 MACH_PORT_RIGHT_RECEIVE, 221 &(ports->port)); 222 if (KERN_SUCCESS != ret) { 223 mach_error("mach_port_allocate(): ", ret); 224 exit(1); 225 } 226 227 ret = mach_port_insert_right(mach_task_self(), 228 ports->port, 229 ports->port, 230 MACH_MSG_TYPE_MAKE_SEND); 231 if (KERN_SUCCESS != ret) { 232 mach_error("mach_port_insert_right(): ", ret); 233 exit(1); 234 } 235 236 ret = task_get_bootstrap_port(mach_task_self(), &bsport); 237 if (KERN_SUCCESS != ret) { 238 mach_error("task_get_bootstrap_port(): ", ret); 239 exit(1); 240 } 241 242 ret = bootstrap_register(bsport, server_port_name, ports->port); 243 if (KERN_SUCCESS != ret) { 244 mach_error("bootstrap_register(): ", ret); 245 exit(1); 246 } 247 if (verbose) { 248 printf("server waiting for IPC messages from client on port '%s'.\n", 249 server_port_name); 250 } 251} 252 253void setup_client_ports(struct port_args *ports) 254{ 255 kern_return_t ret = 0; 256 switch(msg_type) { 257 case msg_type_trivial: 258 ports->req_size = sizeof(ipc_trivial_message); 259 break; 260 case msg_type_inline: 261 ports->req_size = sizeof(ipc_inline_message) + 262 sizeof(u_int32_t) * num_ints; 263 break; 264 case msg_type_complex: 265 ports->req_size = sizeof(ipc_complex_message); 266 break; 267 } 268 ports->req_size -= sizeof(mach_msg_trailer_t); 269 ports->reply_size = sizeof(ipc_trivial_message); 270 ports->req_msg = malloc(ports->req_size); 271 ports->reply_msg = malloc(ports->reply_size); 272 273 ret = mach_port_allocate(mach_task_self(), 274 MACH_PORT_RIGHT_RECEIVE, 275 &(ports->port)); 276 if (KERN_SUCCESS != ret) { 277 mach_error("mach_port_allocate(): ", ret); 278 exit(1); 279 } 280 if (verbose) { 281 printf("Client sending %d %s IPC messages to port '%s' in %s mode.\n", 282 num_msgs, (msg_type == msg_type_inline) ? 283 "inline" : ((msg_type == msg_type_complex) ? 284 "complex" : "trivial"), 285 server_port_name, (oneway ? "oneway" : "rpc")); 286 } 287 288} 289 290void server(struct port_args *args) 291{ 292 int idx; 293 kern_return_t ret; 294 int totalmsg = num_msgs * num_clients; 295 296 unsigned long long starttsc, endtsc, deltatsc; 297 struct timeval starttv, endtv, deltatv; 298 299 /* Call gettimeofday() once and throw away result; some implementations 300 * (like Mach's) cache some time zone info on first call. Then, call 301 * ReadTSR in case that helps warm things up, again discarding the 302 * results. 303 */ 304 gettimeofday(&starttv, NULL); 305 ReadTSR(); 306 307 gettimeofday(&starttv, NULL); 308 starttsc = ReadTSR(); 309 310 for (idx = 0; idx < totalmsg; idx++) { 311 if (verbose) 312 printf("server awaiting message %d\n", idx); 313 args->req_msg->msgh_bits = 0; 314 args->req_msg->msgh_size = args->req_size; 315 args->req_msg->msgh_local_port = args->port; 316 ret = mach_msg(args->req_msg, 317 MACH_RCV_MSG|MACH_RCV_INTERRUPT|MACH_RCV_LARGE, 318 0, 319 args->req_size, 320 args->port, 321 MACH_MSG_TIMEOUT_NONE, 322 MACH_PORT_NULL); 323 if (MACH_MSG_SUCCESS != ret) { 324 mach_error("mach_msg (receive): ", ret); 325 exit(1); 326 } 327 if (verbose) 328 printf("server received message %d\n", idx); 329 if (args->req_msg->msgh_bits & MACH_MSGH_BITS_COMPLEX) { 330 ret = vm_deallocate(mach_task_self(), 331 (vm_address_t)((ipc_complex_message *)args->req_msg)->descriptor.address, 332 ((ipc_complex_message *)args->req_msg)->descriptor.size); 333 } 334 335 if (1 == args->req_msg->msgh_id) { 336 if (verbose) 337 printf("server sending reply %d\n", idx); 338 args->reply_msg->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 339 MACH_MSG_TYPE_MAKE_SEND); 340 args->reply_msg->msgh_size = args->reply_size; 341 args->reply_msg->msgh_remote_port = args->req_msg->msgh_remote_port; 342 args->reply_msg->msgh_local_port = args->req_msg->msgh_local_port; 343 args->reply_msg->msgh_id = 2; 344 ret = mach_msg(args->reply_msg, 345 MACH_SEND_MSG, 346 args->reply_size, 347 0, 348 MACH_PORT_NULL, 349 MACH_MSG_TIMEOUT_NONE, 350 MACH_PORT_NULL); 351 if (MACH_MSG_SUCCESS != ret) { 352 mach_error("mach_msg (send): ", ret); 353 exit(1); 354 } 355 } 356 } 357 358 endtsc = ReadTSR(); 359 gettimeofday(&endtv, NULL); 360 361 /* report results */ 362 deltatsc = endtsc - starttsc; 363 deltatv.tv_sec = endtv.tv_sec - starttv.tv_sec; 364 deltatv.tv_usec = endtv.tv_usec - starttv.tv_usec; 365 if (endtv.tv_usec < starttv.tv_usec) { 366 deltatv.tv_sec--; 367 deltatv.tv_usec += 1000000; 368 } 369 370 double dsecs = (double) deltatv.tv_sec + 371 1.0E-6 * (double) deltatv.tv_usec; 372 373 printf("\n%u messages during %qd time stamp ticks\n", 374 totalmsg, deltatsc); 375 printf("%g time stamp ticks per message\n", 376 (double) deltatsc / (double) totalmsg); 377 printf("\n%u messages during %u.%06u seconds\n", 378 totalmsg, deltatv.tv_sec, deltatv.tv_usec); 379 printf("%g messages per second\n", (double)totalmsg / dsecs); 380 printf("%g microseconds per message\n\n", 381 dsecs * 1.0E6 / (double) totalmsg); 382} 383 384void *client(void *threadarg) 385{ 386 struct port_args args; 387 int idx; 388 mach_msg_header_t *req, *reply; 389 mach_port_t bsport, servport; 390 kern_return_t ret; 391 void *ints = malloc(sizeof(u_int32_t) * num_ints); 392 393 /* find server port */ 394 ret = task_get_bootstrap_port(mach_task_self(), &bsport); 395 if (KERN_SUCCESS != ret) { 396 mach_error("task_get_bootstrap_port(): ", ret); 397 exit(1); 398 } 399 ret = bootstrap_look_up(bsport, server_port_name, &servport); 400 if (KERN_SUCCESS != ret) { 401 mach_error("bootstrap_look_up(): ", ret); 402 exit(1); 403 } 404 405 setup_client_ports(&args); 406 407 /* start message loop */ 408 for (idx = 0; idx < num_msgs; idx++) { 409 req = args.req_msg; 410 reply = args.reply_msg; 411 412 req->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 413 MACH_MSG_TYPE_MAKE_SEND); 414 req->msgh_size = args.req_size; 415 req->msgh_remote_port = servport; 416 req->msgh_local_port = args.port; 417 req->msgh_id = oneway ? 0 : 1; 418 switch (msg_type) { 419 case msg_type_trivial: 420 break; 421 case msg_type_inline: 422 ((ipc_inline_message *)req)->type.msgt_name = MACH_MSG_TYPE_INTEGER_32; 423 ((ipc_inline_message *)req)->type.msgt_size = 32; 424 ((ipc_inline_message *)req)->type.msgt_number = num_ints; 425 ((ipc_inline_message *)req)->type.msgt_inline = TRUE; 426 ((ipc_inline_message *)req)->type.msgt_longform = FALSE; 427 ((ipc_inline_message *)req)->type.msgt_deallocate = FALSE; 428 ((ipc_inline_message *)req)->type.msgt_unused = 0; 429 break; 430 case msg_type_complex: 431 (req)->msgh_bits |= MACH_MSGH_BITS_COMPLEX; 432 ((ipc_complex_message *)req)->body.msgh_descriptor_count = 1; 433 ((ipc_complex_message *)req)->descriptor.address = ints; 434 ((ipc_complex_message *)req)->descriptor.size = 435 num_ints * sizeof(u_int32_t); 436 ((ipc_complex_message *)req)->descriptor.deallocate = FALSE; 437 ((ipc_complex_message *)req)->descriptor.copy = MACH_MSG_VIRTUAL_COPY; 438 ((ipc_complex_message *)req)->descriptor.type = MACH_MSG_OOL_DESCRIPTOR; 439 break; 440 } 441 if (verbose) 442 printf("client sending message %d\n", idx); 443 ret = mach_msg(req, 444 MACH_SEND_MSG, 445 args.req_size, 446 0, 447 MACH_PORT_NULL, 448 MACH_MSG_TIMEOUT_NONE, 449 MACH_PORT_NULL); 450 if (MACH_MSG_SUCCESS != ret) { 451 mach_error("mach_msg (send): ", ret); 452 fprintf(stderr, "bailing after %u iterations\n", idx); 453 exit(1); 454 break; 455 } 456 if (!oneway) { 457 if (verbose) 458 printf("client awaiting reply %d\n", idx); 459 reply->msgh_bits = 0; 460 reply->msgh_size = args.reply_size; 461 reply->msgh_local_port = args.port; 462 ret = mach_msg(args.reply_msg, 463 MACH_RCV_MSG|MACH_RCV_INTERRUPT, 464 0, 465 args.reply_size, 466 args.port, 467 MACH_MSG_TIMEOUT_NONE, 468 MACH_PORT_NULL); 469 if (MACH_MSG_SUCCESS != ret) { 470 mach_error("mach_msg (receive): ", ret); 471 fprintf(stderr, "bailing after %u iterations\n", 472 idx); 473 exit(1); 474 } 475 if (verbose) 476 printf("client received reply %d\n", idx); 477 } 478 479 if (client_delay) { 480 usleep(client_delay); 481 } 482 } 483 484 free(ints); 485 return; 486} 487 488 489int main(int argc, char *argv[]) 490{ 491 struct port_args portargs; 492 int i; 493 494 signal(SIGINT, signal_handler); 495 parse_args(argc, argv); 496 497 setup_server_ports(&portargs); 498 499 if (fork() != 0) { 500 server(&portargs); 501 exit(0); 502 } 503 504 if (num_clients > 1) { 505 for (i = 1; i < num_clients; i++) { 506 if (fork() == 0) { 507 client(NULL); 508 exit(0); 509 } 510 } 511 } 512 513 client(NULL); 514 515 return (0); 516} 517