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 * Testbed Operation (to get stats).
211 static struct GNUNET_TESTBED_Operation *stats_op;
214 * Testbed peer handles.
216 static struct GNUNET_TESTBED_Peer **testbed_handles;
219 * Shutdown task. Cleanup all resources and operations.
222 * @param tc scheduler task context
225 do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
227 struct ActiveContext *ac;
232 for (cnt=0; cnt < num_peers; cnt++)
234 if (NULL != a_ctx[cnt].op)
235 GNUNET_TESTBED_operation_done (a_ctx[cnt].op);
237 /* Cleanup active context if this peer is an active peer */
241 if (GNUNET_SCHEDULER_NO_TASK != ac->delay_task)
242 GNUNET_SCHEDULER_cancel (ac->delay_task);
243 if (NULL != ac->put_data)
244 GNUNET_free (ac->put_data);
245 if (NULL != ac->dht_put)
246 GNUNET_DHT_put_cancel (ac->dht_put);
247 if (NULL != ac->dht_get)
248 GNUNET_DHT_get_stop (ac->dht_get);
253 GNUNET_free_non_null (a_ac);
258 * Stats callback. Finish the stats testbed operation and when all stats have
259 * been iterated, shutdown the test.
262 * @param op the operation that has been finished
263 * @param emsg error message in case the operation has failed; will be NULL if
264 * operation has executed successfully.
267 bandwidth_stats_cont (void *cls,
268 struct GNUNET_TESTBED_Operation *op, const char *emsg)
275 * Process statistic values.
278 * @param peer the peer the statistic belong to
279 * @param subsystem name of subsystem that created the statistic
280 * @param name the name of the datum
281 * @param value the current value
282 * @param is_persistent GNUNET_YES if the value is persistent, GNUNET_NO if not
283 * @return GNUNET_OK to continue, GNUNET_SYSERR to abort iteration
286 bandwidth_stats_iterator (void *cls, const struct GNUNET_TESTBED_Peer *peer,
287 const char *subsystem, const char *name,
288 uint64_t value, int is_persistent)
295 * Task that collects bandwidth used by all the peers.
297 * @param cls Closure (NULL).
298 * @param tc Task Context.
301 collect_bandwidth_stats (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
303 if ((GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason) != 0)
306 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Start collecting bandwidth statistics...\n");
307 //FIXME: what is the name of transport subsystem?
308 stats_op = GNUNET_TESTBED_get_statistics (n_active, testbed_handles,
310 bandwidth_stats_iterator,
311 bandwidth_stats_cont, NULL);
318 INFO ("# PUTS made: %u\n", n_puts);
319 INFO ("# PUTS succeeded: %u\n", n_puts_ok);
320 INFO ("# PUTS failed: %u\n", n_puts_fail);
321 INFO ("# GETS made: %u\n", n_gets);
322 INFO ("# GETS succeeded: %u\n", n_gets_ok);
323 INFO ("# GETS failed: %u\n", n_gets_fail);
324 //FIXME: is this the right place to call b/w stats?
325 GNUNET_SCHEDULER_add_now (&collect_bandwidth_stats, NULL);
326 GNUNET_SCHEDULER_shutdown ();
331 * Task to cancel DHT GET.
334 * @param tc scheduler task context
337 cancel_get (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
339 struct ActiveContext *ac = cls;
340 struct Context *ctx = ac->ctx;
342 ac->delay_task = GNUNET_SCHEDULER_NO_TASK;
343 GNUNET_assert (NULL != ac->dht_get);
344 GNUNET_DHT_get_stop (ac->dht_get);
346 GNUNET_TESTBED_operation_done (ctx->op);
350 /* If profiling is complete, summarize */
351 if (n_active == n_gets_fail + n_gets_ok)
357 * Iterator called on each result obtained for a DHT
358 * operation that expects a reply
361 * @param exp when will this value expire
362 * @param key key of the result
363 * @param get_path peers on reply path (or NULL if not recorded)
364 * [0] = datastore's first neighbor, [length - 1] = local peer
365 * @param get_path_length number of entries in @a get_path
366 * @param put_path peers on the PUT path (or NULL if not recorded)
367 * [0] = origin, [length - 1] = datastore
368 * @param put_path_length number of entries in @a put_path
369 * @param type type of the result
370 * @param size number of bytes in @a data
371 * @param data pointer to the result data
375 struct GNUNET_TIME_Absolute exp,
376 const struct GNUNET_HashCode *key,
377 const struct GNUNET_PeerIdentity *get_path,
378 unsigned int get_path_length,
379 const struct GNUNET_PeerIdentity *put_path,
380 unsigned int put_path_length,
381 enum GNUNET_BLOCK_Type type,
382 size_t size, const void *data)
384 struct ActiveContext *ac = cls;
385 struct ActiveContext *get_ac = ac->get_ac;
386 struct Context *ctx = ac->ctx;
388 /* Check the keys of put and get match or not. */
389 GNUNET_assert (0 == memcmp (key, &get_ac->hash, sizeof (struct GNUNET_HashCode)));
390 /* we found the data we are looking for */
391 DEBUG ("We found a GET request; %u remaining\n", n_gets - (n_gets_fail + n_gets_ok));
394 GNUNET_DHT_get_stop (ac->dht_get);
396 GNUNET_SCHEDULER_cancel (ac->delay_task);
397 ac->delay_task = GNUNET_SCHEDULER_NO_TASK;
398 GNUNET_TESTBED_operation_done (ctx->op);
401 /* Summarize if profiling is complete */
402 if (n_active == n_gets_fail + n_gets_ok)
408 * Task to do DHT GETs
410 * @param cls the active context
411 * @param tc the scheduler task context
414 delayed_get (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
416 struct ActiveContext *ac = cls;
417 struct ActiveContext *get_ac;
420 ac->delay_task = GNUNET_SCHEDULER_NO_TASK;
424 r = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, n_active);
426 if (NULL != get_ac->put_data)
431 DEBUG ("Doing a DHT GET for data of size %u\n", get_ac->put_data_size);
432 ac->dht_get = GNUNET_DHT_get_start (ac->dht,
433 GNUNET_BLOCK_TYPE_TEST,
435 1, /* replication level */
437 NULL, 0, /* extended query and size */
438 get_iter, ac); /* GET iterator and closure
442 /* schedule the timeout task for GET */
443 ac->delay_task = GNUNET_SCHEDULER_add_delayed (timeout, &cancel_get, ac);
448 * Queue up a delayed task for doing DHT GET
450 * @param cls the active context
451 * @param success #GNUNET_OK if the PUT was transmitted,
452 * #GNUNET_NO on timeout,
453 * #GNUNET_SYSERR on disconnect from service
454 * after the PUT message was transmitted
455 * (so we don't know if it was received or not)
458 put_cont (void *cls, int success)
460 struct ActiveContext *ac = cls;
467 ac->delay_task = GNUNET_SCHEDULER_add_delayed (delay, &delayed_get, ac);
471 * Stats callback. Finish the stats testbed operation and when all stats have
472 * been iterated, shutdown the test.
475 * @param op the operation that has been finished
476 * @param emsg error message in case the operation has failed; will be NULL if
477 * operation has executed successfully.
480 finger_stats_cont (void *cls,
481 struct GNUNET_TESTBED_Operation *op,
489 * Process statistic values.
492 * @param peer the peer the statistic belong to
493 * @param subsystem name of subsystem that created the statistic
494 * @param name the name of the datum
495 * @param value the current value
496 * @param is_persistent GNUNET_YES if the value is persistent, GNUNET_NO if not
497 * @return GNUNET_OK to continue, GNUNET_SYSERR to abort iteration
500 finger_stats_iterator (void *cls, const struct GNUNET_TESTBED_Peer *peer,
501 const char *subsystem, const char *name,
502 uint64_t value, int is_persistent)
506 i = GNUNET_TESTBED_get_index (peer);
507 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " STATS %u - %s [%s]: %llu\n",
508 i, subsystem, name, value);
515 * Task check that keepalives were sent and received.
517 * @param cls Closure (NULL).
518 * @param tc Task Context.
521 collect_finger_stats (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
523 if ((GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason) != 0)
526 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Start collecting statistics...\n");
527 /* FIXME: Write subsystem name. */
528 stats_op = GNUNET_TESTBED_get_statistics (n_active, testbed_handles,
530 finger_stats_iterator,
531 finger_stats_cont, NULL);
536 * Task to do DHT PUTS
538 * @param cls the active context
539 * @param tc the scheduler task context
542 delayed_put (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
544 struct ActiveContext *ac = cls;
546 /*FIXME: Before doing anything else, first collect statistics from each peer
547 DHT and check if circle is formed. If yes then go ahead with more puts,
548 else wait for 'delay' time. This function does not return anything, so we
549 should have some way to notify that circle is done or we need to wait.*/
550 GNUNET_SCHEDULER_add_now(collect_finger_stats,NULL);
552 ac->delay_task = GNUNET_SCHEDULER_NO_TASK;
553 /* Generate and DHT PUT some random data */
554 ac->put_data_size = 16; /* minimum */
555 ac->put_data_size += GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
557 ac->put_data = GNUNET_malloc (ac->put_data_size);
558 GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
559 ac->put_data, ac->put_data_size);
560 GNUNET_CRYPTO_hash (ac->put_data, ac->put_data_size, &ac->hash);
561 DEBUG ("Doing a DHT PUT with data of size %u\n", ac->put_data_size);
562 ac->dht_put = GNUNET_DHT_put (ac->dht, &ac->hash,
565 GNUNET_BLOCK_TYPE_TEST,
568 GNUNET_TIME_UNIT_FOREVER_ABS, /* expiration time */
569 timeout, /* PUT timeout */
570 put_cont, ac); /* continuation and its closure */
576 * Connection to DHT has been established. Call the delay task.
578 * @param cls the active context
579 * @param op the operation that has been finished
580 * @param ca_result the service handle returned from GNUNET_TESTBED_ConnectAdapter()
581 * @param emsg error message in case the operation has failed; will be NULL if
582 * operation has executed successfully.
585 dht_connected (void *cls,
586 struct GNUNET_TESTBED_Operation *op,
590 struct ActiveContext *ac = cls;
591 struct Context *ctx = ac->ctx;
593 GNUNET_assert (NULL != ctx);
594 GNUNET_assert (NULL != ctx->op);
595 GNUNET_assert (ctx->op == op);
596 ac->dht = (struct GNUNET_DHT_Handle *) ca_result;
599 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Connection to DHT service failed: %s\n", emsg);
600 GNUNET_TESTBED_operation_done (ctx->op); /* Calls dht_disconnect() */
604 ac->delay_task = GNUNET_SCHEDULER_add_delayed (delay, &delayed_put, ac);
609 * Connect to DHT service and return the DHT client handler
611 * @param cls the active context
612 * @param cfg configuration of the peer to connect to; will be available until
613 * GNUNET_TESTBED_operation_done() is called on the operation returned
614 * from GNUNET_TESTBED_service_connect()
615 * @return service handle to return in 'op_result', NULL on error
618 dht_connect (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg)
621 return GNUNET_DHT_connect (cfg, 10);
626 * Adapter function called to destroy a connection to
629 * @param cls the active context
630 * @param op_result service handle returned from the connect adapter
633 dht_disconnect (void *cls, void *op_result)
635 struct ActiveContext *ac = cls;
637 GNUNET_assert (NULL != ac->dht);
638 GNUNET_assert (ac->dht == op_result);
639 GNUNET_DHT_disconnect (ac->dht);
642 GNUNET_SCHEDULER_shutdown ();
647 * Callback called when DHT service on the peer is started
649 * @param cls the context
650 * @param op the operation that has been finished
651 * @param emsg error message in case the operation has failed; will be NULL if
652 * operation has executed successfully.
655 service_started (void *cls,
656 struct GNUNET_TESTBED_Operation *op,
659 struct Context *ctx = cls;
661 GNUNET_assert (NULL != ctx);
662 GNUNET_assert (NULL != ctx->op);
663 GNUNET_TESTBED_operation_done (ctx->op);
667 /* FIXME: connect to the DHT service and wait before starting a PUT */
668 ctx->op = GNUNET_TESTBED_service_connect (ctx, ctx->peer,
670 &dht_connected, ctx->ac,
678 * Signature of a main function for a testcase.
681 * @param h the run handle
682 * @param num_peers number of peers in 'peers'
683 * @param peers handle to peers run in the testbed
684 * @param links_succeeded the number of overlay link connection attempts that
686 * @param links_failed the number of overlay link
690 struct GNUNET_TESTBED_RunHandle *h,
691 unsigned int num_peers, struct GNUNET_TESTBED_Peer **peers,
692 unsigned int links_succeeded,
693 unsigned int links_failed)
703 INFO ("%u peers started\n", num_peers);
704 a_ctx = GNUNET_malloc (sizeof (struct Context) * num_peers);
706 /* select the peers which actively participate in profiling */
707 n_active = num_peers * PUT_PROBABILITY / 100;
710 GNUNET_SCHEDULER_shutdown ();
714 a_ac = GNUNET_malloc (n_active * sizeof (struct ActiveContext));
716 for (cnt = 0; cnt < num_peers && ac_cnt < n_active; cnt++)
718 if (GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 100) >=
721 a_ctx[cnt].ac = &a_ac[ac_cnt];
722 a_ac[ac_cnt].ctx = &a_ctx[cnt];
726 a_ac = GNUNET_realloc (a_ac, n_active * sizeof (struct ActiveContext));
727 INFO ("Active peers: %u\n", n_active);
729 /* start DHT service on all peers */
730 for (cnt = 0; cnt < num_peers; cnt++)
732 a_ctx[cnt].peer = peers[cnt];
733 a_ctx[cnt].op = GNUNET_TESTBED_peer_manage_service (&a_ctx[cnt],
744 * Main function that will be run by the scheduler.
747 * @param args remaining command-line arguments
748 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
749 * @param config configuration
752 run (void *cls, char *const *args, const char *cfgfile,
753 const struct GNUNET_CONFIGURATION_Handle *config)
759 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Exiting as the number of peers is %u\n"),
763 cfg = GNUNET_CONFIGURATION_dup (config);
765 GNUNET_TESTBED_run (hosts_file, cfg, num_peers, event_mask, NULL,
766 NULL, &test_run, NULL);
767 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &do_shutdown,
775 * @return 0 on success
778 main (int argc, char *const *argv)
782 static struct GNUNET_GETOPT_CommandLineOption options[] = {
783 {'n', "peers", "COUNT",
784 gettext_noop ("number of peers to start"),
785 1, &GNUNET_GETOPT_set_uint, &num_peers},
786 {'H', "hosts", "FILENAME",
787 gettext_noop ("name of the file with the login information for the testbed"),
788 1, &GNUNET_GETOPT_set_string, &hosts_file},
789 {'d', "delay", "DELAY",
790 gettext_noop ("delay for starting DHT PUT and GET"),
791 1, &GNUNET_GETOPT_set_relative_time, &delay},
792 {'r', "replication", "DEGREE",
793 gettext_noop ("replication degree for DHT PUTs"),
794 1, &GNUNET_GETOPT_set_uint, &replication},
795 {'t', "timeout", "TIMEOUT",
796 gettext_noop ("timeout for DHT PUT and GET requests"),
797 1, &GNUNET_GETOPT_set_relative_time, &timeout},
798 GNUNET_GETOPT_OPTION_END
801 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
803 delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 1); /* default delay */
804 timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30); /* default timeout */
805 replication = 1; /* default replication */
808 GNUNET_PROGRAM_run (argc, argv, "dht-profiler",
810 ("Measure quality and performance of the DHT service."),
811 options, &run, NULL))