fix indent
[oweals/gnunet.git] / src / ats-tests / 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 BENCHMARK_DURATION GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
34 #define TESTNAME_PREFIX "perf_ats_"
35 #define DEFAULT_SLAVES_NUM 3
36 #define DEFAULT_MASTERS_NUM 1
37
38 #define TEST_MESSAGE_TYPE_PING 12345
39 #define TEST_MESSAGE_TYPE_PONG 12346
40 #define TEST_MESSAGE_SIZE 1000
41 #define TEST_MESSAGE_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1)
42
43
44 /**
45  * Information we track for a peer in the testbed.
46  */
47 struct BenchmarkPeer
48 {
49   /**
50    * Handle with testbed.
51    */
52   struct GNUNET_TESTBED_Peer *peer;
53
54   /**
55    * Unique identifier
56    */
57   int no;
58
59   /**
60    *  master: GNUNET_YES/NO
61    */
62   int master;
63
64   /**
65    *  Peer ID
66    */
67   struct GNUNET_PeerIdentity id;
68
69   /**
70    *  Core handle
71    */
72   struct GNUNET_CORE_Handle *ch;
73
74   /**
75    * Testbed operation to connect to ATS performance service
76    */
77   struct GNUNET_TESTBED_Operation *ats_perf_op;
78
79   /**
80    * Testbed operation to get peer information
81    */
82   struct GNUNET_TESTBED_Operation *info_op;
83
84   /**
85    * Testbed operation to connect to core
86    */
87   struct GNUNET_TESTBED_Operation *core_op;
88
89   /**
90    * ATS performance handle
91    */
92   struct GNUNET_ATS_PerformanceHandle *p_handle;
93
94   /**
95    * Testbed connecect operation
96    */
97   struct ConnectOperation *connect_ops;
98
99   /**
100    * ATS Measurement Partner
101    */
102   struct BenchmarkPeer *destination;
103
104   GNUNET_SCHEDULER_TaskIdentifier ats_task;
105
106   /* Message exchange */
107
108   /**
109   * Core transmit handle
110   */
111   struct GNUNET_CORE_TransmitHandle *cth;
112
113   /**
114   * DLL for pending messages: head
115   */
116   struct PendingMessages *p_head;
117
118   /**
119   * DLL for pending messages: tail
120   */
121   struct PendingMessages *p_tail;
122
123   /**
124    *  Bit-mask for next partner selection
125    */
126   uint32_t send_mask;
127
128   /**
129    * Current message for partner?
130    */
131   int partner_msg;
132
133   /**
134    * Number of core connections
135    */
136   int core_connections;
137
138   /**
139    * Number of slave connections
140    */
141   int slave_connections;
142
143   /**
144    * Statistics
145    */
146   unsigned int messages_sent;
147   unsigned int messages_sent_partner;
148   unsigned int messages_received;
149 };
150
151
152 static int c_master_peers;
153
154 /**
155  * Array of master peers
156  * Preferences to be set for
157  */
158 static struct BenchmarkPeer *bp_master;
159
160 static int c_slave_peers;
161
162 /**
163  * Array of slave peers
164  * Peer used for measurements
165  */
166 static struct BenchmarkPeer *bp_slaves;
167
168
169 struct BenchmarkState
170 {
171   /* Are we connected to ATS service of all peers: GNUNET_YES/NO */
172   int connected_ATS_service;
173
174   /* Are we connected to CORE service of all peers: GNUNET_YES/NO */
175   int connected_CORE_service;
176
177   /* Are we connected to all peers: GNUNET_YES/NO */
178   int connected_PEERS;
179
180   /* Are we connected to all slave peers on CORE level: GNUNET_YES/NO */
181   int connected_CORE;
182
183   /* Are we connected to CORE service of all peers: GNUNET_YES/NO */
184   int benchmarking;
185
186   int *core_connections;
187
188   uint32_t partner_map;
189 };
190
191 static struct BenchmarkState state;
192
193 /**
194  * Shutdown task
195  */
196 static GNUNET_SCHEDULER_TaskIdentifier shutdown_task;
197
198 static int result;
199 static char *solver;
200 static char *pref_str;
201 static int pref_val;
202
203 /**
204  * Pending Responses
205  */
206 struct PendingMessages
207 {
208   struct PendingMessages *prev;
209   struct PendingMessages *next;
210   struct GNUNET_PeerIdentity target;
211 };
212
213
214 /**
215  * Information we track for a peer in the testbed.
216  */
217 struct ConnectOperation
218 {
219         struct BenchmarkPeer *master;
220
221         struct BenchmarkPeer *slave;
222   /**
223    * Testbed operation to connect peers
224    */
225   struct GNUNET_TESTBED_Operation *connect_op;
226
227 };
228
229
230 static void
231 core_connect_completion_cb (void *cls,
232                             struct GNUNET_TESTBED_Operation *op,
233                             void *ca_result,
234                             const char *emsg );
235
236
237 static void evaluate ()
238 {
239   int c_p;
240   struct BenchmarkPeer *bp;
241   int total_out;
242   int partner_out;
243
244   for (c_p = 0; c_p < c_master_peers; c_p++)
245   {
246     bp = &bp_master[c_p];
247     total_out = (bp->messages_sent * TEST_MESSAGE_SIZE) / 10240;
248     partner_out = (bp->messages_sent_partner * TEST_MESSAGE_SIZE) / 10240;
249
250     fprintf (stderr, _("Master peer %u: Out total: %u KiB/s, out partner %u KiB/s\n"),
251             bp->no,
252             total_out, partner_out
253             /*partner_out / (total_out / 100)*/);
254   }
255 }
256
257 /**
258  * Shutdown nicely
259  *
260  * @param cls NULL
261  * @param tc the task context
262  */
263 static void
264 do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
265 {
266   int c_p;
267   int c_op;
268   struct PendingMessages *cur;
269   struct PendingMessages *next;
270
271   shutdown_task = GNUNET_SCHEDULER_NO_TASK;
272
273   state.benchmarking = GNUNET_NO;
274
275   evaluate ();
276   GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Benchmarking done\n"));
277
278   for (c_p = 0; c_p < c_master_peers; c_p++)
279   {
280       next = bp_master[c_p].p_head;
281       for (cur = next; cur != NULL; cur = next )
282       {
283           next = cur->next;
284           GNUNET_CONTAINER_DLL_remove (bp_master[c_p].p_head, bp_master[c_p].p_tail, cur);
285           GNUNET_free (cur);
286       }
287
288       if (GNUNET_SCHEDULER_NO_TASK != bp_master[c_p].ats_task)
289       {
290           GNUNET_SCHEDULER_cancel (bp_master[c_p].ats_task);
291           bp_master[c_p].ats_task = GNUNET_SCHEDULER_NO_TASK;
292       }
293
294       if (NULL != bp_master[c_p].cth)
295       {
296           GNUNET_CORE_notify_transmit_ready_cancel(bp_master[c_p].cth);
297           bp_master[c_p].cth = NULL;
298       }
299
300       if (NULL != bp_master[c_p].ats_perf_op)
301       {
302           GNUNET_TESTBED_operation_done (bp_master[c_p].ats_perf_op);
303           bp_master[c_p].ats_perf_op = NULL;
304       }
305
306       if (NULL != bp_master[c_p].core_op)
307       {
308           GNUNET_TESTBED_operation_done (bp_master[c_p].core_op);
309           bp_master[c_p].core_op = NULL;
310       }
311
312       if (NULL != bp_master[c_p].info_op)
313       {
314           GNUNET_break (0);
315           GNUNET_TESTBED_operation_done (bp_master[c_p].info_op);
316           bp_master[c_p].info_op = NULL;
317       }
318
319       for (c_op = 0; c_op < c_slave_peers; c_op++)
320       {
321           if (NULL != bp_master[c_p].connect_ops[c_op].connect_op)
322           {
323               GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Failed to connect peer 0 and %u\n"), c_p);
324               GNUNET_TESTBED_operation_done (bp_master[c_p].connect_ops[c_op].connect_op);
325               bp_master[c_p].connect_ops[c_op].connect_op = NULL;
326               result = 1;
327           }
328       }
329       GNUNET_free (bp_master[c_p].connect_ops);
330   }
331
332   for (c_p = 0; c_p < c_slave_peers; c_p++)
333   {
334       next = bp_slaves[c_p].p_head;
335       for (cur = next; cur != NULL; cur = next )
336       {
337           next = cur->next;
338           GNUNET_CONTAINER_DLL_remove (bp_slaves[c_p].p_head, bp_slaves[c_p].p_tail, cur);
339           GNUNET_free (cur);
340       }
341
342       if (NULL != bp_slaves[c_p].cth)
343       {
344           GNUNET_CORE_notify_transmit_ready_cancel(bp_slaves[c_p].cth);
345           bp_slaves[c_p].cth = NULL;
346       }
347
348       if (NULL != bp_slaves[c_p].ats_perf_op)
349       {
350           GNUNET_TESTBED_operation_done (bp_slaves[c_p].ats_perf_op);
351           bp_slaves[c_p].ats_perf_op = NULL;
352       }
353
354       if (NULL != bp_slaves[c_p].core_op)
355       {
356           GNUNET_TESTBED_operation_done (bp_slaves[c_p].core_op);
357           bp_slaves[c_p].core_op = NULL;
358       }
359
360       if (NULL != bp_slaves[c_p].info_op)
361       {
362           GNUNET_break (0);
363           GNUNET_TESTBED_operation_done (bp_slaves[c_p].info_op);
364           bp_slaves[c_p].info_op = NULL;
365       }
366   }
367   GNUNET_SCHEDULER_shutdown();
368 }
369
370 static struct BenchmarkPeer *
371 find_peer (const struct GNUNET_PeerIdentity * peer)
372 {
373   int c_p;
374
375   for (c_p = 0; c_p < c_master_peers; c_p++)
376   {
377       if (0 == memcmp (&bp_master[c_p].id, peer, sizeof (struct GNUNET_PeerIdentity)))
378         return &bp_master[c_p];
379   }
380
381   for (c_p = 0; c_p < c_slave_peers; c_p++)
382   {
383       if (0 == memcmp (&bp_slaves[c_p].id, peer, sizeof (struct GNUNET_PeerIdentity)))
384         return &bp_slaves[c_p];
385   }
386
387   return NULL;
388 }
389
390
391 static void
392 store_information (struct GNUNET_PeerIdentity *id,
393                  const struct GNUNET_HELLO_Address *address,
394                  int address_active,
395                  struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
396                  struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
397                  const struct GNUNET_ATS_Information *ats,
398                  uint32_t ats_count)
399 {
400   struct BenchmarkPeer *bp;
401
402   bp = find_peer (id);
403
404   if (NULL == bp)
405   {
406     GNUNET_break (0);
407     return;
408   }
409 }
410
411 static void
412 ats_performance_info_cb (void *cls,
413                          const struct GNUNET_HELLO_Address *address,
414                          int address_active,
415                          struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
416                          struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
417                          const struct GNUNET_ATS_Information *ats,
418                          uint32_t ats_count)
419 {
420   struct BenchmarkPeer *bp = cls;
421   int c_a;
422   char *peer_id;
423
424   peer_id = GNUNET_strdup (GNUNET_i2s (&bp->id));
425   for (c_a = 0; c_a < ats_count; c_a++)
426   {
427       /*GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("%c %03u: %s %s %u\n"),
428               (GNUNET_YES == p->master) ? 'M' : 'S',
429               p->no,
430     GNUNET_i2s (&address->peer),
431     GNUNET_ATS_print_property_type(ntohl(ats[c_a].type)),
432     ntohl(ats[c_a].value));*/
433   }
434
435   if ((GNUNET_YES == bp->master) &&
436       (0 == memcmp (&address->peer,  &bp->destination->id,
437       sizeof (struct GNUNET_PeerIdentity))))
438   {
439       GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Bandwidth for master %u: %lu %lu\n",
440           bp->no,
441           (long unsigned int) ntohl(bandwidth_in.value__),
442           (long unsigned int) ntohl(bandwidth_in.value__));
443   }
444
445   store_information (&bp->id, address, address_active,
446   bandwidth_in, bandwidth_out,
447   ats, ats_count);
448
449   GNUNET_free (peer_id);
450 }
451
452 static size_t
453 core_send_ready (void *cls, size_t size, void *buf)
454 {
455   static char msgbuf[TEST_MESSAGE_SIZE];
456   struct BenchmarkPeer *bp = cls;
457   struct GNUNET_MessageHeader *msg;
458
459   bp->cth = NULL;
460
461   bp->messages_sent ++;
462   if (GNUNET_YES == bp->partner_msg)
463   {
464       bp->messages_sent_partner ++;
465       bp->partner_msg = GNUNET_NO;
466   }
467
468   msg = (struct GNUNET_MessageHeader *) &msgbuf;
469   memset (&msgbuf, 'a', TEST_MESSAGE_SIZE);
470   msg->type = htons (TEST_MESSAGE_TYPE_PING);
471   msg->size = htons (TEST_MESSAGE_SIZE);
472   memcpy (buf, msg, TEST_MESSAGE_SIZE);
473   return TEST_MESSAGE_SIZE;
474 }
475
476 static struct BenchmarkPeer *
477 get_next (struct BenchmarkPeer *p)
478 {
479   uint32_t b_index;
480   uint32_t index;
481   int counter;
482
483   if (0 == p->send_mask)
484     p->send_mask = (1 << c_slave_peers) - 1; /* Next round */
485
486   GNUNET_assert (p->send_mask <= (1 << c_slave_peers) - 1);
487   counter = 0;
488   do
489   {
490     index = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, c_slave_peers);
491     b_index = 1 << index;
492     counter++;
493   }
494   while ((b_index != (p->send_mask & b_index)) && (counter < c_slave_peers));
495   if ((b_index != (p->send_mask & b_index)) && (counter == c_slave_peers))
496   {
497     /* To many random attempts use fcfs */
498     for (index = 0; index < c_slave_peers - 1; index ++)
499     {
500         b_index = 1 << index;
501         if (b_index == (p->send_mask & b_index))
502           break;
503     }
504   }
505   p->send_mask ^= b_index; /* Remove bit */
506   return &bp_slaves[index];
507 }
508
509
510 static void
511 ats_pref_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
512 {
513   static double last = 1;
514   struct BenchmarkPeer *bp = cls;
515
516   bp->ats_task = GNUNET_SCHEDULER_NO_TASK;
517
518   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Set preference for master %u: %f\n",
519     bp->no, last);
520   GNUNET_ATS_performance_change_preference (bp->p_handle, &bp->destination->id,
521     pref_val, (double) last,
522     GNUNET_ATS_PREFERENCE_END);
523   last++;
524   bp->ats_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
525     &ats_pref_task, bp);
526 }
527
528
529 static void 
530 do_benchmark ()
531 {
532   int c_m;
533   struct BenchmarkPeer *s;
534   struct BenchmarkPeer *bp;
535
536   if ((state.connected_ATS_service == GNUNET_NO) ||
537       (state.connected_CORE_service == GNUNET_NO) ||
538       (state.connected_PEERS == GNUNET_NO) ||
539       (state.connected_CORE == GNUNET_NO))
540     return;
541
542   state.benchmarking = GNUNET_YES;
543   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
544     _("Benchmarking start\n"));
545
546   if (GNUNET_SCHEDULER_NO_TASK != shutdown_task)
547     GNUNET_SCHEDULER_cancel (shutdown_task);
548   shutdown_task = GNUNET_SCHEDULER_add_delayed (BENCHMARK_DURATION, &do_shutdown, NULL);
549
550   /* Start sending test messages */
551   for (c_m = 0; c_m < c_master_peers; c_m ++)
552   {
553       bp = &bp_master[c_m];
554       s = get_next (bp);
555       if (0 == memcmp(&s->id, &bp->destination->id, sizeof (struct GNUNET_PeerIdentity)))
556         bp->partner_msg = GNUNET_YES;
557       bp->cth = GNUNET_CORE_notify_transmit_ready (bp->ch,
558           GNUNET_NO, 0, GNUNET_TIME_UNIT_MINUTES,
559           &s->id, TEST_MESSAGE_SIZE, &core_send_ready, bp);
560       bp->ats_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
561             &ats_pref_task, bp);
562   }
563 }
564
565
566 static void 
567 connect_completion_callback (void *cls,
568                              struct GNUNET_TESTBED_Operation *op,
569                              const char *emsg)
570 {
571   struct ConnectOperation *cop = cls;
572   static int ops = 0 ;
573   int c;
574   if (NULL == emsg)
575   {
576     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
577         _("Connected master peer %u with peer %u\n"), cop->master->no, cop->slave->no);
578   }
579   else
580   {
581     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
582         _("Failed to connect master peer%u with peer %u\n"), cop->master->no, cop->slave->no);
583     GNUNET_break (0);
584     if (GNUNET_SCHEDULER_NO_TASK != shutdown_task)
585             GNUNET_SCHEDULER_cancel(shutdown_task);
586     shutdown_task = GNUNET_SCHEDULER_add_now (do_shutdown, NULL);
587   }
588   GNUNET_TESTBED_operation_done(op);
589   ops++;
590   for (c = 0; c < c_slave_peers; c++)
591   {
592     if (cop == &cop->master->connect_ops[c])
593             cop->master->connect_ops[c].connect_op = NULL;
594   }
595   if (ops == c_master_peers * c_slave_peers)
596   {
597     state.connected_PEERS = GNUNET_YES;
598     GNUNET_SCHEDULER_add_now (&do_benchmark, NULL);
599   }
600 }
601
602
603 static void
604 do_connect_peers (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
605 {
606   int c_m;
607   int c_s;
608   struct BenchmarkPeer *bp;
609
610   if ((state.connected_ATS_service == GNUNET_NO) ||
611       (state.connected_CORE_service == GNUNET_NO))
612   {
613     return;
614   }
615
616   GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Connecting peers on CORE level\n"));
617
618   for (c_m = 0; c_m < c_master_peers; c_m ++)
619   {
620       bp = &bp_master[c_m];
621       bp->connect_ops = GNUNET_malloc (c_slave_peers * sizeof (struct ConnectOperation));
622
623       for (c_s = 0; c_s < c_slave_peers; c_s ++)
624       {
625           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
626               _("Connecting master peer %u with slave peer %u\n"),
627               bp->no, bp_slaves[c_s].no);
628           bp->connect_ops[c_s].master = bp;
629           bp->connect_ops[c_s].slave = &bp_slaves[c_s];
630           bp->connect_ops[c_s].connect_op = GNUNET_TESTBED_overlay_connect( NULL,
631                   &connect_completion_callback,
632                   &bp->connect_ops[c_s],
633                   bp_slaves[c_s].peer,
634                   bp->peer);
635           if (NULL == bp->connect_ops[c_s].connect_op)
636           {
637               GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
638                           _("Could not connect master peer %u and slave peer %u\n"),
639                           bp->no, bp_slaves[c_s].no);
640               GNUNET_break (0);
641               if (GNUNET_SCHEDULER_NO_TASK != shutdown_task)
642                   GNUNET_SCHEDULER_cancel(shutdown_task);
643               shutdown_task = GNUNET_SCHEDULER_add_now (do_shutdown, NULL);
644               return;
645           }
646       }
647   }
648 }
649
650
651 /**
652  * Controller event callback
653  *
654  * @param cls NULL
655  * @param event the controller event
656  */
657 static void
658 controller_event_cb (void *cls,
659                      const struct GNUNET_TESTBED_EventInformation *event)
660 {
661         //struct BenchmarkPeer *p = cls;
662   switch (event->type)
663   {
664   case GNUNET_TESTBED_ET_CONNECT:
665     break;
666   case GNUNET_TESTBED_ET_OPERATION_FINISHED:
667     break;
668   default:
669     GNUNET_break (0);
670     result = 2;
671     GNUNET_SCHEDULER_cancel (shutdown_task);
672     shutdown_task = GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
673   }
674 }
675
676 /**
677  * Method called whenever a given peer connects.
678  *
679  * @param cls closure
680  * @param peer peer identity this notification is about
681  */
682 static void
683 core_connect_cb (void *cls, const struct GNUNET_PeerIdentity * peer)
684 {
685   struct BenchmarkPeer *p = cls;
686   struct BenchmarkPeer *t;
687   char *id;
688   int c;
689   int cs;
690
691   t = find_peer (peer);
692   if (NULL == t)
693   {
694         GNUNET_break (0);
695         return;
696   }
697
698   id = GNUNET_strdup (GNUNET_i2s (&p->id));
699   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
700               "%s %s connected to %s %s\n",
701               (p->master == GNUNET_YES) ? "Master": "Slave",
702               id,
703               (t->master == GNUNET_YES) ? "Master": "Slave",
704               GNUNET_i2s (peer));
705
706   p->core_connections ++;
707   if ((GNUNET_YES == p->master) && (GNUNET_NO == t->master) && (GNUNET_NO == state.connected_CORE))
708   {
709         p->slave_connections ++;
710
711                 if (p->slave_connections == c_slave_peers)
712                 {
713                         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
714                                         "Master %u connected all slaves\n", p->no);
715                 }
716                 cs = GNUNET_YES;
717                 for (c = 0; c < c_master_peers; c ++)
718                 {
719                         if (bp_master[c].slave_connections != c_slave_peers)
720                                 cs = GNUNET_NO;
721                 }
722                 if (GNUNET_YES == cs)
723                 {
724                         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
725                         "All master peers connected all slave peers\n", id, GNUNET_i2s (peer));
726                         state.connected_CORE = GNUNET_YES;
727                         GNUNET_SCHEDULER_add_now (&do_benchmark, NULL);
728                 }
729         }
730         GNUNET_free (id);
731 }
732
733
734 /**
735  * Method called whenever a peer disconnects.
736  *
737  * @param cls closure
738  * @param peer peer identity this notification is about
739  */
740 static void
741 core_disconnect_cb (void *cls, const struct GNUNET_PeerIdentity * peer)
742 {
743   struct BenchmarkPeer *p = cls;
744   struct BenchmarkPeer *t;
745   char *id;
746
747   t = find_peer (peer);
748   if (NULL == t)
749   {
750         GNUNET_break (0);
751         return;
752   }
753
754   id = GNUNET_strdup (GNUNET_i2s (&p->id));
755   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
756               "%s disconnected from %s \n", id, GNUNET_i2s (peer));
757   GNUNET_assert (p->core_connections > 0);
758   p->core_connections --;
759
760   if ((GNUNET_YES == state.benchmarking) &&
761                 ((GNUNET_YES == p->master) || (GNUNET_YES == t->master)))
762   {
763     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
764               "%s disconnected from %s while benchmarking \n", id, GNUNET_i2s (peer));
765   }
766
767   GNUNET_free (id);
768 }
769
770 static size_t
771 core_send_echo_queued_ready (void *cls, size_t size, void *buf);
772
773 static size_t
774 core_send_echo_ready (void *cls, size_t size, void *buf)
775 {
776         static char msgbuf[TEST_MESSAGE_SIZE];
777         struct BenchmarkPeer *bp = cls;
778         struct GNUNET_MessageHeader *msg;
779
780         bp->cth = NULL;
781
782         msg = (struct GNUNET_MessageHeader *) &msgbuf;
783         memset (&msgbuf, 'a', TEST_MESSAGE_SIZE);
784         msg->type = htons (TEST_MESSAGE_TYPE_PONG);
785         msg->size = htons (TEST_MESSAGE_SIZE);
786         memcpy (buf, msg, TEST_MESSAGE_SIZE);
787
788         /* send echo */
789         if (NULL != bp->p_head)
790                 bp->cth = GNUNET_CORE_notify_transmit_ready (bp->ch,
791                                 GNUNET_NO, 0, GNUNET_TIME_UNIT_MINUTES,
792                                 &bp->p_head->target,
793                                 TEST_MESSAGE_SIZE, &core_send_echo_queued_ready, bp);
794
795         return TEST_MESSAGE_SIZE;
796 }
797
798 static size_t
799 core_send_echo_queued_ready (void *cls, size_t size, void *buf)
800 {
801         struct BenchmarkPeer *bp = cls;
802         struct PendingMessages *pm;
803         GNUNET_assert (NULL != bp->p_head);
804
805         pm = bp->p_head;
806         GNUNET_CONTAINER_DLL_remove (bp->p_head, bp->p_tail, pm);
807         GNUNET_free (pm);
808
809
810         return core_send_echo_ready (cls, size, buf);
811
812 }
813
814
815 static int
816 core_handle_ping (void *cls, const struct GNUNET_PeerIdentity *other,
817                 const struct GNUNET_MessageHeader *message)
818 {
819         struct BenchmarkPeer *me = cls;
820         struct BenchmarkPeer *remote;
821         struct PendingMessages *pm;
822
823         remote = find_peer (other);
824
825         if (NULL == remote)
826         {
827                 GNUNET_break (0);
828                 return GNUNET_SYSERR;
829         }
830
831         if (NULL != me->cth)
832         {
833                 pm = GNUNET_malloc (sizeof (struct PendingMessages));
834                 pm->target = (*other);
835                 GNUNET_CONTAINER_DLL_insert_tail (me->p_head, me->p_tail, pm);
836                 return GNUNET_OK;
837         }
838
839         if (GNUNET_NO == remote->master)
840         {
841                 GNUNET_break (0);
842                 return GNUNET_OK;
843         }
844
845         me->messages_received ++;
846         /* send echo */
847         me->cth = GNUNET_CORE_notify_transmit_ready (me->ch,
848                                 GNUNET_NO, 0, GNUNET_TIME_UNIT_MINUTES,
849                                 &remote->id,
850                                 TEST_MESSAGE_SIZE, &core_send_echo_ready, me);
851         return GNUNET_OK;
852 }
853
854 static int
855 core_handle_pong (void *cls, const struct GNUNET_PeerIdentity *other,
856                 const struct GNUNET_MessageHeader *message)
857 {
858         struct BenchmarkPeer *me = cls;
859         struct BenchmarkPeer *remote;
860         struct BenchmarkPeer *next;
861
862         remote = find_peer (other);
863
864         if (NULL == remote)
865         {
866                 GNUNET_break (0);
867                 return GNUNET_SYSERR;
868         }
869
870         if (NULL != me->cth)
871         {
872                 GNUNET_break (0);
873                 return GNUNET_OK;
874         }
875
876         if (GNUNET_YES == remote->master)
877         {
878                 GNUNET_break (0);
879                 return GNUNET_OK;
880         }
881         me->messages_received ++;
882         next = get_next (me);
883         if (0 == memcmp(&remote->id, &me->destination->id, sizeof (struct GNUNET_PeerIdentity)))
884                         me->partner_msg = GNUNET_YES;
885         me->cth = GNUNET_CORE_notify_transmit_ready (me->ch,
886                                 GNUNET_NO, 0, GNUNET_TIME_UNIT_MINUTES,
887                                 &next->id,
888                                 TEST_MESSAGE_SIZE, &core_send_ready, me);
889
890         return GNUNET_OK;
891 }
892
893
894 /**
895  * Called to open a connection to the peer's ATS performance
896  *
897  * @param cls peer context
898  * @param cfg configuration of the peer to connect to; will be available until
899  *          GNUNET_TESTBED_operation_done() is called on the operation returned
900  *          from GNUNET_TESTBED_service_connect()
901  * @return service handle to return in 'op_result', NULL on error
902  */
903 static void *
904 core_connect_adapter (void *cls,
905                       const struct GNUNET_CONFIGURATION_Handle *cfg)
906 {
907   struct BenchmarkPeer *peer = cls;
908
909   static const struct GNUNET_CORE_MessageHandler handlers[] = {
910       {&core_handle_ping, TEST_MESSAGE_TYPE_PING, 0},
911       {&core_handle_pong, TEST_MESSAGE_TYPE_PONG, 0},
912       {NULL, 0, 0}
913   };
914
915   peer->ch = GNUNET_CORE_connect(cfg, peer, NULL,
916                                  core_connect_cb, core_disconnect_cb,
917                                  NULL, GNUNET_NO, NULL, GNUNET_NO, handlers);
918   if (NULL == peer->ch)
919     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
920                 "Failed to create core connection \n");
921   return peer->ch;
922 }
923
924
925 /**
926  * Callback to be called when a service connect operation is completed
927  *
928  * @param cls the callback closure from functions generating an operation
929  * @param op the operation that has been finished
930  * @param ca_result the service handle returned from GNUNET_TESTBED_ConnectAdapter()
931  * @param emsg error message in case the operation has failed; will be NULL if
932  *          operation has executed successfully.
933  */
934 static void
935 core_connect_completion_cb (void *cls,
936                             struct GNUNET_TESTBED_Operation *op,
937                             void *ca_result,
938                             const char *emsg )
939 {
940   static int core_done = 0;
941   if ((NULL != emsg) || (NULL == ca_result))
942   {
943     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
944                     _("Initialization failed, shutdown\n"));
945     GNUNET_break (0);
946     if (GNUNET_SCHEDULER_NO_TASK != shutdown_task)
947             GNUNET_SCHEDULER_cancel(shutdown_task);
948     shutdown_task = GNUNET_SCHEDULER_add_now (do_shutdown, NULL);
949     return;
950   }
951   core_done ++;
952
953   if (core_done == c_slave_peers + c_master_peers)
954   {
955     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
956                     "Connected to all CORE services\n");
957     state.connected_CORE_service = GNUNET_YES;
958     GNUNET_SCHEDULER_add_now (&do_connect_peers, NULL);
959   }
960 }
961
962
963 /**
964  * Called to disconnect from peer's statistics service
965  *
966  * @param cls peer context
967  * @param op_result service handle returned from the connect adapter
968  */
969 static void
970 core_disconnect_adapter (void *cls, void *op_result)
971 {
972   struct BenchmarkPeer *peer = cls;
973
974   GNUNET_CORE_disconnect (peer->ch);
975   peer->ch = NULL;
976 }
977
978 static void
979 do_connect_core (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
980 {
981   int c_p;
982   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
983                   "Connecting to all CORE services\n");
984   for (c_p = 0; c_p < c_master_peers; c_p++)
985   {
986     bp_master[c_p].core_op = GNUNET_TESTBED_service_connect (NULL,
987         bp_master[c_p].peer, "core",
988         core_connect_completion_cb, NULL,
989         &core_connect_adapter,
990         &core_disconnect_adapter,
991         &bp_master[c_p]);
992   }
993
994   for (c_p = 0; c_p < c_slave_peers; c_p++)
995   {
996     bp_slaves[c_p].core_op = GNUNET_TESTBED_service_connect (NULL,
997       bp_slaves[c_p].peer, "core",
998       core_connect_completion_cb, NULL,
999       &core_connect_adapter,
1000       &core_disconnect_adapter,
1001       &bp_slaves[c_p]);
1002   }
1003 }
1004
1005
1006
1007 /**
1008  * Called to open a connection to the peer's ATS performance
1009  *
1010  * @param cls peer context
1011  * @param cfg configuration of the peer to connect to; will be available until
1012  *          GNUNET_TESTBED_operation_done() is called on the operation returned
1013  *          from GNUNET_TESTBED_service_connect()
1014  * @return service handle to return in 'op_result', NULL on error
1015  */
1016 static void *
1017 ats_perf_connect_adapter (void *cls,
1018                       const struct GNUNET_CONFIGURATION_Handle *cfg)
1019 {
1020   struct BenchmarkPeer *peer = cls;
1021
1022   peer->p_handle = GNUNET_ATS_performance_init (cfg, &ats_performance_info_cb, peer);
1023   if (NULL == peer->p_handle)
1024     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to create ATS performance handle \n");
1025   return peer->p_handle;
1026 }
1027
1028
1029 /**
1030  * Called to disconnect from peer's statistics service
1031  *
1032  * @param cls peer context
1033  * @param op_result service handle returned from the connect adapter
1034  */
1035 static void
1036 ats_perf_disconnect_adapter (void *cls, void *op_result)
1037 {
1038   struct BenchmarkPeer *peer = cls;
1039
1040   GNUNET_ATS_performance_done(peer->p_handle);
1041   peer->p_handle = NULL;
1042 }
1043
1044
1045 /**
1046  * Callback to be called when a service connect operation is completed
1047  *
1048  * @param cls the callback closure from functions generating an operation
1049  * @param op the operation that has been finished
1050  * @param ca_result the service handle returned from GNUNET_TESTBED_ConnectAdapter()
1051  * @param emsg error message in case the operation has failed; will be NULL if
1052  *          operation has executed successfully.
1053  */
1054 static void
1055 ats_connect_completion_cb (void *cls,
1056                            struct GNUNET_TESTBED_Operation *op,
1057                            void *ca_result,
1058                            const char *emsg )
1059 {
1060   static int op_done = 0;
1061   if ((NULL != emsg) || (NULL == ca_result))
1062   {
1063     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1064                     _("Initialization failed, shutdown\n"));
1065     GNUNET_break (0);
1066     if (GNUNET_SCHEDULER_NO_TASK != shutdown_task)
1067             GNUNET_SCHEDULER_cancel(shutdown_task);
1068     shutdown_task = GNUNET_SCHEDULER_add_now (do_shutdown, NULL);
1069     return;
1070   }
1071
1072   op_done ++;
1073   if (op_done == (c_slave_peers + c_master_peers))
1074   {
1075     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1076                     "Connected to all ATS services\n");
1077     state.connected_ATS_service = GNUNET_YES;
1078     GNUNET_SCHEDULER_add_now (&do_connect_core, NULL);
1079   }
1080 }
1081
1082 static void
1083 do_connect_ats (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1084 {
1085   int c_p;
1086   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1087                   "Connecting to all ATS services %u\n", c_slave_peers);
1088   for (c_p = 0; c_p < c_master_peers; c_p++)
1089   {
1090     bp_master[c_p].ats_perf_op = GNUNET_TESTBED_service_connect (NULL,
1091         bp_master[c_p].peer, "ats",
1092         ats_connect_completion_cb, NULL,
1093         &ats_perf_connect_adapter,
1094         &ats_perf_disconnect_adapter,
1095         &bp_master[c_p]);
1096
1097   }
1098
1099   for (c_p = 0; c_p < c_slave_peers; c_p++)
1100   {
1101     bp_slaves[c_p].ats_perf_op = GNUNET_TESTBED_service_connect (NULL,
1102         bp_slaves[c_p].peer, "ats",
1103         ats_connect_completion_cb, NULL,
1104         &ats_perf_connect_adapter,
1105         &ats_perf_disconnect_adapter,
1106         &bp_slaves[c_p]);
1107   }
1108
1109 }
1110
1111
1112 /**
1113  * Callback to be called when the requested peer information is available
1114  *
1115  * @param cb_cls the closure from GNUNET_TETSBED_peer_get_information()
1116  * @param op the operation this callback corresponds to
1117  * @param pinfo the result; will be NULL if the operation has failed
1118  * @param emsg error message if the operation has failed; will be NULL if the
1119  *          operation is successfull
1120  */
1121 static void
1122 peerinformation_cb (void *cb_cls,
1123                     struct GNUNET_TESTBED_Operation *op,
1124                     const struct GNUNET_TESTBED_PeerInformation*pinfo,
1125                     const char *emsg)
1126 {
1127   struct BenchmarkPeer *p = cb_cls;
1128         static int done = 0;
1129
1130   if (pinfo->pit == GNUNET_TESTBED_PIT_IDENTITY)
1131   {
1132     p->id = *pinfo->result.id;
1133     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1134                 "[%c %03u] Peers %s\n",
1135                 (p->master == GNUNET_YES) ? 'M' : 'S', p->no, GNUNET_i2s (&p->id));
1136   }
1137   else
1138   {
1139     GNUNET_assert (0);
1140   }
1141   GNUNET_TESTBED_operation_done (op);
1142   p->info_op = NULL;
1143   done++;
1144
1145   if (done == c_master_peers + c_slave_peers)
1146   {
1147     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1148                 "Retrieved all peer ID, connect to ATS\n");
1149     state.connected_CORE_service = GNUNET_YES;
1150     GNUNET_SCHEDULER_add_now (&do_connect_ats, NULL);
1151   }
1152 }
1153
1154
1155 /**
1156  * Signature of a main function for a testcase.
1157  *
1158  * @param cls closure
1159  * @param num_peers number of peers in 'peers'
1160  * @param peers_ handle to peers run in the testbed
1161  * @param links_succeeded the number of overlay link connection attempts that
1162  *          succeeded
1163  * @param links_failed the number of overlay link connection attempts that
1164  *          failed
1165  */
1166 static void
1167 test_main (void *cls,
1168            struct GNUNET_TESTBED_RunHandle *h,
1169            unsigned int num_peers,
1170            struct GNUNET_TESTBED_Peer **peers_,
1171            unsigned int links_succeeded,
1172            unsigned int links_failed)
1173 {
1174   int c_p;
1175
1176   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1177               _("Benchmarking solver `%s' on preference `%s' with %u master and %u slave peers\n"),
1178               solver, pref_str, c_master_peers, c_slave_peers);
1179
1180   shutdown_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply(TEST_TIMEOUT, c_master_peers + c_slave_peers), &do_shutdown, NULL);
1181
1182   GNUNET_assert (NULL == cls);
1183   GNUNET_assert (c_slave_peers + c_master_peers == num_peers);
1184   GNUNET_assert (NULL != peers_);
1185
1186   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1187               _("Initializing... \n"));
1188
1189   /* Setup master peers */
1190   for (c_p = 0; c_p < c_master_peers; c_p++)
1191   {
1192     GNUNET_assert (NULL != peers_[c_p]);
1193     bp_master[c_p].no = c_p;
1194     bp_master[c_p].send_mask = (1 << c_slave_peers) - 1;
1195     bp_master[c_p].master = GNUNET_YES;
1196     bp_master[c_p].peer = peers_[c_p];
1197     bp_master[c_p].info_op = GNUNET_TESTBED_peer_get_information (bp_master[c_p].peer,
1198                                                            GNUNET_TESTBED_PIT_IDENTITY,
1199                                                            &peerinformation_cb, &bp_master[c_p]);
1200
1201     /* Select ATS measurement partner */
1202     bp_master[c_p].destination = &bp_slaves[c_p];
1203   }
1204
1205   /* Setup slave peers */
1206   for (c_p = 0; c_p < c_slave_peers; c_p++)
1207   {
1208     GNUNET_assert (NULL != peers_[c_p + c_master_peers]);
1209     bp_slaves[c_p].no = c_p + c_master_peers;
1210     bp_slaves[c_p].master = GNUNET_NO;
1211     bp_slaves[c_p].peer = peers_[c_p + c_master_peers];
1212     bp_slaves[c_p].info_op = GNUNET_TESTBED_peer_get_information (bp_slaves[c_p].peer,
1213                                                            GNUNET_TESTBED_PIT_IDENTITY, 
1214                                                            &peerinformation_cb, &bp_slaves[c_p]);
1215   }
1216
1217 }
1218
1219
1220 int
1221 main (int argc, char *argv[])
1222 {
1223   char *tmp;
1224   char *tmp_sep;
1225   char *test_name;
1226   char *conf_name;
1227   char *dotexe;
1228   char *prefs[GNUNET_ATS_PreferenceCount] = GNUNET_ATS_PreferenceTypeString;
1229   int c;
1230
1231   result = 0;
1232
1233   /* figure out testname */
1234   tmp = strstr (argv[0], TESTNAME_PREFIX);
1235   if (NULL == tmp)
1236   {
1237         fprintf (stderr, "Unable to parse test name `%s'\n", argv[0]);
1238         return GNUNET_SYSERR;
1239   }
1240   tmp += strlen(TESTNAME_PREFIX);
1241   solver = GNUNET_strdup (tmp);
1242   if (NULL != (dotexe = strstr (solver, ".exe")) &&
1243       dotexe[4] == '\0')
1244     dotexe[0] = '\0';
1245   tmp_sep = strchr (solver, '_');
1246   if (NULL == tmp_sep)
1247   {
1248         fprintf (stderr, "Unable to parse test name `%s'\n", argv[0]);
1249         GNUNET_free (solver);
1250         return GNUNET_SYSERR;
1251   }
1252   tmp_sep[0] = '\0';
1253   pref_str = GNUNET_strdup(tmp_sep + 1);
1254
1255   GNUNET_asprintf(&conf_name, "%s%s_%s.conf", TESTNAME_PREFIX, solver, pref_str);
1256   GNUNET_asprintf(&test_name, "%s%s_%s", TESTNAME_PREFIX, solver, pref_str);
1257
1258   for (c = 0; c <= strlen (pref_str); c++)
1259   {
1260         pref_str[c] = toupper(pref_str[c]);
1261   }
1262   pref_val = -1;
1263         for (c = 1; c < GNUNET_ATS_PreferenceCount; c++)
1264         {
1265                 if (0 == strcmp (pref_str, prefs[c]))
1266                 {
1267                         pref_val = c;
1268                         break;
1269                 }
1270         }
1271         if (-1 == pref_val)
1272         {
1273                 fprintf (stderr, "Unknown preference: `%s'\n", pref_str);
1274           GNUNET_free (solver);
1275           GNUNET_free (pref_str);
1276           return -1;
1277         }
1278
1279   for (c = 0; c < (argc -1); c++)
1280   {
1281         if (0 == strcmp(argv[c], "-s"))
1282                 break;
1283   }
1284   if (c < argc-1)
1285   {
1286     if ((0L != (c_slave_peers = strtol (argv[c + 1], NULL, 10))) && (c_slave_peers >= 1))
1287       fprintf (stderr, "Starting %u slave peers\n", c_slave_peers);
1288     else
1289         c_slave_peers = DEFAULT_SLAVES_NUM;
1290   }
1291   else
1292         c_slave_peers = DEFAULT_SLAVES_NUM;
1293
1294   for (c = 0; c < (argc -1); c++)
1295   {
1296         if (0 == strcmp(argv[c], "-m"))
1297                 break;
1298   }
1299   if (c < argc-1)
1300   {
1301     if ((0L != (c_master_peers = strtol (argv[c + 1], NULL, 10))) && (c_master_peers >= 2))
1302       fprintf (stderr, "Starting %u master peers\n", c_master_peers);
1303     else
1304         c_master_peers = DEFAULT_MASTERS_NUM;
1305   }
1306   else
1307         c_master_peers = DEFAULT_MASTERS_NUM;
1308
1309   bp_slaves = GNUNET_malloc (c_slave_peers * sizeof (struct BenchmarkPeer));
1310   bp_master = GNUNET_malloc (c_master_peers * sizeof (struct BenchmarkPeer));
1311
1312   state.connected_ATS_service = GNUNET_NO;
1313   state.connected_CORE_service = GNUNET_NO;
1314   state.connected_PEERS = GNUNET_NO;
1315
1316   /* Start topology */
1317   uint64_t event_mask;
1318   event_mask = 0;
1319   event_mask |= (1LL << GNUNET_TESTBED_ET_CONNECT);
1320   event_mask |= (1LL << GNUNET_TESTBED_ET_OPERATION_FINISHED);
1321   (void) GNUNET_TESTBED_test_run ("perf_ats",
1322                                   conf_name, c_slave_peers + c_master_peers,
1323                                   event_mask, &controller_event_cb, NULL,
1324                                   &test_main, NULL);
1325
1326   GNUNET_free (solver);
1327   GNUNET_free (pref_str);
1328   GNUNET_free (conf_name);
1329   GNUNET_free (test_name);
1330   GNUNET_free (bp_slaves);
1331
1332   return result;
1333 }
1334
1335 /* end of file perf_ats.c */