1/** \file
2 *  \brief RPC system test code
3 */
4
5/*
6 * Copyright (c) 2010, ETH Zurich.
7 * All rights reserved.
8 *
9 * This file is distributed under the terms in the attached LICENSE file.
10 * If you do not find this file, copies can be found by writing to:
11 * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
12 */
13
14#include <string.h>
15#include <stdio.h>
16#include <stdarg.h>
17#include <barrelfish/barrelfish.h>
18#include <barrelfish/nameservice_client.h>
19
20#include <if/test_rpc_cap_defs.h>
21#include <if/test_rpc_cap_defs.h>
22
23static const char *my_service_name = "rpc_cap_test";
24uint8_t is_server = 0x0;
25int client_id = 0;
26
27__attribute__((format(printf, 1, 2)))
28static void my_debug_printf(const char *fmt, ...)
29{
30    struct thread *me = thread_self();
31    va_list argptr;
32    char id[32] = "-";
33    char str[1024];
34    size_t len;
35
36    if (me)
37        snprintf(id, sizeof(id), "%"PRIuPTR, thread_get_id(me));
38    len = snprintf(str, sizeof(str), "\033[34m%.*s.\033[31m%u.%s\033[0m: ",
39                   DISP_NAME_LEN, disp_name(), disp_get_core_id(), id);
40
41    if(is_server){
42        len += snprintf(str + len, sizeof(str) - len, "server: ");
43    } else {
44        len += snprintf(str + len, sizeof(str) - len, "client(%d): ",
45                client_id);
46    }
47
48    if (len < sizeof(str)) {
49        va_start(argptr, fmt);
50        vsnprintf(str + len, sizeof(str) - len, fmt, argptr);
51        va_end(argptr);
52    }
53    sys_print(str, sizeof(str));
54}
55
56
57
58struct echo_response {
59    struct test_rpc_cap_binding *b;
60    uint32_t response;
61};
62
63static void send_echo_response(void *arg){
64    struct echo_response * resp = arg;
65    errval_t err;
66    err = test_rpc_cap_echo_response__tx(resp->b, NOP_CONT, resp->response);
67    assert(err_is_ok(err));
68}
69
70
71static void handle_echo_call(struct test_rpc_cap_binding *b,
72        uint32_t arg_in)
73{
74    my_debug_printf("handle_echo_call (bind=%p) arg=%"PRIu32"\n", b, arg_in);
75    struct echo_response * resp = malloc(sizeof(*resp));
76    resp->b = b;
77    resp->response = arg_in;
78    send_echo_response(resp);
79}
80
81
82static void handle_send_cap_one_call(struct test_rpc_cap_binding *b,
83        struct capref incap)
84{
85    my_debug_printf("handle_send_cap_one_call (bind=%p)\n", b);
86    errval_t err = SYS_ERR_OK;
87    struct capability cap;
88    err = debug_cap_identify(incap, &cap );
89
90    test_rpc_cap_send_cap_one_response__tx(b, NOP_CONT, err);
91}
92
93static void handle_send_cap_two_call(struct test_rpc_cap_binding *b,
94        struct capref incap1, struct capref incap2)
95{
96    my_debug_printf("handle_send_cap_two_call (bind=%p)\n", b);
97    errval_t err = SYS_ERR_OK;
98    struct capability cap;
99
100    err = debug_cap_identify(incap1, &cap);
101    assert(err_is_ok(err));
102    err = debug_cap_identify(incap2, &cap);
103    assert(err_is_ok(err));
104
105    test_rpc_cap_send_cap_two_response__tx(b, NOP_CONT, err);
106}
107
108static struct test_rpc_cap_rx_vtbl rx_vtbl = {
109    .echo_call = handle_echo_call,
110    .send_cap_one_call = handle_send_cap_one_call,
111    .send_cap_two_call = handle_send_cap_two_call
112};
113
114/* ------------------------------ CLIENT ------------------------------ */
115
116static struct test_rpc_cap_binding *test_rpc_binding;
117
118static void client_call_test_1(void){
119    uint32_t res=0;
120    errval_t err;
121    err = test_rpc_binding->rpc_tx_vtbl.echo(test_rpc_binding, client_id, &res);
122    if(err_is_fail(err)){
123        my_debug_printf("Error in rpc call (1)\n");
124    } else if(res != client_id) {
125        my_debug_printf("Wrong result?\n");
126    } else {
127        my_debug_printf("client_call_test_1 successful!\n");
128    }
129}
130
131static void client_call_test_2(void){
132    struct capref my_frame;
133    errval_t err, msg_err;
134
135    err = frame_alloc(&my_frame, BASE_PAGE_SIZE, NULL);
136    assert(err_is_ok(err));
137
138    err = test_rpc_binding->rpc_tx_vtbl.send_cap_one(test_rpc_binding, my_frame, &msg_err);
139    if(err_is_fail(err)){
140        USER_PANIC_ERR(err, "Error in rpc call (2)\n");
141    } else if(err_is_fail(msg_err)) {
142        USER_PANIC_ERR(err, "Server msg (2)\n");
143    } else {
144        my_debug_printf("client_call_test_2 successful!\n");
145    }
146}
147
148static void client_call_test_3(int i){
149    struct capref frame1, frame2;
150    struct capability cap1, cap2;
151    char buf[1024];
152    int buf_idx=0;
153    errval_t err, msg_err;
154
155    err = frame_alloc(&frame1, BASE_PAGE_SIZE, NULL);
156    assert(err_is_ok(err));
157    err = debug_cap_identify(frame1, &cap1);
158    assert(err_is_ok(err));
159    buf_idx += debug_print_cap(buf, sizeof(buf), &cap1);
160
161    err = frame_alloc(&frame2, BASE_PAGE_SIZE, NULL);
162    assert(err_is_ok(err));
163    err = debug_cap_identify(frame2, &cap2);
164    assert(err_is_ok(err));
165    buf_idx += debug_print_cap(buf+buf_idx, sizeof(buf)-buf_idx, &cap2);
166
167    my_debug_printf("Calling send_cap_two: %s\n", buf);
168
169    err = test_rpc_binding->rpc_tx_vtbl.send_cap_two(test_rpc_binding, frame1, frame2, &msg_err);
170    if(err_is_fail(err)){
171        USER_PANIC_ERR(err, "Error in rpc call (3)\n");
172    } else if(err_is_fail(msg_err)) {
173        USER_PANIC_ERR(err, "Server msg (3)\n");
174    } else {
175        my_debug_printf("client_call_test_3(%d) successful!\n", i);
176    }
177}
178
179static void bind_cb(void *st,
180                    errval_t err,
181                    struct test_rpc_cap_binding *b)
182{
183    if (err_is_fail(err)) {
184        USER_PANIC_ERR(err, "bind failed");
185    }
186
187    my_debug_printf("client: bound!\n");
188
189    test_rpc_binding = b;
190    test_rpc_cap_rpc_client_init(b);
191
192    client_call_test_1();
193    client_call_test_2();
194    for(int i=0; i<100;i++){
195        client_call_test_3(i);
196    }
197    printf("TEST PASSED\n");
198}
199
200static void start_client(void)
201{
202    iref_t iref;
203    errval_t err;
204
205    my_debug_printf("client: looking up '%s' in name service...\n", my_service_name);
206    err = nameservice_blocking_lookup(my_service_name, &iref);
207    if (err_is_fail(err)) {
208        USER_PANIC_ERR(err, "nameservice_blocking_lookup failed");
209    }
210
211    my_debug_printf("client: binding to %"PRIuIREF"...\n", iref);
212    err = test_rpc_cap_bind(iref, bind_cb, NULL ,
213                                 get_default_waitset(),
214                                 IDC_BIND_FLAGS_DEFAULT);
215    if (err_is_fail(err)) {
216        USER_PANIC_ERR(err, "bind failed");
217    }
218}
219
220/* ------------------------------ SERVER ------------------------------ */
221
222
223static void export_cb(void *st,
224                      errval_t err,
225                      iref_t iref)
226{
227    if (err_is_fail(err)) {
228        USER_PANIC_ERR(err, "export failed");
229    }
230
231    // register this iref with the name service
232    err = nameservice_register(my_service_name, iref);
233    if (err_is_fail(err)) {
234        USER_PANIC_ERR(err, "nameservice_register failed");
235    }
236
237    my_debug_printf("server: service %s exported at iref %"PRIuIREF"\n", my_service_name, iref);
238}
239
240static errval_t connect_cb(void *st,
241                           struct test_rpc_cap_binding *b)
242{
243    my_debug_printf("server: client connected!\n");
244
245    // copy my message receive handler vtable to the binding
246    b->rx_vtbl = rx_vtbl;
247
248    // accept the connection (we could return an error to refuse it)
249    return SYS_ERR_OK;
250}
251
252static void start_server(void)
253{
254    errval_t err;
255
256    my_debug_printf("server: Starting server...\n");
257
258    is_server = 0x1;
259
260    err = test_rpc_cap_export(NULL,
261                              export_cb,
262                              connect_cb,
263                              get_default_waitset(),
264                              IDC_EXPORT_FLAGS_DEFAULT);
265    if (err_is_fail(err)) {
266        USER_PANIC_ERR(err, "export failed");
267    }
268}
269
270/* ------------------------------ MAIN ------------------------------ */
271
272static int usage(char * argv[]){
273    printf("Usage: %s client|server [id=INT]\n", argv[0]);
274    return EXIT_FAILURE;
275}
276
277int main(int argc,
278         char *argv[])
279{
280    errval_t err;
281
282    if (argc >= 2 && strcmp(argv[1], "client") == 0) {
283        for(int i = 2; i < argc; i++){
284            if(strncmp(argv[i], "id=", strlen("id=")) == 0){
285                client_id = atoi(argv[i] + 3);
286            } else {
287                printf("Unknonw argument: %s\n", argv[i]);
288                return usage(argv);
289            }
290        }
291        start_client();
292    } else if (argc == 2 && strcmp(argv[1], "server") == 0) {
293        start_server();
294    } else {
295        return usage(argv);
296    }
297
298    struct waitset *ws = get_default_waitset();
299    while (1) {
300        err = event_dispatch(ws);
301        if (err_is_fail(err)) {
302            DEBUG_ERR(err, "in event_dispatch");
303            break;
304        }
305    }
306
307    return EXIT_FAILURE;
308}
309