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