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);
247 * The two cases below are added to avoid warnings, they are only used
250 case NACL_SRPC_ARG_TYPE_OBJECT
:
251 case NACL_SRPC_ARG_TYPE_VARIANT_ARRAY
:
259 int ParseArgs(NaClSrpcArg
* arg
, const TOKEN
* token
, int n
) {
261 for (i
= 0; i
< n
; ++i
) {
262 if (ParseArg(&arg
[i
], token
[i
].start
) < 0)
268 void DumpArg(const NaClSrpcArg
* arg
) {
273 case NACL_SRPC_ARG_TYPE_INVALID
:
276 case NACL_SRPC_ARG_TYPE_BOOL
:
277 printf("b(%d)", arg
->u
.bval
);
279 case NACL_SRPC_ARG_TYPE_CHAR_ARRAY
:
280 for (i
= 0; i
< arg
->u
.caval
.count
; ++i
)
281 putchar(arg
->u
.caval
.carr
[i
]);
283 case NACL_SRPC_ARG_TYPE_DOUBLE
:
284 printf("d(%f)", arg
->u
.dval
);
286 case NACL_SRPC_ARG_TYPE_DOUBLE_ARRAY
:
287 count
= arg
->u
.daval
.count
;
288 printf("D(%d", count
);
289 for (i
=0; i
< count
; ++i
)
290 printf(",%f", arg
->u
.daval
.darr
[i
]);
293 case NACL_SRPC_ARG_TYPE_HANDLE
:
294 printf("h(%x)", (unsigned int) arg
->u
.hval
);
296 case NACL_SRPC_ARG_TYPE_INT
:
297 printf("i(%d)", arg
->u
.ival
);
299 case NACL_SRPC_ARG_TYPE_INT_ARRAY
:
300 count
= arg
->u
.iaval
.count
;
301 printf("I(%d", count
);
302 for (i
=0; i
< count
; ++i
)
303 printf(",%d", arg
->u
.iaval
.iarr
[i
]);
306 case NACL_SRPC_ARG_TYPE_STRING
:
307 /* TODO: do proper escaping */
308 printf("s(\"%s\")", arg
->u
.sval
);
310 case NACL_SRPC_ARG_TYPE_OBJECT
:
311 /* this is a pointer that NaCl module can do nothing with */
312 printf("o(%p)", arg
->u
.oval
);
314 case NACL_SRPC_ARG_TYPE_VARIANT_ARRAY
:
315 count
= arg
->u
.vaval
.count
;
316 printf("A(%d", count
);
317 for (i
=0; i
< count
; ++i
) {
319 DumpArg(&arg
->u
.vaval
.varr
[i
]);
328 void DumpArgs(const NaClSrpcArg
* arg
, int n
) {
330 for (i
=0; i
<n
; ++i
) {
337 void BuildArgVec(NaClSrpcArg
* argv
[], NaClSrpcArg arg
[], int count
) {
339 CHECK(count
< NACL_SRPC_MAX_ARGS
);
340 for (i
= 0; i
< count
; ++i
) {
346 void FreeArrayArgs(NaClSrpcArg arg
[], int count
) {
348 for (i
= 0; i
< count
; ++i
) {
350 case NACL_SRPC_ARG_TYPE_CHAR_ARRAY
:
351 free(arg
[i
].u
.caval
.carr
);
353 case NACL_SRPC_ARG_TYPE_DOUBLE_ARRAY
:
354 free(arg
[i
].u
.daval
.darr
);
356 case NACL_SRPC_ARG_TYPE_INT_ARRAY
:
357 free(arg
[i
].u
.iaval
.iarr
);
359 case NACL_SRPC_ARG_TYPE_VARIANT_ARRAY
:
360 FreeArrayArgs(arg
[i
].u
.vaval
.varr
, arg
[i
].u
.vaval
.count
);
362 case NACL_SRPC_ARG_TYPE_INVALID
:
363 case NACL_SRPC_ARG_TYPE_BOOL
:
364 case NACL_SRPC_ARG_TYPE_DOUBLE
:
365 case NACL_SRPC_ARG_TYPE_HANDLE
:
366 case NACL_SRPC_ARG_TYPE_INT
:
367 case NACL_SRPC_ARG_TYPE_STRING
:
368 case NACL_SRPC_ARG_TYPE_OBJECT
:
375 static void CommandLoop(NaClHandle imc_handle
) {
376 NaClSrpcError errcode
;
377 NaClSrpcChannel rpc_channel
;
378 int command_count
= 0;
380 NaClSrpcClientCtor(&rpc_channel
, NaClSrpcImcDescTypeFromHandle(imc_handle
));
387 TOKEN tokens
[NACL_SRPC_MAX_ARGS
];
391 fprintf(stderr
, "%d> ", command_count
);
394 if (!fgets(buffer
, sizeof(buffer
), stdin
))
397 n
= Tokenize(buffer
, tokens
, NACL_SRPC_MAX_ARGS
);
401 fprintf(stderr
, "bad line\n");
405 command
= tokens
[0].start
;
406 if (0 == strcmp("#", command
)) {
408 } else if (0 == strcmp("service", command
)) {
409 NaClSrpcDumpInterfaceDesc(&rpc_channel
);
410 } else if (0 == strcmp("quit", command
)) {
412 } else if (0 == strcmp("rpc", command
)) {
415 NaClSrpcArg in
[NACL_SRPC_MAX_ARGS
];
416 NaClSrpcArg
* inv
[NACL_SRPC_MAX_ARGS
+ 1];
418 NaClSrpcArg out
[NACL_SRPC_MAX_ARGS
];
419 NaClSrpcArg
* outv
[NACL_SRPC_MAX_ARGS
+ 1];
423 fprintf(stderr
, "bad rpc command\n");
427 for (int_out_sep
= 2; int_out_sep
< n
; ++int_out_sep
) {
428 if (0 == strcmp(tokens
[int_out_sep
].start
, "*"))
432 if (int_out_sep
== n
) {
433 fprintf(stderr
, "no in out arg separator for rpc command\n");
438 * Build the input parameter values.
441 n_in
= int_out_sep
- 2;
442 DEBUG("parsing in args %d\n", n_in
);
443 BuildArgVec(inv
, in
, n_in
);
445 if (ParseArgs(in
, &tokens
[2], n_in
) < 0) {
446 fprintf(stderr
, "bad input args for rpc\n");
451 * Build the output (rpc return) values.
454 n_out
= n
- int_out_sep
- 1;
455 DEBUG("parsing out args %d\n", n_out
);
456 BuildArgVec(outv
, out
, n_out
);
458 if (ParseArgs(out
, &tokens
[int_out_sep
+ 1], n_out
) < 0) {
459 fprintf(stderr
, "bad output args for rpc\n");
463 rpc_num
= NaClSrpcGetRpcNum(&rpc_channel
, tokens
[1].start
);
465 fprintf(stderr
, "unknown rpc\n");
469 fprintf(stderr
,"using rpc %s no %d\n", tokens
[1].start
, rpc_num
);
470 errcode
= NaClSrpcInvokeV(&rpc_channel
, rpc_num
, inv
, outv
);
471 if (NACL_SRPC_RESULT_OK
!= errcode
) {
472 fprintf(stderr
, "rpc call failed %s\n", NaClSrpcErrorString(errcode
));
479 printf("%s RESULTS: ", tokens
[1].start
);
480 DumpArgs(outv
[0], n_out
);
483 * Free the storage allocated for array valued parameters and returns.
485 FreeArrayArgs(in
, n_in
);
486 FreeArrayArgs(out
, n_out
);
488 fprintf(stderr
, "unknown command\n");
494 * Shut down the rpc streams.
496 NaClSrpcDtor(&rpc_channel
);
501 * This function works with the rpc services in tests/srpc to test the
504 static void TestRandomRpcs(NaClHandle imc_handle
) {
508 NaClSrpcArg
* outv
[2];
509 NaClSrpcError errcode
;
510 NaClSrpcChannel rpc_channel
;
522 * Set up the connection to the child process.
524 NaClSrpcClientCtor(&rpc_channel
, NaClSrpcImcDescTypeFromHandle(imc_handle
));
526 * TODO: set up timing on both ends of the IMC channel.
530 if (timed_rpc_method
< 1 || timed_rpc_method
>= rpc_channel
.rpc_count
) {
531 fprintf(stderr
, "method number must be between 1 and %d (inclusive)\n",
532 rpc_channel
.rpc_count
- 1);
536 argument_count
= strlen(rpc_channel
.rpc_descr
[timed_rpc_method
].in_args
);
537 return_count
= strlen(rpc_channel
.rpc_descr
[timed_rpc_method
].out_args
);
539 if (argument_count
!= return_count
) {
540 fprintf(stderr
, "method argument and return count must match\n");
544 if (1 < argument_count
) {
545 fprintf(stderr
, "method must take zero or one argument\n");
549 if (argument_count
== 0) {
553 enum NaClSrpcArgType type
;
554 static char c_in
[MAX_ARRAY_SIZE
];
555 static char c_out
[MAX_ARRAY_SIZE
];
556 static double d_in
[MAX_ARRAY_SIZE
/ sizeof(double)];
557 static double d_out
[MAX_ARRAY_SIZE
/ sizeof(double)];
558 static int i_in
[MAX_ARRAY_SIZE
/ sizeof(int)];
559 static int i_out
[MAX_ARRAY_SIZE
/ sizeof(int)];
561 "She should have died hereafter;"
562 "There would have been a time for such a word."
563 "To-morrow, and to-morrow, and to-morrow,"
564 "Creeps in this petty pace from day to day"
565 "To the last syllable of recorded time,"
566 "And all our yesterdays have lighted fools"
567 "The way to dusty death. Out, out, brief candle!"
568 "Life's but a walking shadow, a poor player"
569 "That struts and frets his hour upon the stage"
570 "And then is heard no more: it is a tale"
571 "Told by an idiot, full of sound and fury,"
572 "Signifying nothing";
574 in
.tag
= rpc_channel
.rpc_descr
[timed_rpc_method
].in_args
[0];
575 out
.tag
= rpc_channel
.rpc_descr
[timed_rpc_method
].out_args
[0];
577 type
= rpc_channel
.rpc_descr
[timed_rpc_method
].in_args
[0];
579 case NACL_SRPC_ARG_TYPE_BOOL
:
582 case NACL_SRPC_ARG_TYPE_CHAR_ARRAY
:
583 in
.u
.caval
.count
= timed_rpc_bytes
;
584 in
.u
.caval
.carr
= c_in
;
585 out
.u
.caval
.count
= in
.u
.iaval
.count
;
586 out
.u
.caval
.carr
= c_out
;
588 case NACL_SRPC_ARG_TYPE_DOUBLE
:
589 in
.u
.dval
= 3.1415926;
591 case NACL_SRPC_ARG_TYPE_DOUBLE_ARRAY
:
592 in
.u
.daval
.count
= timed_rpc_bytes
/ sizeof(double);
593 in
.u
.daval
.darr
= d_in
;
594 out
.u
.daval
.count
= in
.u
.iaval
.count
;
595 out
.u
.daval
.darr
= d_out
;
597 case NACL_SRPC_ARG_TYPE_INT
:
600 case NACL_SRPC_ARG_TYPE_INT_ARRAY
:
601 in
.u
.iaval
.count
= timed_rpc_bytes
/ sizeof(int);
602 in
.u
.iaval
.iarr
= i_in
;
603 out
.u
.iaval
.count
= in
.u
.iaval
.count
;
604 out
.u
.iaval
.iarr
= i_out
;
606 case NACL_SRPC_ARG_TYPE_STRING
:
607 /* TODO: needs length variation */
610 case NACL_SRPC_ARG_TYPE_INVALID
:
611 case NACL_SRPC_ARG_TYPE_HANDLE
:
612 case NACL_SRPC_ARG_TYPE_OBJECT
:
613 case NACL_SRPC_ARG_TYPE_VARIANT_ARRAY
:
621 for (i
= 0; i
< timed_rpc_count
; ++i
) {
622 errcode
= NaClSrpcInvokeV(&rpc_channel
, timed_rpc_method
, inv
, outv
);
623 if (NACL_SRPC_RESULT_OK
!= errcode
) {
624 fprintf(stderr
, "rpc call failed %s\n", NaClSrpcErrorString(errcode
));
629 if (i
== timed_rpc_count
) {
630 NaClSrpcArg
* timer_inv
[] = { NULL
};
632 NaClSrpcArg
* timer_outv
[5];
633 double total_server_usec
;
634 double dummy_receive
;
635 double dummy_imc_read
;
636 double dummy_imc_write
;
638 timer_outv
[0] = &tm
[0];
639 timer_outv
[1] = &tm
[1];
640 timer_outv
[2] = &tm
[2];
641 timer_outv
[3] = &tm
[3];
642 timer_outv
[4] = NULL
;
644 NaClSrpcGetTimes(&rpc_channel
,
649 printf("server time observed by client: %.6f sec\n",
650 total_server_usec
/ 1000000.0);
651 tm
[0].tag
= NACL_SRPC_ARG_TYPE_DOUBLE
;
652 tm
[1].tag
= NACL_SRPC_ARG_TYPE_DOUBLE
;
653 tm
[2].tag
= NACL_SRPC_ARG_TYPE_DOUBLE
;
654 tm
[3].tag
= NACL_SRPC_ARG_TYPE_DOUBLE
;
655 errcode
= NaClSrpcInvokeV(&rpc_channel
,
656 NACL_SRPC_GET_TIMES_METHOD
,
659 printf("server send time: %.6f sec\n", tm
[0].u
.dval
/ 1000000.0);
660 printf("server receive time: %.6f sec\n", tm
[1].u
.dval
/ 1000000.0);
661 printf("imc read time: %.6f sec\n", tm
[2].u
.dval
/ 1000000.0);
662 printf("imc write time: %.6f sec\n", tm
[3].u
.dval
/ 1000000.0);
668 * Shut down the rpc streams.
670 NaClSrpcDtor(&rpc_channel
);
674 #if defined(HAVE_SDL)
678 int main(int argc
, char *argv
[]) {
679 NaClHandle imc_handle
;
680 struct NaClSelLdrLauncher
* launcher
;
682 static char* application_name
;
691 /* Descriptor transfer requires the following. */
692 NaClNrdAllModulesInit();
694 /* The -p option can change the sel_ldr binary used. */
695 g_sel_ldr_pathname
= kSelLdrPathname
;
697 /* We are only testing if this count is not zero. */
700 /* command line parsing */
701 while ((opt
= getopt(argc
, argv
, "f:p:r:v")) != -1) {
704 application_name
= optarg
;
707 g_sel_ldr_pathname
= optarg
;
710 timed_rpc_count
= strtol(optarg
, &nextp
, 0);
712 timed_rpc_method
= strtol(nextp
+ 1, &nextp
, 0);
714 timed_rpc_bytes
= strtol(nextp
+ 1, 0, 0);
717 printf("Testing: %d iterations, method %d, %d bytes\n",
723 NaClLogIncrVerbosity();
727 "Usage: sel_universal [-f nacl_file]\n"
729 " [-r count:method:bytes]\n");
735 * Pass any extra arguments on to sel_ldr or the application.
741 for (i
= 0; i
< argc
; ++i
) {
742 if (tmp_ldr_argv
== NULL
) {
743 /* sel_universal arguments come first. */
744 if (!strcmp("--", argv
[i
])) {
745 tmp_ldr_argv
= &argv
[i
+ 1];
747 } else if (module_argv
== NULL
) {
748 /* sel_ldr arguments come next. */
749 if (!strcmp("--", argv
[i
])) {
750 module_argv
= &argv
[i
+ 1];
755 /* application arguments come last. */
761 * Append the -P 5 option to the command line to pass the channel to the
764 sel_ldr_argv
= (char**) malloc(module_argc
* sizeof(*module_argv
));
765 sel_ldr_argv
[0] = "-P";
766 sel_ldr_argv
[1] = "5";
767 for (i
= 0; i
< sel_ldr_argc
; ++i
) {
768 sel_ldr_argv
[i
+ 2] = tmp_ldr_argv
[i
];
772 * Start sel_ldr with the given application and arguments.
774 launcher
= NaClSelLdrStart(application_name
,
777 (const char**) sel_ldr_argv
,
779 (const char**) module_argv
);
780 imc_handle
= NaClSelLdrGetChannel(launcher
);
782 if (timed_rpc_count
== 0) {
783 CommandLoop(imc_handle
);
785 TestRandomRpcs(imc_handle
);
788 NaClSelLdrShutdown(launcher
);
790 NaClNrdAllModulesFini();
795 /* TODO: create one utility function to get usec times. */
796 static int gettimeofday(struct timeval
*tv
, struct timezone
*tz
) {
797 unsigned __int64 timer
= 0;
799 GetSystemTimeAsFileTime(&filetime
);
800 timer
|= filetime
.dwHighDateTime
;
802 timer
|= filetime
.dwLowDateTime
;
803 /* FILETIME has 100ns precision. Convert to usec. */
805 tv
->tv_sec
= (long) (timer
/ 1000000UL);
806 tv
->tv_usec
= (long) (timer
% 1000000UL);