f88749524c9cc52ccc39edfc85bda4cb2786115f
[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 #if 0
209 /**
210  * Testbed Operation (to get stats).
211  */
212 static struct GNUNET_TESTBED_Operation *stats_op;
213
214 /**
215  * Testbed peer handles.
216  */
217 static struct GNUNET_TESTBED_Peer **testbed_handles;
218 #endif
219 /**
220  * Shutdown task.  Cleanup all resources and operations.
221  *
222  * @param cls NULL
223  * @param tc scheduler task context
224  */
225 static void
226 do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
227 {
228   struct ActiveContext *ac;
229   unsigned int cnt;
230
231   if (NULL != a_ctx)
232   {
233     for (cnt=0; cnt < num_peers; cnt++)
234     {
235       if (NULL != a_ctx[cnt].op)
236         GNUNET_TESTBED_operation_done (a_ctx[cnt].op);
237
238       /* Cleanup active context if this peer is an active peer */
239       ac = a_ctx[cnt].ac;
240       if (NULL == ac)
241         continue;
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);
250     }
251     GNUNET_free (a_ctx);
252     a_ctx = NULL;
253   }
254   GNUNET_free_non_null (a_ac);
255 }
256
257 #if 0
258 /**
259  * Stats callback. Finish the stats testbed operation and when all stats have
260  * been iterated, shutdown the test.
261  *
262  * @param cls closure
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.
266  */
267 static void
268 bandwidth_stats_cont (void *cls, 
269                       struct GNUNET_TESTBED_Operation *op, const char *emsg)
270 {
271   
272 }
273
274
275 /**
276  * Process statistic values.
277  *
278  * @param cls closure
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
285  */
286 static int
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)
290 {
291   return GNUNET_OK;
292 }
293
294
295 /**
296  * Task that collects bandwidth used by all the peers.
297  *
298  * @param cls Closure (NULL).
299  * @param tc Task Context.
300  */
301 static void
302 collect_bandwidth_stats (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
303 {
304   if ((GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason) != 0)
305     return;
306
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,
310                                             NULL, NULL,
311                                             bandwidth_stats_iterator, 
312                                             bandwidth_stats_cont, NULL);
313 }
314 #endif
315
316 static void
317 summarize ()
318 {
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 (); 
328 }
329
330
331 /**
332  * Task to cancel DHT GET.
333  *
334  * @param cls NULL
335  * @param tc scheduler task context
336  */
337 static void
338 cancel_get (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
339 {
340   struct ActiveContext *ac = cls;
341   struct Context *ctx = ac->ctx;
342
343   ac->delay_task = GNUNET_SCHEDULER_NO_TASK;
344   GNUNET_assert (NULL != ac->dht_get);
345   GNUNET_DHT_get_stop (ac->dht_get);
346   ac->dht_get = NULL;
347   GNUNET_TESTBED_operation_done (ctx->op);
348   ctx->op = NULL;
349   n_gets_fail++;
350
351   /* If profiling is complete, summarize */
352   if (n_active == n_gets_fail + n_gets_ok)
353     summarize ();
354 }
355
356
357 /**
358  * Iterator called on each result obtained for a DHT
359  * operation that expects a reply
360  *
361  * @param cls closure
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
373  */
374 static void
375 get_iter (void *cls,
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)
384 {
385   struct ActiveContext *ac = cls;
386   struct ActiveContext *get_ac = ac->get_ac;
387   struct Context *ctx = ac->ctx;
388
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));
393   n_gets_ok++;
394   get_ac->nrefs--;
395   GNUNET_DHT_get_stop (ac->dht_get);
396   ac->dht_get = NULL;
397   GNUNET_SCHEDULER_cancel (ac->delay_task);
398   ac->delay_task = GNUNET_SCHEDULER_NO_TASK;
399   GNUNET_TESTBED_operation_done (ctx->op);
400   ctx->op = NULL;
401   
402   /* Summarize if profiling is complete */
403   if (n_active == n_gets_fail + n_gets_ok)
404     summarize ();
405 }
406
407
408 /**
409  * Task to do DHT GETs
410  *
411  * @param cls the active context
412  * @param tc the scheduler task context
413  */
414 static void
415 delayed_get (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
416 {
417   struct ActiveContext *ac = cls;
418   struct ActiveContext *get_ac;
419   unsigned int r;
420
421   ac->delay_task = GNUNET_SCHEDULER_NO_TASK;
422   get_ac = NULL;
423   while (1)
424   {
425     r = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, n_active);
426     get_ac = &a_ac[r];
427     if (NULL != get_ac->put_data)
428       break;
429   }
430   get_ac->nrefs++;
431   ac->get_ac = get_ac;
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,
435                                       &get_ac->hash,
436                                       1, /* replication level */
437                                       GNUNET_DHT_RO_NONE,
438                                       NULL, 0, /* extended query and size */
439                                       get_iter, ac); /* GET iterator and closure
440                                                         */
441   n_gets++;
442
443   /* schedule the timeout task for GET */
444   ac->delay_task = GNUNET_SCHEDULER_add_delayed (timeout, &cancel_get, ac);
445 }
446
447
448 /**
449  * Queue up a delayed task for doing DHT GET
450  *
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)
457  */
458 static void
459 put_cont (void *cls, int success)
460 {
461   struct ActiveContext *ac = cls;
462
463   ac->dht_put = NULL;
464   if (success)
465     n_puts_ok++;
466   else
467     n_puts_fail++;
468   ac->delay_task = GNUNET_SCHEDULER_add_delayed (delay, &delayed_get, ac);
469 }
470
471 #if 0
472 /**
473  * Stats callback. Finish the stats testbed operation and when all stats have
474  * been iterated, shutdown the test.
475  *
476  * @param cls closure
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.
480  */
481 static void
482 finger_stats_cont (void *cls, 
483                    struct GNUNET_TESTBED_Operation *op, 
484                    const char *emsg)
485 {
486   
487 }
488
489
490 /**
491  * Process statistic values.
492  *
493  * @param cls closure
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
500  */
501 static int
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)
505 {
506   uint32_t i;
507
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);
511
512   return GNUNET_OK;
513 }
514
515
516 /**
517  * Task check that keepalives were sent and received.
518  *
519  * @param cls Closure (NULL).
520  * @param tc Task Context.
521  */
522 static void
523 collect_finger_stats (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
524 {
525   if ((GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason) != 0)
526     return;
527
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,
531                                             "dht", NULL,
532                                             finger_stats_iterator, 
533                                             finger_stats_cont, NULL);
534 }
535 #endif
536
537 /**
538  * Task to do DHT PUTS
539  *
540  * @param cls the active context
541  * @param tc the scheduler task context
542  */
543 static void
544 delayed_put (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
545 {
546   struct ActiveContext *ac = cls;
547
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);
553   
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,
558                                                  (63*1024));
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,
565                                 replication,
566                                 GNUNET_DHT_RO_NONE,
567                                 GNUNET_BLOCK_TYPE_TEST,
568                                 ac->put_data_size,
569                                 ac->put_data,
570                                 GNUNET_TIME_UNIT_FOREVER_ABS, /* expiration time */
571                                 timeout,                      /* PUT timeout */
572                                 put_cont, ac);                /* continuation and its closure */
573   n_puts++;
574 }
575
576
577 /**
578  * Connection to DHT has been established.  Call the delay task.
579  *
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.
585  */
586 static void
587 dht_connected (void *cls,
588                struct GNUNET_TESTBED_Operation *op,
589                void *ca_result,
590                const char *emsg)
591 {
592   struct ActiveContext *ac = cls;
593   struct Context *ctx = ac->ctx;
594
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;
599   if (NULL != emsg)
600   {
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() */
603     ctx->op = NULL;
604     return;
605   }
606   ac->delay_task = GNUNET_SCHEDULER_add_delayed (delay, &delayed_put, ac);
607 }
608
609
610 /**
611  * Connect to DHT service and return the DHT client handler
612  *
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
618  */
619 static void *
620 dht_connect (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg)
621 {
622   n_dht++;
623   return GNUNET_DHT_connect (cfg, 10);
624 }
625
626
627 /**
628  * Adapter function called to destroy a connection to
629  * a service.
630  *
631  * @param cls the active context
632  * @param op_result service handle returned from the connect adapter
633  */
634 static void
635 dht_disconnect (void *cls, void *op_result)
636 {
637   struct ActiveContext *ac = cls;
638
639   GNUNET_assert (NULL != ac->dht);
640   GNUNET_assert (ac->dht == op_result);
641   GNUNET_DHT_disconnect (ac->dht);
642   n_dht--;
643   if (0 == n_dht)
644     GNUNET_SCHEDULER_shutdown ();
645 }
646
647
648 /**
649  * Callback called when DHT service on the peer is started
650  *
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.
655  */
656 static void
657 service_started (void *cls,
658                  struct GNUNET_TESTBED_Operation *op,
659                  const char *emsg)
660 {
661   struct Context *ctx = cls;
662
663   GNUNET_assert (NULL != ctx);
664   GNUNET_assert (NULL != ctx->op);
665   GNUNET_TESTBED_operation_done (ctx->op);
666   ctx->op = NULL;
667   if (NULL == ctx->ac)
668     return;
669   /* FIXME: connect to the DHT service and wait before starting a PUT */
670   ctx->op = GNUNET_TESTBED_service_connect (ctx, ctx->peer,
671                                             "dht",
672                                             &dht_connected, ctx->ac,
673                                             &dht_connect,
674                                             &dht_disconnect,
675                                             ctx->ac);
676 }
677
678
679 /**
680  * Signature of a main function for a testcase.
681  *
682  * @param cls closure
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
687  *          succeeded
688  * @param links_failed the number of overlay link
689  */
690 static void
691 test_run (void *cls,
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)
696 {
697   unsigned int cnt;
698   unsigned int ac_cnt;
699     
700   if (NULL == peers)
701   {
702     /* exit */
703     GNUNET_assert (0);
704   }
705   INFO ("%u peers started\n", num_peers);
706   a_ctx = GNUNET_malloc (sizeof (struct Context) * num_peers);
707
708   /* select the peers which actively participate in profiling */
709   n_active = num_peers * PUT_PROBABILITY / 100;
710   if (0 == n_active)
711   {
712     GNUNET_SCHEDULER_shutdown ();
713     GNUNET_free (a_ctx);
714     return;
715   }
716   a_ac = GNUNET_malloc (n_active * sizeof (struct ActiveContext));
717   ac_cnt = 0;
718   for (cnt = 0; cnt < num_peers && ac_cnt < n_active; cnt++)
719   {
720     if (GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 100) >=
721         PUT_PROBABILITY)
722       continue;
723     a_ctx[cnt].ac = &a_ac[ac_cnt];
724     a_ac[ac_cnt].ctx = &a_ctx[cnt];
725     ac_cnt++;
726   }
727   n_active = ac_cnt;
728   a_ac = GNUNET_realloc (a_ac, n_active * sizeof (struct ActiveContext));
729   INFO ("Active peers: %u\n", n_active);
730
731   /* start DHT service on all peers */
732   for (cnt = 0; cnt < num_peers; cnt++)
733   {
734     a_ctx[cnt].peer = peers[cnt];
735     a_ctx[cnt].op = GNUNET_TESTBED_peer_manage_service (&a_ctx[cnt],
736                                                         peers[cnt],
737                                                         "dht",
738                                                         &service_started,
739                                                         &a_ctx[cnt],
740                                                         1);
741   }
742 }
743
744
745 /**
746  * Main function that will be run by the scheduler.
747  *
748  * @param cls closure
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
752  */
753 static void
754 run (void *cls, char *const *args, const char *cfgfile,
755      const struct GNUNET_CONFIGURATION_Handle *config)
756 {
757   uint64_t event_mask;
758
759   if (0 == num_peers)
760   {
761     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Exiting as the number of peers is %u\n"),
762                 num_peers);
763     return;
764   }
765   cfg = GNUNET_CONFIGURATION_dup (config);
766   event_mask = 0;
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,
770                                 NULL);
771 }
772
773
774 /**
775  * Main function.
776  *
777  * @return 0 on success
778  */
779 int
780 main (int argc, char *const *argv)
781 {
782   int rc;
783
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
801   };
802
803   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
804     return 2;
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 */
808   rc = 0;
809   if (GNUNET_OK !=
810       GNUNET_PROGRAM_run (argc, argv, "dht-profiler",
811                           gettext_noop
812                           ("Measure quality and performance of the DHT service."),
813                           options, &run, NULL))
814     rc = 1;
815   return rc;
816 }