/* This file is part of GNUnet Copyright (C) 2013 GNUnet e.V. GNUnet is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. GNUnet is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ /** * @file set/gnunet-set-profiler.c * @brief profiling tool for set * @author Florian Dold */ #include "platform.h" #include "gnunet_util_lib.h" #include "gnunet_statistics_service.h" #include "gnunet_set_service.h" #include "gnunet_testbed_service.h" static int ret; static unsigned int num_a = 5; static unsigned int num_b = 5; static unsigned int num_c = 20; static char *op_str = "union"; const static struct GNUNET_CONFIGURATION_Handle *config; struct SetInfo { char *id; struct GNUNET_SET_Handle *set; struct GNUNET_SET_OperationHandle *oh; struct GNUNET_CONTAINER_MultiHashMap *sent; struct GNUNET_CONTAINER_MultiHashMap *received; int done; } info1, info2; static struct GNUNET_CONTAINER_MultiHashMap *common_sent; static struct GNUNET_HashCode app_id; static struct GNUNET_PeerIdentity local_peer; static struct GNUNET_SET_ListenHandle *set_listener; static int byzantine; static unsigned int force_delta; static unsigned int force_full; static unsigned int element_size = 32; /** * Handle to the statistics service. */ static struct GNUNET_STATISTICS_Handle *statistics; /** * The profiler will write statistics * for all peers to the file with this name. */ static char *statistics_filename; /** * The profiler will write statistics * for all peers to this file. */ static FILE *statistics_file; static int map_remove_iterator (void *cls, const struct GNUNET_HashCode *key, void *value) { struct GNUNET_CONTAINER_MultiHashMap *m = cls; int ret; GNUNET_assert (NULL != key); ret = GNUNET_CONTAINER_multihashmap_remove_all (m, key); if (GNUNET_OK != ret) printf ("spurious element\n"); return GNUNET_YES; } /** * Callback function to process statistic values. * * @param cls closure * @param subsystem name of subsystem that created the statistic * @param name the name of the datum * @param value the current value * @param is_persistent #GNUNET_YES if the value is persistent, #GNUNET_NO if not * @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration */ static int statistics_result (void *cls, const char *subsystem, const char *name, uint64_t value, int is_persistent) { if (NULL != statistics_file) { fprintf (statistics_file, "%s\t%s\t%lu\n", subsystem, name, (unsigned long) value); } return GNUNET_OK; } static void statistics_done (void *cls, int success) { GNUNET_assert (GNUNET_YES == success); if (NULL != statistics_file) fclose (statistics_file); GNUNET_SCHEDULER_shutdown (); } static void check_all_done (void) { if (info1.done == GNUNET_NO || info2.done == GNUNET_NO) return; GNUNET_CONTAINER_multihashmap_iterate (info1.received, map_remove_iterator, info2.sent); GNUNET_CONTAINER_multihashmap_iterate (info2.received, map_remove_iterator, info1.sent); printf ("set a: %d missing elements\n", GNUNET_CONTAINER_multihashmap_size (info1.sent)); printf ("set b: %d missing elements\n", GNUNET_CONTAINER_multihashmap_size (info2.sent)); if (NULL == statistics_filename) { GNUNET_SCHEDULER_shutdown (); return; } statistics_file = fopen (statistics_filename, "w"); GNUNET_STATISTICS_get (statistics, NULL, NULL, &statistics_done, &statistics_result, NULL); } static void set_result_cb (void *cls, const struct GNUNET_SET_Element *element, uint64_t current_size, enum GNUNET_SET_Status status) { struct SetInfo *info = cls; struct GNUNET_HashCode hash; GNUNET_assert (GNUNET_NO == info->done); switch (status) { case GNUNET_SET_STATUS_DONE: case GNUNET_SET_STATUS_HALF_DONE: info->done = GNUNET_YES; GNUNET_log (GNUNET_ERROR_TYPE_INFO, "set %s done\n", info->id); check_all_done (); info->oh = NULL; return; case GNUNET_SET_STATUS_FAILURE: info->oh = NULL; GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "failure\n"); GNUNET_SCHEDULER_shutdown (); return; case GNUNET_SET_STATUS_ADD_LOCAL: GNUNET_log (GNUNET_ERROR_TYPE_INFO, "set %s: local element\n", info->id); break; case GNUNET_SET_STATUS_ADD_REMOTE: GNUNET_CRYPTO_hash (element->data, element->size, &hash); GNUNET_log (GNUNET_ERROR_TYPE_INFO, "set %s: remote element %s\n", info->id, GNUNET_h2s (&hash)); // XXX: record and check return; default: GNUNET_assert (0); } if (element->size != element_size) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, "wrong element size: %u, expected %u\n", element->size, (unsigned int) sizeof (struct GNUNET_HashCode)); GNUNET_assert (0); } GNUNET_log (GNUNET_ERROR_TYPE_INFO, "set %s: got element (%s)\n", info->id, GNUNET_h2s (element->data)); GNUNET_assert (NULL != element->data); struct GNUNET_HashCode data_hash; GNUNET_CRYPTO_hash (element->data, element_size, &data_hash); GNUNET_CONTAINER_multihashmap_put (info->received, &data_hash, NULL, GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE); } static void set_listen_cb (void *cls, const struct GNUNET_PeerIdentity *other_peer, const struct GNUNET_MessageHeader *context_msg, struct GNUNET_SET_Request *request) { /* max. 2 options plus terminator */ struct GNUNET_SET_Option opts[3] = {{0}}; unsigned int n_opts = 0; if (NULL == request) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "listener failed\n"); return; } GNUNET_assert (NULL == info2.oh); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "set listen cb called\n"); if (byzantine) { opts[n_opts++] = (struct GNUNET_SET_Option) { .type = GNUNET_SET_OPTION_BYZANTINE }; } GNUNET_assert (!(force_full && force_delta)); if (force_full) { opts[n_opts++] = (struct GNUNET_SET_Option) { .type = GNUNET_SET_OPTION_FORCE_FULL }; } if (force_delta) { opts[n_opts++] = (struct GNUNET_SET_Option) { .type = GNUNET_SET_OPTION_FORCE_DELTA }; } opts[n_opts].type = 0; info2.oh = GNUNET_SET_accept (request, GNUNET_SET_RESULT_SYMMETRIC, opts, set_result_cb, &info2); GNUNET_SET_commit (info2.oh, info2.set); } static int set_insert_iterator (void *cls, const struct GNUNET_HashCode *key, void *value) { struct GNUNET_SET_Handle *set = cls; struct GNUNET_SET_Element el; el.element_type = 0; el.data = value; el.size = element_size; GNUNET_SET_add_element (set, &el, NULL, NULL); return GNUNET_YES; } static void handle_shutdown (void *cls) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Shutting down set profiler\n"); if (NULL != set_listener) { GNUNET_SET_listen_cancel (set_listener); set_listener = NULL; } if (NULL != info1.oh) { GNUNET_SET_operation_cancel (info1.oh); info1.oh = NULL; } if (NULL != info2.oh) { GNUNET_SET_operation_cancel (info2.oh); info2.oh = NULL; } if (NULL != info1.set) { GNUNET_SET_destroy (info1.set); info1.set = NULL; } if (NULL != info2.set) { GNUNET_SET_destroy (info2.set); info2.set = NULL; } GNUNET_STATISTICS_destroy (statistics, GNUNET_NO); } static void run (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg, struct GNUNET_TESTING_Peer *peer) { unsigned int i; struct GNUNET_HashCode hash; /* max. 2 options plus terminator */ struct GNUNET_SET_Option opts[3] = {{0}}; unsigned int n_opts = 0; config = cfg; GNUNET_assert (element_size > 0); if (GNUNET_OK != GNUNET_CRYPTO_get_peer_identity (cfg, &local_peer)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "could not retrieve host identity\n"); ret = 0; return; } statistics = GNUNET_STATISTICS_create ("set-profiler", cfg); GNUNET_SCHEDULER_add_shutdown (&handle_shutdown, NULL); info1.id = "a"; info2.id = "b"; info1.sent = GNUNET_CONTAINER_multihashmap_create (num_a+1, GNUNET_NO); info2.sent = GNUNET_CONTAINER_multihashmap_create (num_b+1, GNUNET_NO); common_sent = GNUNET_CONTAINER_multihashmap_create (num_c+1, GNUNET_NO); info1.received = GNUNET_CONTAINER_multihashmap_create (num_a+1, GNUNET_NO); info2.received = GNUNET_CONTAINER_multihashmap_create (num_b+1, GNUNET_NO); for (i = 0; i < num_a; i++) { char *data = GNUNET_malloc (element_size); GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, data, element_size); GNUNET_CRYPTO_hash (data, element_size, &hash); GNUNET_CONTAINER_multihashmap_put (info1.sent, &hash, data, GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE); } for (i = 0; i < num_b; i++) { char *data = GNUNET_malloc (element_size); GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, data, element_size); GNUNET_CRYPTO_hash (data, element_size, &hash); GNUNET_CONTAINER_multihashmap_put (info2.sent, &hash, data, GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE); } for (i = 0; i < num_c; i++) { char *data = GNUNET_malloc (element_size); GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, data, element_size); GNUNET_CRYPTO_hash (data, element_size, &hash); GNUNET_CONTAINER_multihashmap_put (common_sent, &hash, data, GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE); } GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_STRONG, &app_id); /* FIXME: also implement intersection etc. */ info1.set = GNUNET_SET_create (config, GNUNET_SET_OPERATION_UNION); info2.set = GNUNET_SET_create (config, GNUNET_SET_OPERATION_UNION); GNUNET_CONTAINER_multihashmap_iterate (info1.sent, set_insert_iterator, info1.set); GNUNET_CONTAINER_multihashmap_iterate (info2.sent, set_insert_iterator, info2.set); GNUNET_CONTAINER_multihashmap_iterate (common_sent, set_insert_iterator, info1.set); GNUNET_CONTAINER_multihashmap_iterate (common_sent, set_insert_iterator, info2.set); set_listener = GNUNET_SET_listen (config, GNUNET_SET_OPERATION_UNION, &app_id, set_listen_cb, NULL); if (byzantine) { opts[n_opts++] = (struct GNUNET_SET_Option) { .type = GNUNET_SET_OPTION_BYZANTINE }; } GNUNET_assert (!(force_full && force_delta)); if (force_full) { opts[n_opts++] = (struct GNUNET_SET_Option) { .type = GNUNET_SET_OPTION_FORCE_FULL }; } if (force_delta) { opts[n_opts++] = (struct GNUNET_SET_Option) { .type = GNUNET_SET_OPTION_FORCE_DELTA }; } opts[n_opts].type = 0; info1.oh = GNUNET_SET_prepare (&local_peer, &app_id, NULL, GNUNET_SET_RESULT_SYMMETRIC, opts, set_result_cb, &info1); GNUNET_SET_commit (info1.oh, info1.set); GNUNET_SET_destroy (info1.set); info1.set = NULL; } static void pre_run (void *cls, char *const *args, const char *cfgfile, const struct GNUNET_CONFIGURATION_Handle *cfg) { if (0 != GNUNET_TESTING_peer_run ("set-profiler", cfgfile, &run, NULL)) ret = 2; } int main (int argc, char **argv) { struct GNUNET_GETOPT_CommandLineOption options[] = { GNUNET_GETOPT_option_uint ('A', "num-first", NULL, gettext_noop ("number of values"), &num_a), GNUNET_GETOPT_option_uint ('B', "num-second", NULL, gettext_noop ("number of values"), &num_b), GNUNET_GETOPT_option_flag ('b', "byzantine", gettext_noop ("use byzantine mode"), &byzantine), GNUNET_GETOPT_option_uint ('f', "force-full", NULL, gettext_noop ("force sending full set"), &force_full), GNUNET_GETOPT_option_uint ('d', "force-delta", NULL, gettext_noop ("number delta operation"), &force_delta), GNUNET_GETOPT_option_uint ('C', "num-common", NULL, gettext_noop ("number of values"), &num_c), GNUNET_GETOPT_option_string ('x', "operation", NULL, gettext_noop ("operation to execute"), &op_str), GNUNET_GETOPT_option_uint ('w', "element-size", NULL, gettext_noop ("element size"), &element_size), GNUNET_GETOPT_option_filename ('s', "statistics", "FILENAME", gettext_noop ("write statistics to file"), &statistics_filename), GNUNET_GETOPT_OPTION_END }; GNUNET_PROGRAM_run2 (argc, argv, "gnunet-set-profiler", "help", options, &pre_run, NULL, GNUNET_YES); return ret; }