c67933bd214975bfcb2d44f4158edb29a27bbdcc
[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 *preference;
201
202 /**
203  * Pending Responses
204  */
205 struct PendingMessages
206 {
207         struct PendingMessages *prev;
208         struct PendingMessages *next;
209         struct GNUNET_PeerIdentity target;
210 };
211
212
213 /**
214  * Information we track for a peer in the testbed.
215  */
216 struct ConnectOperation
217 {
218         struct BenchmarkPeer *master;
219
220         struct BenchmarkPeer *slave;
221   /**
222    * Testbed operation to connect peers
223    */
224   struct GNUNET_TESTBED_Operation *connect_op;
225
226 };
227
228
229 static void
230 core_connect_completion_cb (void *cls,
231                             struct GNUNET_TESTBED_Operation *op,
232                             void *ca_result,
233                             const char *emsg );
234
235
236 static void evaluate ()
237 {
238         int c_p;
239         struct BenchmarkPeer *bp;
240         int total_out;
241         int partner_out;
242
243   for (c_p = 0; c_p < c_master_peers; c_p++)
244   {
245         bp = &bp_master[c_p];
246         total_out = (bp->messages_sent * TEST_MESSAGE_SIZE) / 10240;
247         partner_out = (bp->messages_sent_partner * TEST_MESSAGE_SIZE) / 10240;
248     fprintf (stderr, _("Peer %u: Out total: %u KiB/s, out partner %u KiB/s\n"),
249                 bp->no,
250                 total_out, partner_out
251                 /*partner_out / (total_out / 100)*/);
252   }
253 }
254
255 /**
256  * Shutdown nicely
257  *
258  * @param cls NULL
259  * @param tc the task context
260  */
261 static void
262 do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
263 {
264   int c_p;
265   int c_op;
266   struct PendingMessages *cur;
267   struct PendingMessages *next;
268
269   shutdown_task = GNUNET_SCHEDULER_NO_TASK;
270
271   state.benchmarking = GNUNET_NO;
272
273   evaluate ();
274   GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Benchmarking done\n"));
275
276   for (c_p = 0; c_p < c_master_peers; c_p++)
277   {
278         next = bp_master[c_p].p_head;
279         for (cur = next; cur != NULL; cur = next )
280         {
281                 next = cur->next;
282                 GNUNET_CONTAINER_DLL_remove (bp_master[c_p].p_head, bp_master[c_p].p_tail, cur);
283                 GNUNET_free (cur);
284         }
285
286         if (GNUNET_SCHEDULER_NO_TASK != bp_master[c_p].ats_task)
287         {
288                 GNUNET_SCHEDULER_cancel (bp_master[c_p].ats_task);
289                 bp_master[c_p].ats_task = GNUNET_SCHEDULER_NO_TASK;
290         }
291
292         if (NULL != bp_master[c_p].cth)
293         {
294                 GNUNET_CORE_notify_transmit_ready_cancel(bp_master[c_p].cth);
295                 bp_master[c_p].cth = NULL;
296         }
297
298         if (NULL != bp_master[c_p].ats_perf_op)
299         {
300                 GNUNET_TESTBED_operation_done (bp_master[c_p].ats_perf_op);
301                 bp_master[c_p].ats_perf_op = NULL;
302         }
303
304         if (NULL != bp_master[c_p].core_op)
305         {
306                 GNUNET_TESTBED_operation_done (bp_master[c_p].core_op);
307                 bp_master[c_p].core_op = NULL;
308         }
309
310         if (NULL != bp_master[c_p].info_op)
311         {
312                 GNUNET_break (0);
313                 GNUNET_TESTBED_operation_done (bp_master[c_p].info_op);
314                 bp_master[c_p].info_op = NULL;
315         }
316
317         for (c_op = 0; c_op < c_slave_peers; c_op++)
318         {
319                 if (NULL != bp_master[c_p].connect_ops[c_op].connect_op)
320                 {
321                 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Failed to connect peer 0 and %u\n"), c_p);
322                 GNUNET_TESTBED_operation_done (bp_master[c_p].connect_ops[c_op].connect_op);
323                 bp_master[c_p].connect_ops[c_op].connect_op = NULL;
324         result = 1;
325                 }
326         }
327         GNUNET_free (bp_master[c_p].connect_ops);
328   }
329
330   for (c_p = 0; c_p < c_slave_peers; c_p++)
331   {
332         next = bp_slaves[c_p].p_head;
333         for (cur = next; cur != NULL; cur = next )
334         {
335                 next = cur->next;
336                 GNUNET_CONTAINER_DLL_remove (bp_slaves[c_p].p_head, bp_slaves[c_p].p_tail, cur);
337                 GNUNET_free (cur);
338         }
339
340         if (NULL != bp_slaves[c_p].cth)
341         {
342                 GNUNET_CORE_notify_transmit_ready_cancel(bp_slaves[c_p].cth);
343                 bp_slaves[c_p].cth = NULL;
344         }
345
346         if (NULL != bp_slaves[c_p].ats_perf_op)
347         {
348                 GNUNET_TESTBED_operation_done (bp_slaves[c_p].ats_perf_op);
349                 bp_slaves[c_p].ats_perf_op = NULL;
350         }
351
352         if (NULL != bp_slaves[c_p].core_op)
353         {
354                 GNUNET_TESTBED_operation_done (bp_slaves[c_p].core_op);
355                 bp_slaves[c_p].core_op = NULL;
356         }
357
358         if (NULL != bp_slaves[c_p].info_op)
359         {
360                 GNUNET_break (0);
361                 GNUNET_TESTBED_operation_done (bp_slaves[c_p].info_op);
362                 bp_slaves[c_p].info_op = NULL;
363         }
364
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
511 static void
512 ats_pref_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
513 {
514         static double last = 1;
515         struct BenchmarkPeer *bp = cls;
516
517         bp->ats_task = GNUNET_SCHEDULER_NO_TASK;
518
519         GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Set preference for master %u: %f\n",
520                         bp->no, last);
521         GNUNET_ATS_performance_change_preference (bp->p_handle, &bp->destination->id,
522                         GNUNET_ATS_PREFERENCE_BANDWIDTH, (double) last,
523                         GNUNET_ATS_PREFERENCE_END);
524         last++;
525         bp->ats_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
526                         &ats_pref_task, bp);
527 }
528
529
530 static void 
531 do_benchmark ()
532 {
533         int c_m;
534         struct BenchmarkPeer *s;
535         struct BenchmarkPeer *bp;
536
537         if ((state.connected_ATS_service == GNUNET_NO) ||
538                         (state.connected_CORE_service == GNUNET_NO) ||
539                         (state.connected_PEERS == GNUNET_NO) ||
540                         (state.connected_CORE == GNUNET_NO))
541                 return;
542
543         state.benchmarking = GNUNET_YES;
544         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
545                         _("Benchmarking start\n"));
546
547         if (GNUNET_SCHEDULER_NO_TASK != shutdown_task)
548                 GNUNET_SCHEDULER_cancel (shutdown_task);
549         shutdown_task = GNUNET_SCHEDULER_add_delayed (BENCHMARK_DURATION, &do_shutdown, NULL);
550
551         /* Start sending test messages */
552         for (c_m = 0; c_m < c_master_peers; c_m ++)
553         {
554                 bp = &bp_master[c_m];
555                 s = get_next (bp);
556                 if (0 == memcmp(&s->id, &bp->destination->id, sizeof (struct GNUNET_PeerIdentity)))
557                         bp->partner_msg = GNUNET_YES;
558                 bp->cth = GNUNET_CORE_notify_transmit_ready (bp->ch,
559                                         GNUNET_NO, 0, GNUNET_TIME_UNIT_MINUTES,
560                                         &s->id,
561                                         TEST_MESSAGE_SIZE, &core_send_ready, bp);
562                 bp->ats_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
563                                 &ats_pref_task, bp);
564         }
565
566
567 }
568
569
570 static void 
571 connect_completion_callback (void *cls,
572                              struct GNUNET_TESTBED_Operation *op,
573                              const char *emsg)
574 {
575         struct ConnectOperation *cop = cls;
576         static int ops = 0 ;
577         int c;
578         if (NULL == emsg)
579         {
580                 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
581                                 _("Connected master peer %u with peer %u\n"), cop->master->no, cop->slave->no);
582         }
583         else
584         {
585                 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
586                                 _("Failed to connect master peer%u with peer %u\n"), cop->master->no, cop->slave->no);
587                 GNUNET_break (0);
588                 if (GNUNET_SCHEDULER_NO_TASK != shutdown_task)
589                         GNUNET_SCHEDULER_cancel(shutdown_task);
590                 shutdown_task = GNUNET_SCHEDULER_add_now (do_shutdown, NULL);
591         }
592         GNUNET_TESTBED_operation_done(op);
593         ops++;
594         for (c = 0; c < c_slave_peers; c++)
595         {
596                 if (cop == &cop->master->connect_ops[c])
597                         cop->master->connect_ops[c].connect_op = NULL;
598         }
599         if (ops == c_master_peers * c_slave_peers)
600         {
601                 state.connected_PEERS = GNUNET_YES;
602                 GNUNET_SCHEDULER_add_now (&do_benchmark, NULL);
603         }
604 }
605
606
607 static void
608 do_connect_peers (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
609 {
610         int c_m;
611         int c_s;
612         struct BenchmarkPeer *bp;
613
614         if ((state.connected_ATS_service == GNUNET_NO) ||
615                         (state.connected_CORE_service == GNUNET_NO))
616         {
617                 return;
618         }
619
620         GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Connecting peers on CORE level\n"));
621
622         for (c_m = 0; c_m < c_master_peers; c_m ++)
623         {
624                 bp = &bp_master[c_m];
625                 bp->connect_ops = GNUNET_malloc (c_slave_peers * sizeof (struct ConnectOperation));
626
627                 for (c_s = 0; c_s < c_slave_peers; c_s ++)
628                 {
629                         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Connecting master peer %u with slave peer %u\n"),
630                                         bp->no, bp_slaves[c_s].no);
631
632                         bp->connect_ops[c_s].master = bp;
633                         bp->connect_ops[c_s].slave = &bp_slaves[c_s];
634                         bp->connect_ops[c_s].connect_op = GNUNET_TESTBED_overlay_connect( NULL,
635                                         &connect_completion_callback,
636                                         &bp->connect_ops[c_s],
637                                         bp_slaves[c_s].peer,
638                                         bp->peer);
639
640                         if (NULL == bp->connect_ops[c_s].connect_op)
641                         {
642                                 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
643                                                 _("Could not connect master peer %u and slave peer %u\n"),
644                                                 bp->no, bp_slaves[c_s].no);
645                                 GNUNET_break (0);
646                                 if (GNUNET_SCHEDULER_NO_TASK != shutdown_task)
647                                         GNUNET_SCHEDULER_cancel(shutdown_task);
648                                 shutdown_task = GNUNET_SCHEDULER_add_now (do_shutdown, NULL);
649                                 return;
650                         }
651                 }
652         }
653 }
654
655
656 /**
657  * Controller event callback
658  *
659  * @param cls NULL
660  * @param event the controller event
661  */
662 static void
663 controller_event_cb (void *cls,
664                      const struct GNUNET_TESTBED_EventInformation *event)
665 {
666         //struct BenchmarkPeer *p = cls;
667   switch (event->type)
668   {
669   case GNUNET_TESTBED_ET_CONNECT:
670     break;
671   case GNUNET_TESTBED_ET_OPERATION_FINISHED:
672     break;
673   default:
674     GNUNET_break (0);
675     result = 2;
676     GNUNET_SCHEDULER_cancel (shutdown_task);
677     shutdown_task = GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
678   }
679 }
680
681 /**
682  * Method called whenever a given peer connects.
683  *
684  * @param cls closure
685  * @param peer peer identity this notification is about
686  */
687 static void
688 core_connect_cb (void *cls, const struct GNUNET_PeerIdentity * peer)
689 {
690   struct BenchmarkPeer *p = cls;
691   struct BenchmarkPeer *t;
692   char *id;
693   int c;
694   int cs;
695
696   t = find_peer (peer);
697   if (NULL == t)
698   {
699         GNUNET_break (0);
700         return;
701   }
702
703   id = GNUNET_strdup (GNUNET_i2s (&p->id));
704   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
705               "%s %s connected to %s %s\n",
706               (p->master == GNUNET_YES) ? "Master": "Slave",
707               id,
708               (t->master == GNUNET_YES) ? "Master": "Slave",
709               GNUNET_i2s (peer));
710
711   p->core_connections ++;
712   if ((GNUNET_YES == p->master) && (GNUNET_NO == t->master) && (GNUNET_NO == state.connected_CORE))
713   {
714         p->slave_connections ++;
715
716                 if (p->slave_connections == c_slave_peers)
717                 {
718                         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
719                                         "Master %u connected all slaves\n", p->no);
720                 }
721                 cs = GNUNET_YES;
722                 for (c = 0; c < c_master_peers; c ++)
723                 {
724                         if (bp_master[c].slave_connections != c_slave_peers)
725                                 cs = GNUNET_NO;
726                 }
727                 if (GNUNET_YES == cs)
728                 {
729                         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
730                         "All master peers connected all slave peers\n", id, GNUNET_i2s (peer));
731                         state.connected_CORE = GNUNET_YES;
732                         GNUNET_SCHEDULER_add_now (&do_benchmark, NULL);
733                 }
734         }
735         GNUNET_free (id);
736 }
737
738
739 /**
740  * Method called whenever a peer disconnects.
741  *
742  * @param cls closure
743  * @param peer peer identity this notification is about
744  */
745 static void
746 core_disconnect_cb (void *cls, const struct GNUNET_PeerIdentity * peer)
747 {
748   struct BenchmarkPeer *p = cls;
749   struct BenchmarkPeer *t;
750   char *id;
751
752   t = find_peer (peer);
753   if (NULL == t)
754   {
755         GNUNET_break (0);
756         return;
757   }
758
759   id = GNUNET_strdup (GNUNET_i2s (&p->id));
760   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
761               "%s disconnected from %s \n", id, GNUNET_i2s (peer));
762   GNUNET_assert (p->core_connections > 0);
763   p->core_connections --;
764
765   if ((GNUNET_YES == state.benchmarking) &&
766                 ((GNUNET_YES == p->master) || (GNUNET_YES == t->master)))
767   {
768     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
769               "%s disconnected from %s while benchmarking \n", id, GNUNET_i2s (peer));
770   }
771
772   GNUNET_free (id);
773 }
774
775 static size_t
776 core_send_echo_queued_ready (void *cls, size_t size, void *buf);
777
778 static size_t
779 core_send_echo_ready (void *cls, size_t size, void *buf)
780 {
781         static char msgbuf[TEST_MESSAGE_SIZE];
782         struct BenchmarkPeer *bp = cls;
783         struct GNUNET_MessageHeader *msg;
784
785         bp->cth = NULL;
786
787         msg = (struct GNUNET_MessageHeader *) &msgbuf;
788         memset (&msgbuf, 'a', TEST_MESSAGE_SIZE);
789         msg->type = htons (TEST_MESSAGE_TYPE_PONG);
790         msg->size = htons (TEST_MESSAGE_SIZE);
791         memcpy (buf, msg, TEST_MESSAGE_SIZE);
792
793         /* send echo */
794         if (NULL != bp->p_head)
795                 bp->cth = GNUNET_CORE_notify_transmit_ready (bp->ch,
796                                 GNUNET_NO, 0, GNUNET_TIME_UNIT_MINUTES,
797                                 &bp->p_head->target,
798                                 TEST_MESSAGE_SIZE, &core_send_echo_queued_ready, bp);
799
800         return TEST_MESSAGE_SIZE;
801 }
802
803 static size_t
804 core_send_echo_queued_ready (void *cls, size_t size, void *buf)
805 {
806         struct BenchmarkPeer *bp = cls;
807         struct PendingMessages *pm;
808         GNUNET_assert (NULL != bp->p_head);
809
810         pm = bp->p_head;
811         GNUNET_CONTAINER_DLL_remove (bp->p_head, bp->p_tail, pm);
812         GNUNET_free (pm);
813
814
815         return core_send_echo_ready (cls, size, buf);
816
817 }
818
819
820 static int
821 core_handle_ping (void *cls, const struct GNUNET_PeerIdentity *other,
822                 const struct GNUNET_MessageHeader *message)
823 {
824         struct BenchmarkPeer *me = cls;
825         struct BenchmarkPeer *remote;
826         struct PendingMessages *pm;
827
828         remote = find_peer (other);
829
830         if (NULL == remote)
831         {
832                 GNUNET_break (0);
833                 return GNUNET_SYSERR;
834         }
835
836         if (NULL != me->cth)
837         {
838                 pm = GNUNET_malloc (sizeof (struct PendingMessages));
839                 pm->target = (*other);
840                 GNUNET_CONTAINER_DLL_insert_tail (me->p_head, me->p_tail, pm);
841                 return GNUNET_OK;
842         }
843
844         if (GNUNET_NO == remote->master)
845         {
846                 GNUNET_break (0);
847                 return GNUNET_OK;
848         }
849
850         me->messages_received ++;
851         /* send echo */
852         me->cth = GNUNET_CORE_notify_transmit_ready (me->ch,
853                                 GNUNET_NO, 0, GNUNET_TIME_UNIT_MINUTES,
854                                 &remote->id,
855                                 TEST_MESSAGE_SIZE, &core_send_echo_ready, me);
856         return GNUNET_OK;
857 }
858
859 static int
860 core_handle_pong (void *cls, const struct GNUNET_PeerIdentity *other,
861                 const struct GNUNET_MessageHeader *message)
862 {
863         struct BenchmarkPeer *me = cls;
864         struct BenchmarkPeer *remote;
865         struct BenchmarkPeer *next;
866
867         remote = find_peer (other);
868
869         if (NULL == remote)
870         {
871                 GNUNET_break (0);
872                 return GNUNET_SYSERR;
873         }
874
875         if (NULL != me->cth)
876         {
877                 GNUNET_break (0);
878                 return GNUNET_OK;
879         }
880
881         if (GNUNET_YES == remote->master)
882         {
883                 GNUNET_break (0);
884                 return GNUNET_OK;
885         }
886         me->messages_received ++;
887         next = get_next (me);
888         if (0 == memcmp(&remote->id, &me->destination->id, sizeof (struct GNUNET_PeerIdentity)))
889                         me->partner_msg = GNUNET_YES;
890         me->cth = GNUNET_CORE_notify_transmit_ready (me->ch,
891                                 GNUNET_NO, 0, GNUNET_TIME_UNIT_MINUTES,
892                                 &next->id,
893                                 TEST_MESSAGE_SIZE, &core_send_ready, me);
894
895         return GNUNET_OK;
896 }
897
898
899 /**
900  * Called to open a connection to the peer's ATS performance
901  *
902  * @param cls peer context
903  * @param cfg configuration of the peer to connect to; will be available until
904  *          GNUNET_TESTBED_operation_done() is called on the operation returned
905  *          from GNUNET_TESTBED_service_connect()
906  * @return service handle to return in 'op_result', NULL on error
907  */
908 static void *
909 core_connect_adapter (void *cls,
910                       const struct GNUNET_CONFIGURATION_Handle *cfg)
911 {
912   struct BenchmarkPeer *peer = cls;
913
914   static const struct GNUNET_CORE_MessageHandler handlers[] = {
915       {&core_handle_ping, TEST_MESSAGE_TYPE_PING, 0},
916       {&core_handle_pong, TEST_MESSAGE_TYPE_PONG, 0},
917       {NULL, 0, 0}
918   };
919
920   peer->ch = GNUNET_CORE_connect(cfg, peer, NULL,
921                                  core_connect_cb, core_disconnect_cb,
922                                  NULL, GNUNET_NO, NULL, GNUNET_NO, handlers);
923   if (NULL == peer->ch)
924     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
925                 "Failed to create core connection \n");
926   return peer->ch;
927 }
928
929
930 /**
931  * Callback to be called when a service connect operation is completed
932  *
933  * @param cls the callback closure from functions generating an operation
934  * @param op the operation that has been finished
935  * @param ca_result the service handle returned from GNUNET_TESTBED_ConnectAdapter()
936  * @param emsg error message in case the operation has failed; will be NULL if
937  *          operation has executed successfully.
938  */
939 static void
940 core_connect_completion_cb (void *cls,
941                             struct GNUNET_TESTBED_Operation *op,
942                             void *ca_result,
943                             const char *emsg )
944 {
945         static int core_done = 0;
946         if ((NULL != emsg) || (NULL == ca_result))
947         {
948                 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
949                                 _("Initialization failed, shutdown\n"));
950                 GNUNET_break (0);
951                 if (GNUNET_SCHEDULER_NO_TASK != shutdown_task)
952                         GNUNET_SCHEDULER_cancel(shutdown_task);
953                 shutdown_task = GNUNET_SCHEDULER_add_now (do_shutdown, NULL);
954                 return;
955         }
956         core_done ++;
957
958         if (core_done == c_slave_peers + c_master_peers)
959         {
960                 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
961                                 "Connected to all CORE services\n");
962                 state.connected_CORE_service = GNUNET_YES;
963                 GNUNET_SCHEDULER_add_now (&do_connect_peers, NULL);
964         }
965 }
966
967
968 /**
969  * Called to disconnect from peer's statistics service
970  *
971  * @param cls peer context
972  * @param op_result service handle returned from the connect adapter
973  */
974 static void
975 core_disconnect_adapter (void *cls, void *op_result)
976 {
977   struct BenchmarkPeer *peer = cls;
978
979   GNUNET_CORE_disconnect (peer->ch);
980   peer->ch = NULL;
981 }
982
983 static void
984 do_connect_core (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
985 {
986         int c_p;
987         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
988                         "Connecting to all CORE services\n");
989   for (c_p = 0; c_p < c_master_peers; c_p++)
990   {
991     bp_master[c_p].core_op = GNUNET_TESTBED_service_connect (NULL,
992                                                         bp_master[c_p].peer, "core",
993                                                       core_connect_completion_cb, NULL,
994                                                       &core_connect_adapter,
995                                                       &core_disconnect_adapter,
996                                                       &bp_master[c_p]);
997
998   }
999
1000   for (c_p = 0; c_p < c_slave_peers; c_p++)
1001   {
1002     bp_slaves[c_p].core_op = GNUNET_TESTBED_service_connect (NULL,
1003                                                 bp_slaves[c_p].peer, "core",
1004                                               core_connect_completion_cb, NULL,
1005                                               &core_connect_adapter,
1006                                               &core_disconnect_adapter,
1007                                               &bp_slaves[c_p]);
1008   }
1009 }
1010
1011
1012
1013 /**
1014  * Called to open a connection to the peer's ATS performance
1015  *
1016  * @param cls peer context
1017  * @param cfg configuration of the peer to connect to; will be available until
1018  *          GNUNET_TESTBED_operation_done() is called on the operation returned
1019  *          from GNUNET_TESTBED_service_connect()
1020  * @return service handle to return in 'op_result', NULL on error
1021  */
1022 static void *
1023 ats_perf_connect_adapter (void *cls,
1024                       const struct GNUNET_CONFIGURATION_Handle *cfg)
1025 {
1026   struct BenchmarkPeer *peer = cls;
1027
1028   peer->p_handle = GNUNET_ATS_performance_init (cfg, &ats_performance_info_cb, peer);
1029   if (NULL == peer->p_handle)
1030     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to create ATS performance handle \n");
1031   return peer->p_handle;
1032 }
1033
1034
1035 /**
1036  * Called to disconnect from peer's statistics service
1037  *
1038  * @param cls peer context
1039  * @param op_result service handle returned from the connect adapter
1040  */
1041 static void
1042 ats_perf_disconnect_adapter (void *cls, void *op_result)
1043 {
1044   struct BenchmarkPeer *peer = cls;
1045
1046   GNUNET_ATS_performance_done(peer->p_handle);
1047   peer->p_handle = NULL;
1048 }
1049
1050
1051 /**
1052  * Callback to be called when a service connect operation is completed
1053  *
1054  * @param cls the callback closure from functions generating an operation
1055  * @param op the operation that has been finished
1056  * @param ca_result the service handle returned from GNUNET_TESTBED_ConnectAdapter()
1057  * @param emsg error message in case the operation has failed; will be NULL if
1058  *          operation has executed successfully.
1059  */
1060 static void
1061 ats_connect_completion_cb (void *cls,
1062                            struct GNUNET_TESTBED_Operation *op,
1063                            void *ca_result,
1064                            const char *emsg )
1065 {
1066         static int op_done = 0;
1067         if ((NULL != emsg) || (NULL == ca_result))
1068         {
1069                 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1070                                 _("Initialization failed, shutdown\n"));
1071                 GNUNET_break (0);
1072                 if (GNUNET_SCHEDULER_NO_TASK != shutdown_task)
1073                         GNUNET_SCHEDULER_cancel(shutdown_task);
1074                 shutdown_task = GNUNET_SCHEDULER_add_now (do_shutdown, NULL);
1075                 return;
1076         }
1077
1078         op_done ++;
1079         if (op_done == (c_slave_peers + c_master_peers))
1080         {
1081                 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1082                                 "Connected to all ATS services\n");
1083                 state.connected_ATS_service = GNUNET_YES;
1084                 GNUNET_SCHEDULER_add_now (&do_connect_core, NULL);
1085         }
1086 }
1087
1088 static void
1089 do_connect_ats (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1090 {
1091         int c_p;
1092         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1093                         "Connecting to all ATS services %u\n", c_slave_peers);
1094   for (c_p = 0; c_p < c_master_peers; c_p++)
1095   {
1096     bp_master[c_p].ats_perf_op = GNUNET_TESTBED_service_connect (NULL,
1097                                                 bp_master[c_p].peer, "ats",
1098                                                           ats_connect_completion_cb, NULL,
1099                                                           &ats_perf_connect_adapter,
1100                                                           &ats_perf_disconnect_adapter,
1101                                                           &bp_master[c_p]);
1102
1103   }
1104
1105   for (c_p = 0; c_p < c_slave_peers; c_p++)
1106   {
1107     bp_slaves[c_p].ats_perf_op = GNUNET_TESTBED_service_connect (NULL,
1108                                                 bp_slaves[c_p].peer, "ats",
1109                                                           ats_connect_completion_cb, NULL,
1110                                                           &ats_perf_connect_adapter,
1111                                                           &ats_perf_disconnect_adapter,
1112                                                           &bp_slaves[c_p]);
1113   }
1114
1115 }
1116
1117
1118 /**
1119  * Callback to be called when the requested peer information is available
1120  *
1121  * @param cb_cls the closure from GNUNET_TETSBED_peer_get_information()
1122  * @param op the operation this callback corresponds to
1123  * @param pinfo the result; will be NULL if the operation has failed
1124  * @param emsg error message if the operation has failed; will be NULL if the
1125  *          operation is successfull
1126  */
1127 static void
1128 peerinformation_cb (void *cb_cls,
1129                     struct GNUNET_TESTBED_Operation *op,
1130                     const struct GNUNET_TESTBED_PeerInformation*pinfo,
1131                     const char *emsg)
1132 {
1133   struct BenchmarkPeer *p = cb_cls;
1134         static int done = 0;
1135
1136   if (pinfo->pit == GNUNET_TESTBED_PIT_IDENTITY)
1137   {
1138     p->id = *pinfo->result.id;
1139     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1140                 "[%c %03u] Peers %s\n",
1141                 (p->master == GNUNET_YES) ? 'M' : 'S', p->no, GNUNET_i2s (&p->id));
1142   }
1143   else
1144   {
1145     GNUNET_assert (0);
1146   }
1147   GNUNET_TESTBED_operation_done (op);
1148   p->info_op = NULL;
1149   done++;
1150
1151   if (done == c_master_peers + c_slave_peers)
1152   {
1153                 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1154                                 "Retrieved all peer ID, connect to ATS\n");
1155                 state.connected_CORE_service = GNUNET_YES;
1156                 GNUNET_SCHEDULER_add_now (&do_connect_ats, NULL);
1157   }
1158 }
1159
1160
1161 /**
1162  * Signature of a main function for a testcase.
1163  *
1164  * @param cls closure
1165  * @param num_peers number of peers in 'peers'
1166  * @param peers_ handle to peers run in the testbed
1167  * @param links_succeeded the number of overlay link connection attempts that
1168  *          succeeded
1169  * @param links_failed the number of overlay link connection attempts that
1170  *          failed
1171  */
1172 static void
1173 test_main (void *cls,
1174            struct GNUNET_TESTBED_RunHandle *h,
1175            unsigned int num_peers,
1176            struct GNUNET_TESTBED_Peer **peers_,
1177            unsigned int links_succeeded,
1178            unsigned int links_failed)
1179 {
1180   int c_p;
1181
1182   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1183               _("Benchmarking solver `%s' on preference `%s' with %u master and %u slave peers\n"),
1184               solver, preference, c_master_peers, c_slave_peers);
1185
1186   shutdown_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply(TEST_TIMEOUT, c_master_peers + c_slave_peers), &do_shutdown, NULL);
1187
1188   GNUNET_assert (NULL == cls);
1189   GNUNET_assert (c_slave_peers + c_master_peers == num_peers);
1190   GNUNET_assert (NULL != peers_);
1191
1192   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1193               _("Initializing... \n"));
1194
1195   for (c_p = 0; c_p < c_master_peers; c_p++)
1196   {
1197     GNUNET_assert (NULL != peers_[c_p]);
1198     bp_master[c_p].no = c_p;
1199     bp_master[c_p].send_mask = (1 << c_slave_peers) - 1;
1200     bp_master[c_p].master = GNUNET_YES;
1201     bp_master[c_p].peer = peers_[c_p];
1202     bp_master[c_p].info_op = GNUNET_TESTBED_peer_get_information (bp_master[c_p].peer,
1203                                                            GNUNET_TESTBED_PIT_IDENTITY,
1204                                                            &peerinformation_cb, &bp_master[c_p]);
1205
1206     /* Select ATS measurement partner */
1207     bp_master[c_p].destination = &bp_slaves[c_p];
1208   }
1209
1210   for (c_p = 0; c_p < c_slave_peers; c_p++)
1211   {
1212     GNUNET_assert (NULL != peers_[c_p + c_master_peers]);
1213     bp_slaves[c_p].no = c_p + c_master_peers;
1214     bp_slaves[c_p].master = GNUNET_NO;
1215     bp_slaves[c_p].peer = peers_[c_p + c_master_peers];
1216     bp_slaves[c_p].info_op = GNUNET_TESTBED_peer_get_information (bp_slaves[c_p].peer,
1217                                                            GNUNET_TESTBED_PIT_IDENTITY, 
1218                                                            &peerinformation_cb, &bp_slaves[c_p]);
1219   }
1220
1221 }
1222
1223
1224 int
1225 main (int argc, char *argv[])
1226 {
1227   char *tmp;
1228   char *tmp_sep;
1229   char *test_name;
1230   char *conf_name;
1231   char *dotexe;
1232   int c;
1233
1234   result = 0;
1235
1236   /* figure out testname */
1237   tmp = strstr (argv[0], TESTNAME_PREFIX);
1238   if (NULL == tmp)
1239   {
1240         fprintf (stderr, "Unable to parse test name `%s'\n", argv[0]);
1241         return GNUNET_SYSERR;
1242   }
1243   tmp += strlen(TESTNAME_PREFIX);
1244   solver = GNUNET_strdup (tmp);
1245   if (NULL != (dotexe = strstr (solver, ".exe")) &&
1246       dotexe[4] == '\0')
1247     dotexe[0] = '\0';
1248   tmp_sep = strchr (solver, '_');
1249   if (NULL == tmp_sep)
1250   {
1251         fprintf (stderr, "Unable to parse test name `%s'\n", argv[0]);
1252         GNUNET_free (solver);
1253         return GNUNET_SYSERR;
1254   }
1255   tmp_sep[0] = '\0';
1256   preference = GNUNET_strdup(tmp_sep + 1);
1257
1258   GNUNET_asprintf(&conf_name, "%s%s_%s.conf", TESTNAME_PREFIX, solver, preference);
1259   GNUNET_asprintf(&test_name, "%s%s_%s", TESTNAME_PREFIX, solver, preference);
1260
1261   for (c = 0; c < (argc -1); c++)
1262   {
1263         if (0 == strcmp(argv[c], "-s"))
1264                 break;
1265   }
1266   if (c < argc-1)
1267   {
1268     if ((0L != (c_slave_peers = strtol (argv[c + 1], NULL, 10))) && (c_slave_peers >= 1))
1269       fprintf (stderr, "Starting %u slave peers\n", c_slave_peers);
1270     else
1271         c_slave_peers = DEFAULT_SLAVES_NUM;
1272   }
1273   else
1274         c_slave_peers = DEFAULT_SLAVES_NUM;
1275
1276   for (c = 0; c < (argc -1); c++)
1277   {
1278         if (0 == strcmp(argv[c], "-m"))
1279                 break;
1280   }
1281   if (c < argc-1)
1282   {
1283     if ((0L != (c_master_peers = strtol (argv[c + 1], NULL, 10))) && (c_master_peers >= 2))
1284       fprintf (stderr, "Starting %u master peers\n", c_master_peers);
1285     else
1286         c_master_peers = DEFAULT_MASTERS_NUM;
1287   }
1288   else
1289         c_master_peers = DEFAULT_MASTERS_NUM;
1290
1291   bp_slaves = GNUNET_malloc (c_slave_peers * sizeof (struct BenchmarkPeer));
1292   bp_master = GNUNET_malloc (c_master_peers * sizeof (struct BenchmarkPeer));
1293
1294   state.connected_ATS_service = GNUNET_NO;
1295   state.connected_CORE_service = GNUNET_NO;
1296   state.connected_PEERS = GNUNET_NO;
1297
1298   /* Start topology */
1299   uint64_t event_mask;
1300   event_mask = 0;
1301   event_mask |= (1LL << GNUNET_TESTBED_ET_CONNECT);
1302   event_mask |= (1LL << GNUNET_TESTBED_ET_OPERATION_FINISHED);
1303   (void) GNUNET_TESTBED_test_run (test_name,
1304                                   conf_name, c_slave_peers + c_master_peers,
1305                                   event_mask, &controller_event_cb, NULL,
1306                                   &test_main, NULL);
1307
1308   GNUNET_free (solver);
1309   GNUNET_free (preference);
1310   GNUNET_free (conf_name);
1311   GNUNET_free (test_name);
1312   GNUNET_free (bp_slaves);
1313
1314   return result;
1315 }
1316
1317 /* end of file perf_ats.c */