2 This file is part of GNUnet.
3 (C) 2014 Christian Grothoff (and other contributing authors)
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
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 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
22 * @file dht/gnunet_dht_profiler.c
23 * @brief Profiler for GNUnet DHT
24 * @author Sree Harsha Totakura <sreeharsha@totakura.in>
28 #include "gnunet_util_lib.h"
29 #include "gnunet_testbed_service.h"
30 #include "gnunet_dht_service.h"
33 GNUNET_log (GNUNET_ERROR_TYPE_INFO, __VA_ARGS__)
36 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, __VA_ARGS__)
39 * Number of peers which should perform a PUT out of 100 peers
41 #define PUT_PROBABILITY 100
46 static struct GNUNET_CONFIGURATION_Handle *cfg;
49 * Name of the file with the hosts to run the test over
51 static char *hosts_file;
54 * Context for a peer which actively does DHT PUT/GET
59 * Context to hold data of peer
65 * The testbed peer this context belongs to
67 struct GNUNET_TESTBED_Peer *peer;
70 * Testbed operation acting on this peer
72 struct GNUNET_TESTBED_Operation *op;
75 * Active context; NULL if this peer is not an active peer
77 struct ActiveContext *ac;
82 * Context for a peer which actively does DHT PUT/GET
87 * The linked peer context
92 * Handler to the DHT service
94 struct GNUNET_DHT_Handle *dht;
97 * The data used for do a PUT. Will be NULL if a PUT hasn't been performed yet
102 * The active context used for our DHT GET
104 struct ActiveContext *get_ac;
109 struct GNUNET_DHT_PutHandle *dht_put;
114 struct GNUNET_DHT_GetHandle *dht_get;
117 * The hash of the @e put_data
119 struct GNUNET_HashCode hash;
124 GNUNET_SCHEDULER_TaskIdentifier delay_task;
127 * The size of the @e put_data
129 uint16_t put_data_size;
132 * The number of peers currently doing GET on our data
139 * An array of contexts. The size of this array should be equal to @a num_peers
141 static struct Context *a_ctx;
144 * Array of active peers
146 static struct ActiveContext *a_ac;
149 * The delay between starting to do PUTS and GETS
151 static struct GNUNET_TIME_Relative delay;
154 * The timeout for GET and PUT
156 static struct GNUNET_TIME_Relative timeout;
161 static unsigned int num_peers;
164 * Number of active peers
166 static unsigned int n_active;
169 * Number of DHT service connections we currently have
171 static unsigned int n_dht;
174 * Number of DHT PUTs made
176 static unsigned int n_puts;
179 * Number of DHT PUTs succeeded
181 static unsigned int n_puts_ok;
184 * Number of DHT PUTs failed
186 static unsigned int n_puts_fail;
189 * Number of DHT GETs made
191 static unsigned int n_gets;
194 * Number of DHT GETs succeeded
196 static unsigned int n_gets_ok;
199 * Number of DHT GETs succeeded
201 static unsigned int n_gets_fail;
206 static unsigned int replication;
209 * Number of times we try to find the successor circle formation
211 static unsigned int max_searches;
214 * Testbed Operation (to get stats).
216 static struct GNUNET_TESTBED_Operation *bandwidth_stats_op;
219 * To get successor stats.
221 static struct GNUNET_TESTBED_Operation *successor_stats_op;
224 * Testbed peer handles.
226 static struct GNUNET_TESTBED_Peer **testbed_handles;
229 * Total number of messages sent by peer.
231 static uint64_t outgoing_bandwidth;
234 * Total number of messages received by peer.
236 static uint64_t incoming_bandwidth;
239 * Average number of hops taken to do put.
241 static unsigned int average_put_path_length;
244 * Average number of hops taken to do get.
246 static unsigned int average_get_path_length;
251 static unsigned int total_put_path_length;
256 static unsigned int total_get_path_length;
261 static struct GNUNET_CONTAINER_MultiHashMap *successor_peer_hashmap;
266 static struct GNUNET_HashCode *start_key;
274 * Shutdown task. Cleanup all resources and operations.
277 * @param tc scheduler task context
280 do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
282 struct ActiveContext *ac;
287 for (cnt=0; cnt < num_peers; cnt++)
289 if (NULL != a_ctx[cnt].op)
290 GNUNET_TESTBED_operation_done (a_ctx[cnt].op);
292 /* Cleanup active context if this peer is an active peer */
296 if (GNUNET_SCHEDULER_NO_TASK != ac->delay_task)
297 GNUNET_SCHEDULER_cancel (ac->delay_task);
298 if (NULL != ac->put_data)
299 GNUNET_free (ac->put_data);
300 if (NULL != ac->dht_put)
301 GNUNET_DHT_put_cancel (ac->dht_put);
302 if (NULL != ac->dht_get)
303 GNUNET_DHT_get_stop (ac->dht_get);
308 if(NULL != bandwidth_stats_op)
309 GNUNET_TESTBED_operation_done (bandwidth_stats_op);
310 bandwidth_stats_op = NULL;
311 GNUNET_free_non_null (a_ac);
316 * Stats callback. Finish the stats testbed operation and when all stats have
317 * been iterated, shutdown the test.
320 * @param op the operation that has been finished
321 * @param emsg error message in case the operation has failed; will be NULL if
322 * operation has executed successfully.
325 bandwidth_stats_cont (void *cls,
326 struct GNUNET_TESTBED_Operation *op,
329 INFO ("# Outgoing bandwidth: %u\n", outgoing_bandwidth);
330 INFO ("# Incoming bandwidth: %u\n", incoming_bandwidth);
331 GNUNET_SCHEDULER_shutdown ();
336 * Process statistic values.
339 * @param peer the peer the statistic belong to
340 * @param subsystem name of subsystem that created the statistic
341 * @param name the name of the datum
342 * @param value the current value
343 * @param is_persistent GNUNET_YES if the value is persistent, GNUNET_NO if not
344 * @return GNUNET_OK to continue, GNUNET_SYSERR to abort iteration
347 bandwidth_stats_iterator (void *cls,
348 const struct GNUNET_TESTBED_Peer *peer,
349 const char *subsystem,
354 static const char *s_sent = "# Bytes transmitted to other peers";
355 static const char *s_recv = "# Bytes received from other peers";
357 if (0 == strncmp (s_sent, name, strlen (s_sent)))
358 outgoing_bandwidth = outgoing_bandwidth + value;
359 else if (0 == strncmp(s_recv, name, strlen (s_recv)))
360 incoming_bandwidth = incoming_bandwidth + value;
363 DEBUG ("Bandwith - Out: %lu; In: %lu\n",
364 (unsigned long) outgoing_bandwidth,
365 (unsigned long) incoming_bandwidth);
373 INFO ("# PUTS made: %u\n", n_puts);
374 INFO ("# PUTS succeeded: %u\n", n_puts_ok);
375 INFO ("# PUTS failed: %u\n", n_puts_fail);
376 INFO ("# GETS made: %u\n", n_gets);
377 INFO ("# GETS succeeded: %u\n", n_gets_ok);
378 INFO ("# GETS failed: %u\n", n_gets_fail);
379 INFO ("# average_put_path_length: %u\n", average_put_path_length);
380 INFO ("# average_get_path_length: %u\n", average_get_path_length);
382 if (NULL == testbed_handles)
384 INFO ("No peers found\n");
388 bandwidth_stats_op = GNUNET_TESTBED_get_statistics (n_active, testbed_handles,
390 bandwidth_stats_iterator,
391 bandwidth_stats_cont, NULL);
396 * Task to cancel DHT GET.
399 * @param tc scheduler task context
402 cancel_get (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
404 struct ActiveContext *ac = cls;
406 ac->delay_task = GNUNET_SCHEDULER_NO_TASK;
407 GNUNET_assert (NULL != ac->dht_get);
408 GNUNET_DHT_get_stop (ac->dht_get);
412 /* If profiling is complete, summarize */
413 if (n_active == n_gets_fail + n_gets_ok)
420 * Iterator called on each result obtained for a DHT
421 * operation that expects a reply
424 * @param exp when will this value expire
425 * @param key key of the result
426 * @param get_path peers on reply path (or NULL if not recorded)
427 * [0] = datastore's first neighbor, [length - 1] = local peer
428 * @param get_path_length number of entries in @a get_path
429 * @param put_path peers on the PUT path (or NULL if not recorded)
430 * [0] = origin, [length - 1] = datastore
431 * @param put_path_length number of entries in @a put_path
432 * @param type type of the result
433 * @param size number of bytes in @a data
434 * @param data pointer to the result data
438 struct GNUNET_TIME_Absolute exp,
439 const struct GNUNET_HashCode *key,
440 const struct GNUNET_PeerIdentity *get_path,
441 unsigned int get_path_length,
442 const struct GNUNET_PeerIdentity *put_path,
443 unsigned int put_path_length,
444 enum GNUNET_BLOCK_Type type,
445 size_t size, const void *data)
447 struct ActiveContext *ac = cls;
448 struct ActiveContext *get_ac = ac->get_ac;
450 /* Check the keys of put and get match or not. */
451 GNUNET_assert (0 == memcmp (key, &get_ac->hash, sizeof (struct GNUNET_HashCode)));
452 /* we found the data we are looking for */
453 DEBUG ("We found a GET request; %u remaining\n", n_gets - (n_gets_fail + n_gets_ok));
456 GNUNET_DHT_get_stop (ac->dht_get);
458 GNUNET_SCHEDULER_cancel (ac->delay_task);
459 ac->delay_task = GNUNET_SCHEDULER_NO_TASK;
461 total_put_path_length = total_put_path_length + put_path_length;
462 total_get_path_length = total_get_path_length + get_path_length;
464 /* Summarize if profiling is complete */
465 if (n_active == n_gets_fail + n_gets_ok)
467 average_put_path_length = total_put_path_length/n_active;
468 average_get_path_length = total_get_path_length/n_active;
475 * Task to do DHT GETs
477 * @param cls the active context
478 * @param tc the scheduler task context
481 delayed_get (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
483 struct ActiveContext *ac = cls;
484 struct ActiveContext *get_ac;
487 ac->delay_task = GNUNET_SCHEDULER_NO_TASK;
491 r = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, n_active);
493 if (NULL != get_ac->put_data)
498 DEBUG ("Doing a DHT GET for data of size %u\n", get_ac->put_data_size);
499 ac->dht_get = GNUNET_DHT_get_start (ac->dht,
500 GNUNET_BLOCK_TYPE_TEST,
502 1, /* replication level */
504 NULL, 0, /* extended query and size */
505 get_iter, ac); /* GET iterator and closure
509 /* schedule the timeout task for GET */
510 ac->delay_task = GNUNET_SCHEDULER_add_delayed (timeout, &cancel_get, ac);
515 * Queue up a delayed task for doing DHT GET
517 * @param cls the active context
518 * @param success #GNUNET_OK if the PUT was transmitted,
519 * #GNUNET_NO on timeout,
520 * #GNUNET_SYSERR on disconnect from service
521 * after the PUT message was transmitted
522 * (so we don't know if it was received or not)
525 put_cont (void *cls, int success)
527 struct ActiveContext *ac = cls;
534 ac->delay_task = GNUNET_SCHEDULER_add_delayed (delay, &delayed_get, ac);
539 * Stats callback. Finish the stats testbed operation and when all stats have
540 * been iterated, shutdown the test.
543 * @param op the operation that has been finished
544 * @param emsg error message in case the operation has failed; will be NULL if
545 * operation has executed successfully.
548 successor_stats_cont (void *cls,
549 struct GNUNET_TESTBED_Operation *op,
552 /* Check if ring is formed. If yes then schedule put. */
553 struct GNUNET_HashCode *val;
554 struct GNUNET_HashCode *start_val;
556 struct GNUNET_HashCode *key;
558 start_val = GNUNET_CONTAINER_multihashmap_get(successor_peer_hashmap,
561 val = GNUNET_new(struct GNUNET_HashCode);
563 while (count < n_active)
566 val = GNUNET_CONTAINER_multihashmap_get (successor_peer_hashmap,
571 if (start_val == val)
573 DEBUG("Circle complete\n");
574 /* FIXME: Schedule the delayed PUT task */
578 static unsigned int tries;
580 DEBUG("Circle not complete\n");
581 if (max_searches == ++tries)
583 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
584 "Maximum tries %u exceeded while checking successor"
585 " cirle formation. Exiting\n",
587 GNUNET_SCHEDULER_shutdown ();
590 /* FIXME: Re-schedule the successor stats gathering task to run after some
597 * Process successor statistic values.
600 * @param peer the peer the statistic belong to
601 * @param subsystem name of subsystem that created the statistic
602 * @param name the name of the datum
603 * @param value the current value
604 * @param is_persistent GNUNET_YES if the value is persistent, GNUNET_NO if not
605 * @return GNUNET_OK to continue, GNUNET_SYSERR to abort iteration
608 successor_stats_iterator (void *cls,
609 const struct GNUNET_TESTBED_Peer *peer,
610 const char *subsystem,
615 static const char *key_string = "XDHT";
617 DEBUG (" Inside successor stats,name = %s\n",name);
618 if (0 == strncmp (key_string, name, strlen (key_string)))
621 char *successor_id_str;
622 struct GNUNET_HashCode *my_id;
623 struct GNUNET_HashCode *successor_id;
625 /* Parse the string to get the peer and its successor. */
626 strtok((char *)name,":");
627 my_id_str = strtok(NULL,":");
628 successor_id_str = strtok(NULL,":");
630 /* Get Hash of my_id_str and successor_id_str */
631 my_id = GNUNET_new(struct GNUNET_HashCode);
632 successor_id = GNUNET_new(struct GNUNET_HashCode);
633 GNUNET_CRYPTO_hash (my_id_str, sizeof(my_id_str),my_id);
634 GNUNET_CRYPTO_hash (successor_id_str, sizeof(successor_id_str),successor_id);
642 GNUNET_CONTAINER_multihashmap_put (successor_peer_hashmap,
643 my_id, (void *)successor_id,
644 GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
654 * Task to do DHT PUTS
656 * @param cls the active context
657 * @param tc the scheduler task context
660 delayed_put (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
662 struct ActiveContext *ac = cls;
664 successor_peer_hashmap = GNUNET_CONTAINER_multihashmap_create (n_active,
666 /* Check for successor pointer, don't start put till the virtual ring topology
669 GNUNET_TESTBED_get_statistics (n_active, testbed_handles,
671 successor_stats_iterator,
672 successor_stats_cont, NULL);
674 ac->delay_task = GNUNET_SCHEDULER_NO_TASK;
675 /* Generate and DHT PUT some random data */
676 ac->put_data_size = 16; /* minimum */
677 ac->put_data_size += GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
679 ac->put_data = GNUNET_malloc (ac->put_data_size);
680 GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
681 ac->put_data, ac->put_data_size);
682 GNUNET_CRYPTO_hash (ac->put_data, ac->put_data_size, &ac->hash);
683 DEBUG ("Doing a DHT PUT with data of size %u\n", ac->put_data_size);
684 ac->dht_put = GNUNET_DHT_put (ac->dht, &ac->hash,
687 GNUNET_BLOCK_TYPE_TEST,
690 GNUNET_TIME_UNIT_FOREVER_ABS, /* expiration time */
691 timeout, /* PUT timeout */
692 put_cont, ac); /* continuation and its closure */
699 * Connection to DHT has been established. Call the delay task.
701 * @param cls the active context
702 * @param op the operation that has been finished
703 * @param ca_result the service handle returned from GNUNET_TESTBED_ConnectAdapter()
704 * @param emsg error message in case the operation has failed; will be NULL if
705 * operation has executed successfully.
708 dht_connected (void *cls,
709 struct GNUNET_TESTBED_Operation *op,
713 struct ActiveContext *ac = cls;
714 struct Context *ctx = ac->ctx;
716 GNUNET_assert (NULL != ctx);
717 GNUNET_assert (NULL != ctx->op);
718 GNUNET_assert (ctx->op == op);
719 ac->dht = (struct GNUNET_DHT_Handle *) ca_result;
722 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Connection to DHT service failed: %s\n", emsg);
723 GNUNET_TESTBED_operation_done (ctx->op); /* Calls dht_disconnect() */
728 DEBUG (" Call stats \n");
730 /* FIXME: move this to happen after the successor circle formation is
732 ac->delay_task = GNUNET_SCHEDULER_add_delayed (delay, &delayed_put, ac);
737 * Connect to DHT service and return the DHT client handler
739 * @param cls the active context
740 * @param cfg configuration of the peer to connect to; will be available until
741 * GNUNET_TESTBED_operation_done() is called on the operation returned
742 * from GNUNET_TESTBED_service_connect()
743 * @return service handle to return in 'op_result', NULL on error
746 dht_connect (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg)
749 return GNUNET_DHT_connect (cfg, 10);
754 * Adapter function called to destroy a connection to
757 * @param cls the active context
758 * @param op_result service handle returned from the connect adapter
761 dht_disconnect (void *cls, void *op_result)
763 struct ActiveContext *ac = cls;
765 GNUNET_assert (NULL != ac->dht);
766 GNUNET_assert (ac->dht == op_result);
767 GNUNET_DHT_disconnect (ac->dht);
770 GNUNET_SCHEDULER_shutdown ();
775 * Callback called when DHT service on the peer is started
777 * @param cls the context
778 * @param op the operation that has been finished
779 * @param emsg error message in case the operation has failed; will be NULL if
780 * operation has executed successfully.
783 service_started (void *cls,
784 struct GNUNET_TESTBED_Operation *op,
787 struct Context *ctx = cls;
788 static unsigned int nstarted;
790 GNUNET_assert (NULL != ctx);
791 GNUNET_assert (NULL != ctx->op);
792 GNUNET_TESTBED_operation_done (ctx->op);
796 ctx->op = GNUNET_TESTBED_service_connect (ctx, ctx->peer,
798 &dht_connected, ctx->ac,
802 if (num_peers == ++nstarted)
804 /* FIXME: schedule a delayed task to scan the successors from statistics of
811 * Signature of a main function for a testcase.
814 * @param h the run handle
815 * @param num_peers number of peers in 'peers'
816 * @param peers handle to peers run in the testbed
817 * @param links_succeeded the number of overlay link connection attempts that
819 * @param links_failed the number of overlay link
823 struct GNUNET_TESTBED_RunHandle *h,
824 unsigned int num_peers, struct GNUNET_TESTBED_Peer **peers,
825 unsigned int links_succeeded,
826 unsigned int links_failed)
831 testbed_handles = peers;
837 INFO ("%u peers started\n", num_peers);
838 a_ctx = GNUNET_malloc (sizeof (struct Context) * num_peers);
840 /* select the peers which actively participate in profiling */
841 n_active = num_peers * PUT_PROBABILITY / 100;
844 GNUNET_SCHEDULER_shutdown ();
849 a_ac = GNUNET_malloc (n_active * sizeof (struct ActiveContext));
851 for (cnt = 0; cnt < num_peers && ac_cnt < n_active; cnt++)
853 if (GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 100) >=
856 a_ctx[cnt].ac = &a_ac[ac_cnt];
857 a_ac[ac_cnt].ctx = &a_ctx[cnt];
861 a_ac = GNUNET_realloc (a_ac, n_active * sizeof (struct ActiveContext));
862 INFO ("Active peers: %u\n", n_active);
864 /* start DHT service on all peers */
865 for (cnt = 0; cnt < num_peers; cnt++)
867 a_ctx[cnt].peer = peers[cnt];
868 a_ctx[cnt].op = GNUNET_TESTBED_peer_manage_service (&a_ctx[cnt],
879 * Main function that will be run by the scheduler.
882 * @param args remaining command-line arguments
883 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
884 * @param config configuration
887 run (void *cls, char *const *args, const char *cfgfile,
888 const struct GNUNET_CONFIGURATION_Handle *config)
894 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Exiting as the number of peers is %u\n"),
898 cfg = GNUNET_CONFIGURATION_dup (config);
900 GNUNET_TESTBED_run (hosts_file, cfg, num_peers, event_mask, NULL,
901 NULL, &test_run, NULL);
902 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &do_shutdown,
910 * @return 0 on success
913 main (int argc, char *const *argv)
917 static struct GNUNET_GETOPT_CommandLineOption options[] = {
918 {'n', "peers", "COUNT",
919 gettext_noop ("number of peers to start"),
920 1, &GNUNET_GETOPT_set_uint, &num_peers},
921 {'s', "searches", "COUNT",
922 gettext_noop ("maximum number of times we try to search for successor circle formation (default is 1)"),
923 1, &GNUNET_GETOPT_set_uint, &max_searches},
924 {'H', "hosts", "FILENAME",
925 gettext_noop ("name of the file with the login information for the testbed"),
926 1, &GNUNET_GETOPT_set_string, &hosts_file},
927 {'d', "delay", "DELAY",
928 gettext_noop ("delay for starting DHT PUT and GET"),
929 1, &GNUNET_GETOPT_set_relative_time, &delay},
930 {'r', "replication", "DEGREE",
931 gettext_noop ("replication degree for DHT PUTs"),
932 1, &GNUNET_GETOPT_set_uint, &replication},
933 {'t', "timeout", "TIMEOUT",
934 gettext_noop ("timeout for DHT PUT and GET requests"),
935 1, &GNUNET_GETOPT_set_relative_time, &timeout},
936 GNUNET_GETOPT_OPTION_END
940 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
942 delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 1); /* default delay */
943 timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1); /* default timeout */
944 replication = 1; /* default replication */
947 GNUNET_PROGRAM_run (argc, argv, "dht-profiler",
949 ("Measure quality and performance of the DHT service."),
950 options, &run, NULL))