- delayed requests correctly when in 'begin' round
[oweals/gnunet.git] / src / ats / perf_ats.c
1 /*
2      This file is part of GNUnet.
3      (C) 2010-2013 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  * @file ats/perf_ats.c
22  * @brief ats benchmark: start peers and modify preferences, monitor change over time
23  * @author Christian Grothoff
24  * @author Matthias Wachs
25  */
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "gnunet_testbed_service.h"
29 #include "gnunet_ats_service.h"
30 #include "gnunet_core_service.h"
31
32 #define TEST_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10)
33 #define TESTNAME_PREFIX "perf_ats_"
34 #define DEFAULT_NUM 5
35
36 /**
37  * Information we track for a peer in the testbed.
38  */
39 struct BenchmarkPeer
40 {
41   /**
42    * Handle with testbed.
43    */
44   struct GNUNET_TESTBED_Peer *peer;
45
46   int no;
47
48   struct GNUNET_PeerIdentity id;
49
50   struct GNUNET_CORE_Handle *ch;
51
52   /**
53    * Testbed operation to connect to ATS performance service
54    */
55   struct GNUNET_TESTBED_Operation *ats_perf_op;
56
57   /**
58    * Testbed operation to get peer information
59    */
60   struct GNUNET_TESTBED_Operation *info_op;
61
62   /**
63    * Testbed operation to connect peers
64    */
65   struct GNUNET_TESTBED_Operation *connect_op;
66
67   /**
68    * Testbed operation to connect to core
69    */
70   struct GNUNET_TESTBED_Operation *core_op;
71
72   /**
73    * ATS performance handle
74    */
75   struct GNUNET_ATS_PerformanceHandle *p_handle;
76
77   int core_connections;
78 };
79
80 struct BenchmarkPeer *ph;
81
82 struct BenchmarkState
83 {
84         int connected_ATS_SRV;
85         int connected_CORE_SRV;
86         int connected_PEERS;
87         int connected_CORE;
88
89         int *core_connections;
90 };
91
92 static struct BenchmarkState state;
93
94 /**
95  * Shutdown task
96  */
97 static GNUNET_SCHEDULER_TaskIdentifier shutdown_task;
98
99 static int result;
100 static char *solver;
101 static char *preference;
102
103 static int peers;
104
105 static void
106 core_connect_completion_cb (void *cls,
107                             struct GNUNET_TESTBED_Operation *op,
108                             void *ca_result,
109                             const char *emsg );
110
111 /**
112  * Shutdown nicely
113  *
114  * @param cls NULL
115  * @param tc the task context
116  */
117 static void
118 do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
119 {
120   int c_p;
121   shutdown_task = GNUNET_SCHEDULER_NO_TASK;
122
123   GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Benchmarking done\n"));
124   for (c_p = 0; c_p < peers; c_p++)
125   {
126         if (NULL != ph[c_p].ats_perf_op)
127         {
128                 GNUNET_TESTBED_operation_done (ph[c_p].ats_perf_op);
129                 ph[c_p].ats_perf_op = NULL;
130         }
131
132         if (NULL != ph[c_p].core_op)
133         {
134                 GNUNET_TESTBED_operation_done (ph[c_p].core_op);
135                 ph[c_p].core_op = NULL;
136         }
137
138         if (NULL != ph[c_p].info_op)
139         {
140                 GNUNET_break (0);
141                 GNUNET_TESTBED_operation_done (ph[c_p].info_op);
142                 ph[c_p].info_op = NULL;
143         }
144         if (NULL != ph[c_p].connect_op)
145         {
146                 GNUNET_break (0);
147                 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Failed to connect peer 0 and %u\n"), c_p);
148                 GNUNET_TESTBED_operation_done (ph[c_p].connect_op);
149         ph[c_p].connect_op = NULL;
150         result = 1;
151         }
152   }
153
154         GNUNET_SCHEDULER_shutdown();
155 }
156
157
158 static void
159 ats_performance_info_cb (void *cls,
160                          const struct GNUNET_HELLO_Address *address,
161                          int address_active,
162                          struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
163                          struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
164                          const struct GNUNET_ATS_Information *ats,
165                          uint32_t ats_count)
166 {
167         struct BenchmarkPeer *p = cls;
168         int c_a;
169         char *peer_id;
170         if (p != &ph[0])
171                 return; /* print only master peer */
172         peer_id = GNUNET_strdup (GNUNET_i2s (&p->id));
173         for (c_a = 0; c_a < ats_count; c_a++)
174         {
175                 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("%s: %s %s %u\n"), 
176                             peer_id, 
177                             GNUNET_i2s (&address->peer),
178                             GNUNET_ATS_print_property_type(ntohl(ats[c_a].type)),
179                             ntohl(ats[c_a].value));
180         }
181         GNUNET_free (peer_id);
182 }
183
184
185 static void 
186 do_benchmark ()
187 {
188         if ((state.connected_ATS_SRV == GNUNET_NO) ||
189                         (state.connected_CORE_SRV == GNUNET_NO) ||
190                         (state.connected_PEERS == GNUNET_NO) ||
191                         (state.connected_CORE == GNUNET_NO))
192                 return;
193
194         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
195                         _("BENCHMARKING\n"));
196 }
197
198
199 static void 
200 connect_completion_callback (void *cls,
201                              struct GNUNET_TESTBED_Operation *op,
202                              const char *emsg)
203 {
204         static int connections = 0;
205         struct BenchmarkPeer *p = cls;
206
207         if (NULL == emsg)
208         {
209                 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
210                                 _("Connected peer 0 with peer %p\n"), p->peer);
211         }
212         else
213         {
214                 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
215                                 _("Failed to connect peer 0 with peer %p\n"), p->peer);
216                 GNUNET_break (0);
217                 if (GNUNET_SCHEDULER_NO_TASK != shutdown_task)
218                         GNUNET_SCHEDULER_cancel(shutdown_task);
219                 shutdown_task = GNUNET_SCHEDULER_add_now (do_shutdown, NULL);
220         }
221         GNUNET_TESTBED_operation_done(op);
222         p->connect_op = NULL;
223         connections++;
224         if (connections == peers -1)
225         {
226                 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
227                                 "All peers connected, start benchmarking \n");
228                  GNUNET_SCHEDULER_add_now (&do_benchmark, NULL);
229                  state.connected_PEERS = GNUNET_YES;
230         }
231 }
232
233
234 static void
235 do_connect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
236 {
237         int c_p;
238
239         if ((state.connected_ATS_SRV == GNUNET_NO) ||
240                         (state.connected_CORE_SRV == GNUNET_NO))
241                 return;
242
243         for (c_p = 1; c_p < peers; c_p ++)
244         {
245                 ph[c_p].connect_op = GNUNET_TESTBED_overlay_connect( NULL,
246                                 &connect_completion_callback, &ph[c_p], ph[0].peer, ph[c_p].peer);
247                 if (NULL == ph[c_p].connect_op)
248                 {
249                         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
250                                         _("Could not connect peer 0 and peer %u\n"), c_p);
251                         GNUNET_break (0);
252                         if (GNUNET_SCHEDULER_NO_TASK != shutdown_task)
253                                 GNUNET_SCHEDULER_cancel(shutdown_task);
254                         shutdown_task = GNUNET_SCHEDULER_add_now (do_shutdown, NULL);
255                         return;
256                 }
257         }
258 }
259
260
261 /**
262  * Controller event callback
263  *
264  * @param cls NULL
265  * @param event the controller event
266  */
267 static void
268 controller_event_cb (void *cls,
269                      const struct GNUNET_TESTBED_EventInformation *event)
270 {
271         //struct BenchmarkPeer *p = cls;
272   switch (event->type)
273   {
274   case GNUNET_TESTBED_ET_CONNECT:
275     break;
276   case GNUNET_TESTBED_ET_OPERATION_FINISHED:
277     break;
278   default:
279     GNUNET_break (0);
280     result = GNUNET_SYSERR;
281     GNUNET_SCHEDULER_cancel (shutdown_task);
282     shutdown_task = GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
283   }
284 }
285
286
287 /**
288  * Called to open a connection to the peer's ATS performance
289  *
290  * @param cls peer context
291  * @param cfg configuration of the peer to connect to; will be available until
292  *          GNUNET_TESTBED_operation_done() is called on the operation returned
293  *          from GNUNET_TESTBED_service_connect()
294  * @return service handle to return in 'op_result', NULL on error
295  */
296 static void *
297 ats_perf_connect_adapter (void *cls,
298                       const struct GNUNET_CONFIGURATION_Handle *cfg)
299 {
300   struct BenchmarkPeer *peer = cls;
301
302   peer->p_handle = GNUNET_ATS_performance_init (cfg, &ats_performance_info_cb, peer);
303   if (NULL == peer->p_handle)
304     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to create ATS performance handle \n");
305   return peer->p_handle;
306 }
307
308
309 /**
310  * Called to disconnect from peer's statistics service
311  *
312  * @param cls peer context
313  * @param op_result service handle returned from the connect adapter
314  */
315 static void
316 ats_perf_disconnect_adapter (void *cls, void *op_result)
317 {
318   struct BenchmarkPeer *peer = cls;
319
320   GNUNET_ATS_performance_done(peer->p_handle);
321   peer->p_handle = NULL;
322 }
323
324
325 /**
326  * Callback to be called when a service connect operation is completed
327  *
328  * @param cls the callback closure from functions generating an operation
329  * @param op the operation that has been finished
330  * @param ca_result the service handle returned from GNUNET_TESTBED_ConnectAdapter()
331  * @param emsg error message in case the operation has failed; will be NULL if
332  *          operation has executed successfully.
333  */
334 static void 
335 ats_connect_completion_cb (void *cls,
336                            struct GNUNET_TESTBED_Operation *op,
337                            void *ca_result,
338                            const char *emsg )
339 {
340         static int op_done = 0;
341         if ((NULL != emsg) || (NULL == ca_result))
342         {
343                 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
344                                 _("Initialization failed, shutdown\n"));
345                 GNUNET_break (0);
346                 if (GNUNET_SCHEDULER_NO_TASK != shutdown_task)
347                         GNUNET_SCHEDULER_cancel(shutdown_task);
348                 shutdown_task = GNUNET_SCHEDULER_add_now (do_shutdown, NULL);
349                 return;
350         }
351
352         op_done ++;
353         if (op_done ==  peers)
354         {
355                 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
356                                 _("Initialization done, connecting peers\n"));
357                 state.connected_ATS_SRV = GNUNET_YES;
358                 GNUNET_SCHEDULER_add_now (&do_connect, NULL);
359         }
360 }
361
362
363 /**
364  * Method called whenever a given peer connects.
365  *
366  * @param cls closure
367  * @param peer peer identity this notification is about
368  */
369 static void
370 core_connect_cb (void *cls, const struct GNUNET_PeerIdentity * peer)
371 {
372   struct BenchmarkPeer *p = cls;
373   char *id;
374
375   p->core_connections ++;
376   id = GNUNET_strdup (GNUNET_i2s (&p->id));
377   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
378               "%s connected to %s \n",
379               id, GNUNET_i2s (peer));
380   if (p->core_connections == peers)
381   {
382     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
383                 "%s connected all peers\n", 
384                 id);
385   }
386   if ((p->core_connections == peers) && (p == &ph[0]))
387   {
388     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
389                 "Master peer %s connected all peers on CORE level\n", id, GNUNET_i2s (peer));
390     state.connected_CORE = GNUNET_YES;
391     GNUNET_SCHEDULER_add_now (&do_benchmark, NULL);
392   }
393   GNUNET_free (id);
394 }
395
396
397 /**
398  * Method called whenever a peer disconnects.
399  *
400  * @param cls closure
401  * @param peer peer identity this notification is about
402  */
403 static void
404 core_disconnect_cb (void *cls, const struct GNUNET_PeerIdentity * peer)
405 {
406   struct BenchmarkPeer *p = cls;
407   char *id;
408
409   id = GNUNET_strdup (GNUNET_i2s (&p->id));
410   GNUNET_log (GNUNET_ERROR_TYPE_INFO, 
411               "%s disconnected from %s \n", id, GNUNET_i2s (peer));
412   GNUNET_free (id);
413 }
414
415
416 /**
417  * Called to open a connection to the peer's ATS performance
418  *
419  * @param cls peer context
420  * @param cfg configuration of the peer to connect to; will be available until
421  *          GNUNET_TESTBED_operation_done() is called on the operation returned
422  *          from GNUNET_TESTBED_service_connect()
423  * @return service handle to return in 'op_result', NULL on error
424  */
425 static void *
426 core_connect_adapter (void *cls,
427                       const struct GNUNET_CONFIGURATION_Handle *cfg)
428 {
429   struct BenchmarkPeer *peer = cls;
430
431   peer->ch = GNUNET_CORE_connect(cfg, peer, NULL,
432                                  core_connect_cb,
433                                  core_disconnect_cb,
434                                  NULL, GNUNET_NO, NULL, GNUNET_NO, NULL);
435   if (NULL == peer->ch)
436     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
437                 "Failed to create core connection \n");
438   return peer->ch;
439 }
440
441
442 /**
443  * Callback to be called when a service connect operation is completed
444  *
445  * @param cls the callback closure from functions generating an operation
446  * @param op the operation that has been finished
447  * @param ca_result the service handle returned from GNUNET_TESTBED_ConnectAdapter()
448  * @param emsg error message in case the operation has failed; will be NULL if
449  *          operation has executed successfully.
450  */
451 static void
452 core_connect_completion_cb (void *cls,
453                             struct GNUNET_TESTBED_Operation *op,
454                             void *ca_result,
455                             const char *emsg )
456 {
457         static int core_done = 0;
458         if ((NULL != emsg) || (NULL == ca_result))
459         {
460                 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
461                                 _("Initialization failed, shutdown\n"));
462                 GNUNET_break (0);
463                 if (GNUNET_SCHEDULER_NO_TASK != shutdown_task)
464                         GNUNET_SCHEDULER_cancel(shutdown_task);
465                 shutdown_task = GNUNET_SCHEDULER_add_now (do_shutdown, NULL);
466                 return;
467         }
468         core_done ++;
469         if (core_done == peers)
470         {
471                 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
472                                 "Connected to all CORE services\n");
473                 state.connected_CORE_SRV = GNUNET_YES;
474                 GNUNET_SCHEDULER_add_now (&do_connect, NULL);
475         }
476 }
477
478
479 /**
480  * Called to disconnect from peer's statistics service
481  *
482  * @param cls peer context
483  * @param op_result service handle returned from the connect adapter
484  */
485 static void
486 core_disconnect_adapter (void *cls, void *op_result)
487 {
488   struct BenchmarkPeer *peer = cls;
489
490   GNUNET_CORE_disconnect (peer->ch);
491   peer->ch = NULL;
492 }
493
494
495 /**
496  * Callback to be called when the requested peer information is available
497  *
498  * @param cb_cls the closure from GNUNET_TETSBED_peer_get_information()
499  * @param op the operation this callback corresponds to
500  * @param pinfo the result; will be NULL if the operation has failed
501  * @param emsg error message if the operation has failed; will be NULL if the
502  *          operation is successfull
503  */
504 static void
505 peerinformation_cb (void *cb_cls,
506                     struct GNUNET_TESTBED_Operation *op,
507                     const struct GNUNET_TESTBED_PeerInformation*pinfo,
508                     const char *emsg)
509 {
510   struct BenchmarkPeer *p = cb_cls;
511
512   if (pinfo->pit == GNUNET_TESTBED_PIT_IDENTITY)
513   {
514     p->id = *pinfo->result.id;
515     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
516                 "[%3u] Peers %s\n", 
517                 p->no, GNUNET_i2s (&p->id));
518   }
519   else
520   {
521     GNUNET_assert (0);
522   }
523   GNUNET_TESTBED_operation_done (op);
524   p->info_op = NULL;
525 }
526
527
528 /**
529  * Signature of a main function for a testcase.
530  *
531  * @param cls closure
532  * @param num_peers number of peers in 'peers'
533  * @param peers_ handle to peers run in the testbed
534  * @param links_succeeded the number of overlay link connection attempts that
535  *          succeeded
536  * @param links_failed the number of overlay link connection attempts that
537  *          failed
538  */
539 static void
540 test_main (void *cls, unsigned int num_peers,
541            struct GNUNET_TESTBED_Peer **peers_,
542            unsigned int links_succeeded,
543            unsigned int links_failed)
544 {
545   int c_p;
546
547   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
548               _("Benchmarking solver `%s' on preference `%s' with %u peers\n"),
549               solver, preference, peers);
550
551   shutdown_task = GNUNET_SCHEDULER_add_delayed (TEST_TIMEOUT, &do_shutdown, NULL);
552
553   GNUNET_assert (NULL == cls);
554   GNUNET_assert (peers == num_peers);
555   GNUNET_assert (NULL != peers_);
556
557   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
558               _("Initializing... \n"));
559
560   for (c_p = 0; c_p < num_peers; c_p++)
561   {
562     GNUNET_assert (NULL != peers_[c_p]);
563     ph[c_p].no = c_p;
564     /* Connect to ATS performance service */
565     ph[c_p].peer = peers_[c_p];
566
567     ph[c_p].info_op = GNUNET_TESTBED_peer_get_information (ph[c_p].peer,
568                                                            GNUNET_TESTBED_PIT_IDENTITY, 
569                                                            &peerinformation_cb, &ph[c_p]);
570
571     ph[c_p].core_op = GNUNET_TESTBED_service_connect (NULL,
572                                                       peers_[c_p], "ats",
573                                                       core_connect_completion_cb, NULL,
574                                                       &core_connect_adapter,
575                                                       &core_disconnect_adapter,
576                                                       &ph[c_p]);
577
578     ph[c_p].ats_perf_op = GNUNET_TESTBED_service_connect (NULL,
579                                                           peers_[c_p], "ats",
580                                                           ats_connect_completion_cb, NULL,
581                                                           &ats_perf_connect_adapter,
582                                                           &ats_perf_disconnect_adapter,
583                                                           &ph[c_p]);
584   }
585 }
586
587
588 int
589 main (int argc, char *argv[])
590 {
591   char *tmp;
592   char *tmp_sep;
593   char *test_name;
594   char *conf_name;
595   int c;
596   
597   peers = 0;
598   result = 1;
599
600   /* figure out testname */
601   tmp = strstr (argv[0], TESTNAME_PREFIX);
602   if (NULL == tmp)
603   {
604         fprintf (stderr, "Unable to parse test name `%s'\n", argv[0]);
605         return GNUNET_SYSERR;
606   }
607   tmp += strlen(TESTNAME_PREFIX);
608   solver = GNUNET_strdup (tmp);
609   tmp_sep = strchr (solver, '_');
610   if (NULL == tmp_sep)
611   {
612         fprintf (stderr, "Unable to parse test name `%s'\n", argv[0]);
613         GNUNET_free (solver);
614         return GNUNET_SYSERR;
615   }
616   tmp_sep[0] = '\0';
617   preference = GNUNET_strdup(tmp_sep + 1);
618
619   GNUNET_asprintf(&conf_name, "%s%s_%s.conf", TESTNAME_PREFIX, solver, preference);
620   GNUNET_asprintf(&test_name, "%s%s_%s", TESTNAME_PREFIX, solver, preference);
621
622   for (c = 0; c < (argc -1); c++)
623   {
624         if (0 == strcmp(argv[c], "-c"))
625                 break;
626   }
627   if (c < argc-1)
628   {
629     if ((0L != (peers = strtol (argv[c + 1], NULL, 10))) && (peers >= 2))
630       fprintf (stderr, "Starting %u peers\n", peers);
631     else
632         peers = DEFAULT_NUM;
633   }
634   else
635         peers = DEFAULT_NUM;
636
637   ph = GNUNET_malloc (peers * sizeof (struct BenchmarkPeer));
638   state.connected_ATS_SRV = GNUNET_NO;
639   state.connected_CORE_SRV = GNUNET_NO;
640   state.connected_PEERS = GNUNET_NO;
641   /* Start topology */
642   uint64_t event_mask;
643   result = GNUNET_SYSERR;
644   event_mask = 0;
645   event_mask |= (1LL << GNUNET_TESTBED_ET_CONNECT);
646   event_mask |= (1LL << GNUNET_TESTBED_ET_OPERATION_FINISHED);
647   (void) GNUNET_TESTBED_test_run (test_name,
648                                   conf_name, peers,
649                                   event_mask, &controller_event_cb, NULL,
650                                   &test_main, NULL);
651
652   GNUNET_free (solver);
653   GNUNET_free (preference);
654   GNUNET_free (conf_name);
655   GNUNET_free (test_name);
656   GNUNET_free (ph);
657
658   return result;
659 }
660
661 /* end of file perf_ats.c */