2 * Copyright 2008, Google Inc.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
15 * * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 #include "native_client/include/portability.h"
37 #include "native_client/intermodule_comm/nacl_imc_c.h"
38 #include "native_client/nonnacl_util/sel_ldr_launcher_c.h"
43 #include <sys/types.h>
48 static int gettimeofday(struct timeval
*tv
, struct timezone
*tz
);
55 #include "native_client/service_runtime/nacl_check.h"
56 #include "native_client/tools/libsrpc/nacl_srpc.h"
58 #define MAX_ARRAY_SIZE 4096
59 #define MAX_COMMAND_LINE_ARGS 256
60 #define SEL_LDR_CMD_LINE_EXEC_POS 4
63 static char* kSelLdrPathname
= "/usr/local/nacl-sdk/nacl/bin/sel_ldr.exe";
65 static char* kSelLdrPathname
= "/usr/local/nacl-sdk/nacl/bin/sel_ldr";
68 static char* g_sel_ldr_pathname
;
70 static int timed_rpc_count
;
71 static uint32_t timed_rpc_method
= 1;
72 static int timed_rpc_bytes
= 4000;
74 void blackhole(char *s
, ...) {}
75 /* #define DEBUG printf */
76 #define DEBUG blackhole
78 /* simple destructive tokenizer */
84 /* expects *from to point to leading \" and returns pointer to trailing \" */
85 const char* ScanEscapeString(char* to
, const char* from
) {
92 }else if (*from
!= '\\') {
93 if (to
) *to
++ = *from
;
102 if (to
) *to
++ = next
;
105 if (to
) *to
++ = '\n';
115 int Tokenize(char* line
, TOKEN
*array
, int n
) {
119 for( ; count
< n
; count
++ ) {
122 /* skip leading white space */
123 while (line
[pos_start
]) {
124 const char c
= line
[pos_start
];
132 if (!line
[pos_start
]) break;
134 /* find token end from current pos_start */
137 while (line
[pos_end
]) {
138 const char c
= line
[pos_end
];
142 } else if (c
== '\"') {
143 const char* end
= ScanEscapeString(0, &line
[pos_end
]);
145 pos_end
= end
- &line
[0];
151 array
[count
].start
= &line
[pos_start
];
152 array
[count
].length
= pos_end
- pos_start
;
155 line
[pos_end
] = '\0'; /* DESTRUCTION!!! */
159 /* printf("TOKEN %s\n", array[count].start); */
165 /* NOTE: This is leaking memory left and right */
166 int ParseArg(NaClSrpcArg
* arg
, const char* token
) {
172 DEBUG("TOKEN %s\n", token
);
173 CHECK(token
[1] == '(');
175 /* TODO: All the array versions leak memory. Fix them. */
177 case NACL_SRPC_ARG_TYPE_INVALID
:
178 arg
->tag
= NACL_SRPC_ARG_TYPE_INVALID
;
180 case NACL_SRPC_ARG_TYPE_BOOL
:
181 val
= strtol(&token
[2], 0, 0);
182 arg
->tag
= NACL_SRPC_ARG_TYPE_BOOL
;
185 case NACL_SRPC_ARG_TYPE_CHAR_ARRAY
:
186 dim
= strtol(&token
[2], 0, 0);
187 arg
->tag
= NACL_SRPC_ARG_TYPE_CHAR_ARRAY
;
188 arg
->u
.caval
.carr
= (char*) calloc(dim
, sizeof(char));
189 arg
->u
.caval
.count
= dim
;
190 comma
= strstr(token
, ",");
193 for (p
= comma
+1, i
= 0; *p
!= ')' && i
< dim
; ++p
, ++i
)
194 arg
->u
.caval
.carr
[i
] = *p
;
197 case NACL_SRPC_ARG_TYPE_DOUBLE
:
198 arg
->tag
= NACL_SRPC_ARG_TYPE_DOUBLE
;
199 arg
->u
.dval
= strtod(&token
[2], 0);
201 case NACL_SRPC_ARG_TYPE_DOUBLE_ARRAY
:
202 dim
= strtol(&token
[2], 0, 0);
203 arg
->tag
= NACL_SRPC_ARG_TYPE_DOUBLE_ARRAY
;
204 arg
->u
.daval
.darr
= (double*) calloc(dim
, sizeof(double));
205 CHECK(arg
->u
.daval
.darr
);
206 arg
->u
.daval
.count
= dim
;
208 for (i
= 0; i
< dim
; ++i
) {
209 comma
= strstr(comma
, ",");
212 arg
->u
.daval
.darr
[i
] = strtod(comma
, 0);
215 case NACL_SRPC_ARG_TYPE_HANDLE
:
216 val
= strtol(&token
[2], 0, 0);
217 arg
->tag
= NACL_SRPC_ARG_TYPE_HANDLE
;
218 arg
->u
.hval
= (void *)val
;
220 case NACL_SRPC_ARG_TYPE_INT
:
221 val
= strtol(&token
[2], 0, 0);
222 arg
->tag
= NACL_SRPC_ARG_TYPE_INT
;
225 case NACL_SRPC_ARG_TYPE_INT_ARRAY
:
226 dim
= strtol(&token
[2], 0, 0);
227 arg
->tag
= NACL_SRPC_ARG_TYPE_INT_ARRAY
;
229 arg
->u
.iaval
.iarr
= (int*) calloc(dim
, sizeof(int));
230 CHECK(arg
->u
.iaval
.iarr
);
231 arg
->u
.iaval
.count
= dim
;
233 for (i
= 0; i
< dim
; ++i
) {
234 comma
= strstr(comma
, ",");
237 arg
->u
.iaval
.iarr
[i
] = strtol(comma
, 0, 0);
240 case NACL_SRPC_ARG_TYPE_STRING
:
241 arg
->tag
= NACL_SRPC_ARG_TYPE_STRING
;
242 /* this is a conservative estimate */
243 arg
->u
.sval
= malloc(strlen(token
));
244 ScanEscapeString(arg
->u
.sval
, token
+ 2);
253 int ParseArgs(NaClSrpcArg
* arg
, const TOKEN
* token
, int n
) {
255 for (i
= 0; i
< n
; ++i
) {
256 if (ParseArg(&arg
[i
], token
[i
].start
) < 0)
262 void DumpArg(const NaClSrpcArg
* arg
) {
267 case NACL_SRPC_ARG_TYPE_INVALID
:
270 case NACL_SRPC_ARG_TYPE_BOOL
:
271 printf("b(%d)", arg
->u
.bval
);
273 case NACL_SRPC_ARG_TYPE_CHAR_ARRAY
:
274 for (i
= 0; i
< arg
->u
.caval
.count
; ++i
)
275 putchar(arg
->u
.caval
.carr
[i
]);
277 case NACL_SRPC_ARG_TYPE_DOUBLE
:
278 printf("d(%f)", arg
->u
.dval
);
280 case NACL_SRPC_ARG_TYPE_DOUBLE_ARRAY
:
281 count
= arg
->u
.daval
.count
;
282 printf("D(%d", count
);
283 for (i
=0; i
< count
; ++i
)
284 printf(",%f", arg
->u
.daval
.darr
[i
]);
287 case NACL_SRPC_ARG_TYPE_HANDLE
:
288 printf("h(%x)", (unsigned int) arg
->u
.hval
);
290 case NACL_SRPC_ARG_TYPE_INT
:
291 printf("i(%d)", arg
->u
.ival
);
293 case NACL_SRPC_ARG_TYPE_INT_ARRAY
:
294 count
= arg
->u
.iaval
.count
;
295 printf("I(%d", count
);
296 for (i
=0; i
< count
; ++i
)
297 printf(",%d", arg
->u
.iaval
.iarr
[i
]);
300 case NACL_SRPC_ARG_TYPE_STRING
:
301 /* TODO: do proper escaping */
302 printf("s(\"%s\")", arg
->u
.sval
);
309 void DumpArgs(const NaClSrpcArg
* arg
, int n
) {
311 for (i
=0; i
<n
; ++i
) {
318 void BuildArgVec(NaClSrpcArg
* argv
[], NaClSrpcArg arg
[], int count
) {
320 CHECK(count
< NACL_SRPC_MAX_ARGS
);
321 for (i
= 0; i
< count
; ++i
) {
327 void FreeArrayArgs(NaClSrpcArg arg
[], int count
) {
329 for (i
= 0; i
< count
; ++i
) {
331 case NACL_SRPC_ARG_TYPE_CHAR_ARRAY
:
332 free(arg
[i
].u
.caval
.carr
);
334 case NACL_SRPC_ARG_TYPE_DOUBLE_ARRAY
:
335 free(arg
[i
].u
.daval
.darr
);
337 case NACL_SRPC_ARG_TYPE_INT_ARRAY
:
338 free(arg
[i
].u
.iaval
.iarr
);
340 case NACL_SRPC_ARG_TYPE_INVALID
:
341 case NACL_SRPC_ARG_TYPE_BOOL
:
342 case NACL_SRPC_ARG_TYPE_DOUBLE
:
343 case NACL_SRPC_ARG_TYPE_HANDLE
:
344 case NACL_SRPC_ARG_TYPE_INT
:
345 case NACL_SRPC_ARG_TYPE_STRING
:
352 static void CommandLoop(NaClHandle imc_handle
) {
353 NaClSrpcError errcode
;
354 NaClSrpcChannel rpc_channel
;
355 int command_count
= 0;
357 NaClSrpcClientCtor(&rpc_channel
, NaClSrpcImcDescTypeFromHandle(imc_handle
));
364 TOKEN tokens
[NACL_SRPC_MAX_ARGS
];
368 fprintf(stderr
, "%d> ", command_count
);
371 if (!fgets(buffer
, sizeof(buffer
), stdin
))
374 n
= Tokenize(buffer
, tokens
, NACL_SRPC_MAX_ARGS
);
378 fprintf(stderr
, "bad line\n");
382 command
= tokens
[0].start
;
383 if (0 == strcmp("#", command
)) {
385 } else if (0 == strcmp("service", command
)) {
386 NaClSrpcDumpInterfaceDesc(&rpc_channel
);
387 } else if (0 == strcmp("quit", command
)) {
389 } else if (0 == strcmp("rpc", command
)) {
392 NaClSrpcArg in
[NACL_SRPC_MAX_ARGS
];
393 NaClSrpcArg
* inv
[NACL_SRPC_MAX_ARGS
+ 1];
395 NaClSrpcArg out
[NACL_SRPC_MAX_ARGS
];
396 NaClSrpcArg
* outv
[NACL_SRPC_MAX_ARGS
+ 1];
400 fprintf(stderr
, "bad rpc command\n");
404 for (int_out_sep
= 2; int_out_sep
< n
; ++int_out_sep
) {
405 if (0 == strcmp(tokens
[int_out_sep
].start
, "*"))
409 if (int_out_sep
== n
) {
410 fprintf(stderr
, "no in out arg separator for rpc command\n");
415 * Build the input parameter values.
418 n_in
= int_out_sep
- 2;
419 DEBUG("parsing in args %d\n", n_in
);
420 BuildArgVec(inv
, in
, n_in
);
422 if (ParseArgs(in
, &tokens
[2], n_in
) < 0) {
423 fprintf(stderr
, "bad input args for rpc\n");
428 * Build the output (rpc return) values.
431 n_out
= n
- int_out_sep
- 1;
432 DEBUG("parsing out args %d\n", n_out
);
433 BuildArgVec(outv
, out
, n_out
);
435 if (ParseArgs(out
, &tokens
[int_out_sep
+ 1], n_out
) < 0) {
436 fprintf(stderr
, "bad output args for rpc\n");
440 rpc_num
= NaClSrpcGetRpcNum(&rpc_channel
, tokens
[1].start
);
442 fprintf(stderr
, "unknown rpc\n");
446 fprintf(stderr
,"using rpc %s no %d\n", tokens
[1].start
, rpc_num
);
447 errcode
= NaClSrpcInvokeV(&rpc_channel
, rpc_num
, inv
, outv
);
448 if (NACL_SRPC_RESULT_OK
!= errcode
) {
449 fprintf(stderr
, "rpc call failed %s\n", NaClSrpcErrorString(errcode
));
456 printf("%s RESULTS: ", tokens
[1].start
);
457 DumpArgs(outv
[0], n_out
);
460 * Free the storage allocated for array valued parameters and returns.
462 FreeArrayArgs(in
, n_in
);
463 FreeArrayArgs(out
, n_out
);
465 fprintf(stderr
, "unknown command\n");
471 * Shut down the rpc streams.
473 NaClSrpcDtor(&rpc_channel
);
478 * This function works with the rpc services in tests/srpc to test the
481 static void TestRandomRpcs(NaClHandle imc_handle
) {
483 NaClSrpcArg
* inv
[] = { &in
, NULL
};
485 NaClSrpcArg
* outv
[] = { &out
, NULL
};
486 NaClSrpcError errcode
;
487 NaClSrpcChannel rpc_channel
;
493 * Set up the connection to the child process.
495 NaClSrpcClientCtor(&rpc_channel
, NaClSrpcImcDescTypeFromHandle(imc_handle
));
497 * TODO: set up timing on both ends of the IMC channel.
501 if (timed_rpc_method
< 1 || timed_rpc_method
>= rpc_channel
.rpc_count
) {
502 fprintf(stderr
, "method number must be between 1 and %d (inclusive)\n",
503 rpc_channel
.rpc_count
- 1);
507 argument_count
= strlen(rpc_channel
.rpc_descr
[timed_rpc_method
].in_args
);
508 return_count
= strlen(rpc_channel
.rpc_descr
[timed_rpc_method
].out_args
);
510 if (argument_count
!= return_count
) {
511 fprintf(stderr
, "method argument and return count must match\n");
515 if (1 < argument_count
) {
516 fprintf(stderr
, "method must take zero or one argument\n");
520 if (argument_count
== 0) {
524 enum NaClSrpcArgType type
;
525 static char c_in
[MAX_ARRAY_SIZE
];
526 static char c_out
[MAX_ARRAY_SIZE
];
527 static double d_in
[MAX_ARRAY_SIZE
/ sizeof(double)];
528 static double d_out
[MAX_ARRAY_SIZE
/ sizeof(double)];
529 static int i_in
[MAX_ARRAY_SIZE
/ sizeof(int)];
530 static int i_out
[MAX_ARRAY_SIZE
/ sizeof(int)];
532 "She should have died hereafter;"
533 "There would have been a time for such a word."
534 "To-morrow, and to-morrow, and to-morrow,"
535 "Creeps in this petty pace from day to day"
536 "To the last syllable of recorded time,"
537 "And all our yesterdays have lighted fools"
538 "The way to dusty death. Out, out, brief candle!"
539 "Life's but a walking shadow, a poor player"
540 "That struts and frets his hour upon the stage"
541 "And then is heard no more: it is a tale"
542 "Told by an idiot, full of sound and fury,"
543 "Signifying nothing";
545 in
.tag
= rpc_channel
.rpc_descr
[timed_rpc_method
].in_args
[0];
546 out
.tag
= rpc_channel
.rpc_descr
[timed_rpc_method
].out_args
[0];
548 type
= rpc_channel
.rpc_descr
[timed_rpc_method
].in_args
[0];
550 case NACL_SRPC_ARG_TYPE_BOOL
:
553 case NACL_SRPC_ARG_TYPE_CHAR_ARRAY
:
554 in
.u
.caval
.count
= timed_rpc_bytes
;
555 in
.u
.caval
.carr
= c_in
;
556 out
.u
.caval
.count
= in
.u
.iaval
.count
;
557 out
.u
.caval
.carr
= c_out
;
559 case NACL_SRPC_ARG_TYPE_DOUBLE
:
560 in
.u
.dval
= 3.1415926;
562 case NACL_SRPC_ARG_TYPE_DOUBLE_ARRAY
:
563 in
.u
.daval
.count
= timed_rpc_bytes
/ sizeof(double);
564 in
.u
.daval
.darr
= d_in
;
565 out
.u
.daval
.count
= in
.u
.iaval
.count
;
566 out
.u
.daval
.darr
= d_out
;
568 case NACL_SRPC_ARG_TYPE_INT
:
571 case NACL_SRPC_ARG_TYPE_INT_ARRAY
:
572 in
.u
.iaval
.count
= timed_rpc_bytes
/ sizeof(int);
573 in
.u
.iaval
.iarr
= i_in
;
574 out
.u
.iaval
.count
= in
.u
.iaval
.count
;
575 out
.u
.iaval
.iarr
= i_out
;
577 case NACL_SRPC_ARG_TYPE_STRING
:
578 /* TODO: needs length variation */
581 case NACL_SRPC_ARG_TYPE_INVALID
:
582 case NACL_SRPC_ARG_TYPE_HANDLE
:
590 for (i
= 0; i
< timed_rpc_count
; ++i
) {
591 errcode
= NaClSrpcInvokeV(&rpc_channel
, timed_rpc_method
, inv
, outv
);
592 if (NACL_SRPC_RESULT_OK
!= errcode
) {
593 fprintf(stderr
, "rpc call failed %s\n", NaClSrpcErrorString(errcode
));
598 if (i
== timed_rpc_count
) {
599 NaClSrpcArg
* timer_inv
[] = { NULL
};
601 NaClSrpcArg
* timer_outv
[] = { &tm
[0], &tm
[1], &tm
[2], &tm
[3], NULL
};
602 double total_server_usec
;
603 double dummy_receive
;
604 double dummy_imc_read
;
605 double dummy_imc_write
;
607 NaClSrpcGetUsecTimes(&rpc_channel
,
612 printf("server time observed by client: %.6f sec\n",
613 total_server_usec
/ 1000000.0);
614 tm
[0].tag
= NACL_SRPC_ARG_TYPE_DOUBLE
;
615 tm
[1].tag
= NACL_SRPC_ARG_TYPE_DOUBLE
;
616 tm
[2].tag
= NACL_SRPC_ARG_TYPE_DOUBLE
;
617 tm
[3].tag
= NACL_SRPC_ARG_TYPE_DOUBLE
;
618 errcode
= NaClSrpcInvokeV(&rpc_channel
,
619 NACL_SRPC_GET_TIMES_METHOD
,
622 printf("server send time: %.6f sec\n", tm
[0].u
.dval
/ 1000000.0);
623 printf("server receive time: %.6f sec\n", tm
[1].u
.dval
/ 1000000.0);
624 printf("imc read time: %.6f sec\n", tm
[2].u
.dval
/ 1000000.0);
625 printf("imc write time: %.6f sec\n", tm
[3].u
.dval
/ 1000000.0);
631 * Shut down the rpc streams.
633 NaClSrpcDtor(&rpc_channel
);
637 #if defined(HAVE_SDL)
641 int main(int argc
, char *argv
[]) {
642 NaClHandle imc_handle
;
643 struct NaClSelLdrLauncher
* launcher
;
645 static char* application_name
;
654 /* Descriptor transfer requires the following. */
655 NaClNrdAllModulesInit();
657 /* The -p option can change the sel_ldr binary used. */
658 g_sel_ldr_pathname
= kSelLdrPathname
;
660 /* We are only testing if this count is not zero. */
663 /* command line parsing */
664 while ((opt
= getopt(argc
, argv
, "f:p:r:v")) != -1) {
667 application_name
= optarg
;
670 g_sel_ldr_pathname
= optarg
;
673 timed_rpc_count
= strtol(optarg
, &nextp
, 0);
675 timed_rpc_method
= strtol(nextp
+ 1, &nextp
, 0);
677 timed_rpc_bytes
= strtol(nextp
+ 1, 0, 0);
680 printf("Testing: %d iterations, method %d, %d bytes\n",
686 NaClLogIncrVerbosity();
690 "Usage: sel_universal [-f nacl_file]\n"
692 " [-r count:method:bytes]\n");
698 * Pass any extra arguments on to sel_ldr or the application.
704 for (i
= 0; i
< argc
; ++i
) {
705 if (tmp_ldr_argv
== NULL
) {
706 // sel_universal arguments come first.
707 if (!strcmp("--", argv
[i
])) {
708 tmp_ldr_argv
= &argv
[i
+ 1];
710 } else if (module_argv
== NULL
) {
711 // sel_ldr arguments come next.
712 if (!strcmp("--", argv
[i
])) {
713 module_argv
= &argv
[i
+ 1];
718 // application arguments come last.
724 * Append the -P 5 option to the command line to pass the channel to the
727 sel_ldr_argv
= (char**) malloc(module_argc
* sizeof(*module_argv
));
728 sel_ldr_argv
[0] = "-P";
729 sel_ldr_argv
[1] = "5";
730 for (i
= 0; i
< sel_ldr_argc
; ++i
) {
731 sel_ldr_argv
[i
+ 2] = tmp_ldr_argv
[i
];
735 * Start sel_ldr with the given application and arguments.
737 launcher
= NaClSelLdrStart(application_name
,
740 (const char**) sel_ldr_argv
,
742 (const char**) module_argv
);
743 imc_handle
= NaClSelLdrGetChannel(launcher
);
745 if (timed_rpc_count
== 0) {
746 CommandLoop(imc_handle
);
748 TestRandomRpcs(imc_handle
);
751 NaClSelLdrShutdown(launcher
);
753 NaClNrdAllModulesFini();
758 /* TODO: create one utility function to get usec times. */
759 static int gettimeofday(struct timeval
*tv
, struct timezone
*tz
) {
760 unsigned __int64 timer
= 0;
762 GetSystemTimeAsFileTime(&filetime
);
763 timer
|= filetime
.dwHighDateTime
;
765 timer
|= filetime
.dwLowDateTime
;
766 /* FILETIME has 100ns precision. Convert to usec. */
768 tv
->tv_sec
= (long) (timer
/ 1000000UL);
769 tv
->tv_usec
= (long) (timer
% 1000000UL);