1 /*
2 * xs-test.c
3 *
4 * Do Xenstore tests.
5 *
6 * Copyright (C) 2016 Juergen Gross <jgross@suse.com>,
7 * SUSE Linux GmbH
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms and conditions of the GNU General Public
11 * License, version 2, as published by the Free Software Foundation.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public
19 * License along with this program; If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #define _GNU_SOURCE
23 #include <getopt.h>
24 #include <inttypes.h>
25 #include <stdbool.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
30 #include <time.h>
31 #include <xenstore.h>
32
33 #define TEST_PATH "xenstore-test"
34 #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
35 #define WRITE_BUFFERS_N 10
36 #define WRITE_BUFFERS_SIZE 4000
37 #define MAX_TA_LOOPS 100
38
39 struct test {
40 char *name;
41 int (*func_init)(uintptr_t par);
42 int (*func)(uintptr_t par);
43 int (*func_deinit)(uintptr_t par);
44 uintptr_t par;
45 char *descr;
46 };
47
48 static struct xs_handle *xsh;
49 static char *path;
50 static char *paths[WRITE_BUFFERS_N];
51 static char write_buffers[WRITE_BUFFERS_N][WRITE_BUFFERS_SIZE];
52 static int ta_loops;
53
54 static struct option options[] = {
55 { "list-tests", 0, NULL, 'l' },
56 { "test", 1, NULL, 't' },
57 { "random", 1, NULL, 'r' },
58 { "help", 0, NULL, 'h' },
59 { "iterations", 1, NULL, 'i' },
60 { NULL, 0, NULL, 0 }
61 };
62
call_test(struct test * tst,int iters,bool no_clock)63 static int call_test(struct test *tst, int iters, bool no_clock)
64 {
65 char *stage = "?";
66 struct timespec tp1, tp2;
67 uint64_t nsec, nsec_min, nsec_max, nsec_sum;
68 int i, ret;
69
70 nsec_min = -1;
71 nsec_max = 0;
72 nsec_sum = 0;
73
74 for ( i = 0; i < iters; i++ )
75 {
76 stage = "pre-init";
77 xs_rm(xsh, XBT_NULL, path);
78 if ( !xs_write(xsh, XBT_NULL, path, "", 0) )
79 {
80 ret = errno;
81 break;
82 }
83 stage = "init";
84 ret = tst->func_init(tst->par);
85 if ( ret )
86 break;
87 if ( clock_gettime(CLOCK_REALTIME, &tp1) )
88 no_clock = true;
89 stage = "run";
90 ret = tst->func(tst->par);
91 if ( ret )
92 break;
93 if ( clock_gettime(CLOCK_REALTIME, &tp2) )
94 no_clock = true;
95 if ( !no_clock )
96 {
97 nsec = tp2.tv_sec * 1000000000 + tp2.tv_nsec -
98 tp1.tv_sec * 1000000000 - tp1.tv_nsec;
99 if ( nsec < nsec_min )
100 nsec_min = nsec;
101 if ( nsec > nsec_max )
102 nsec_max = nsec;
103 nsec_sum += nsec;
104 }
105 stage = "deinit";
106 ret = tst->func_deinit(tst->par);
107 if ( ret )
108 break;
109 }
110
111 if ( ret )
112 printf("%-10s: failed (ret = %d, stage %s)\n", tst->name, ret, stage);
113 else if ( !no_clock )
114 {
115 printf("%-10s:", tst->name);
116 if ( iters > 1 )
117 printf(" avg: %"PRIu64" ns (%"PRIu64" ns .. %"PRIu64" ns)",
118 nsec_sum / iters, nsec_min, nsec_max);
119 else
120 printf(" %"PRIu64" ns", nsec_sum);
121 printf("\n");
122 }
123
124 return ret;
125 }
126
usage(int ret)127 static void usage(int ret)
128 {
129 FILE *out;
130
131 out = ret ? stderr : stdout;
132
133 fprintf(out, "usage: xs-test [<options>]\n");
134 fprintf(out, " <options> are:\n");
135 fprintf(out, " -i|--iterations <i> perform each test <i> times (default 1)\n");
136 fprintf(out, " -l|--list-tests list available tests\n");
137 fprintf(out, " -r|--random <time> perform random tests for <time> seconds\n");
138 fprintf(out, " -t|--test <test> run <test> (default is all tests)\n");
139 fprintf(out, " -h|--help print this usage information\n");
140 exit(ret);
141 }
142
ret0(uintptr_t par)143 static int ret0(uintptr_t par)
144 {
145 return 0;
146 }
147
verify_node(char * node,char * data,unsigned int size)148 static int verify_node(char *node, char *data, unsigned int size)
149 {
150 char *buf;
151 unsigned int len;
152 int ret;
153
154 buf = xs_read(xsh, XBT_NULL, node, &len);
155 if ( !buf )
156 return errno;
157
158 ret = (len == size && !memcmp(buf, data, len)) ? 0 : ENODATA;
159 free(buf);
160
161 return ret;
162 }
163
test_read_init(uintptr_t par)164 static int test_read_init(uintptr_t par)
165 {
166 if ( par > WRITE_BUFFERS_SIZE )
167 return EFBIG;
168 return xs_write(xsh, XBT_NULL, paths[0], write_buffers[0], par) ? 0 : errno;
169 }
170
test_read(uintptr_t par)171 static int test_read(uintptr_t par)
172 {
173 char *buf;
174 unsigned int len;
175
176 buf = xs_read(xsh, XBT_NULL, paths[0], &len);
177 if ( !buf )
178 return errno;
179 free(buf);
180 return 0;
181 }
182
183 #define test_read_deinit ret0
184
test_write_init(uintptr_t par)185 static int test_write_init(uintptr_t par)
186 {
187 return (par > WRITE_BUFFERS_SIZE) ? EFBIG : 0;
188 }
189
test_write(uintptr_t par)190 static int test_write(uintptr_t par)
191 {
192 return xs_write(xsh, XBT_NULL, paths[0], write_buffers[0], par) ? 0 : errno;
193 }
194
test_write_deinit(uintptr_t par)195 static int test_write_deinit(uintptr_t par)
196 {
197 return verify_node(paths[0], write_buffers[0], par);
198 }
199
test_dir_init(uintptr_t par)200 static int test_dir_init(uintptr_t par)
201 {
202 unsigned int i;
203
204 for ( i = 0; i < WRITE_BUFFERS_N; i++ )
205 if ( !xs_write(xsh, XBT_NULL, paths[i], write_buffers[i], 1) )
206 return errno;
207
208 return 0;
209 }
210
test_dir(uintptr_t par)211 static int test_dir(uintptr_t par)
212 {
213 char **dir;
214 unsigned int num;
215
216 dir = xs_directory(xsh, XBT_NULL, path, &num);
217 if ( !dir )
218 return errno;
219
220 free(dir);
221 return 0;
222 }
223
test_dir_deinit(uintptr_t par)224 static int test_dir_deinit(uintptr_t par)
225 {
226 char **dir;
227 unsigned int i, j, num;
228 int rc = 0;
229
230 dir = xs_directory(xsh, XBT_NULL, path, &num);
231 if ( !dir )
232 return errno;
233
234 for ( j = 0; j < WRITE_BUFFERS_N; j++ )
235 {
236 for ( i = 0; i < num; i++ )
237 if ( dir[i][0] == 'a' + j && dir[i][1] == 0 )
238 break;
239 if ( i == num )
240 rc = ENODATA;
241 }
242 if ( num != WRITE_BUFFERS_N )
243 rc = ENODATA;
244 free(dir);
245 return rc;
246 }
247
test_rm_init(uintptr_t par)248 static int test_rm_init(uintptr_t par)
249 {
250 unsigned int i;
251
252 if ( par > WRITE_BUFFERS_N )
253 return EFBIG;
254
255 for ( i = 0; i < par; i++ )
256 if ( xs_write(xsh, XBT_NULL, paths[i], write_buffers[i], 1) )
257 return errno;
258
259 return 0;
260 }
261
test_rm(uintptr_t par)262 static int test_rm(uintptr_t par)
263 {
264 if ( !xs_rm(xsh, XBT_NULL, path) )
265 return errno;
266
267 return 0;
268 }
269
270 #define test_rm_deinit ret0
271
272 #define test_ta1_init ret0
273
test_ta1(uintptr_t par)274 static int test_ta1(uintptr_t par)
275 {
276 xs_transaction_t t;
277 int l;
278
279 for ( l = 0; l < MAX_TA_LOOPS; l++ )
280 {
281 t = xs_transaction_start(xsh);
282 if ( t == XBT_NULL )
283 return errno;
284 if ( xs_transaction_end(xsh, t, par ? true : false) )
285 return 0;
286 if ( errno != EAGAIN )
287 return errno;
288 }
289
290 ta_loops++;
291 return 0;
292 }
293
294 #define test_ta1_deinit ret0
295
test_ta2_init(uintptr_t par)296 static int test_ta2_init(uintptr_t par)
297 {
298 return xs_write(xsh, XBT_NULL, paths[0], write_buffers[0], 1) ? 0 : errno;
299 }
300
test_ta2(uintptr_t par)301 static int test_ta2(uintptr_t par)
302 {
303 xs_transaction_t t;
304 char *buf;
305 unsigned int len;
306 int ret;
307 int l;
308
309 for ( l = 0; l < MAX_TA_LOOPS; l++ )
310 {
311 t = xs_transaction_start(xsh);
312 if ( t == XBT_NULL )
313 return errno;
314 buf = xs_read(xsh, t, paths[0], &len);
315 if ( !buf )
316 goto out;
317 free(buf);
318 if ( !xs_write(xsh, t, paths[0], "b", 1) )
319 goto out;
320 buf = xs_read(xsh, t, paths[0], &len);
321 if ( !buf )
322 goto out;
323 errno = (len == 1 && buf[0] == 'b') ? 0 : ENODATA;
324 free(buf);
325 if ( errno )
326 goto out;
327 buf = xs_read(xsh, XBT_NULL, paths[0], &len);
328 if ( !buf )
329 goto out;
330 errno = (len == 1 && buf[0] == 'a') ? 0 : ENODATA;
331 free(buf);
332 if ( errno )
333 goto out;
334 if ( xs_transaction_end(xsh, t, par ? true : false) )
335 return 0;
336 if ( errno != EAGAIN )
337 return errno;
338 }
339
340 ta_loops++;
341 return 0;
342
343 out:
344 ret = errno;
345 xs_transaction_end(xsh, t, true);
346 return ret;
347 }
348
test_ta2_deinit(uintptr_t par)349 static int test_ta2_deinit(uintptr_t par)
350 {
351 return verify_node(paths[0], par ? "a" : "b", 1);
352 }
353
test_ta3_init(uintptr_t par)354 static int test_ta3_init(uintptr_t par)
355 {
356 return xs_write(xsh, XBT_NULL, paths[0], write_buffers[0], 1) ? 0 : errno;
357 }
358
test_ta3(uintptr_t par)359 static int test_ta3(uintptr_t par)
360 {
361 xs_transaction_t t;
362 char *buf;
363 unsigned int len;
364 int ret;
365
366 t = xs_transaction_start(xsh);
367 if ( t == XBT_NULL )
368 return errno;
369 buf = xs_read(xsh, t, paths[0], &len);
370 if ( !buf )
371 goto out;
372 free(buf);
373 if ( !xs_write(xsh, XBT_NULL, paths[0], "b", 1) )
374 goto out;
375 buf = xs_read(xsh, t, paths[0], &len);
376 if ( !buf )
377 goto out;
378 errno = (len == 1 && buf[0] == 'a') ? 0 : ENODATA;
379 free(buf);
380 if ( errno )
381 goto out;
382 if ( !xs_write(xsh, t, paths[0], "c", 1) )
383 goto out;
384 buf = xs_read(xsh, t, paths[0], &len);
385 if ( !buf )
386 goto out;
387 errno = (len == 1 && buf[0] == 'c') ? 0 : ENODATA;
388 free(buf);
389 if ( errno )
390 goto out;
391 if ( xs_transaction_end(xsh, t, false) || errno != EAGAIN )
392 return ENODATA;
393 return 0;
394
395 out:
396 ret = errno;
397 xs_transaction_end(xsh, t, true);
398 return ret;
399 }
400
test_ta3_deinit(uintptr_t par)401 static int test_ta3_deinit(uintptr_t par)
402 {
403 return verify_node(paths[0], "b", 1);
404 }
405
406 #define TEST(s, f, p, l) { s, f ## _init, f, f ## _deinit, (uintptr_t)(p), l }
407 struct test tests[] = {
408 TEST("read 1", test_read, 1, "Read node with 1 byte data"),
409 TEST("read 3000", test_read, 3000, "Read node with 3000 bytes data"),
410 TEST("write 1", test_write, 1, "Write node with 1 byte data"),
411 TEST("write 3000", test_write, 3000, "Write node with 3000 bytes data"),
412 TEST("dir", test_dir, 0, "List directory"),
413 TEST("rm node", test_rm, 0, "Remove single node"),
414 TEST("rm dir", test_rm, WRITE_BUFFERS_N, "Remove node with sub-nodes"),
415 TEST("ta empty", test_ta1, 0, "Empty transaction"),
416 TEST("ta empty x", test_ta1, 1, "Empty transaction abort"),
417 TEST("ta rmw", test_ta2, 0, "Read-modify-write transaction"),
418 TEST("ta rmw x", test_ta2, 1, "Read-modify-write transaction abort"),
419 TEST("ta err", test_ta3, 0, "Transaction with conflict"),
420 };
421
cleanup(void)422 static void cleanup(void)
423 {
424 xs_transaction_t t;
425 char **dir;
426 unsigned int num;
427
428 xs_rm(xsh, XBT_NULL, path);
429
430 while ( true )
431 {
432 t = xs_transaction_start(xsh);
433 if ( t == XBT_NULL )
434 return;
435
436 dir = xs_directory(xsh, t, TEST_PATH, &num);
437 if ( dir && !num )
438 xs_rm(xsh, t, TEST_PATH);
439 free(dir);
440
441 if ( xs_transaction_end(xsh, t, false) || errno != EAGAIN )
442 return;
443 }
444 }
445
main(int argc,char * argv[])446 int main(int argc, char *argv[])
447 {
448 int opt, t, iters = 1, ret = 0, randtime = 0;
449 char *test = NULL;
450 bool list = false;
451 time_t stop;
452
453 while ( (opt = getopt_long(argc, argv, "lr:t:hi:", options,
454 NULL)) != -1 )
455 {
456 switch ( opt )
457 {
458 case 'i':
459 iters = atoi(optarg);
460 break;
461 case 'l':
462 list = true;
463 break;
464 case 'r':
465 randtime = atoi(optarg);
466 break;
467 case 't':
468 test = optarg;
469 break;
470 case 'h':
471 usage(0);
472 break;
473 }
474 }
475 if ( optind != argc )
476 usage(1);
477
478 if ( list )
479 {
480 for ( t = 0; t < ARRAY_SIZE(tests); t++ )
481 printf("%-10s: %s\n", tests[t].name, tests[t].descr);
482 return 0;
483 }
484
485 asprintf(&path, "%s/%u", TEST_PATH, getpid());
486 for ( t = 0; t < WRITE_BUFFERS_N; t++ )
487 {
488 memset(write_buffers[t], 'a' + t, WRITE_BUFFERS_SIZE);
489 asprintf(&paths[t], "%s/%c", path, 'a' + t);
490 }
491
492 xsh = xs_open(0);
493 if ( !xsh )
494 {
495 fprintf(stderr, "could not connect to xenstore\n");
496 exit(2);
497 }
498
499 if ( randtime )
500 {
501 stop = time(NULL) + randtime;
502 srandom((unsigned int)stop);
503
504 while ( time(NULL) < stop )
505 {
506 t = random() % ARRAY_SIZE(tests);
507 ret = call_test(tests + t, iters, true);
508 }
509 }
510 else
511 for ( t = 0; t < ARRAY_SIZE(tests); t++ )
512 {
513 if ( !test || !strcmp(test, tests[t].name) )
514 ret = call_test(tests + t, iters, false);
515 }
516
517 if ( !ret )
518 cleanup();
519
520 xs_close(xsh);
521
522 if ( ta_loops )
523 printf("Exhaustive transaction retries (%d) occurrred %d times.\n",
524 MAX_TA_LOOPS, ta_loops);
525
526 return 0;
527 }
528
529 /*
530 * Local variables:
531 * mode: C
532 * c-file-style: "BSD"
533 * c-basic-offset: 4
534 * indent-tabs-mode: nil
535 * End:
536 */
537