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;
210 * Testbed Operation (to get stats).
212 static struct GNUNET_TESTBED_Operation *stats_op;
215 * Testbed peer handles.
217 static struct GNUNET_TESTBED_Peer **testbed_handles;
220 * Shutdown task. Cleanup all resources and operations.
223 * @param tc scheduler task context
226 do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
228 struct ActiveContext *ac;
233 for (cnt=0; cnt < num_peers; cnt++)
235 if (NULL != a_ctx[cnt].op)
236 GNUNET_TESTBED_operation_done (a_ctx[cnt].op);
238 /* Cleanup active context if this peer is an active peer */
242 if (GNUNET_SCHEDULER_NO_TASK != ac->delay_task)
243 GNUNET_SCHEDULER_cancel (ac->delay_task);
244 if (NULL != ac->put_data)
245 GNUNET_free (ac->put_data);
246 if (NULL != ac->dht_put)
247 GNUNET_DHT_put_cancel (ac->dht_put);
248 if (NULL != ac->dht_get)
249 GNUNET_DHT_get_stop (ac->dht_get);
254 GNUNET_free_non_null (a_ac);
259 * Stats callback. Finish the stats testbed operation and when all stats have
260 * been iterated, shutdown the test.
263 * @param op the operation that has been finished
264 * @param emsg error message in case the operation has failed; will be NULL if
265 * operation has executed successfully.
268 bandwidth_stats_cont (void *cls,
269 struct GNUNET_TESTBED_Operation *op, const char *emsg)
276 * Process statistic values.
279 * @param peer the peer the statistic belong to
280 * @param subsystem name of subsystem that created the statistic
281 * @param name the name of the datum
282 * @param value the current value
283 * @param is_persistent GNUNET_YES if the value is persistent, GNUNET_NO if not
284 * @return GNUNET_OK to continue, GNUNET_SYSERR to abort iteration
287 bandwidth_stats_iterator (void *cls, const struct GNUNET_TESTBED_Peer *peer,
288 const char *subsystem, const char *name,
289 uint64_t value, int is_persistent)
296 * Task that collects bandwidth used by all the peers.
298 * @param cls Closure (NULL).
299 * @param tc Task Context.
302 collect_bandwidth_stats (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
304 if ((GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason) != 0)
307 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Start collecting bandwidth statistics...\n");
308 //FIXME: what is the name of transport subsystem?
309 stats_op = GNUNET_TESTBED_get_statistics (n_active, testbed_handles,
311 bandwidth_stats_iterator,
312 bandwidth_stats_cont, NULL);
319 INFO ("# PUTS made: %u\n", n_puts);
320 INFO ("# PUTS succeeded: %u\n", n_puts_ok);
321 INFO ("# PUTS failed: %u\n", n_puts_fail);
322 INFO ("# GETS made: %u\n", n_gets);
323 INFO ("# GETS succeeded: %u\n", n_gets_ok);
324 INFO ("# GETS failed: %u\n", n_gets_fail);
325 //FIXME: is this the right place to call b/w stats?
326 //GNUNET_SCHEDULER_add_now (&collect_bandwidth_stats, NULL);
327 GNUNET_SCHEDULER_shutdown ();
332 * Task to cancel DHT GET.
335 * @param tc scheduler task context
338 cancel_get (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
340 struct ActiveContext *ac = cls;
341 struct Context *ctx = ac->ctx;
343 ac->delay_task = GNUNET_SCHEDULER_NO_TASK;
344 GNUNET_assert (NULL != ac->dht_get);
345 GNUNET_DHT_get_stop (ac->dht_get);
347 GNUNET_TESTBED_operation_done (ctx->op);
351 /* If profiling is complete, summarize */
352 if (n_active == n_gets_fail + n_gets_ok)
358 * Iterator called on each result obtained for a DHT
359 * operation that expects a reply
362 * @param exp when will this value expire
363 * @param key key of the result
364 * @param get_path peers on reply path (or NULL if not recorded)
365 * [0] = datastore's first neighbor, [length - 1] = local peer
366 * @param get_path_length number of entries in @a get_path
367 * @param put_path peers on the PUT path (or NULL if not recorded)
368 * [0] = origin, [length - 1] = datastore
369 * @param put_path_length number of entries in @a put_path
370 * @param type type of the result
371 * @param size number of bytes in @a data
372 * @param data pointer to the result data
376 struct GNUNET_TIME_Absolute exp,
377 const struct GNUNET_HashCode *key,
378 const struct GNUNET_PeerIdentity *get_path,
379 unsigned int get_path_length,
380 const struct GNUNET_PeerIdentity *put_path,
381 unsigned int put_path_length,
382 enum GNUNET_BLOCK_Type type,
383 size_t size, const void *data)
385 struct ActiveContext *ac = cls;
386 struct ActiveContext *get_ac = ac->get_ac;
387 struct Context *ctx = ac->ctx;
389 /* Check the keys of put and get match or not. */
390 GNUNET_assert (0 == memcmp (key, &get_ac->hash, sizeof (struct GNUNET_HashCode)));
391 /* we found the data we are looking for */
392 DEBUG ("We found a GET request; %u remaining\n", n_gets - (n_gets_fail + n_gets_ok));
395 GNUNET_DHT_get_stop (ac->dht_get);
397 GNUNET_SCHEDULER_cancel (ac->delay_task);
398 ac->delay_task = GNUNET_SCHEDULER_NO_TASK;
399 GNUNET_TESTBED_operation_done (ctx->op);
402 /* Summarize if profiling is complete */
403 if (n_active == n_gets_fail + n_gets_ok)
409 * Task to do DHT GETs
411 * @param cls the active context
412 * @param tc the scheduler task context
415 delayed_get (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
417 struct ActiveContext *ac = cls;
418 struct ActiveContext *get_ac;
421 ac->delay_task = GNUNET_SCHEDULER_NO_TASK;
425 r = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, n_active);
427 if (NULL != get_ac->put_data)
432 DEBUG ("Doing a DHT GET for data of size %u\n", get_ac->put_data_size);
433 ac->dht_get = GNUNET_DHT_get_start (ac->dht,
434 GNUNET_BLOCK_TYPE_TEST,
436 1, /* replication level */
438 NULL, 0, /* extended query and size */
439 get_iter, ac); /* GET iterator and closure
443 /* schedule the timeout task for GET */
444 ac->delay_task = GNUNET_SCHEDULER_add_delayed (timeout, &cancel_get, ac);
449 * Queue up a delayed task for doing DHT GET
451 * @param cls the active context
452 * @param success #GNUNET_OK if the PUT was transmitted,
453 * #GNUNET_NO on timeout,
454 * #GNUNET_SYSERR on disconnect from service
455 * after the PUT message was transmitted
456 * (so we don't know if it was received or not)
459 put_cont (void *cls, int success)
461 struct ActiveContext *ac = cls;
468 ac->delay_task = GNUNET_SCHEDULER_add_delayed (delay, &delayed_get, ac);
473 * Stats callback. Finish the stats testbed operation and when all stats have
474 * been iterated, shutdown the test.
477 * @param op the operation that has been finished
478 * @param emsg error message in case the operation has failed; will be NULL if
479 * operation has executed successfully.
482 finger_stats_cont (void *cls,
483 struct GNUNET_TESTBED_Operation *op,
491 * Process statistic values.
494 * @param peer the peer the statistic belong to
495 * @param subsystem name of subsystem that created the statistic
496 * @param name the name of the datum
497 * @param value the current value
498 * @param is_persistent GNUNET_YES if the value is persistent, GNUNET_NO if not
499 * @return GNUNET_OK to continue, GNUNET_SYSERR to abort iteration
502 finger_stats_iterator (void *cls, const struct GNUNET_TESTBED_Peer *peer,
503 const char *subsystem, const char *name,
504 uint64_t value, int is_persistent)
508 i = GNUNET_TESTBED_get_index (peer);
509 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " STATS %u - %s [%s]: %llu\n",
510 i, subsystem, name, value);
517 * Task check that keepalives were sent and received.
519 * @param cls Closure (NULL).
520 * @param tc Task Context.
523 collect_finger_stats (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
525 if ((GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason) != 0)
528 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Start collecting statistics...\n");
529 /* FIXME: Write subsystem name. */
530 stats_op = GNUNET_TESTBED_get_statistics (n_active, testbed_handles,
532 finger_stats_iterator,
533 finger_stats_cont, NULL);
538 * Task to do DHT PUTS
540 * @param cls the active context
541 * @param tc the scheduler task context
544 delayed_put (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
546 struct ActiveContext *ac = cls;
548 /*FIXME: Before doing anything else, first collect statistics from each peer
549 DHT and check if circle is formed. If yes then go ahead with more puts,
550 else wait for 'delay' time. This function does not return anything, so we
551 should have some way to notify that circle is done or we need to wait.*/
552 //GNUNET_SCHEDULER_add_now(collect_finger_stats,NULL);
554 ac->delay_task = GNUNET_SCHEDULER_NO_TASK;
555 /* Generate and DHT PUT some random data */
556 ac->put_data_size = 16; /* minimum */
557 ac->put_data_size += GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
559 ac->put_data = GNUNET_malloc (ac->put_data_size);
560 GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
561 ac->put_data, ac->put_data_size);
562 GNUNET_CRYPTO_hash (ac->put_data, ac->put_data_size, &ac->hash);
563 DEBUG ("Doing a DHT PUT with data of size %u\n", ac->put_data_size);
564 ac->dht_put = GNUNET_DHT_put (ac->dht, &ac->hash,
567 GNUNET_BLOCK_TYPE_TEST,
570 GNUNET_TIME_UNIT_FOREVER_ABS, /* expiration time */
571 timeout, /* PUT timeout */
572 put_cont, ac); /* continuation and its closure */
578 * Connection to DHT has been established. Call the delay task.
580 * @param cls the active context
581 * @param op the operation that has been finished
582 * @param ca_result the service handle returned from GNUNET_TESTBED_ConnectAdapter()
583 * @param emsg error message in case the operation has failed; will be NULL if
584 * operation has executed successfully.
587 dht_connected (void *cls,
588 struct GNUNET_TESTBED_Operation *op,
592 struct ActiveContext *ac = cls;
593 struct Context *ctx = ac->ctx;
595 GNUNET_assert (NULL != ctx);
596 GNUNET_assert (NULL != ctx->op);
597 GNUNET_assert (ctx->op == op);
598 ac->dht = (struct GNUNET_DHT_Handle *) ca_result;
601 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Connection to DHT service failed: %s\n", emsg);
602 GNUNET_TESTBED_operation_done (ctx->op); /* Calls dht_disconnect() */
606 ac->delay_task = GNUNET_SCHEDULER_add_delayed (delay, &delayed_put, ac);
611 * Connect to DHT service and return the DHT client handler
613 * @param cls the active context
614 * @param cfg configuration of the peer to connect to; will be available until
615 * GNUNET_TESTBED_operation_done() is called on the operation returned
616 * from GNUNET_TESTBED_service_connect()
617 * @return service handle to return in 'op_result', NULL on error
620 dht_connect (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg)
623 return GNUNET_DHT_connect (cfg, 10);
628 * Adapter function called to destroy a connection to
631 * @param cls the active context
632 * @param op_result service handle returned from the connect adapter
635 dht_disconnect (void *cls, void *op_result)
637 struct ActiveContext *ac = cls;
639 GNUNET_assert (NULL != ac->dht);
640 GNUNET_assert (ac->dht == op_result);
641 GNUNET_DHT_disconnect (ac->dht);
644 GNUNET_SCHEDULER_shutdown ();
649 * Callback called when DHT service on the peer is started
651 * @param cls the context
652 * @param op the operation that has been finished
653 * @param emsg error message in case the operation has failed; will be NULL if
654 * operation has executed successfully.
657 service_started (void *cls,
658 struct GNUNET_TESTBED_Operation *op,
661 struct Context *ctx = cls;
663 GNUNET_assert (NULL != ctx);
664 GNUNET_assert (NULL != ctx->op);
665 GNUNET_TESTBED_operation_done (ctx->op);
669 /* FIXME: connect to the DHT service and wait before starting a PUT */
670 ctx->op = GNUNET_TESTBED_service_connect (ctx, ctx->peer,
672 &dht_connected, ctx->ac,
680 * Signature of a main function for a testcase.
683 * @param h the run handle
684 * @param num_peers number of peers in 'peers'
685 * @param peers handle to peers run in the testbed
686 * @param links_succeeded the number of overlay link connection attempts that
688 * @param links_failed the number of overlay link
692 struct GNUNET_TESTBED_RunHandle *h,
693 unsigned int num_peers, struct GNUNET_TESTBED_Peer **peers,
694 unsigned int links_succeeded,
695 unsigned int links_failed)
705 INFO ("%u peers started\n", num_peers);
706 a_ctx = GNUNET_malloc (sizeof (struct Context) * num_peers);
708 /* select the peers which actively participate in profiling */
709 n_active = num_peers * PUT_PROBABILITY / 100;
712 GNUNET_SCHEDULER_shutdown ();
716 a_ac = GNUNET_malloc (n_active * sizeof (struct ActiveContext));
718 for (cnt = 0; cnt < num_peers && ac_cnt < n_active; cnt++)
720 if (GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 100) >=
723 a_ctx[cnt].ac = &a_ac[ac_cnt];
724 a_ac[ac_cnt].ctx = &a_ctx[cnt];
728 a_ac = GNUNET_realloc (a_ac, n_active * sizeof (struct ActiveContext));
729 INFO ("Active peers: %u\n", n_active);
731 /* start DHT service on all peers */
732 for (cnt = 0; cnt < num_peers; cnt++)
734 a_ctx[cnt].peer = peers[cnt];
735 a_ctx[cnt].op = GNUNET_TESTBED_peer_manage_service (&a_ctx[cnt],
746 * Main function that will be run by the scheduler.
749 * @param args remaining command-line arguments
750 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
751 * @param config configuration
754 run (void *cls, char *const *args, const char *cfgfile,
755 const struct GNUNET_CONFIGURATION_Handle *config)
761 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Exiting as the number of peers is %u\n"),
765 cfg = GNUNET_CONFIGURATION_dup (config);
767 GNUNET_TESTBED_run (hosts_file, cfg, num_peers, event_mask, NULL,
768 NULL, &test_run, NULL);
769 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &do_shutdown,
777 * @return 0 on success
780 main (int argc, char *const *argv)
784 static struct GNUNET_GETOPT_CommandLineOption options[] = {
785 {'n', "peers", "COUNT",
786 gettext_noop ("number of peers to start"),
787 1, &GNUNET_GETOPT_set_uint, &num_peers},
788 {'H', "hosts", "FILENAME",
789 gettext_noop ("name of the file with the login information for the testbed"),
790 1, &GNUNET_GETOPT_set_string, &hosts_file},
791 {'d', "delay", "DELAY",
792 gettext_noop ("delay for starting DHT PUT and GET"),
793 1, &GNUNET_GETOPT_set_relative_time, &delay},
794 {'r', "replication", "DEGREE",
795 gettext_noop ("replication degree for DHT PUTs"),
796 1, &GNUNET_GETOPT_set_uint, &replication},
797 {'t', "timeout", "TIMEOUT",
798 gettext_noop ("timeout for DHT PUT and GET requests"),
799 1, &GNUNET_GETOPT_set_relative_time, &timeout},
800 GNUNET_GETOPT_OPTION_END
803 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
805 delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5); /* default delay */
806 timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30); /* default timeout */
807 replication = 1; /* default replication */
810 GNUNET_PROGRAM_run (argc, argv, "dht-profiler",
812 ("Measure quality and performance of the DHT service."),
813 options, &run, NULL))