diff options
Diffstat (limited to 'src/plugin/datastore/perf_plugin_datastore.c')
-rw-r--r-- | src/plugin/datastore/perf_plugin_datastore.c | 573 |
1 files changed, 573 insertions, 0 deletions
diff --git a/src/plugin/datastore/perf_plugin_datastore.c b/src/plugin/datastore/perf_plugin_datastore.c new file mode 100644 index 000000000..8e63b08e6 --- /dev/null +++ b/src/plugin/datastore/perf_plugin_datastore.c | |||
@@ -0,0 +1,573 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2004, 2005, 2006, 2007, 2009, 2011 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | /* | ||
21 | * @file perf_plugin_datastore.c | ||
22 | * @brief Profile database plugin directly, focusing on iterators. | ||
23 | * @author Christian Grothoff | ||
24 | */ | ||
25 | |||
26 | #include "platform.h" | ||
27 | #include "gnunet_util_lib.h" | ||
28 | #include "gnunet_protocols.h" | ||
29 | #include "gnunet_datastore_plugin.h" | ||
30 | #include "gnunet_testing_lib.h" | ||
31 | #include <gauger.h> | ||
32 | |||
33 | /** | ||
34 | * Target datastore size (in bytes). Realistic sizes are | ||
35 | * more like 16 GB (not the default of 16 MB); however, | ||
36 | * those take too long to run them in the usual "make check" | ||
37 | * sequence. Hence the value used for shipping is tiny. | ||
38 | */ | ||
39 | #define MAX_SIZE 1024LL * 1024 * 16 * 1 | ||
40 | |||
41 | #define ITERATIONS 2 | ||
42 | |||
43 | /** | ||
44 | * Number of put operations equivalent to 1/10th of MAX_SIZE | ||
45 | */ | ||
46 | #define PUT_10 (MAX_SIZE / 32 / 1024 / ITERATIONS) | ||
47 | |||
48 | static char category[256]; | ||
49 | |||
50 | static unsigned int hits[PUT_10 / 8 + 1]; | ||
51 | |||
52 | static unsigned long long stored_bytes; | ||
53 | |||
54 | static unsigned long long stored_entries; | ||
55 | |||
56 | static unsigned long long stored_ops; | ||
57 | |||
58 | static const char *plugin_name; | ||
59 | |||
60 | static int ok; | ||
61 | |||
62 | enum RunPhase | ||
63 | { | ||
64 | RP_ERROR = 0, | ||
65 | RP_PUT, | ||
66 | RP_REP_GET, | ||
67 | RP_ZA_GET, | ||
68 | RP_EXP_GET, | ||
69 | RP_DONE | ||
70 | }; | ||
71 | |||
72 | |||
73 | struct CpsRunContext | ||
74 | { | ||
75 | unsigned int i; | ||
76 | struct GNUNET_TIME_Absolute start; | ||
77 | struct GNUNET_TIME_Absolute end; | ||
78 | const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
79 | struct GNUNET_DATASTORE_PluginFunctions *api; | ||
80 | enum RunPhase phase; | ||
81 | unsigned int cnt; | ||
82 | unsigned int iter; | ||
83 | uint64_t offset; | ||
84 | }; | ||
85 | |||
86 | |||
87 | /** | ||
88 | * Function called by plugins to notify us about a | ||
89 | * change in their disk utilization. | ||
90 | * | ||
91 | * @param cls closure (NULL) | ||
92 | * @param delta change in disk utilization, | ||
93 | * 0 for "reset to empty" | ||
94 | */ | ||
95 | static void | ||
96 | disk_utilization_change_cb (void *cls, int delta) | ||
97 | { | ||
98 | } | ||
99 | |||
100 | |||
101 | static void | ||
102 | test (void *cls); | ||
103 | |||
104 | |||
105 | /** | ||
106 | * Put continuation. | ||
107 | * | ||
108 | * @param cls closure | ||
109 | * @param key key for the item stored | ||
110 | * @param size size of the item stored | ||
111 | * @param status #GNUNET_OK or #GNUNET_SYSERROR | ||
112 | * @param msg error message on error | ||
113 | */ | ||
114 | static void | ||
115 | put_continuation (void *cls, | ||
116 | const struct GNUNET_HashCode *key, | ||
117 | uint32_t size, | ||
118 | int status, | ||
119 | const char *msg) | ||
120 | { | ||
121 | struct CpsRunContext *crc = cls; | ||
122 | |||
123 | if (GNUNET_OK != status) | ||
124 | { | ||
125 | fprintf (stderr, "ERROR: `%s'\n", msg); | ||
126 | } | ||
127 | else | ||
128 | { | ||
129 | stored_bytes += size; | ||
130 | stored_ops++; | ||
131 | stored_entries++; | ||
132 | } | ||
133 | GNUNET_SCHEDULER_add_now (&test, crc); | ||
134 | } | ||
135 | |||
136 | |||
137 | static void | ||
138 | do_put (struct CpsRunContext *crc) | ||
139 | { | ||
140 | char value[65536]; | ||
141 | size_t size; | ||
142 | static struct GNUNET_HashCode key; | ||
143 | static int i; | ||
144 | unsigned int prio; | ||
145 | |||
146 | if (0 == i) | ||
147 | crc->start = GNUNET_TIME_absolute_get (); | ||
148 | if (PUT_10 == i) | ||
149 | { | ||
150 | i = 0; | ||
151 | crc->end = GNUNET_TIME_absolute_get (); | ||
152 | { | ||
153 | printf ("%s took %s for %llu items\n", "Storing an item", | ||
154 | GNUNET_STRINGS_relative_time_to_string ( | ||
155 | GNUNET_TIME_absolute_get_difference (crc->start, | ||
156 | crc | ||
157 | ->end), | ||
158 | GNUNET_YES), | ||
159 | PUT_10); | ||
160 | if (PUT_10 > 0) | ||
161 | GAUGER (category, "Storing an item", | ||
162 | (crc->end.abs_value_us - crc->start.abs_value_us) / 1000LL | ||
163 | / PUT_10, | ||
164 | "ms/item"); | ||
165 | } | ||
166 | crc->i++; | ||
167 | crc->start = GNUNET_TIME_absolute_get (); | ||
168 | crc->phase++; | ||
169 | GNUNET_SCHEDULER_add_now (&test, crc); | ||
170 | return; | ||
171 | } | ||
172 | /* most content is 32k */ | ||
173 | size = 32 * 1024; | ||
174 | if (GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 16) == 0) /* but some of it is less! */ | ||
175 | size = 8 + GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 32 * 1024); | ||
176 | size = size - (size & 7); /* always multiple of 8 */ | ||
177 | |||
178 | /* generate random key */ | ||
179 | key.bits[0] = (unsigned int) GNUNET_TIME_absolute_get ().abs_value_us; | ||
180 | GNUNET_CRYPTO_hash (&key, sizeof(struct GNUNET_HashCode), &key); | ||
181 | memset (value, i, size); | ||
182 | if (i > 255) | ||
183 | memset (value, i - 255, size / 2); | ||
184 | value[0] = crc->i; | ||
185 | GNUNET_memcpy (&value[4], &i, sizeof(i)); | ||
186 | prio = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 100); | ||
187 | crc->api->put (crc->api->cls, | ||
188 | &key, | ||
189 | false /* absent */, | ||
190 | size, | ||
191 | value, | ||
192 | 1 + i % 4 /* type */, | ||
193 | prio, | ||
194 | i % 4 /* anonymity */, | ||
195 | 0 /* replication */, | ||
196 | GNUNET_TIME_relative_to_absolute | ||
197 | (GNUNET_TIME_relative_multiply | ||
198 | (GNUNET_TIME_UNIT_MILLISECONDS, | ||
199 | 60 * 60 * 60 * 1000 | ||
200 | + GNUNET_CRYPTO_random_u32 | ||
201 | (GNUNET_CRYPTO_QUALITY_WEAK, 1000))), | ||
202 | put_continuation, | ||
203 | crc); | ||
204 | i++; | ||
205 | } | ||
206 | |||
207 | |||
208 | static int | ||
209 | iterate_zeros (void *cls, | ||
210 | const struct GNUNET_HashCode *key, | ||
211 | uint32_t size, | ||
212 | const void *data, | ||
213 | enum GNUNET_BLOCK_Type type, | ||
214 | uint32_t priority, | ||
215 | uint32_t anonymity, | ||
216 | uint32_t replication, | ||
217 | struct GNUNET_TIME_Absolute expiration, | ||
218 | uint64_t uid) | ||
219 | { | ||
220 | struct CpsRunContext *crc = cls; | ||
221 | int i; | ||
222 | const char *cdata = data; | ||
223 | |||
224 | GNUNET_assert (key != NULL); | ||
225 | GNUNET_assert (size >= 8); | ||
226 | GNUNET_memcpy (&i, &cdata[4], sizeof(i)); | ||
227 | hits[i / 8] |= (1 << (i % 8)); | ||
228 | |||
229 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
230 | "Found result %d type=%u, priority=%u, size=%u, expire=%s\n", | ||
231 | i, | ||
232 | type, priority, size, | ||
233 | GNUNET_STRINGS_absolute_time_to_string (expiration)); | ||
234 | crc->cnt++; | ||
235 | if (crc->cnt == PUT_10 / 4 - 1) | ||
236 | { | ||
237 | unsigned int bc; | ||
238 | |||
239 | bc = 0; | ||
240 | for (i = 0; i < PUT_10; i++) | ||
241 | if (0 != (hits[i / 8] & (1 << (i % 8)))) | ||
242 | bc++; | ||
243 | |||
244 | crc->end = GNUNET_TIME_absolute_get (); | ||
245 | printf ("%s took %s yielding %u/%u items\n", | ||
246 | "Select random zero-anonymity item", | ||
247 | GNUNET_STRINGS_relative_time_to_string ( | ||
248 | GNUNET_TIME_absolute_get_difference (crc->start, | ||
249 | crc | ||
250 | ->end), | ||
251 | GNUNET_YES), | ||
252 | bc, crc->cnt); | ||
253 | if (crc->cnt > 0) | ||
254 | GAUGER (category, "Select random zero-anonymity item", | ||
255 | (crc->end.abs_value_us - crc->start.abs_value_us) / 1000LL | ||
256 | / crc->cnt, | ||
257 | "ms/item"); | ||
258 | memset (hits, 0, sizeof(hits)); | ||
259 | crc->phase++; | ||
260 | crc->cnt = 0; | ||
261 | crc->start = GNUNET_TIME_absolute_get (); | ||
262 | } | ||
263 | GNUNET_SCHEDULER_add_now (&test, crc); | ||
264 | return GNUNET_OK; | ||
265 | } | ||
266 | |||
267 | |||
268 | static int | ||
269 | expiration_get (void *cls, | ||
270 | const struct GNUNET_HashCode *key, | ||
271 | uint32_t size, | ||
272 | const void *data, | ||
273 | enum GNUNET_BLOCK_Type type, | ||
274 | uint32_t priority, | ||
275 | uint32_t anonymity, | ||
276 | uint32_t replication, | ||
277 | struct GNUNET_TIME_Absolute expiration, | ||
278 | uint64_t uid) | ||
279 | { | ||
280 | struct CpsRunContext *crc = cls; | ||
281 | int i; | ||
282 | const char *cdata = data; | ||
283 | |||
284 | GNUNET_assert (size >= 8); | ||
285 | GNUNET_memcpy (&i, &cdata[4], sizeof(i)); | ||
286 | hits[i / 8] |= (1 << (i % 8)); | ||
287 | crc->cnt++; | ||
288 | if (PUT_10 <= crc->cnt) | ||
289 | { | ||
290 | unsigned int bc; | ||
291 | |||
292 | bc = 0; | ||
293 | for (i = 0; i < PUT_10; i++) | ||
294 | if (0 != (hits[i / 8] & (1 << (i % 8)))) | ||
295 | bc++; | ||
296 | |||
297 | crc->end = GNUNET_TIME_absolute_get (); | ||
298 | printf ("%s took %s yielding %u/%u items\n", | ||
299 | "Selecting and deleting by expiration", | ||
300 | GNUNET_STRINGS_relative_time_to_string ( | ||
301 | GNUNET_TIME_absolute_get_difference (crc->start, | ||
302 | crc | ||
303 | ->end), | ||
304 | GNUNET_YES), | ||
305 | bc, (unsigned int) PUT_10); | ||
306 | if (crc->cnt > 0) | ||
307 | GAUGER (category, "Selecting and deleting by expiration", | ||
308 | (crc->end.abs_value_us - crc->start.abs_value_us) / 1000LL | ||
309 | / crc->cnt, | ||
310 | "ms/item"); | ||
311 | memset (hits, 0, sizeof(hits)); | ||
312 | if (++crc->iter == ITERATIONS) | ||
313 | crc->phase++; | ||
314 | else | ||
315 | crc->phase = RP_PUT; | ||
316 | crc->cnt = 0; | ||
317 | crc->start = GNUNET_TIME_absolute_get (); | ||
318 | } | ||
319 | GNUNET_SCHEDULER_add_now (&test, crc); | ||
320 | return GNUNET_NO; | ||
321 | } | ||
322 | |||
323 | |||
324 | static int | ||
325 | replication_get (void *cls, | ||
326 | const struct GNUNET_HashCode *key, | ||
327 | uint32_t size, | ||
328 | const void *data, | ||
329 | enum GNUNET_BLOCK_Type type, | ||
330 | uint32_t priority, | ||
331 | uint32_t anonymity, | ||
332 | uint32_t replication, | ||
333 | struct GNUNET_TIME_Absolute expiration, | ||
334 | uint64_t uid) | ||
335 | { | ||
336 | struct CpsRunContext *crc = cls; | ||
337 | int i; | ||
338 | const char *cdata = data; | ||
339 | |||
340 | GNUNET_assert (NULL != key); | ||
341 | GNUNET_assert (size >= 8); | ||
342 | GNUNET_memcpy (&i, &cdata[4], sizeof(i)); | ||
343 | hits[i / 8] |= (1 << (i % 8)); | ||
344 | crc->cnt++; | ||
345 | if (PUT_10 <= crc->cnt) | ||
346 | { | ||
347 | unsigned int bc; | ||
348 | |||
349 | bc = 0; | ||
350 | for (i = 0; i < PUT_10; i++) | ||
351 | if (0 != (hits[i / 8] & (1 << (i % 8)))) | ||
352 | bc++; | ||
353 | |||
354 | crc->end = GNUNET_TIME_absolute_get (); | ||
355 | printf ("%s took %s yielding %u/%u items\n", | ||
356 | "Selecting random item for replication", | ||
357 | GNUNET_STRINGS_relative_time_to_string ( | ||
358 | GNUNET_TIME_absolute_get_difference (crc->start, | ||
359 | crc | ||
360 | ->end), | ||
361 | GNUNET_YES), | ||
362 | bc, (unsigned int) PUT_10); | ||
363 | if (crc->cnt > 0) | ||
364 | GAUGER (category, "Selecting random item for replication", | ||
365 | (crc->end.abs_value_us - crc->start.abs_value_us) / 1000LL | ||
366 | / crc->cnt, | ||
367 | "ms/item"); | ||
368 | memset (hits, 0, sizeof(hits)); | ||
369 | crc->phase++; | ||
370 | crc->offset = 0; | ||
371 | crc->cnt = 0; | ||
372 | crc->start = GNUNET_TIME_absolute_get (); | ||
373 | } | ||
374 | |||
375 | GNUNET_SCHEDULER_add_now (&test, crc); | ||
376 | return GNUNET_OK; | ||
377 | } | ||
378 | |||
379 | |||
380 | /** | ||
381 | * Function called when the service shuts | ||
382 | * down. Unloads our datastore plugin. | ||
383 | * | ||
384 | * @param api api to unload | ||
385 | * @param cfg configuration to use | ||
386 | */ | ||
387 | static void | ||
388 | unload_plugin (struct GNUNET_DATASTORE_PluginFunctions *api, | ||
389 | const struct GNUNET_CONFIGURATION_Handle *cfg) | ||
390 | { | ||
391 | char *name; | ||
392 | char *libname; | ||
393 | |||
394 | if (GNUNET_OK != | ||
395 | GNUNET_CONFIGURATION_get_value_string (cfg, "DATASTORE", "DATABASE", | ||
396 | &name)) | ||
397 | { | ||
398 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
399 | _ ("No `%s' specified for `%s' in configuration!\n"), | ||
400 | "DATABASE", | ||
401 | "DATASTORE"); | ||
402 | return; | ||
403 | } | ||
404 | GNUNET_asprintf (&libname, "libgnunet_plugin_datastore_%s", name); | ||
405 | GNUNET_break (NULL == GNUNET_PLUGIN_unload (libname, api)); | ||
406 | GNUNET_free (libname); | ||
407 | GNUNET_free (name); | ||
408 | } | ||
409 | |||
410 | |||
411 | /** | ||
412 | * Last task run during shutdown. Disconnects us from | ||
413 | * the transport and core. | ||
414 | */ | ||
415 | static void | ||
416 | cleaning_task (void *cls) | ||
417 | { | ||
418 | struct CpsRunContext *crc = cls; | ||
419 | |||
420 | unload_plugin (crc->api, crc->cfg); | ||
421 | GNUNET_free (crc); | ||
422 | } | ||
423 | |||
424 | |||
425 | static void | ||
426 | test (void *cls) | ||
427 | { | ||
428 | struct CpsRunContext *crc = cls; | ||
429 | |||
430 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
431 | "In phase %d, iteration %u\n", crc->phase, crc->cnt); | ||
432 | switch (crc->phase) | ||
433 | { | ||
434 | case RP_ERROR: | ||
435 | GNUNET_break (0); | ||
436 | crc->api->drop (crc->api->cls); | ||
437 | ok = 1; | ||
438 | GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE, | ||
439 | &cleaning_task, crc); | ||
440 | break; | ||
441 | |||
442 | case RP_PUT: | ||
443 | do_put (crc); | ||
444 | break; | ||
445 | |||
446 | case RP_REP_GET: | ||
447 | crc->api->get_replication (crc->api->cls, &replication_get, crc); | ||
448 | break; | ||
449 | |||
450 | case RP_ZA_GET: | ||
451 | crc->api->get_zero_anonymity (crc->api->cls, crc->offset++, 1, | ||
452 | &iterate_zeros, crc); | ||
453 | break; | ||
454 | |||
455 | case RP_EXP_GET: | ||
456 | crc->api->get_expiration (crc->api->cls, &expiration_get, crc); | ||
457 | break; | ||
458 | |||
459 | case RP_DONE: | ||
460 | crc->api->drop (crc->api->cls); | ||
461 | ok = 0; | ||
462 | GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE, | ||
463 | &cleaning_task, crc); | ||
464 | break; | ||
465 | } | ||
466 | } | ||
467 | |||
468 | |||
469 | /** | ||
470 | * Load the datastore plugin. | ||
471 | */ | ||
472 | static struct GNUNET_DATASTORE_PluginFunctions * | ||
473 | load_plugin (const struct GNUNET_CONFIGURATION_Handle *cfg) | ||
474 | { | ||
475 | static struct GNUNET_DATASTORE_PluginEnvironment env; | ||
476 | struct GNUNET_DATASTORE_PluginFunctions *ret; | ||
477 | char *name; | ||
478 | char *libname; | ||
479 | |||
480 | if (GNUNET_OK != | ||
481 | GNUNET_CONFIGURATION_get_value_string (cfg, "DATASTORE", "DATABASE", | ||
482 | &name)) | ||
483 | { | ||
484 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
485 | _ ("No `%s' specified for `%s' in configuration!\n"), | ||
486 | "DATABASE", | ||
487 | "DATASTORE"); | ||
488 | return NULL; | ||
489 | } | ||
490 | env.cfg = cfg; | ||
491 | env.duc = &disk_utilization_change_cb; | ||
492 | env.cls = NULL; | ||
493 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, _ ("Loading `%s' datastore plugin\n"), | ||
494 | name); | ||
495 | GNUNET_asprintf (&libname, "libgnunet_plugin_datastore_%s", name); | ||
496 | if (NULL == (ret = GNUNET_PLUGIN_load (libname, &env))) | ||
497 | { | ||
498 | fprintf (stderr, "Failed to load plugin `%s'!\n", name); | ||
499 | GNUNET_free (name); | ||
500 | GNUNET_free (libname); | ||
501 | return NULL; | ||
502 | } | ||
503 | GNUNET_free (libname); | ||
504 | GNUNET_free (name); | ||
505 | return ret; | ||
506 | } | ||
507 | |||
508 | |||
509 | static void | ||
510 | run (void *cls, char *const *args, const char *cfgfile, | ||
511 | const struct GNUNET_CONFIGURATION_Handle *c) | ||
512 | { | ||
513 | struct GNUNET_DATASTORE_PluginFunctions *api; | ||
514 | struct CpsRunContext *crc; | ||
515 | |||
516 | if (NULL == c) | ||
517 | { | ||
518 | GNUNET_break (0); | ||
519 | return; | ||
520 | } | ||
521 | api = load_plugin (c); | ||
522 | if (api == NULL) | ||
523 | { | ||
524 | fprintf (stderr, | ||
525 | "%s", | ||
526 | "Could not initialize plugin, assuming database not configured. Test not run!\n"); | ||
527 | return; | ||
528 | } | ||
529 | crc = GNUNET_new (struct CpsRunContext); | ||
530 | crc->api = api; | ||
531 | crc->cfg = c; | ||
532 | crc->phase = RP_PUT; | ||
533 | ok = 2; | ||
534 | GNUNET_SCHEDULER_add_now (&test, crc); | ||
535 | } | ||
536 | |||
537 | |||
538 | int | ||
539 | main (int argc, char *argv[]) | ||
540 | { | ||
541 | char dir_name[PATH_MAX]; | ||
542 | char cfg_name[PATH_MAX]; | ||
543 | char *const xargv[] = { | ||
544 | "perf-plugin-datastore", | ||
545 | "-c", | ||
546 | cfg_name, | ||
547 | NULL | ||
548 | }; | ||
549 | struct GNUNET_GETOPT_CommandLineOption options[] = { | ||
550 | GNUNET_GETOPT_OPTION_END | ||
551 | }; | ||
552 | |||
553 | plugin_name = GNUNET_STRINGS_get_suffix_from_binary_name (argv[0]); | ||
554 | GNUNET_snprintf (dir_name, sizeof(dir_name), "/tmp/perf-gnunet-datastore-%s", | ||
555 | plugin_name); | ||
556 | GNUNET_DISK_directory_remove (dir_name); | ||
557 | GNUNET_log_setup ("perf-plugin-datastore", | ||
558 | "WARNING", | ||
559 | NULL); | ||
560 | GNUNET_snprintf (category, sizeof(category), "DATASTORE-%s", plugin_name); | ||
561 | GNUNET_snprintf (cfg_name, sizeof(cfg_name), | ||
562 | "perf_plugin_datastore_data_%s.conf", plugin_name); | ||
563 | GNUNET_PROGRAM_run ((sizeof(xargv) / sizeof(char *)) - 1, xargv, | ||
564 | "perf-plugin-datastore", "nohelp", options, &run, NULL); | ||
565 | if (ok != 0) | ||
566 | fprintf (stderr, "Missed some testcases: %u\n", ok); | ||
567 | GNUNET_DISK_directory_remove (dir_name); | ||
568 | |||
569 | return ok; | ||
570 | } | ||
571 | |||
572 | |||
573 | /* end of perf_plugin_datastore.c */ | ||