- added basic layout for b/w and successor stats collection in profiler
[oweals/gnunet.git] / src / dht / gnunet_dht_profiler.c
1 /*
2      This file is part of GNUnet.
3      (C) 2014 Christian Grothoff (and other contributing authors)
4
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.
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      General Public License for more details.
14
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.
19 */
20
21 /**
22  * @file dht/gnunet_dht_profiler.c
23  * @brief Profiler for GNUnet DHT
24  * @author Sree Harsha Totakura <sreeharsha@totakura.in>
25  */
26
27 #include "platform.h"
28 #include "gnunet_util_lib.h"
29 #include "gnunet_testbed_service.h"
30 #include "gnunet_dht_service.h"
31
32 #define INFO(...)                                       \
33   GNUNET_log (GNUNET_ERROR_TYPE_INFO, __VA_ARGS__)
34
35 #define DEBUG(...)                                           \
36   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, __VA_ARGS__)
37
38 /**
39  * Number of peers which should perform a PUT out of 100 peers
40  */
41 #define PUT_PROBABILITY 100
42
43 /**
44  * Configuration
45  */
46 static struct GNUNET_CONFIGURATION_Handle *cfg;
47
48 /**
49  * Name of the file with the hosts to run the test over
50  */
51 static char *hosts_file;
52
53 /**
54  * Context for a peer which actively does DHT PUT/GET
55  */
56 struct ActiveContext;
57
58 /**
59  * Context to hold data of peer
60  */
61 struct Context
62 {
63
64   /**
65    * The testbed peer this context belongs to
66    */
67   struct GNUNET_TESTBED_Peer *peer;
68
69   /**
70    * Testbed operation acting on this peer
71    */
72   struct GNUNET_TESTBED_Operation *op;
73
74   /**
75    * Active context; NULL if this peer is not an active peer
76    */
77   struct ActiveContext *ac;
78 };
79
80
81 /**
82  * Context for a peer which actively does DHT PUT/GET
83  */
84 struct ActiveContext
85 {
86   /**
87    * The linked peer context
88    */
89   struct Context *ctx;
90
91   /**
92    * Handler to the DHT service
93    */
94   struct GNUNET_DHT_Handle *dht;
95
96   /**
97    * The data used for do a PUT.  Will be NULL if a PUT hasn't been performed yet
98    */
99   void *put_data;
100
101   /**
102    * The active context used for our DHT GET
103    */
104   struct ActiveContext *get_ac;
105
106   /**
107    * The put handle
108    */
109   struct GNUNET_DHT_PutHandle *dht_put;
110
111   /**
112    * The get handle
113    */
114   struct GNUNET_DHT_GetHandle *dht_get;
115
116   /**
117    * The hash of the @e put_data
118    */
119   struct GNUNET_HashCode hash;
120
121   /**
122    * Delay task
123    */
124   GNUNET_SCHEDULER_TaskIdentifier delay_task;
125
126   /**
127    * The size of the @e put_data
128    */
129   uint16_t put_data_size;
130
131   /**
132    * The number of peers currently doing GET on our data
133    */
134   uint16_t nrefs;
135 };
136
137
138 /**
139  * An array of contexts.  The size of this array should be equal to @a num_peers
140  */
141 static struct Context *a_ctx;
142
143 /**
144  * Array of active peers
145  */
146 static struct ActiveContext *a_ac;
147
148 /**
149  * The delay between starting to do PUTS and GETS
150  */
151 static struct GNUNET_TIME_Relative delay;
152
153 /**
154  * The timeout for GET and PUT
155  */
156 static struct GNUNET_TIME_Relative timeout;
157
158 /**
159  * Number of peers
160  */
161 static unsigned int num_peers;
162
163 /**
164  * Number of active peers
165  */
166 static unsigned int n_active;
167
168 /**
169  * Number of DHT service connections we currently have
170  */
171 static unsigned int n_dht;
172
173 /**
174  * Number of DHT PUTs made
175  */
176 static unsigned int n_puts;
177
178 /**
179  * Number of DHT PUTs succeeded
180  */
181 static unsigned int n_puts_ok;
182
183 /**
184  * Number of DHT PUTs failed
185  */
186 static unsigned int n_puts_fail;
187
188 /**
189  * Number of DHT GETs made
190  */
191 static unsigned int n_gets;
192
193 /**
194  * Number of DHT GETs succeeded
195  */
196 static unsigned int n_gets_ok;
197
198 /**
199  * Number of DHT GETs succeeded
200  */
201 static unsigned int n_gets_fail;
202
203 /**
204  * Replication degree
205  */
206 static unsigned int replication;
207
208 /**
209  * Testbed Operation (to get stats).
210  */
211 static struct GNUNET_TESTBED_Operation *stats_op;
212
213 /**
214  * Testbed peer handles.
215  */
216 static struct GNUNET_TESTBED_Peer **testbed_handles;
217
218 /**
219  * Shutdown task.  Cleanup all resources and operations.
220  *
221  * @param cls NULL
222  * @param tc scheduler task context
223  */
224 static void
225 do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
226 {
227   struct ActiveContext *ac;
228   unsigned int cnt;
229
230   if (NULL != a_ctx)
231   {
232     for (cnt=0; cnt < num_peers; cnt++)
233     {
234       if (NULL != a_ctx[cnt].op)
235         GNUNET_TESTBED_operation_done (a_ctx[cnt].op);
236
237       /* Cleanup active context if this peer is an active peer */
238       ac = a_ctx[cnt].ac;
239       if (NULL == ac)
240         continue;
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);
249     }
250     GNUNET_free (a_ctx);
251     a_ctx = NULL;
252   }
253   GNUNET_free_non_null (a_ac);
254 }
255
256
257 /**
258  * Stats callback. Finish the stats testbed operation and when all stats have
259  * been iterated, shutdown the test.
260  *
261  * @param cls closure
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.
265  */
266 static void
267 bandwidth_stats_cont (void *cls, 
268                       struct GNUNET_TESTBED_Operation *op, const char *emsg)
269 {
270   
271 }
272
273
274 /**
275  * Process statistic values.
276  *
277  * @param cls closure
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
284  */
285 static int
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)
289 {
290   return GNUNET_OK;
291 }
292
293
294 /**
295  * Task that collects bandwidth used by all the peers.
296  *
297  * @param cls Closure (NULL).
298  * @param tc Task Context.
299  */
300 static void
301 collect_bandwidth_stats (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
302 {
303   if ((GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason) != 0)
304     return;
305
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,
309                                             NULL, NULL,
310                                             bandwidth_stats_iterator, 
311                                             bandwidth_stats_cont, NULL);
312 }
313
314
315 static void
316 summarize ()
317 {
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 (); 
327 }
328
329
330 /**
331  * Task to cancel DHT GET.
332  *
333  * @param cls NULL
334  * @param tc scheduler task context
335  */
336 static void
337 cancel_get (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
338 {
339   struct ActiveContext *ac = cls;
340   struct Context *ctx = ac->ctx;
341
342   ac->delay_task = GNUNET_SCHEDULER_NO_TASK;
343   GNUNET_assert (NULL != ac->dht_get);
344   GNUNET_DHT_get_stop (ac->dht_get);
345   ac->dht_get = NULL;
346   GNUNET_TESTBED_operation_done (ctx->op);
347   ctx->op = NULL;
348   n_gets_fail++;
349
350   /* If profiling is complete, summarize */
351   if (n_active == n_gets_fail + n_gets_ok)
352     summarize ();
353 }
354
355
356 /**
357  * Iterator called on each result obtained for a DHT
358  * operation that expects a reply
359  *
360  * @param cls closure
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
372  */
373 static void
374 get_iter (void *cls,
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)
383 {
384   struct ActiveContext *ac = cls;
385   struct ActiveContext *get_ac = ac->get_ac;
386   struct Context *ctx = ac->ctx;
387
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));
392   n_gets_ok++;
393   get_ac->nrefs--;
394   GNUNET_DHT_get_stop (ac->dht_get);
395   ac->dht_get = NULL;
396   GNUNET_SCHEDULER_cancel (ac->delay_task);
397   ac->delay_task = GNUNET_SCHEDULER_NO_TASK;
398   GNUNET_TESTBED_operation_done (ctx->op);
399   ctx->op = NULL;
400   
401   /* Summarize if profiling is complete */
402   if (n_active == n_gets_fail + n_gets_ok)
403     summarize ();
404 }
405
406
407 /**
408  * Task to do DHT GETs
409  *
410  * @param cls the active context
411  * @param tc the scheduler task context
412  */
413 static void
414 delayed_get (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
415 {
416   struct ActiveContext *ac = cls;
417   struct ActiveContext *get_ac;
418   unsigned int r;
419
420   ac->delay_task = GNUNET_SCHEDULER_NO_TASK;
421   get_ac = NULL;
422   while (1)
423   {
424     r = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, n_active);
425     get_ac = &a_ac[r];
426     if (NULL != get_ac->put_data)
427       break;
428   }
429   get_ac->nrefs++;
430   ac->get_ac = get_ac;
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,
434                                       &get_ac->hash,
435                                       1, /* replication level */
436                                       GNUNET_DHT_RO_NONE,
437                                       NULL, 0, /* extended query and size */
438                                       get_iter, ac); /* GET iterator and closure
439                                                         */
440   n_gets++;
441
442   /* schedule the timeout task for GET */
443   ac->delay_task = GNUNET_SCHEDULER_add_delayed (timeout, &cancel_get, ac);
444 }
445
446
447 /**
448  * Queue up a delayed task for doing DHT GET
449  *
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)
456  */
457 static void
458 put_cont (void *cls, int success)
459 {
460   struct ActiveContext *ac = cls;
461
462   ac->dht_put = NULL;
463   if (success)
464     n_puts_ok++;
465   else
466     n_puts_fail++;
467   ac->delay_task = GNUNET_SCHEDULER_add_delayed (delay, &delayed_get, ac);
468 }
469
470 /**
471  * Stats callback. Finish the stats testbed operation and when all stats have
472  * been iterated, shutdown the test.
473  *
474  * @param cls closure
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.
478  */
479 static void
480 finger_stats_cont (void *cls, 
481                    struct GNUNET_TESTBED_Operation *op, 
482                    const char *emsg)
483 {
484   
485 }
486
487
488 /**
489  * Process statistic values.
490  *
491  * @param cls closure
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
498  */
499 static int
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)
503 {
504   uint32_t i;
505
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);
509
510   return GNUNET_OK;
511 }
512
513
514 /**
515  * Task check that keepalives were sent and received.
516  *
517  * @param cls Closure (NULL).
518  * @param tc Task Context.
519  */
520 static void
521 collect_finger_stats (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
522 {
523   if ((GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason) != 0)
524     return;
525
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,
529                                             "dht", NULL,
530                                             finger_stats_iterator, 
531                                             finger_stats_cont, NULL);
532 }
533
534
535 /**
536  * Task to do DHT PUTS
537  *
538  * @param cls the active context
539  * @param tc the scheduler task context
540  */
541 static void
542 delayed_put (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
543 {
544   struct ActiveContext *ac = cls;
545
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);
551   
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,
556                                                  (63*1024));
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,
563                                 replication,
564                                 GNUNET_DHT_RO_NONE,
565                                 GNUNET_BLOCK_TYPE_TEST,
566                                 ac->put_data_size,
567                                 ac->put_data,
568                                 GNUNET_TIME_UNIT_FOREVER_ABS, /* expiration time */
569                                 timeout,                      /* PUT timeout */
570                                 put_cont, ac);                /* continuation and its closure */
571   n_puts++;
572 }
573
574
575 /**
576  * Connection to DHT has been established.  Call the delay task.
577  *
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.
583  */
584 static void
585 dht_connected (void *cls,
586                struct GNUNET_TESTBED_Operation *op,
587                void *ca_result,
588                const char *emsg)
589 {
590   struct ActiveContext *ac = cls;
591   struct Context *ctx = ac->ctx;
592
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;
597   if (NULL != emsg)
598   {
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() */
601     ctx->op = NULL;
602     return;
603   }
604   ac->delay_task = GNUNET_SCHEDULER_add_delayed (delay, &delayed_put, ac);
605 }
606
607
608 /**
609  * Connect to DHT service and return the DHT client handler
610  *
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
616  */
617 static void *
618 dht_connect (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg)
619 {
620   n_dht++;
621   return GNUNET_DHT_connect (cfg, 10);
622 }
623
624
625 /**
626  * Adapter function called to destroy a connection to
627  * a service.
628  *
629  * @param cls the active context
630  * @param op_result service handle returned from the connect adapter
631  */
632 static void
633 dht_disconnect (void *cls, void *op_result)
634 {
635   struct ActiveContext *ac = cls;
636
637   GNUNET_assert (NULL != ac->dht);
638   GNUNET_assert (ac->dht == op_result);
639   GNUNET_DHT_disconnect (ac->dht);
640   n_dht--;
641   if (0 == n_dht)
642     GNUNET_SCHEDULER_shutdown ();
643 }
644
645
646 /**
647  * Callback called when DHT service on the peer is started
648  *
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.
653  */
654 static void
655 service_started (void *cls,
656                  struct GNUNET_TESTBED_Operation *op,
657                  const char *emsg)
658 {
659   struct Context *ctx = cls;
660
661   GNUNET_assert (NULL != ctx);
662   GNUNET_assert (NULL != ctx->op);
663   GNUNET_TESTBED_operation_done (ctx->op);
664   ctx->op = NULL;
665   if (NULL == ctx->ac)
666     return;
667   /* FIXME: connect to the DHT service and wait before starting a PUT */
668   ctx->op = GNUNET_TESTBED_service_connect (ctx, ctx->peer,
669                                             "dht",
670                                             &dht_connected, ctx->ac,
671                                             &dht_connect,
672                                             &dht_disconnect,
673                                             ctx->ac);
674 }
675
676
677 /**
678  * Signature of a main function for a testcase.
679  *
680  * @param cls closure
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
685  *          succeeded
686  * @param links_failed the number of overlay link
687  */
688 static void
689 test_run (void *cls,
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)
694 {
695   unsigned int cnt;
696   unsigned int ac_cnt;
697     
698   if (NULL == peers)
699   {
700     /* exit */
701     GNUNET_assert (0);
702   }
703   INFO ("%u peers started\n", num_peers);
704   a_ctx = GNUNET_malloc (sizeof (struct Context) * num_peers);
705
706   /* select the peers which actively participate in profiling */
707   n_active = num_peers * PUT_PROBABILITY / 100;
708   if (0 == n_active)
709   {
710     GNUNET_SCHEDULER_shutdown ();
711     GNUNET_free (a_ctx);
712     return;
713   }
714   a_ac = GNUNET_malloc (n_active * sizeof (struct ActiveContext));
715   ac_cnt = 0;
716   for (cnt = 0; cnt < num_peers && ac_cnt < n_active; cnt++)
717   {
718     if (GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 100) >=
719         PUT_PROBABILITY)
720       continue;
721     a_ctx[cnt].ac = &a_ac[ac_cnt];
722     a_ac[ac_cnt].ctx = &a_ctx[cnt];
723     ac_cnt++;
724   }
725   n_active = ac_cnt;
726   a_ac = GNUNET_realloc (a_ac, n_active * sizeof (struct ActiveContext));
727   INFO ("Active peers: %u\n", n_active);
728
729   /* start DHT service on all peers */
730   for (cnt = 0; cnt < num_peers; cnt++)
731   {
732     a_ctx[cnt].peer = peers[cnt];
733     a_ctx[cnt].op = GNUNET_TESTBED_peer_manage_service (&a_ctx[cnt],
734                                                         peers[cnt],
735                                                         "dht",
736                                                         &service_started,
737                                                         &a_ctx[cnt],
738                                                         1);
739   }
740 }
741
742
743 /**
744  * Main function that will be run by the scheduler.
745  *
746  * @param cls closure
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
750  */
751 static void
752 run (void *cls, char *const *args, const char *cfgfile,
753      const struct GNUNET_CONFIGURATION_Handle *config)
754 {
755   uint64_t event_mask;
756
757   if (0 == num_peers)
758   {
759     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Exiting as the number of peers is %u\n"),
760                 num_peers);
761     return;
762   }
763   cfg = GNUNET_CONFIGURATION_dup (config);
764   event_mask = 0;
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,
768                                 NULL);
769 }
770
771
772 /**
773  * Main function.
774  *
775  * @return 0 on success
776  */
777 int
778 main (int argc, char *const *argv)
779 {
780   int rc;
781
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
799   };
800
801   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
802     return 2;
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 */
806   rc = 0;
807   if (GNUNET_OK !=
808       GNUNET_PROGRAM_run (argc, argv, "dht-profiler",
809                           gettext_noop
810                           ("Measure quality and performance of the DHT service."),
811                           options, &run, NULL))
812     rc = 1;
813   return rc;
814 }