95528b3f9409a132f44e53b09a49c8ade5bd5ddc
[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  * Total number of messages sent by peer. 
220  */
221 static uint64_t outgoing_bandwidth;
222
223 /**
224  * Total number of messages received by peer.
225  */
226 static uint64_t incoming_bandwidth;
227
228 /**
229  * Average number of hops taken to do put.
230  */
231 static unsigned int average_put_path_length;
232
233 /**
234  * Average number of hops taken to do get. 
235  */
236 static unsigned int average_get_path_length;
237
238 static unsigned int total_put_path_length;
239
240 static unsigned int total_get_path_length;
241 /**
242  * Shutdown task.  Cleanup all resources and operations.
243  *
244  * @param cls NULL
245  * @param tc scheduler task context
246  */
247 static void
248 do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
249 {
250   struct ActiveContext *ac;
251   unsigned int cnt;
252
253   if (NULL != a_ctx)
254   {
255     for (cnt=0; cnt < num_peers; cnt++)
256     {
257       if (NULL != a_ctx[cnt].op)
258         GNUNET_TESTBED_operation_done (a_ctx[cnt].op);
259
260       /* Cleanup active context if this peer is an active peer */
261       ac = a_ctx[cnt].ac;
262       if (NULL == ac)
263         continue;
264       if (GNUNET_SCHEDULER_NO_TASK != ac->delay_task)
265         GNUNET_SCHEDULER_cancel (ac->delay_task);
266       if (NULL != ac->put_data)
267         GNUNET_free (ac->put_data);
268       if (NULL != ac->dht_put)
269         GNUNET_DHT_put_cancel (ac->dht_put);
270       if (NULL != ac->dht_get)
271         GNUNET_DHT_get_stop (ac->dht_get);
272     }
273     GNUNET_free (a_ctx);
274     a_ctx = NULL;
275   }
276   if(NULL != stats_op)
277     GNUNET_TESTBED_operation_done (stats_op);
278   stats_op = NULL;
279   GNUNET_free_non_null (a_ac);
280 }
281
282
283 /**
284  * Stats callback. Finish the stats testbed operation and when all stats have
285  * been iterated, shutdown the test.
286  *
287  * @param cls closure
288  * @param op the operation that has been finished
289  * @param emsg error message in case the operation has failed; will be NULL if
290  *          operation has executed successfully.
291  */
292 static void
293 bandwidth_stats_cont (void *cls, 
294                       struct GNUNET_TESTBED_Operation *op, 
295                       const char *emsg)
296 {
297   INFO ("# Outgoing bandwidth: %u\n", outgoing_bandwidth);
298   INFO ("# Incoming bandwidth: %u\n", incoming_bandwidth);
299   GNUNET_SCHEDULER_shutdown (); 
300 }
301
302
303 /**
304  * Process statistic values.
305  *
306  * @param cls closure
307  * @param peer the peer the statistic belong to
308  * @param subsystem name of subsystem that created the statistic
309  * @param name the name of the datum
310  * @param value the current value
311  * @param is_persistent GNUNET_YES if the value is persistent, GNUNET_NO if not
312  * @return GNUNET_OK to continue, GNUNET_SYSERR to abort iteration
313  */
314 static int
315 bandwidth_stats_iterator (void *cls, 
316                           const struct GNUNET_TESTBED_Peer *peer,
317                           const char *subsystem, 
318                           const char *name,
319                           uint64_t value, 
320                           int is_persistent)
321 {
322    static const char *s_sent = "# Bytes transmitted to other peers";
323    static const char *s_recv = "# Bytes received from other peers";
324
325    if (0 == strncmp (s_sent, name, strlen (s_sent)))
326      outgoing_bandwidth = outgoing_bandwidth + value;
327    else if (0 == strncmp(s_recv, name, strlen (s_recv)))
328      incoming_bandwidth = incoming_bandwidth + value;
329    else
330      return GNUNET_OK;
331    DEBUG ("Bandwith - Out: %lu; In: %lu\n",
332           (unsigned long) outgoing_bandwidth,
333           (unsigned long) incoming_bandwidth);
334    return GNUNET_OK;
335 }
336
337
338 static void
339 summarize ()
340 {
341   INFO ("# PUTS made: %u\n", n_puts);
342   INFO ("# PUTS succeeded: %u\n", n_puts_ok);
343   INFO ("# PUTS failed: %u\n", n_puts_fail);
344   INFO ("# GETS made: %u\n", n_gets);
345   INFO ("# GETS succeeded: %u\n", n_gets_ok);
346   INFO ("# GETS failed: %u\n", n_gets_fail);
347   INFO ("# average_put_path_length: %u\n", average_put_path_length);
348   INFO ("# average_get_path_length: %u\n", average_get_path_length);
349   
350   if (NULL == testbed_handles)
351   {
352     INFO ("No peers found\n");
353     return;
354   }
355   /* Collect Stats*/
356   stats_op = GNUNET_TESTBED_get_statistics (n_active, testbed_handles,
357                                             "dht", NULL,
358                                             bandwidth_stats_iterator, 
359                                             bandwidth_stats_cont, NULL);
360 }
361
362
363 /**
364  * Task to cancel DHT GET.
365  *
366  * @param cls NULL
367  * @param tc scheduler task context
368  */
369 static void
370 cancel_get (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
371 {
372   struct ActiveContext *ac = cls;
373
374   ac->delay_task = GNUNET_SCHEDULER_NO_TASK;
375   GNUNET_assert (NULL != ac->dht_get);
376   GNUNET_DHT_get_stop (ac->dht_get);
377   ac->dht_get = NULL;
378   n_gets_fail++;
379
380   /* If profiling is complete, summarize */
381   if (n_active == n_gets_fail + n_gets_ok)
382     summarize ();
383
384 }
385
386
387 /**
388  * Iterator called on each result obtained for a DHT
389  * operation that expects a reply
390  *
391  * @param cls closure
392  * @param exp when will this value expire
393  * @param key key of the result
394  * @param get_path peers on reply path (or NULL if not recorded)
395  *                 [0] = datastore's first neighbor, [length - 1] = local peer
396  * @param get_path_length number of entries in @a get_path
397  * @param put_path peers on the PUT path (or NULL if not recorded)
398  *                 [0] = origin, [length - 1] = datastore
399  * @param put_path_length number of entries in @a put_path
400  * @param type type of the result
401  * @param size number of bytes in @a data
402  * @param data pointer to the result data
403  */
404 static void
405 get_iter (void *cls,
406           struct GNUNET_TIME_Absolute exp,
407           const struct GNUNET_HashCode *key,
408           const struct GNUNET_PeerIdentity *get_path,
409           unsigned int get_path_length,
410           const struct GNUNET_PeerIdentity *put_path,
411           unsigned int put_path_length,
412           enum GNUNET_BLOCK_Type type,
413           size_t size, const void *data)
414 {
415   struct ActiveContext *ac = cls;
416   struct ActiveContext *get_ac = ac->get_ac;
417
418   /* Check the keys of put and get match or not. */
419   GNUNET_assert (0 == memcmp (key, &get_ac->hash, sizeof (struct GNUNET_HashCode)));
420   /* we found the data we are looking for */
421   DEBUG ("We found a GET request; %u remaining\n", n_gets - (n_gets_fail + n_gets_ok));
422   n_gets_ok++;
423   get_ac->nrefs--;
424   GNUNET_DHT_get_stop (ac->dht_get);
425   ac->dht_get = NULL;
426   GNUNET_SCHEDULER_cancel (ac->delay_task);
427   ac->delay_task = GNUNET_SCHEDULER_NO_TASK;
428   
429   total_put_path_length = total_put_path_length + put_path_length;
430   total_get_path_length = total_get_path_length + get_path_length;
431   
432   /* Summarize if profiling is complete */
433   if (n_active == n_gets_fail + n_gets_ok)
434   {
435     average_put_path_length = total_put_path_length/n_active;
436     average_get_path_length = total_get_path_length/n_active;
437     summarize ();
438   }
439 }
440
441
442 /**
443  * Task to do DHT GETs
444  *
445  * @param cls the active context
446  * @param tc the scheduler task context
447  */
448 static void
449 delayed_get (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
450 {
451   struct ActiveContext *ac = cls;
452   struct ActiveContext *get_ac;
453   unsigned int r;
454
455   ac->delay_task = GNUNET_SCHEDULER_NO_TASK;
456   get_ac = NULL;
457   while (1)
458   {
459     r = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, n_active);
460     get_ac = &a_ac[r];
461     if (NULL != get_ac->put_data)
462       break;
463   }
464   get_ac->nrefs++;
465   ac->get_ac = get_ac;
466   DEBUG ("Doing a DHT GET for data of size %u\n", get_ac->put_data_size);
467   ac->dht_get = GNUNET_DHT_get_start (ac->dht,
468                                       GNUNET_BLOCK_TYPE_TEST,
469                                       &get_ac->hash,
470                                       1, /* replication level */
471                                       GNUNET_DHT_RO_NONE,
472                                       NULL, 0, /* extended query and size */
473                                       get_iter, ac); /* GET iterator and closure
474                                                         */
475   n_gets++;
476
477   /* schedule the timeout task for GET */
478   ac->delay_task = GNUNET_SCHEDULER_add_delayed (timeout, &cancel_get, ac);
479 }
480
481
482 /**
483  * Queue up a delayed task for doing DHT GET
484  *
485  * @param cls the active context
486  * @param success #GNUNET_OK if the PUT was transmitted,
487  *                #GNUNET_NO on timeout,
488  *                #GNUNET_SYSERR on disconnect from service
489  *                after the PUT message was transmitted
490  *                (so we don't know if it was received or not)
491  */
492 static void
493 put_cont (void *cls, int success)
494 {
495   struct ActiveContext *ac = cls;
496
497   ac->dht_put = NULL;
498   if (success)
499     n_puts_ok++;
500   else
501     n_puts_fail++;
502   ac->delay_task = GNUNET_SCHEDULER_add_delayed (delay, &delayed_get, ac);
503 }
504
505
506 /**
507  * Task to do DHT PUTS
508  *
509  * @param cls the active context
510  * @param tc the scheduler task context
511  */
512 static void
513 delayed_put (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
514 {
515   struct ActiveContext *ac = cls;
516
517   ac->delay_task = GNUNET_SCHEDULER_NO_TASK;
518   /* Generate and DHT PUT some random data */
519   ac->put_data_size = 16;       /* minimum */
520   ac->put_data_size += GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
521                                                  (63*1024));
522   ac->put_data = GNUNET_malloc (ac->put_data_size);
523   GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
524                               ac->put_data, ac->put_data_size);
525   GNUNET_CRYPTO_hash (ac->put_data, ac->put_data_size, &ac->hash);
526   DEBUG ("Doing a DHT PUT with data of size %u\n", ac->put_data_size);
527   ac->dht_put = GNUNET_DHT_put (ac->dht, &ac->hash,
528                                 replication,
529                                 GNUNET_DHT_RO_NONE,
530                                 GNUNET_BLOCK_TYPE_TEST,
531                                 ac->put_data_size,
532                                 ac->put_data,
533                                 GNUNET_TIME_UNIT_FOREVER_ABS, /* expiration time */
534                                 timeout,                      /* PUT timeout */
535                                 put_cont, ac);                /* continuation and its closure */
536   n_puts++;
537 }
538
539
540 /**
541  * Connection to DHT has been established.  Call the delay task.
542  *
543  * @param cls the active context
544  * @param op the operation that has been finished
545  * @param ca_result the service handle returned from GNUNET_TESTBED_ConnectAdapter()
546  * @param emsg error message in case the operation has failed; will be NULL if
547  *          operation has executed successfully.
548  */
549 static void
550 dht_connected (void *cls,
551                struct GNUNET_TESTBED_Operation *op,
552                void *ca_result,
553                const char *emsg)
554 {
555   struct ActiveContext *ac = cls;
556   struct Context *ctx = ac->ctx;
557
558   GNUNET_assert (NULL != ctx);
559   GNUNET_assert (NULL != ctx->op);
560   GNUNET_assert (ctx->op == op);
561   ac->dht = (struct GNUNET_DHT_Handle *) ca_result;
562   if (NULL != emsg)
563   {
564     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Connection to DHT service failed: %s\n", emsg);
565     GNUNET_TESTBED_operation_done (ctx->op); /* Calls dht_disconnect() */
566     ctx->op = NULL;
567     return;
568   }
569   ac->delay_task = GNUNET_SCHEDULER_add_delayed (delay, &delayed_put, ac);
570 }
571
572
573 /**
574  * Connect to DHT service and return the DHT client handler
575  *
576  * @param cls the active context
577  * @param cfg configuration of the peer to connect to; will be available until
578  *          GNUNET_TESTBED_operation_done() is called on the operation returned
579  *          from GNUNET_TESTBED_service_connect()
580  * @return service handle to return in 'op_result', NULL on error
581  */
582 static void *
583 dht_connect (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg)
584 {
585   n_dht++;
586   return GNUNET_DHT_connect (cfg, 10);
587 }
588
589
590 /**
591  * Adapter function called to destroy a connection to
592  * a service.
593  *
594  * @param cls the active context
595  * @param op_result service handle returned from the connect adapter
596  */
597 static void
598 dht_disconnect (void *cls, void *op_result)
599 {
600   struct ActiveContext *ac = cls;
601
602   GNUNET_assert (NULL != ac->dht);
603   GNUNET_assert (ac->dht == op_result);
604   GNUNET_DHT_disconnect (ac->dht);
605   n_dht--;
606   if (0 == n_dht)
607     GNUNET_SCHEDULER_shutdown ();
608 }
609
610
611 /**
612  * Callback called when DHT service on the peer is started
613  *
614  * @param cls the context
615  * @param op the operation that has been finished
616  * @param emsg error message in case the operation has failed; will be NULL if
617  *          operation has executed successfully.
618  */
619 static void
620 service_started (void *cls,
621                  struct GNUNET_TESTBED_Operation *op,
622                  const char *emsg)
623 {
624   struct Context *ctx = cls;
625
626   GNUNET_assert (NULL != ctx);
627   GNUNET_assert (NULL != ctx->op);
628   GNUNET_TESTBED_operation_done (ctx->op);
629   ctx->op = NULL;
630   if (NULL == ctx->ac)
631     return;
632   /* FIXME: connect to the DHT service and wait before starting a PUT */
633   ctx->op = GNUNET_TESTBED_service_connect (ctx, ctx->peer,
634                                             "dht",
635                                             &dht_connected, ctx->ac,
636                                             &dht_connect,
637                                             &dht_disconnect,
638                                             ctx->ac);
639 }
640
641
642 /**
643  * Signature of a main function for a testcase.
644  *
645  * @param cls closure
646  * @param h the run handle
647  * @param num_peers number of peers in 'peers'
648  * @param peers handle to peers run in the testbed
649  * @param links_succeeded the number of overlay link connection attempts that
650  *          succeeded
651  * @param links_failed the number of overlay link
652  */
653 static void
654 test_run (void *cls,
655           struct GNUNET_TESTBED_RunHandle *h,
656           unsigned int num_peers, struct GNUNET_TESTBED_Peer **peers,
657           unsigned int links_succeeded,
658           unsigned int links_failed)
659 {
660   unsigned int cnt;
661   unsigned int ac_cnt;
662   
663   testbed_handles = peers;  
664   if (NULL == peers)
665   {
666     /* exit */
667     GNUNET_assert (0);
668   }
669   INFO ("%u peers started\n", num_peers);
670   a_ctx = GNUNET_malloc (sizeof (struct Context) * num_peers);
671
672   /* select the peers which actively participate in profiling */
673   n_active = num_peers * PUT_PROBABILITY / 100;
674   if (0 == n_active)
675   {
676     GNUNET_SCHEDULER_shutdown ();
677     GNUNET_free (a_ctx);
678     return;
679   }
680   a_ac = GNUNET_malloc (n_active * sizeof (struct ActiveContext));
681   ac_cnt = 0;
682   for (cnt = 0; cnt < num_peers && ac_cnt < n_active; cnt++)
683   {
684     if (GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 100) >=
685         PUT_PROBABILITY)
686       continue;
687     a_ctx[cnt].ac = &a_ac[ac_cnt];
688     a_ac[ac_cnt].ctx = &a_ctx[cnt];
689     ac_cnt++;
690   }
691   n_active = ac_cnt;
692   a_ac = GNUNET_realloc (a_ac, n_active * sizeof (struct ActiveContext));
693   INFO ("Active peers: %u\n", n_active);
694
695   /* start DHT service on all peers */
696   for (cnt = 0; cnt < num_peers; cnt++)
697   {
698     a_ctx[cnt].peer = peers[cnt];
699     a_ctx[cnt].op = GNUNET_TESTBED_peer_manage_service (&a_ctx[cnt],
700                                                         peers[cnt],
701                                                         "dht",
702                                                         &service_started,
703                                                         &a_ctx[cnt],
704                                                         1);
705   }
706 }
707
708
709 /**
710  * Main function that will be run by the scheduler.
711  *
712  * @param cls closure
713  * @param args remaining command-line arguments
714  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
715  * @param config configuration
716  */
717 static void
718 run (void *cls, char *const *args, const char *cfgfile,
719      const struct GNUNET_CONFIGURATION_Handle *config)
720 {
721   uint64_t event_mask;
722
723   if (0 == num_peers)
724   {
725     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Exiting as the number of peers is %u\n"),
726                 num_peers);
727     return;
728   }
729   cfg = GNUNET_CONFIGURATION_dup (config);
730   event_mask = 0;
731   GNUNET_TESTBED_run (hosts_file, cfg, num_peers, event_mask, NULL,
732                       NULL, &test_run, NULL);
733   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &do_shutdown,
734                                 NULL);
735 }
736
737
738 /**
739  * Main function.
740  *
741  * @return 0 on success
742  */
743 int
744 main (int argc, char *const *argv)
745 {
746   int rc;
747
748   static struct GNUNET_GETOPT_CommandLineOption options[] = {
749     {'n', "peers", "COUNT",
750      gettext_noop ("number of peers to start"),
751      1, &GNUNET_GETOPT_set_uint, &num_peers},
752     {'H', "hosts", "FILENAME",
753      gettext_noop ("name of the file with the login information for the testbed"),
754      1, &GNUNET_GETOPT_set_string, &hosts_file},
755     {'d', "delay", "DELAY",
756      gettext_noop ("delay for starting DHT PUT and GET"),
757      1, &GNUNET_GETOPT_set_relative_time, &delay},
758     {'r', "replication", "DEGREE",
759      gettext_noop ("replication degree for DHT PUTs"),
760      1, &GNUNET_GETOPT_set_uint, &replication},
761     {'t', "timeout", "TIMEOUT",
762      gettext_noop ("timeout for DHT PUT and GET requests"),
763      1, &GNUNET_GETOPT_set_relative_time, &timeout},
764     GNUNET_GETOPT_OPTION_END
765   };
766
767   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
768     return 2;
769   delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30); /* default delay */
770   timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1); /* default timeout */
771   replication = 1;      /* default replication */
772   rc = 0;
773   if (GNUNET_OK !=
774       GNUNET_PROGRAM_run (argc, argv, "dht-profiler",
775                           gettext_noop
776                           ("Measure quality and performance of the DHT service."),
777                           options, &run, NULL))
778     rc = 1;
779   return rc;
780 }