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