src: for every AGPL3.0 file, add SPDX identifier.
[oweals/gnunet.git] / src / dht / gnunet_dht_profiler.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2014, 2018 GNUnet e.V.
4
5      GNUnet is free software: you can redistribute it and/or modify it
6      under the terms of the GNU Affero General Public License as published
7      by the Free Software Foundation, either version 3 of the License,
8      or (at your 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      Affero General Public License for more details.
14     
15      You should have received a copy of the GNU Affero General Public License
16      along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18      SPDX-License-Identifier: AGPL3.0-or-later
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 #include "gnunet_constants.h"
32
33
34 #define MESSAGE(...)                                       \
35   GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, __VA_ARGS__)
36
37 #define DEBUG(...)                                           \
38   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, __VA_ARGS__)
39
40 /**
41  * Number of peers which should perform a PUT out of 100 peers
42  */
43 static unsigned int put_probability = 100;
44
45 /**
46  * Configuration
47  */
48 static const struct GNUNET_CONFIGURATION_Handle *cfg;
49
50 /**
51  * Name of the file with the hosts to run the test over
52  */
53 static char *hosts_file;
54
55 /**
56  * Context for a peer which actively does DHT PUT/GET
57  */
58 struct ActiveContext;
59
60 /**
61  * Context to hold data of peer
62  */
63 struct Context
64 {
65   /**
66    * The testbed peer this context belongs to
67    */
68   struct GNUNET_TESTBED_Peer *peer;
69
70   /**
71    * Testbed operation acting on this peer
72    */
73   struct GNUNET_TESTBED_Operation *op;
74
75   /**
76    * Active context; NULL if this peer is not an active peer
77    */
78   struct ActiveContext *ac;
79
80 };
81
82
83 /**
84  * Context for a peer which actively does DHT PUT/GET
85  */
86 struct ActiveContext
87 {
88   /**
89    * The linked peer context
90    */
91   struct Context *ctx;
92
93   /**
94    * Handler to the DHT service
95    */
96   struct GNUNET_DHT_Handle *dht;
97
98   /**
99    * The active context used for our DHT GET
100    */
101   struct ActiveContext *get_ac;
102
103   /**
104    * The put handle
105    */
106   struct GNUNET_DHT_PutHandle *dht_put;
107
108   /**
109    * The get handle
110    */
111   struct GNUNET_DHT_GetHandle *dht_get;
112
113   /**
114    * The hashes of the values stored via this activity context.
115    * Array of length #num_puts_per_peer.
116    */
117   struct GNUNET_HashCode *hash;
118
119   /**
120    * Delay task
121    */
122   struct GNUNET_SCHEDULER_Task *delay_task;
123
124   /**
125    * How many puts should we still issue?
126    */
127   unsigned int put_count;
128
129   /**
130    * The number of peers currently doing GET on our data
131    */
132   uint16_t nrefs;
133 };
134
135
136 /**
137  * An array of contexts.  The size of this array should be equal to @a num_peers
138  */
139 static struct Context *a_ctx;
140
141 /**
142  * Array of active peers
143  */
144 static struct ActiveContext *a_ac;
145
146 /**
147  * The delay between rounds for collecting statistics
148  */
149 static struct GNUNET_TIME_Relative delay_stats;
150
151 /**
152  * The delay to start puts.
153  */
154 static struct GNUNET_TIME_Relative delay_put;
155
156 /**
157  * The delay to start puts.
158  */
159 static struct GNUNET_TIME_Relative delay_get;
160
161 /**
162  * The timeout for GET and PUT
163  */
164 static struct GNUNET_TIME_Relative timeout;
165
166 /**
167  * Number of peers
168  */
169 static unsigned int num_peers;
170
171 /**
172  * Number of active peers
173  */
174 static unsigned int n_active;
175
176 /**
177  * Number of DHT service connections we currently have
178  */
179 static unsigned int n_dht;
180
181 /**
182  * Number of DHT PUTs made
183  */
184 static unsigned long long n_puts;
185
186 /**
187  * Number of DHT PUTs to be made per peer.
188  */
189 static unsigned int num_puts_per_peer = 1;
190
191 /**
192  * Number of DHT PUTs succeeded
193  */
194 static unsigned long long n_puts_ok;
195
196 /**
197  * Number of DHT GETs made
198  */
199 static unsigned int n_gets;
200
201 /**
202  * Number of DHT GETs succeeded
203  */
204 static unsigned int n_gets_ok;
205
206 /**
207  * Number of DHT GETs succeeded
208  */
209 static unsigned int n_gets_fail;
210
211 /**
212  * Replication degree
213  */
214 static unsigned int replication;
215
216 /**
217  * Testbed Operation (to get stats).
218  */
219 static struct GNUNET_TESTBED_Operation *bandwidth_stats_op;
220
221 /**
222  * Testbed peer handles.
223  */
224 static struct GNUNET_TESTBED_Peer **testbed_handles;
225
226 /**
227  * Total number of messages sent by peer.
228  */
229 static uint64_t outgoing_bandwidth;
230
231 /**
232  * Total number of messages received by peer.
233  */
234 static uint64_t incoming_bandwidth;
235
236 /**
237  * Average number of hops taken to do put.
238  */
239 static double average_put_path_length;
240
241 /**
242  * Average number of hops taken to do get.
243  */
244 static double average_get_path_length;
245
246 /**
247  * Total put path length across all peers.
248  */
249 static unsigned int total_put_path_length;
250
251 /**
252  * Total get path length across all peers.
253  */
254 static unsigned int total_get_path_length;
255
256 /**
257  * Counter to keep track of peers added to peer_context lists.
258  */
259 static int peers_started = 0;
260
261 /**
262  * Should we do a PUT (mode = 0) or GET (mode = 1);
263  */
264 static enum
265 {
266   MODE_PUT = 0,
267
268   MODE_GET = 1
269 } mode;
270
271
272 /**
273  * Are we shutting down
274  */
275 static int in_shutdown = 0;
276
277
278 /**
279  * Connect to DHT services of active peers
280  */
281 static void
282 start_profiling (void);
283
284
285 /**
286  * Shutdown task.  Cleanup all resources and operations.
287  *
288  * @param cls NULL
289  */
290 static void
291 do_shutdown (void *cls)
292 {
293   struct ActiveContext *ac;
294
295   in_shutdown = GNUNET_YES;
296   if (NULL != a_ctx)
297   {
298     for (unsigned int cnt=0; cnt < num_peers; cnt++)
299     {
300       /* Cleanup active context if this peer is an active peer */
301       ac = a_ctx[cnt].ac;
302       if (NULL != ac)
303       {
304         if (NULL != ac->delay_task)
305           GNUNET_SCHEDULER_cancel (ac->delay_task);
306         if (NULL != ac->hash)
307           free (ac->hash);
308         if (NULL != ac->dht_put)
309           GNUNET_DHT_put_cancel (ac->dht_put);
310         if (NULL != ac->dht_get)
311           GNUNET_DHT_get_stop (ac->dht_get);
312       }
313       /* Cleanup testbed operation handle at the last as this operation may
314          contain service connection to DHT */
315       if (NULL != a_ctx[cnt].op)
316         GNUNET_TESTBED_operation_done (a_ctx[cnt].op);
317     }
318     GNUNET_free (a_ctx);
319     a_ctx = NULL;
320   }
321   //FIXME: Should we collect stats only for put/get not for other messages.
322   if (NULL != bandwidth_stats_op)
323   {
324     GNUNET_TESTBED_operation_done (bandwidth_stats_op);
325     bandwidth_stats_op = NULL;
326   }
327   GNUNET_free_non_null (a_ac);
328 }
329
330
331 /**
332  * Stats callback. Finish the stats testbed operation and when all stats have
333  * been iterated, shutdown the test.
334  *
335  * @param cls closure
336  * @param op the operation that has been finished
337  * @param emsg error message in case the operation has failed; will be NULL if
338  *          operation has executed successfully.
339  */
340 static void
341 bandwidth_stats_cont (void *cls,
342                       struct GNUNET_TESTBED_Operation *op,
343                       const char *emsg)
344 {
345   MESSAGE ("# Outgoing (core) bandwidth: %llu bytes\n",
346            (unsigned long long) outgoing_bandwidth);
347   MESSAGE ("# Incoming (core) bandwidth: %llu bytes\n",
348            (unsigned long long) incoming_bandwidth);
349   fprintf (stderr,
350            "Benchmark done. Collect data via gnunet-statistics, then press ENTER to exit.\n");
351   (void) getchar ();
352   GNUNET_SCHEDULER_shutdown ();
353 }
354
355
356 /**
357  * Process statistic values.
358  *
359  * @param cls closure
360  * @param peer the peer the statistic belong to
361  * @param subsystem name of subsystem that created the statistic
362  * @param name the name of the datum
363  * @param value the current value
364  * @param is_persistent #GNUNET_YES if the value is persistent, #GNUNET_NO if not
365  * @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration
366  */
367 static int
368 bandwidth_stats_iterator (void *cls,
369                           const struct GNUNET_TESTBED_Peer *peer,
370                           const char *subsystem,
371                           const char *name,
372                           uint64_t value,
373                           int is_persistent)
374 {
375   static const char *s_sent = "# bytes encrypted";
376   static const char *s_recv = "# bytes decrypted";
377
378   if (0 == strncmp (s_sent, name, strlen (s_sent)))
379     outgoing_bandwidth = outgoing_bandwidth + value;
380   else if (0 == strncmp(s_recv, name, strlen (s_recv)))
381     incoming_bandwidth = incoming_bandwidth + value;
382   return GNUNET_OK;
383 }
384
385
386 static void
387 summarize ()
388 {
389   MESSAGE ("# PUTS started: %llu\n",
390         n_puts);
391   MESSAGE ("# PUTS succeeded: %llu\n",
392         n_puts_ok);
393   MESSAGE ("# GETS made: %u\n",
394         n_gets);
395   MESSAGE ("# GETS succeeded: %u\n",
396         n_gets_ok);
397   MESSAGE ("# GETS failed: %u\n",
398         n_gets_fail);
399   MESSAGE ("# average_put_path_length: %f\n",
400         average_put_path_length);
401   MESSAGE ("# average_get_path_length: %f\n",
402         average_get_path_length);
403
404   if (NULL == testbed_handles)
405   {
406     MESSAGE ("No peers found\n");
407     return;
408   }
409   /* Collect Stats*/
410   bandwidth_stats_op = GNUNET_TESTBED_get_statistics (n_active,
411                                                       testbed_handles,
412                                                       "core",
413                                                       NULL,
414                                                       &bandwidth_stats_iterator,
415                                                       &bandwidth_stats_cont,
416                                                       NULL);
417 }
418
419
420 /**
421  * Task to cancel DHT GET.
422  *
423  * @param cls NULL
424  */
425 static void
426 cancel_get (void *cls)
427 {
428   struct ActiveContext *ac = cls;
429   struct Context *ctx = ac->ctx;
430
431   ac->delay_task = NULL;
432   GNUNET_assert (NULL != ac->dht_get);
433   GNUNET_DHT_get_stop (ac->dht_get);
434   ac->dht_get = NULL;
435   n_gets_fail++;
436   GNUNET_assert (NULL != ctx->op);
437   GNUNET_TESTBED_operation_done (ctx->op);
438   ctx->op = NULL;
439
440   /* If profiling is complete, summarize */
441   if (n_active == n_gets_fail + n_gets_ok)
442   {
443     average_put_path_length = (double)total_put_path_length/(double)n_active;
444     average_get_path_length = (double)total_get_path_length/(double )n_gets_ok;
445     summarize ();
446   }
447 }
448
449
450 /**
451  * Iterator called on each result obtained for a DHT
452  * operation that expects a reply
453  *
454  * @param cls closure
455  * @param exp when will this value expire
456  * @param key key of the result
457  * @param get_path peers on reply path (or NULL if not recorded)
458  *                 [0] = datastore's first neighbor, [length - 1] = local peer
459  * @param get_path_length number of entries in @a get_path
460  * @param put_path peers on the PUT path (or NULL if not recorded)
461  *                 [0] = origin, [length - 1] = datastore
462  * @param put_path_length number of entries in @a put_path
463  * @param type type of the result
464  * @param size number of bytes in @a data
465  * @param data pointer to the result data
466  */
467 static void
468 get_iter (void *cls,
469           struct GNUNET_TIME_Absolute exp,
470           const struct GNUNET_HashCode *key,
471           const struct GNUNET_PeerIdentity *get_path,
472           unsigned int get_path_length,
473           const struct GNUNET_PeerIdentity *put_path,
474           unsigned int put_path_length,
475           enum GNUNET_BLOCK_Type type,
476           size_t size, const void *data)
477 {
478   struct ActiveContext *ac = cls;
479   struct ActiveContext *get_ac = ac->get_ac;
480   struct Context *ctx = ac->ctx;
481
482   /* we found the data we are looking for */
483   DEBUG ("We found a GET request; %u remaining\n",
484          n_gets - (n_gets_fail + n_gets_ok)); //FIXME: It always prints 1.
485   n_gets_ok++;
486   get_ac->nrefs--;
487   GNUNET_DHT_get_stop (ac->dht_get);
488   ac->dht_get = NULL;
489   if (ac->delay_task != NULL)
490     GNUNET_SCHEDULER_cancel (ac->delay_task);
491   ac->delay_task = NULL;
492   GNUNET_assert (NULL != ctx->op);
493   GNUNET_TESTBED_operation_done (ctx->op);
494   ctx->op = NULL;
495
496   total_put_path_length = total_put_path_length + (double)put_path_length;
497   total_get_path_length = total_get_path_length + (double)get_path_length;
498   DEBUG ("total_put_path_length = %u,put_path \n",
499          total_put_path_length);
500   /* Summarize if profiling is complete */
501   if (n_active == n_gets_fail + n_gets_ok)
502   {
503     average_put_path_length = (double)total_put_path_length/(double)n_active;
504     average_get_path_length = (double)total_get_path_length/(double )n_gets_ok;
505     summarize ();
506   }
507 }
508
509
510 /**
511  * Task to do DHT GETs
512  *
513  * @param cls the active context
514  */
515 static void
516 delayed_get (void *cls)
517 {
518   struct ActiveContext *ac = cls;
519   struct ActiveContext *get_ac;
520   unsigned int r;
521
522   ac->delay_task = NULL;
523   get_ac = NULL;
524   while (1)
525   {
526     r = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
527                                   n_active);
528     get_ac = &a_ac[r];
529     if (NULL != get_ac->hash)
530       break;
531   }
532   get_ac->nrefs++;
533   ac->get_ac = get_ac;
534   r = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
535                                 num_puts_per_peer);
536   DEBUG ("GET_REQUEST_START key %s \n",
537          GNUNET_h2s(&get_ac->hash[r]));
538   ac->dht_get = GNUNET_DHT_get_start (ac->dht,
539                                       GNUNET_BLOCK_TYPE_TEST,
540                                       &get_ac->hash[r],
541                                       1, /* replication level */
542                                       GNUNET_DHT_RO_NONE,
543                                       NULL,
544                                       0, /* extended query and size */
545                                       &get_iter,
546                                       ac); /* GET iterator and closure */
547   n_gets++;
548
549   /* schedule the timeout task for GET */
550   ac->delay_task = GNUNET_SCHEDULER_add_delayed (timeout,
551                                                  &cancel_get,
552                                                  ac);
553 }
554
555
556 /**
557  * Task to do DHT PUTs.  If the "put_count" hits zero,
558  * we stop the TESTBED operation (connection to DHT)
559  * so that others PUTs have a chance.
560  *
561  * @param cls the active context
562  */
563 static void
564 delayed_put (void *cls);
565
566
567 /**
568  * Conclude individual PUT operation, schedule the
569  * next one.
570  *
571  * @param cls the active context
572  */
573 static void
574 put_cont (void *cls)
575 {
576   struct ActiveContext *ac = cls;
577
578   ac->dht_put = NULL;
579   n_puts_ok++;
580   ac->delay_task = GNUNET_SCHEDULER_add_now (&delayed_put,
581                                              ac);
582 }
583
584
585 /**
586  * Task to do DHT PUTs.  If the "put_count" hits zero,
587  * we stop the TESTBED operation (connection to DHT)
588  * so that others PUTs have a chance.
589  *
590  * @param cls the active context
591  */
592 static void
593 delayed_put (void *cls)
594 {
595   struct ActiveContext *ac = cls;
596   char block[65536];
597   size_t block_size;
598
599   ac->delay_task = NULL;
600   if (0 == ac->put_count)
601   {
602     struct Context *ctx = ac->ctx;
603     struct GNUNET_TESTBED_Operation *op;
604
605     GNUNET_assert (NULL != ctx);
606     op = ctx->op;
607     ctx->op = NULL;
608     GNUNET_TESTBED_operation_done (op);
609     return;
610   }
611
612
613   /* Generate and DHT PUT some random data */
614   block_size = 16; /* minimum */
615   /* make random payload, reserve 512 - 16 bytes for DHT headers */
616   block_size += GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
617                                           GNUNET_CONSTANTS_MAX_ENCRYPTED_MESSAGE_SIZE - 512);
618   GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
619                               block,
620                               block_size);
621   ac->put_count--;
622   GNUNET_CRYPTO_hash (block,
623                       block_size,
624                       &ac->hash[ac->put_count]);
625   DEBUG ("PUT_REQUEST_START key %s\n",
626          GNUNET_h2s (&ac->hash[ac->put_count]));
627   ac->dht_put = GNUNET_DHT_put (ac->dht,
628                                 &ac->hash[ac->put_count],
629                                 replication,
630                                 GNUNET_DHT_RO_RECORD_ROUTE,
631                                 GNUNET_BLOCK_TYPE_TEST,
632                                 block_size,
633                                 block,
634                                 GNUNET_TIME_UNIT_FOREVER_ABS, /* expiration time */
635                                 &put_cont,
636                                 ac);                /* continuation and its closure */
637   n_puts++;
638 }
639
640
641 /**
642  * Connection to DHT has been established.  Call the delay task.
643  *
644  * @param cls the active context
645  * @param op the operation that has been finished
646  * @param ca_result the service handle returned from GNUNET_TESTBED_ConnectAdapter()
647  * @param emsg error message in case the operation has failed; will be NULL if
648  *          operation has executed successfully.
649  */
650 static void
651 dht_connected (void *cls,
652                struct GNUNET_TESTBED_Operation *op,
653                void *ca_result,
654                const char *emsg)
655 {
656   struct ActiveContext *ac = cls;
657   struct Context *ctx = ac->ctx;
658
659   GNUNET_assert (NULL != ctx); //FIXME: Fails
660   GNUNET_assert (NULL != ctx->op);
661   GNUNET_assert (ctx->op == op);
662   ac->dht = (struct GNUNET_DHT_Handle *) ca_result;
663   if (NULL != emsg)
664   {
665     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
666                 "Connection to DHT service failed: %s\n",
667                 emsg);
668     GNUNET_TESTBED_operation_done (ctx->op); /* Calls dht_disconnect() */
669     ctx->op = NULL;
670     return;
671   }
672   switch (mode)
673   {
674   case MODE_PUT:
675     {
676       struct GNUNET_TIME_Relative peer_delay_put;
677
678       peer_delay_put.rel_value_us =
679         GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
680                                   delay_put.rel_value_us);
681       ac->put_count = num_puts_per_peer;
682       ac->hash = calloc (ac->put_count,
683                          sizeof (struct GNUNET_HashCode));
684       if (NULL == ac->hash)
685       {
686         GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
687                              "calloc");
688         GNUNET_SCHEDULER_shutdown ();
689         return;
690       }
691       ac->delay_task = GNUNET_SCHEDULER_add_delayed (peer_delay_put,
692                                                      &delayed_put,
693                                                      ac);
694       break;
695     }
696   case MODE_GET:
697     {
698       struct GNUNET_TIME_Relative peer_delay_get;
699
700       peer_delay_get.rel_value_us =
701         delay_get.rel_value_us +
702         GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
703                                   delay_get.rel_value_us);
704       ac->delay_task = GNUNET_SCHEDULER_add_delayed (peer_delay_get,
705                                                      &delayed_get,
706                                                      ac);
707       break;
708     }
709   }
710 }
711
712
713 /**
714  * Connect to DHT service and return the DHT client handler
715  *
716  * @param cls the active context
717  * @param cfg configuration of the peer to connect to; will be available until
718  *          GNUNET_TESTBED_operation_done() is called on the operation returned
719  *          from GNUNET_TESTBED_service_connect()
720  * @return service handle to return in 'op_result', NULL on error
721  */
722 static void *
723 dht_connect (void *cls,
724              const struct GNUNET_CONFIGURATION_Handle *cfg)
725 {
726   n_dht++;
727   return GNUNET_DHT_connect (cfg,
728                              10);
729 }
730
731
732 /**
733  * Adapter function called to destroy a connection to
734  * a service.
735  *
736  * @param cls the active context
737  * @param op_result service handle returned from the connect adapter
738  */
739 static void
740 dht_disconnect (void *cls,
741                 void *op_result)
742 {
743   struct ActiveContext *ac = cls;
744
745   GNUNET_assert (NULL != ac->dht);
746   GNUNET_assert (ac->dht == op_result);
747   GNUNET_DHT_disconnect (ac->dht);
748   ac->dht = NULL;
749   n_dht--;
750   if (0 != n_dht)
751     return;
752   if (GNUNET_YES == in_shutdown)
753     return;
754   switch (mode)
755   {
756   case MODE_PUT:
757     if (n_puts_ok != ((unsigned long long) n_active) * num_puts_per_peer)
758       return;
759     /* Start GETs if all PUTs have been made */
760     mode = MODE_GET;
761     start_profiling ();
762     return;
763   case MODE_GET:
764     if ((n_gets_ok + n_gets_fail) != n_active)
765       return;
766     break;
767   }
768 }
769
770
771 /**
772  * Connect to DHT services of active peers
773  */
774 static void
775 start_profiling()
776 {
777   struct Context *ctx;
778
779   DEBUG ("GNUNET_TESTBED_service_connect\n");
780   GNUNET_break (GNUNET_YES != in_shutdown);
781   for (unsigned int i = 0; i < n_active; i++)
782   {
783     struct ActiveContext *ac = &a_ac[i];
784     GNUNET_assert (NULL != (ctx = ac->ctx));
785     GNUNET_assert (NULL == ctx->op);
786     ctx->op = GNUNET_TESTBED_service_connect (ctx,
787                                               ctx->peer,
788                                               "dht",
789                                               &dht_connected, ac,
790                                               &dht_connect,
791                                               &dht_disconnect,
792                                               ac);
793   }
794 }
795
796
797 /**
798  * Callback called when DHT service on the peer is started
799  *
800  * @param cls the context
801  * @param op the operation that has been finished
802  * @param emsg error message in case the operation has failed; will be NULL if
803  *          operation has executed successfully.
804  */
805 static void
806 service_started (void *cls,
807                  struct GNUNET_TESTBED_Operation *op,
808                  const char *emsg)
809 {
810   struct Context *ctx = cls;
811
812   GNUNET_assert (NULL != ctx);
813   GNUNET_assert (NULL != ctx->op);
814   GNUNET_TESTBED_operation_done (ctx->op);
815   ctx->op = NULL;
816   peers_started++;
817   DEBUG ("Peers Started = %d; num_peers = %d \n",
818          peers_started,
819          num_peers);
820   if (peers_started == num_peers)
821     start_profiling ();
822 }
823
824
825 /**
826  * Signature of a main function for a testcase.
827  *
828  * @param cls closure
829  * @param h the run handle
830  * @param num_peers number of peers in 'peers'
831  * @param peers handle to peers run in the testbed
832  * @param links_succeeded the number of overlay link connection attempts that
833  *          succeeded
834  * @param links_failed the number of overlay link
835  */
836 static void
837 test_run (void *cls,
838           struct GNUNET_TESTBED_RunHandle *h,
839           unsigned int num_peers,
840           struct GNUNET_TESTBED_Peer **peers,
841           unsigned int links_succeeded,
842           unsigned int links_failed)
843 {
844   unsigned int ac_cnt;
845
846   testbed_handles = peers;
847   if (NULL == peers)
848   {
849     /* exit */
850     GNUNET_assert (0);
851   }
852   MESSAGE ("%u peers started, %u/%u links up\n",
853            num_peers,
854            links_succeeded,
855            links_succeeded + links_failed);
856   a_ctx = GNUNET_new_array (num_peers,
857                             struct Context);
858   /* select the peers which actively participate in profiling */
859   n_active = num_peers * put_probability / 100;
860   if (0 == n_active)
861   {
862     GNUNET_SCHEDULER_shutdown ();
863     GNUNET_free (a_ctx);
864     a_ctx = NULL;
865     return;
866   }
867
868   a_ac = GNUNET_new_array (n_active,
869                            struct ActiveContext);
870   ac_cnt = 0;
871   for (unsigned int cnt = 0; cnt < num_peers && ac_cnt < n_active; cnt++)
872   {
873     if (GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
874                                   100) >= put_probability)
875       continue;
876
877     a_ctx[cnt].ac = &a_ac[ac_cnt];
878     a_ac[ac_cnt].ctx = &a_ctx[cnt];
879     ac_cnt++;
880   }
881   n_active = ac_cnt;
882
883   /* start DHT service on all peers */
884   for (unsigned int cnt = 0; cnt < num_peers; cnt++)
885   {
886     a_ctx[cnt].peer = peers[cnt];
887     a_ctx[cnt].op = GNUNET_TESTBED_peer_manage_service (&a_ctx[cnt],
888                                                         peers[cnt],
889                                                         "dht",
890                                                         &service_started,
891                                                         &a_ctx[cnt],
892                                                         1);
893   }
894 }
895
896
897 /**
898  * Main function that will be run by the scheduler.
899  *
900  * @param cls closure
901  * @param args remaining command-line arguments
902  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
903  * @param config configuration
904  */
905 static void
906 run (void *cls,
907      char *const *args,
908      const char *cfgfile,
909      const struct GNUNET_CONFIGURATION_Handle *config)
910 {
911   uint64_t event_mask;
912
913   if (0 == num_peers)
914   {
915     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
916                 _("Exiting as the number of peers is %u\n"),
917                 num_peers);
918     return;
919   }
920   cfg = config;
921   event_mask = 0;
922   GNUNET_TESTBED_run (hosts_file,
923                       cfg,
924                       num_peers,
925                       event_mask,
926                       NULL,
927                       NULL,
928                       &test_run,
929                       NULL);
930   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
931                                  NULL);
932 }
933
934
935 /**
936  * Main function.
937  *
938  * @return 0 on success
939  */
940 int
941 main (int argc,
942       char *const *argv)
943 {
944   int rc;
945   struct GNUNET_GETOPT_CommandLineOption options[] = {
946     GNUNET_GETOPT_option_uint ('n',
947                                "peers",
948                                "COUNT",
949                                gettext_noop ("number of peers to start"),
950                                &num_peers),
951     GNUNET_GETOPT_option_uint ('p',
952                                "peer-put-count",
953                                "COUNT",
954                                gettext_noop ("number of PUTs to perform per peer"),
955                                &num_puts_per_peer),
956     GNUNET_GETOPT_option_string ('H',
957                                  "hosts",
958                                  "FILENAME",
959                                  gettext_noop ("name of the file with the login information for the testbed"),
960                                  &hosts_file),
961     GNUNET_GETOPT_option_relative_time ('D',
962                                         "delay",
963                                         "DELAY",
964                                         gettext_noop ("delay between rounds for collecting statistics (default: 30 sec)"),
965                                         &delay_stats),
966     GNUNET_GETOPT_option_relative_time ('P',
967                                         "PUT-delay",
968                                         "DELAY",
969                                         gettext_noop ("delay to start doing PUTs (default: 1 sec)"),
970                                         &delay_put),
971     GNUNET_GETOPT_option_relative_time ('G',
972                                         "GET-delay",
973                                         "DELAY",
974                                         gettext_noop ("delay to start doing GETs (default: 5 min)"),
975                                         &delay_get),
976     GNUNET_GETOPT_option_uint ('r',
977                                "replication",
978                                "DEGREE",
979                                gettext_noop ("replication degree for DHT PUTs"),
980                                &replication),
981     GNUNET_GETOPT_option_uint ('R',
982                                "random-chance",
983                                "PROBABILITY",
984                                gettext_noop ("chance that a peer is selected at random for PUTs"),
985                                &put_probability),
986     GNUNET_GETOPT_option_relative_time ('t',
987                                         "timeout",
988                                         "TIMEOUT",
989                                         gettext_noop ("timeout for DHT PUT and GET requests (default: 1 min)"),
990                                         &timeout),
991     GNUNET_GETOPT_OPTION_END
992   };
993
994   if (GNUNET_OK !=
995       GNUNET_STRINGS_get_utf8_args (argc, argv,
996                                     &argc, &argv))
997     return 2;
998   /* set default delays */
999   delay_stats = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10);
1000   delay_put = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10);
1001   delay_get = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10);
1002   timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10);
1003   replication = 1;      /* default replication */
1004   rc = 0;
1005   if (GNUNET_OK !=
1006       GNUNET_PROGRAM_run (argc,
1007                           argv,
1008                           "gnunet-dht-profiler",
1009                           gettext_noop ("Measure quality and performance of the DHT service."),
1010                           options,
1011                           &run,
1012                           NULL))
1013     rc = 1;
1014   return rc;
1015 }