1/*
2 * Copyright (c) 2006 Apple Inc.  All Rights Reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29
30/*
31 *	Order of Execution
32 *
33 *	benchmark_init
34 *
35 *	benchmark_optswitch
36 *
37 *		benchmark_initrun
38 *
39 *			benchmark_initworker
40 *				benchmark_initbatch
41 *					benchmark
42 *				benchmark_finibatch
43 *				benchmark_initbatch
44 *					benchmark
45 *				benchmark_finibatch, etc.
46 *			benchmark_finiworker
47 *
48 *		benchmark_result
49 *
50 *		benchmark_finirun
51 *
52 *	benchmark_fini
53 */
54
55
56
57#ifdef	__sun
58#pragma ident	"@(#)lb_mmtest.c	1.0	08/21/06 Apple Inc."
59#endif
60
61
62
63#include <unistd.h>
64#include <stdlib.h>
65#include <stdio.h>
66
67#include <mach/boolean.h>
68#include <mach/mach_error.h>
69#include <mach/mach.h>
70#include <mach/notify.h>
71#include <servers/bootstrap.h>
72#include <signal.h>
73#include <stdio.h>
74#include <stdlib.h>
75#include <string.h>
76#include <sys/signal.h>
77#include <sys/time.h>
78#include <sys/types.h>
79
80#include "../libmicro.h"
81
82/*
83 *	Your state variables should live in the tsd_t struct below
84 */
85typedef struct {
86    int server_mode;
87    boolean_t verbose;
88    boolean_t oneway;
89    int overwrite;
90    int msg_type;
91    int num_ints;
92    int num_msgs;
93    const char *server_port_name;
94    mach_port_t server_port;
95    mach_port_t reply_port;
96    int request_msg_size;
97    void *request_msg;
98    int reply_msg_size;
99    void *reply_msg;
100    void *ints;
101    long pid;
102} tsd_t;
103
104static boolean_t 	opt_verbose;
105static boolean_t 	opt_oneway;
106static int 			opt_num_msgs;
107static int	 		opt_msg_type;
108static int 			opt_num_ints;
109static char *		opt_server_port_name;
110
111#pragma mark *** definitions from MMTest.c
112/*
113 *	These variables were taken from MMtest.c
114 */
115typedef struct {
116    mach_msg_header_t	header;
117    mach_msg_trailer_t	trailer;		// subtract this when sending
118} ipc_trivial_message;
119
120typedef struct {
121    mach_msg_header_t	header;
122    u_int32_t		numbers[0];
123    mach_msg_trailer_t	trailer;		// subtract this when sending
124} ipc_inline_message;
125
126typedef struct {
127    mach_msg_header_t		header;
128    mach_msg_body_t		body;
129    mach_msg_ool_descriptor_t	descriptor;
130    mach_msg_trailer_t		trailer;	// subtract this when sending
131} ipc_complex_message;
132
133void signal_handler(int sig) {
134}
135
136enum {
137    msg_type_trivial = 0,
138    msg_type_inline = 1,
139    msg_type_complex = 2
140};
141
142void server(void *tsd);
143void client(void *tsd);
144
145#pragma mark *** routines from MMTest.c
146/*
147 *	These routines were taken from MMtest.c
148 */
149
150void server(void *tsd) {
151    mach_msg_header_t *request;
152    mach_msg_header_t *reply;
153    mach_msg_option_t option;
154    kern_return_t ret;
155
156	tsd_t			*ts = (tsd_t *)tsd;
157
158    request = (mach_msg_header_t *)ts->request_msg;
159
160    reply = (mach_msg_header_t *)ts->reply_msg;
161
162#ifndef OPTIMIZED_SERVER
163    for (;;) {
164#endif /* !OPTIMIZED_SERVER */
165
166    	if (ts->verbose) printf("Awaiting message\n");
167	option = MACH_RCV_MSG|MACH_RCV_INTERRUPT|MACH_RCV_LARGE;
168	ret = mach_msg(request,
169		       option,
170		       0,
171		       ts->request_msg_size,
172		       ts->server_port,
173		       MACH_MSG_TIMEOUT_NONE,
174		       MACH_PORT_NULL);
175
176#ifdef OPTIMIZED_SERVER
177    for (;;) {
178    	mach_msg_header_t *tmp;
179#endif /* OPTIMIZED_SERVER */
180
181	if (MACH_MSG_SUCCESS != ret)
182		break;
183	if (ts->verbose) printf("Received message\n");
184	if (request->msgh_bits & MACH_MSGH_BITS_COMPLEX) {
185		ipc_complex_message *complex_request;
186
187		complex_request = (ipc_complex_message *)ts->request_msg;
188		ret = vm_deallocate(mach_task_self(),
189				    (vm_address_t)complex_request->descriptor.address,
190				    complex_request->descriptor.size);
191	}
192	if (1 == request->msgh_id) {
193	    	if (ts->verbose) printf("Sending reply\n");
194	    	reply->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE, 0);
195		reply->msgh_size = ts->reply_msg_size;
196	    	reply->msgh_remote_port = request->msgh_remote_port;
197		reply->msgh_local_port = MACH_PORT_NULL;
198	    	reply->msgh_id = 2;
199
200#ifdef OPTIMIZED_SERVER
201		option = MACH_SEND_MSG|MACH_RCV_MSG|MACH_RCV_INTERRUPT|MACH_RCV_LARGE;
202	} else {
203		option = MACH_RCV_MSG|MACH_RCV_INTERRUPT|MACH_RCV_LARGE;
204	}
205
206	ret = mach_msg(	reply,
207			option,
208			ts->reply_msg_size,
209			ts->request_msg_size,
210			ts->server_port,
211			MACH_MSG_TIMEOUT_NONE,
212			MACH_PORT_NULL);
213	tmp = reply;
214	reply = request;
215	request = tmp;
216#else /* !OPTIMIZED_SERVER */
217		ret = mach_msg(reply,
218			       MACH_SEND_MSG,
219			       ts->reply_msg_size,
220			       0,
221			       MACH_PORT_NULL,
222			       MACH_MSG_TIMEOUT_NONE,
223			       MACH_PORT_NULL);
224		if (ret != MACH_MSG_SUCCESS)
225			break;
226        }
227#endif /* !OPTIMIZED_SERVER */
228    }
229
230    if (MACH_RCV_INTERRUPTED != ret) {
231    	mach_error("mach_msg: ", ret);
232		exit(1);
233    }
234}
235
236void client(void *tsd) {
237	mach_msg_header_t *request;
238	mach_msg_header_t *reply;
239	mach_msg_option_t option;
240	kern_return_t ret;
241	int idx;
242
243	tsd_t			*ts = (tsd_t *)tsd;
244
245#ifdef SWAP_BUFFERS
246	mach_msg_header_t *tmp;
247#endif
248
249	request = (mach_msg_header_t *)ts->request_msg;
250	reply = (mach_msg_header_t *)ts->reply_msg;
251
252    for (idx = 0; idx < ts->num_msgs; idx++) {
253	request->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND,
254					    MACH_MSG_TYPE_MAKE_SEND_ONCE);
255	request->msgh_size = ts->request_msg_size;
256	request->msgh_remote_port = ts->server_port;
257	request->msgh_local_port = ts->reply_port;
258
259	if (ts->msg_type == msg_type_complex) {
260	    ipc_complex_message *complexmsg = (ipc_complex_message *)request;
261
262	    request->msgh_bits |=  MACH_MSGH_BITS_COMPLEX;
263	    complexmsg->body.msgh_descriptor_count = 1;
264	    complexmsg->descriptor.address =  ts->ints;
265	    complexmsg->descriptor.size = ts->num_ints * sizeof(u_int32_t);
266	    complexmsg->descriptor.deallocate = FALSE;
267	    complexmsg->descriptor.copy = MACH_MSG_VIRTUAL_COPY;
268	    complexmsg->descriptor.type =  MACH_MSG_OOL_DESCRIPTOR;
269	}
270
271	if (ts->oneway) {
272	    request->msgh_id = 0;
273	    option = MACH_SEND_MSG;
274	} else {
275	    request->msgh_id = 1;
276	    option = MACH_SEND_MSG|MACH_RCV_MSG;
277	}
278
279	if (ts->verbose) printf("Sending request\n");
280#ifdef SWAP_BUFFERS
281	ret = mach_msg(	request,
282			option,
283			ts->request_msg_size,
284			ts->reply_msg_size,
285			ts->reply_port,
286			MACH_MSG_TIMEOUT_NONE,
287			MACH_PORT_NULL);
288	if (MACH_MSG_SUCCESS != ret) {
289	    mach_error("client: mach_msg: ", ret);
290	    fprintf(stderr, "bailing after %u iterations\n", idx);
291	    exit(1);
292	}
293	tmp = request;
294	request = reply;
295	reply = tmp;
296#else
297	ret = mach_msg_overwrite(request,
298				 option,
299				 ts->request_msg_size,
300				 ts->reply_msg_size,
301				 ts->reply_port,
302				 MACH_MSG_TIMEOUT_NONE,
303				 MACH_PORT_NULL,
304				 reply,
305				 0);
306	if (MACH_MSG_SUCCESS != ret) {
307	    mach_error("client: mach_msg_overwrite: ", ret);
308	    fprintf(stderr, "bailing after %u iterations\n", idx);
309	    exit(1);
310	}
311#endif
312	if (ts->verbose && !ts->oneway) printf("Received reply\n");
313    }
314}
315
316
317#pragma mark *** Darbench routines
318
319/*
320 *	These routines are required by darbench
321 */
322
323/*ARGSUSED*/
324int
325benchmark_initbatch(void *tsd)
326{
327	/*
328	 * initialize your state variables here second
329	 */
330	long	pid;
331	tsd_t	*ts = (tsd_t *)tsd;
332
333    ts->server_mode = -1;
334    ts->verbose = opt_verbose;
335    ts->oneway = opt_oneway;
336    ts->overwrite = 0;
337    ts->msg_type = opt_msg_type;
338    ts->num_ints = opt_num_ints;
339    ts->num_msgs = opt_num_msgs;
340    ts->server_port_name = opt_server_port_name;
341    ts->server_port = MACH_PORT_NULL;
342    ts->reply_port = MACH_PORT_NULL;
343    ts->request_msg = NULL;
344    ts->request_msg_size = 0;
345    ts->reply_msg = NULL;
346    ts->reply_msg_size = 0;
347
348	switch (ts->msg_type) {
349	case msg_type_trivial:
350	  ts->request_msg_size = sizeof(ipc_trivial_message);
351		break;
352
353	case msg_type_inline:
354	  ts->request_msg_size = sizeof(ipc_inline_message) +
355		sizeof(u_int32_t) * ts->num_ints;
356		break;
357
358	case msg_type_complex:
359	  ts->request_msg_size = sizeof(ipc_complex_message);
360	  ts->ints = malloc(sizeof(u_int32_t) * ts->num_ints);
361	  break;
362	}
363
364    ts->request_msg = malloc(ts->request_msg_size);
365    ts->reply_msg = malloc(ts->reply_msg_size);
366
367    if (ts->server_mode) {
368		kern_return_t ret = 0;
369		mach_port_t bsport;
370
371		ts->reply_msg_size -= sizeof(mach_msg_trailer_t);
372		ret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE,
373					 &(ts->server_port));
374		if (KERN_SUCCESS != ret) {
375			mach_error("mach_port_allocate(): ", ret);
376			exit(1);
377		}
378		ret = mach_port_insert_right(mach_task_self(), ts->server_port,
379                ts->server_port, MACH_MSG_TYPE_MAKE_SEND);
380		if (KERN_SUCCESS != ret) {
381			mach_error("mach_port_insert_right(): ", ret);
382			exit(1);
383		}
384		ret = task_get_bootstrap_port(mach_task_self(), &bsport);
385		if (KERN_SUCCESS != ret) {
386			mach_error("task_get_bootstrap_port(): ", ret);
387			exit(1);
388		}
389		ret = bootstrap_check_in(bsport, (char *)ts->server_port_name,
390                	&ts->server_port);
391		if (KERN_SUCCESS != ret) {
392			mach_error("bootstrap_register(): ", ret);
393			exit(1);
394		}
395    } else {   /* client mode */
396		kern_return_t ret = 0;
397		mach_port_t bsport;
398
399		ts->request_msg_size -= sizeof(mach_msg_trailer_t);
400
401		ret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE,
402					 &(ts->reply_port));
403		if (KERN_SUCCESS != ret) {
404			mach_error("mach_port_allocate(): ", ret);
405			exit(1);
406		}
407
408		ret = task_get_bootstrap_port(mach_task_self(), &bsport);
409		if (KERN_SUCCESS != ret) {
410			mach_error("task_get_bootstrap_port(): ", ret);
411			exit(1);
412		}
413		ret = bootstrap_look_up(bsport, (char *)ts->server_port_name,
414			&(ts->server_port));
415		if (KERN_SUCCESS != ret) {
416	   		mach_error("bootstrap_look_up(): ", ret);
417			exit(1);
418		}
419    }
420
421    if (ts->verbose) {
422		if (ts->server_mode) {
423			printf("Server waiting for IPC messages from client on port  '%s'.\n",
424			       ts->server_port_name);
425		} else {
426			printf("Client sending %d %s IPC messages to port '%s' in %s  mode.\n",
427			       ts->num_msgs, (ts->msg_type == msg_type_inline) ? "inline" :
428			       ((ts->msg_type == msg_type_complex) ? "complex" : "trivial"),
429			       ts->server_port_name, (ts->oneway ? "oneway" : "rpc"));
430		}
431    }
432
433	pid = fork();
434	switch (pid) {
435		case 0:
436			server(tsd);
437			exit(0);
438			break;
439		case -1:
440			return (-1);
441		default:
442			ts->pid = pid;
443			break;
444		}
445	return (0);
446}
447
448int
449benchmark_finirun()
450{
451	(void) fprintf(stderr, "benchmark_finirun\n");
452	return (0);
453}
454
455int
456benchmark_init()
457{
458	/*
459	 *	the lm_optstr must be defined here or no options for you
460	 * 	...and the framework will throw an error
461	 *	lm_optstr has two kinds of arguments, boolean (single
462	 *	lower case character) and with an argument (single lower
463	 *	case character plus a :, indicating the next option is
464	 *	the argument)
465	 *
466	 */
467	(void) sprintf(lm_optstr, "voc:t:n:p:");
468	/*
469	 * 	tsd_t is the struct that we can pass around our
470	 *	state info in
471	 *
472	 *	lm_tsdsize will allocate the space we need for this
473	 *	structure throughout the rest of the framework
474	 */
475	lm_tsdsize = sizeof (tsd_t);
476
477	(void) sprintf(lm_usage,
478	"    -v\t\tbe verbose\n"
479	"    -o\t\tdo not request return reply (client)\n"
480	"    -c num\t\tnumber of messages to send (client)\n"
481	"    -t trivial|inline|complex\ttype of messages to  send (client)\n"
482	"    -n num\tnumber of 32-bit ints to send in  messages\n"
483	"\t\t\t(client's value must be <= the server's)\n"
484	"    -p portname\tname of port on which to communicate\n"
485	"\t\t\t(client and server must use the same value)\n");
486
487	opt_verbose = FALSE;
488	opt_oneway = FALSE;
489	opt_num_msgs = 10000;
490	opt_msg_type = msg_type_trivial;
491	opt_num_ints = 64;
492	opt_server_port_name = malloc(32);
493	strcpy(opt_server_port_name, "TEST");
494
495	return (0);
496}
497
498int
499benchmark_fini()
500{
501	free(opt_server_port_name);
502	return (0);
503}
504
505int
506benchmark_finibatch(void *tsd)
507{
508	tsd_t			*ts = (tsd_t *)tsd;
509	kill(ts->pid, SIGKILL);
510	return (0);
511}
512
513char *
514benchmark_result()
515{
516	static char		result = '\0';
517	(void) fprintf(stderr, "benchmark_result\n");
518	return (&result);
519}
520
521int
522benchmark_finiworker(void *tsd)
523{
524//	tsd_t			*ts = (tsd_t *)tsd;
525	return (0);
526}
527
528int
529benchmark_optswitch(int opt, char *optarg)
530{
531	(void) fprintf(stderr, "benchmark_optswitch\n");
532
533	switch (opt) {
534	case 'v':
535		opt_verbose = TRUE;
536		break;
537	case 'o':
538		opt_oneway = TRUE;
539		break;
540	case 'c':
541		opt_num_msgs = sizetoint(optarg);
542		break;
543	case 't':
544		if ( 0 == strcmp("trivial", optarg) )
545			opt_msg_type = msg_type_trivial;
546		else if ( 0 == strcmp("inline", optarg) )
547			opt_msg_type = msg_type_inline;
548		else if ( 0 == strcmp("complex", optarg) )
549			opt_msg_type = msg_type_complex;
550		else {
551			(void) fprintf(stderr, "incorrect argument for message type %s\n", optarg);
552			return (-1);
553		}
554		break;
555	case 'n':
556		opt_num_ints = sizetoint(optarg);
557		break;
558	case 'p':
559		strncpy(opt_server_port_name, optarg, 32);
560		break;
561	default:
562		return (-1);
563	}
564	return (0);
565}
566
567int
568benchmark_initworker(void *tsd)
569{
570	/*
571	 *	initialize your state variables here first
572	 */
573//	tsd_t			*ts = (tsd_t *)tsd;
574	return (0);
575}
576
577int
578benchmark_initrun()
579{
580	(void) fprintf(stderr, "benchmark_initrun\n");
581	return (0);
582}
583
584int
585benchmark(void *tsd, result_t *res)
586{
587	/*
588	 *	initialize your state variables here last
589	 *
590	 * 	and realize that you are paying for your initialization here
591	 *	and it is really a bad idea
592	 */
593//	tsd_t			*ts = (tsd_t *)tsd;
594	int			i;
595
596	for (i = 0; i < lm_optB; i++) {
597		client(tsd);
598	}
599
600	return (0);
601}
602