consensus
[oweals/gnunet.git] / src / consensus / gnunet-consensus-profiler.c
1 /*
2       This file is part of GNUnet
3       Copyright (C) 2012 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., 51 Franklin Street, Fifth Floor,
18       Boston, MA 02110-1301, USA.
19  */
20
21 /**
22  * @file consensus/gnunet-consensus-profiler.c
23  * @brief profiling tool for gnunet-consensus
24  * @author Florian Dold
25  */
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "gnunet_time_lib.h"
29 #include "gnunet_consensus_service.h"
30 #include "gnunet_testbed_service.h"
31
32 static unsigned int num_peers = 2;
33
34 static unsigned int replication = 1;
35
36 static unsigned int num_values = 5;
37
38 static struct GNUNET_TIME_Relative conclude_timeout;
39
40 static struct GNUNET_TIME_Relative consensus_delay;
41
42 static struct GNUNET_CONSENSUS_Handle **consensus_handles;
43
44 static struct GNUNET_TESTBED_Operation **testbed_operations;
45
46 static unsigned int num_connected_handles;
47
48 static struct GNUNET_TESTBED_Peer **peers;
49
50 static struct GNUNET_PeerIdentity *peer_ids;
51
52 static unsigned int num_retrieved_peer_ids;
53
54 static struct GNUNET_HashCode session_id;
55
56 static unsigned int peers_done = 0;
57
58 static unsigned *results_for_peer;
59
60 static int verbose;
61
62 /**
63  * Start time for all consensuses.
64  */
65 static struct GNUNET_TIME_Absolute start;
66
67 /**
68  * Deadline for all consensuses.
69  */
70 static struct GNUNET_TIME_Absolute deadline;
71
72
73 /**
74  * Signature of the event handler function called by the
75  * respective event controller.
76  *
77  * @param cls closure
78  * @param event information about the event
79  */
80 static void
81 controller_cb (void *cls,
82                const struct GNUNET_TESTBED_EventInformation *event)
83 {
84   GNUNET_assert (0);
85 }
86
87
88 static void
89 statistics_done_db (void *cls,
90                     struct
91                     GNUNET_TESTBED_Operation
92                     *op,
93                     const char *emsg)
94 {
95   GNUNET_assert (NULL == emsg);
96   GNUNET_TESTBED_operation_done (op);
97   printf ("statistics done\n");
98   GNUNET_SCHEDULER_shutdown ();
99 }
100
101
102 /**
103  * Callback function to process statistic values from all peers.
104  *
105  * @param cls closure
106  * @param peer the peer the statistic belong to
107  * @param subsystem name of subsystem that created the statistic
108  * @param name the name of the datum
109  * @param value the current value
110  * @param is_persistent GNUNET_YES if the value is persistent, GNUNET_NO if not
111  * @return GNUNET_OK to continue, GNUNET_SYSERR to abort iteration
112  */
113 static int
114 statistics_cb (void *cls,
115                const struct GNUNET_TESTBED_Peer *peer,
116                const char *subsystem,
117                const char *name,
118                uint64_t value,
119                int is_persistent)
120 {
121   printf ("stat P%u: %s/%s=%lu\n", GNUNET_TESTBED_get_index (peer), subsystem, name, (unsigned long) value);
122   return GNUNET_OK;
123 }
124
125
126 static void
127 destroy (void *cls, const struct GNUNET_SCHEDULER_TaskContext *ctx)
128 {
129   struct GNUNET_CONSENSUS_Handle *consensus = cls;
130
131   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
132               "destroying consensus\n");
133   GNUNET_CONSENSUS_destroy (consensus);
134   peers_done++;
135   if (peers_done == num_peers)
136   {
137     unsigned int i;
138     for (i = 0; i < num_peers; i++)
139       GNUNET_TESTBED_operation_done (testbed_operations[i]);
140     for (i = 0; i < num_peers; i++)
141       printf ("P%u got %u of %u elements\n",
142               i,
143               results_for_peer[i],
144               num_values);
145     GNUNET_TESTBED_get_statistics (num_peers, peers, NULL, NULL,
146                                    statistics_cb,
147                                    statistics_done_db,
148                                    NULL);
149   }
150 }
151
152
153 /**
154  * Called when a conclusion was successful.
155  *
156  * @param cls closure, the consensus handle
157  * @return #GNUNET_YES if more consensus groups should be offered,
158  *         #GNUNET_NO if not
159  */
160 static void
161 conclude_cb (void *cls)
162 {
163   struct GNUNET_CONSENSUS_Handle **chp = cls;
164
165   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
166               "consensus %d done\n",
167               chp - consensus_handles);
168   GNUNET_SCHEDULER_add_now (destroy, *chp);
169 }
170
171
172 static void
173 generate_indices (int *indices)
174 {
175   int j;
176   j = 0;
177   while (j < replication)
178   {
179     int n;
180     int k;
181     int repeat;
182     n = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, num_peers);
183     repeat = GNUNET_NO;
184     for (k = 0; k < j; k++)
185       if (indices[k] == n)
186       {
187         repeat = GNUNET_YES;
188         break;
189       }
190     if (GNUNET_NO == repeat)
191       indices[j++] = n;
192   }
193 }
194
195
196 static void
197 do_consensus ()
198 {
199   int unique_indices[replication];
200   unsigned int i;
201
202   for (i = 0; i < num_values; i++)
203   {
204     unsigned int j;
205     struct GNUNET_HashCode val;
206     struct GNUNET_SET_Element element;
207
208     generate_indices (unique_indices);
209     GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_WEAK, &val);
210
211     element.data = &val;
212     element.size = sizeof (val);
213     for (j = 0; j < replication; j++)
214     {
215       int cid;
216
217       cid = unique_indices[j];
218       GNUNET_CONSENSUS_insert (consensus_handles[cid],
219                                &element,
220                                NULL, NULL);
221     }
222   }
223
224   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
225               "all elements inserted, calling conclude\n");
226
227   for (i = 0; i < num_peers; i++)
228     GNUNET_CONSENSUS_conclude (consensus_handles[i],
229                                conclude_cb, &consensus_handles[i]);
230 }
231
232
233 /**
234  * Callback to be called when a service connect operation is completed
235  *
236  * @param cls the callback closure from functions generating an operation
237  * @param op the operation that has been finished
238  * @param ca_result the service handle returned from GNUNET_TESTBED_ConnectAdapter()
239  * @param emsg error message in case the operation has failed; will be NULL if
240  *          operation has executed successfully.
241  */
242 static void
243 connect_complete (void *cls,
244                   struct GNUNET_TESTBED_Operation *op,
245                   void *ca_result,
246                   const char *emsg)
247 {
248
249   if (NULL != emsg)
250   {
251     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
252                 "testbed connect emsg: %s\n",
253                 emsg);
254     GNUNET_assert (0);
255   }
256
257   num_connected_handles++;
258
259   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
260               "connect complete\n");
261
262   if (num_connected_handles == num_peers)
263   {
264     do_consensus ();
265   }
266 }
267
268
269 static void
270 new_element_cb (void *cls,
271                 const struct GNUNET_SET_Element *element)
272 {
273   struct GNUNET_CONSENSUS_Handle **chp = cls;
274   int idx = chp - consensus_handles;
275
276   GNUNET_assert (NULL != cls);
277
278   results_for_peer[idx]++;
279
280   GNUNET_assert (sizeof (struct GNUNET_HashCode) == element->size);
281
282   if (GNUNET_YES == verbose)
283   {
284     printf ("P%d received %s\n",
285             idx,
286             GNUNET_h2s ((struct GNUNET_HashCode *) element->data));
287   }
288 }
289
290
291 /**
292  * Adapter function called to establish a connection to
293  * a service.
294  *
295  * @param cls closure
296  * @param cfg configuration of the peer to connect to; will be available until
297  *          GNUNET_TESTBED_operation_done() is called on the operation returned
298  *          from GNUNET_TESTBED_service_connect()
299  * @return service handle to return in 'op_result', NULL on error
300  */
301 static void *
302 connect_adapter (void *cls,
303                  const struct GNUNET_CONFIGURATION_Handle *cfg)
304 {
305   struct GNUNET_CONSENSUS_Handle **chp = cls;
306   struct GNUNET_CONSENSUS_Handle *consensus;
307   chp = (struct GNUNET_CONSENSUS_Handle **) cls;
308
309   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
310               "connect adapter, %d peers\n",
311               num_peers);
312   consensus = GNUNET_CONSENSUS_create (cfg,
313                                        num_peers, peer_ids,
314                                        &session_id,
315                                        start,
316                                        deadline,
317                                        &new_element_cb, chp);
318   *chp = (struct GNUNET_CONSENSUS_Handle *) consensus;
319   return consensus;
320 }
321
322
323 /**
324  * Adapter function called to destroy a connection to
325  * a service.
326  *
327  * @param cls closure
328  * @param op_result service handle returned from the connect adapter
329  */
330 static void
331 disconnect_adapter(void *cls, void *op_result)
332 {
333   /* FIXME: what to do here? */
334 }
335
336
337 /**
338  * Callback to be called when the requested peer information is available
339  *
340  * @param cb_cls the closure from GNUNET_TETSBED_peer_get_information()
341  * @param op the operation this callback corresponds to
342  * @param pinfo the result; will be NULL if the operation has failed
343  * @param emsg error message if the operation has failed; will be NULL if the
344  *          operation is successfull
345  */
346 static void
347 peer_info_cb (void *cb_cls,
348               struct GNUNET_TESTBED_Operation *op,
349               const struct GNUNET_TESTBED_PeerInformation *pinfo,
350               const char *emsg)
351 {
352   struct GNUNET_PeerIdentity *p;
353   int i;
354
355   GNUNET_assert (NULL == emsg);
356
357   p = (struct GNUNET_PeerIdentity *) cb_cls;
358
359   if (pinfo->pit == GNUNET_TESTBED_PIT_IDENTITY)
360   {
361     *p = *pinfo->result.id;
362     num_retrieved_peer_ids++;
363     if (num_retrieved_peer_ids == num_peers)
364       for (i = 0; i < num_peers; i++)
365         testbed_operations[i] =
366             GNUNET_TESTBED_service_connect (NULL, peers[i], "consensus", connect_complete, NULL,
367                                             connect_adapter, disconnect_adapter, &consensus_handles[i]);
368   }
369   else
370   {
371     GNUNET_assert (0);
372   }
373
374   GNUNET_TESTBED_operation_done (op);
375 }
376
377
378 /**
379  * Signature of a main function for a testcase.
380  *
381  * @param cls closure
382  * @param h the run handle
383  * @param num_peers number of peers in 'peers'
384  * @param started_peers handle to peers run in the testbed.  NULL upon timeout (see
385  *          GNUNET_TESTBED_test_run()).
386  * @param links_succeeded the number of overlay link connection attempts that
387  *          succeeded
388  * @param links_failed the number of overlay link connection attempts that
389  *          failed
390  */
391 static void
392 test_master (void *cls,
393              struct GNUNET_TESTBED_RunHandle *h,
394              unsigned int num_peers,
395              struct GNUNET_TESTBED_Peer **started_peers,
396              unsigned int links_succeeded,
397              unsigned int links_failed)
398 {
399   int i;
400
401   GNUNET_log_setup ("gnunet-consensus", "INFO", NULL);
402
403   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "test master\n");
404
405   peers = started_peers;
406
407   peer_ids = GNUNET_malloc (num_peers * sizeof (struct GNUNET_PeerIdentity));
408
409   results_for_peer = GNUNET_malloc (num_peers * sizeof (unsigned int));
410   consensus_handles = GNUNET_malloc (num_peers * sizeof (struct ConsensusHandle *));
411   testbed_operations = GNUNET_malloc (num_peers * sizeof (struct ConsensusHandle *));
412
413   for (i = 0; i < num_peers; i++)
414     GNUNET_TESTBED_peer_get_information (peers[i],
415                                          GNUNET_TESTBED_PIT_IDENTITY,
416                                          peer_info_cb,
417                                          &peer_ids[i]);
418 }
419
420
421 static void
422 run (void *cls, char *const *args, const char *cfgfile,
423      const struct GNUNET_CONFIGURATION_Handle *cfg)
424 {
425   static char *session_str = "gnunet-consensus/test";
426   char *topology;
427   int topology_cmp_result;
428
429   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, "testbed", "OVERLAY_TOPOLOGY", &topology))
430   {
431     fprintf (stderr,
432              "'OVERLAY_TOPOLOGY' not found in 'testbed' config section, "
433              "seems like you passed the wrong configuration file\n");
434     return;
435   }
436
437   topology_cmp_result = strcasecmp (topology, "NONE");
438   GNUNET_free (topology);
439
440   if (0 == topology_cmp_result)
441   {
442     fprintf (stderr,
443              "'OVERLAY_TOPOLOGY' set to 'NONE', "
444              "seems like you passed the wrong configuration file\n");
445     return;
446   }
447
448   if (num_peers < replication)
449   {
450     fprintf (stderr, "k must be <=n\n");
451     return;
452   }
453
454   start = GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get (), consensus_delay);
455   deadline = GNUNET_TIME_absolute_add (start, conclude_timeout);
456
457   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
458               "running gnunet-consensus\n");
459
460   GNUNET_CRYPTO_hash (session_str, strlen(session_str), &session_id);
461
462   (void) GNUNET_TESTBED_test_run ("gnunet-consensus",
463                                   cfgfile,
464                                   num_peers,
465                                   0,
466                                   controller_cb,
467                                   NULL,
468                                   test_master,
469                                   NULL);
470 }
471
472
473 int
474 main (int argc, char **argv)
475 {
476    static const struct GNUNET_GETOPT_CommandLineOption options[] = {
477       { 'n', "num-peers", NULL,
478         gettext_noop ("number of peers in consensus"),
479         GNUNET_YES, &GNUNET_GETOPT_set_uint, &num_peers },
480       { 'k', "value-replication", NULL,
481         gettext_noop ("how many peers (random selection without replacement) receive one value?"),
482         GNUNET_YES, &GNUNET_GETOPT_set_uint, &replication },
483       { 'x', "num-values", NULL,
484         gettext_noop ("number of values"),
485         GNUNET_YES, &GNUNET_GETOPT_set_uint, &num_values },
486       { 't', "timeout", NULL,
487         gettext_noop ("consensus timeout"),
488         GNUNET_YES, &GNUNET_GETOPT_set_relative_time, &conclude_timeout },
489       { 'd', "delay", NULL,
490         gettext_noop ("delay until consensus starts"),
491         GNUNET_YES, &GNUNET_GETOPT_set_relative_time, &consensus_delay },
492       { 'V', "verbose", NULL,
493         gettext_noop ("be more verbose (print received values)"),
494         GNUNET_NO, &GNUNET_GETOPT_set_one, &verbose },
495       GNUNET_GETOPT_OPTION_END
496   };
497   conclude_timeout = GNUNET_TIME_UNIT_SECONDS;
498   GNUNET_PROGRAM_run2 (argc, argv, "gnunet-consensus-profiler",
499                       "help",
500                       options, &run, NULL, GNUNET_YES);
501   return 0;
502 }
503