Fix the fix
[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   id = GNUNET_strdup (GNUNET_i2s (&p->id));
697
698   t = find_peer (peer);
699   if (NULL == t)
700   {
701         GNUNET_break (0);
702         return;
703   }
704
705   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
706               "%s %s connected to %s %s\n",
707               (p->master == GNUNET_YES) ? "Master": "Slave",
708               id,
709               (t->master == GNUNET_YES) ? "Master": "Slave",
710               GNUNET_i2s (peer));
711
712   p->core_connections ++;
713   if ((GNUNET_YES == p->master) && (GNUNET_NO == t->master) && (GNUNET_NO == state.connected_CORE))
714   {
715         p->slave_connections ++;
716
717                 if (p->slave_connections == c_slave_peers)
718                 {
719                         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
720                                         "Master %u connected all slaves\n", p->no);
721                 }
722                 cs = GNUNET_YES;
723                 for (c = 0; c < c_master_peers; c ++)
724                 {
725                         if (bp_master[c].slave_connections != c_slave_peers)
726                                 cs = GNUNET_NO;
727                 }
728                 if (GNUNET_YES == cs)
729                 {
730                         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
731                         "All master peers connected all slave peers\n", id, GNUNET_i2s (peer));
732                         state.connected_CORE = GNUNET_YES;
733                         GNUNET_SCHEDULER_add_now (&do_benchmark, NULL);
734                 }
735         }
736         GNUNET_free (id);
737 }
738
739
740 /**
741  * Method called whenever a peer disconnects.
742  *
743  * @param cls closure
744  * @param peer peer identity this notification is about
745  */
746 static void
747 core_disconnect_cb (void *cls, const struct GNUNET_PeerIdentity * peer)
748 {
749   struct BenchmarkPeer *p = cls;
750   struct BenchmarkPeer *t;
751   char *id;
752
753   t = find_peer (peer);
754   if (NULL == t)
755   {
756         GNUNET_break (0);
757         return;
758   }
759
760   id = GNUNET_strdup (GNUNET_i2s (&p->id));
761   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
762               "%s disconnected from %s \n", id, GNUNET_i2s (peer));
763   GNUNET_assert (p->core_connections > 0);
764   p->core_connections --;
765
766   if ((GNUNET_YES == state.benchmarking) &&
767                 ((GNUNET_YES == p->master) || (GNUNET_YES == t->master)))
768   {
769     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
770               "%s disconnected from %s while benchmarking \n", id, GNUNET_i2s (peer));
771   }
772
773   GNUNET_free (id);
774 }
775
776 static size_t
777 core_send_echo_queued_ready (void *cls, size_t size, void *buf);
778
779 static size_t
780 core_send_echo_ready (void *cls, size_t size, void *buf)
781 {
782         static char msgbuf[TEST_MESSAGE_SIZE];
783         struct BenchmarkPeer *bp = cls;
784         struct GNUNET_MessageHeader *msg;
785
786         bp->cth = NULL;
787
788         msg = (struct GNUNET_MessageHeader *) &msgbuf;
789         memset (&msgbuf, 'a', TEST_MESSAGE_SIZE);
790         msg->type = htons (TEST_MESSAGE_TYPE_PONG);
791         msg->size = htons (TEST_MESSAGE_SIZE);
792         memcpy (buf, msg, TEST_MESSAGE_SIZE);
793
794         /* send echo */
795         if (NULL != bp->p_head)
796                 bp->cth = GNUNET_CORE_notify_transmit_ready (bp->ch,
797                                 GNUNET_NO, 0, GNUNET_TIME_UNIT_MINUTES,
798                                 &bp->p_head->target,
799                                 TEST_MESSAGE_SIZE, &core_send_echo_queued_ready, bp);
800
801         return TEST_MESSAGE_SIZE;
802 }
803
804 static size_t
805 core_send_echo_queued_ready (void *cls, size_t size, void *buf)
806 {
807         struct BenchmarkPeer *bp = cls;
808         struct PendingMessages *pm;
809         GNUNET_assert (NULL != bp->p_head);
810
811         pm = bp->p_head;
812         GNUNET_CONTAINER_DLL_remove (bp->p_head, bp->p_tail, pm);
813         GNUNET_free (pm);
814
815
816         return core_send_echo_ready (cls, size, buf);
817
818 }
819
820
821 static int
822 core_handle_ping (void *cls, const struct GNUNET_PeerIdentity *other,
823                 const struct GNUNET_MessageHeader *message)
824 {
825         struct BenchmarkPeer *me = cls;
826         struct BenchmarkPeer *remote;
827         struct PendingMessages *pm;
828
829         remote = find_peer (other);
830
831         if (NULL == remote)
832         {
833                 GNUNET_break (0);
834                 return GNUNET_SYSERR;
835         }
836
837         if (NULL != me->cth)
838         {
839                 pm = GNUNET_malloc (sizeof (struct PendingMessages));
840                 pm->target = (*other);
841                 GNUNET_CONTAINER_DLL_insert_tail (me->p_head, me->p_tail, pm);
842                 return GNUNET_OK;
843         }
844
845         if (GNUNET_NO == remote->master)
846         {
847                 GNUNET_break (0);
848                 return GNUNET_OK;
849         }
850
851         me->messages_received ++;
852         /* send echo */
853         me->cth = GNUNET_CORE_notify_transmit_ready (me->ch,
854                                 GNUNET_NO, 0, GNUNET_TIME_UNIT_MINUTES,
855                                 &remote->id,
856                                 TEST_MESSAGE_SIZE, &core_send_echo_ready, me);
857         return GNUNET_OK;
858 }
859
860 static int
861 core_handle_pong (void *cls, const struct GNUNET_PeerIdentity *other,
862                 const struct GNUNET_MessageHeader *message)
863 {
864         struct BenchmarkPeer *me = cls;
865         struct BenchmarkPeer *remote;
866         struct BenchmarkPeer *next;
867
868         remote = find_peer (other);
869
870         if (NULL == remote)
871         {
872                 GNUNET_break (0);
873                 return GNUNET_SYSERR;
874         }
875
876         if (NULL != me->cth)
877         {
878                 GNUNET_break (0);
879                 return GNUNET_OK;
880         }
881
882         if (GNUNET_YES == remote->master)
883         {
884                 GNUNET_break (0);
885                 return GNUNET_OK;
886         }
887         me->messages_received ++;
888         next = get_next (me);
889         if (0 == memcmp(&remote->id, &me->destination->id, sizeof (struct GNUNET_PeerIdentity)))
890                         me->partner_msg = GNUNET_YES;
891         me->cth = GNUNET_CORE_notify_transmit_ready (me->ch,
892                                 GNUNET_NO, 0, GNUNET_TIME_UNIT_MINUTES,
893                                 &next->id,
894                                 TEST_MESSAGE_SIZE, &core_send_ready, me);
895
896         return GNUNET_OK;
897 }
898
899
900 /**
901  * Called to open a connection to the peer's ATS performance
902  *
903  * @param cls peer context
904  * @param cfg configuration of the peer to connect to; will be available until
905  *          GNUNET_TESTBED_operation_done() is called on the operation returned
906  *          from GNUNET_TESTBED_service_connect()
907  * @return service handle to return in 'op_result', NULL on error
908  */
909 static void *
910 core_connect_adapter (void *cls,
911                       const struct GNUNET_CONFIGURATION_Handle *cfg)
912 {
913   struct BenchmarkPeer *peer = cls;
914
915   static const struct GNUNET_CORE_MessageHandler handlers[] = {
916       {&core_handle_ping, TEST_MESSAGE_TYPE_PING, 0},
917       {&core_handle_pong, TEST_MESSAGE_TYPE_PONG, 0},
918       {NULL, 0, 0}
919   };
920
921   peer->ch = GNUNET_CORE_connect(cfg, peer, NULL,
922                                  core_connect_cb, core_disconnect_cb,
923                                  NULL, GNUNET_NO, NULL, GNUNET_NO, handlers);
924   if (NULL == peer->ch)
925     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
926                 "Failed to create core connection \n");
927   return peer->ch;
928 }
929
930
931 /**
932  * Callback to be called when a service connect operation is completed
933  *
934  * @param cls the callback closure from functions generating an operation
935  * @param op the operation that has been finished
936  * @param ca_result the service handle returned from GNUNET_TESTBED_ConnectAdapter()
937  * @param emsg error message in case the operation has failed; will be NULL if
938  *          operation has executed successfully.
939  */
940 static void
941 core_connect_completion_cb (void *cls,
942                             struct GNUNET_TESTBED_Operation *op,
943                             void *ca_result,
944                             const char *emsg )
945 {
946         static int core_done = 0;
947         if ((NULL != emsg) || (NULL == ca_result))
948         {
949                 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
950                                 _("Initialization failed, shutdown\n"));
951                 GNUNET_break (0);
952                 if (GNUNET_SCHEDULER_NO_TASK != shutdown_task)
953                         GNUNET_SCHEDULER_cancel(shutdown_task);
954                 shutdown_task = GNUNET_SCHEDULER_add_now (do_shutdown, NULL);
955                 return;
956         }
957         core_done ++;
958
959         if (core_done == c_slave_peers + c_master_peers)
960         {
961                 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
962                                 "Connected to all CORE services\n");
963                 state.connected_CORE_service = GNUNET_YES;
964                 GNUNET_SCHEDULER_add_now (&do_connect_peers, NULL);
965         }
966 }
967
968
969 /**
970  * Called to disconnect from peer's statistics service
971  *
972  * @param cls peer context
973  * @param op_result service handle returned from the connect adapter
974  */
975 static void
976 core_disconnect_adapter (void *cls, void *op_result)
977 {
978   struct BenchmarkPeer *peer = cls;
979
980   GNUNET_CORE_disconnect (peer->ch);
981   peer->ch = NULL;
982 }
983
984 static void
985 do_connect_core (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
986 {
987         int c_p;
988         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
989                         "Connecting to all CORE services\n");
990   for (c_p = 0; c_p < c_master_peers; c_p++)
991   {
992     bp_master[c_p].core_op = GNUNET_TESTBED_service_connect (NULL,
993                                                         bp_master[c_p].peer, "core",
994                                                       core_connect_completion_cb, NULL,
995                                                       &core_connect_adapter,
996                                                       &core_disconnect_adapter,
997                                                       &bp_master[c_p]);
998
999   }
1000
1001   for (c_p = 0; c_p < c_slave_peers; c_p++)
1002   {
1003     bp_slaves[c_p].core_op = GNUNET_TESTBED_service_connect (NULL,
1004                                                 bp_slaves[c_p].peer, "core",
1005                                               core_connect_completion_cb, NULL,
1006                                               &core_connect_adapter,
1007                                               &core_disconnect_adapter,
1008                                               &bp_slaves[c_p]);
1009   }
1010 }
1011
1012
1013
1014 /**
1015  * Called to open a connection to the peer's ATS performance
1016  *
1017  * @param cls peer context
1018  * @param cfg configuration of the peer to connect to; will be available until
1019  *          GNUNET_TESTBED_operation_done() is called on the operation returned
1020  *          from GNUNET_TESTBED_service_connect()
1021  * @return service handle to return in 'op_result', NULL on error
1022  */
1023 static void *
1024 ats_perf_connect_adapter (void *cls,
1025                       const struct GNUNET_CONFIGURATION_Handle *cfg)
1026 {
1027   struct BenchmarkPeer *peer = cls;
1028
1029   peer->p_handle = GNUNET_ATS_performance_init (cfg, &ats_performance_info_cb, peer);
1030   if (NULL == peer->p_handle)
1031     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to create ATS performance handle \n");
1032   return peer->p_handle;
1033 }
1034
1035
1036 /**
1037  * Called to disconnect from peer's statistics service
1038  *
1039  * @param cls peer context
1040  * @param op_result service handle returned from the connect adapter
1041  */
1042 static void
1043 ats_perf_disconnect_adapter (void *cls, void *op_result)
1044 {
1045   struct BenchmarkPeer *peer = cls;
1046
1047   GNUNET_ATS_performance_done(peer->p_handle);
1048   peer->p_handle = NULL;
1049 }
1050
1051
1052 /**
1053  * Callback to be called when a service connect operation is completed
1054  *
1055  * @param cls the callback closure from functions generating an operation
1056  * @param op the operation that has been finished
1057  * @param ca_result the service handle returned from GNUNET_TESTBED_ConnectAdapter()
1058  * @param emsg error message in case the operation has failed; will be NULL if
1059  *          operation has executed successfully.
1060  */
1061 static void
1062 ats_connect_completion_cb (void *cls,
1063                            struct GNUNET_TESTBED_Operation *op,
1064                            void *ca_result,
1065                            const char *emsg )
1066 {
1067         static int op_done = 0;
1068         if ((NULL != emsg) || (NULL == ca_result))
1069         {
1070                 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1071                                 _("Initialization failed, shutdown\n"));
1072                 GNUNET_break (0);
1073                 if (GNUNET_SCHEDULER_NO_TASK != shutdown_task)
1074                         GNUNET_SCHEDULER_cancel(shutdown_task);
1075                 shutdown_task = GNUNET_SCHEDULER_add_now (do_shutdown, NULL);
1076                 return;
1077         }
1078
1079         op_done ++;
1080         if (op_done == (c_slave_peers + c_master_peers))
1081         {
1082                 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1083                                 "Connected to all ATS services\n");
1084                 state.connected_ATS_service = GNUNET_YES;
1085                 GNUNET_SCHEDULER_add_now (&do_connect_core, NULL);
1086         }
1087 }
1088
1089 static void
1090 do_connect_ats (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1091 {
1092         int c_p;
1093         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1094                         "Connecting to all ATS services %u\n", c_slave_peers);
1095   for (c_p = 0; c_p < c_master_peers; c_p++)
1096   {
1097     bp_master[c_p].ats_perf_op = GNUNET_TESTBED_service_connect (NULL,
1098                                                 bp_master[c_p].peer, "ats",
1099                                                           ats_connect_completion_cb, NULL,
1100                                                           &ats_perf_connect_adapter,
1101                                                           &ats_perf_disconnect_adapter,
1102                                                           &bp_master[c_p]);
1103
1104   }
1105
1106   for (c_p = 0; c_p < c_slave_peers; c_p++)
1107   {
1108     bp_slaves[c_p].ats_perf_op = GNUNET_TESTBED_service_connect (NULL,
1109                                                 bp_slaves[c_p].peer, "ats",
1110                                                           ats_connect_completion_cb, NULL,
1111                                                           &ats_perf_connect_adapter,
1112                                                           &ats_perf_disconnect_adapter,
1113                                                           &bp_slaves[c_p]);
1114   }
1115
1116 }
1117
1118
1119 /**
1120  * Callback to be called when the requested peer information is available
1121  *
1122  * @param cb_cls the closure from GNUNET_TETSBED_peer_get_information()
1123  * @param op the operation this callback corresponds to
1124  * @param pinfo the result; will be NULL if the operation has failed
1125  * @param emsg error message if the operation has failed; will be NULL if the
1126  *          operation is successfull
1127  */
1128 static void
1129 peerinformation_cb (void *cb_cls,
1130                     struct GNUNET_TESTBED_Operation *op,
1131                     const struct GNUNET_TESTBED_PeerInformation*pinfo,
1132                     const char *emsg)
1133 {
1134   struct BenchmarkPeer *p = cb_cls;
1135         static int done = 0;
1136
1137   if (pinfo->pit == GNUNET_TESTBED_PIT_IDENTITY)
1138   {
1139     p->id = *pinfo->result.id;
1140     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1141                 "[%c %03u] Peers %s\n",
1142                 (p->master == GNUNET_YES) ? 'M' : 'S', p->no, GNUNET_i2s (&p->id));
1143   }
1144   else
1145   {
1146     GNUNET_assert (0);
1147   }
1148   GNUNET_TESTBED_operation_done (op);
1149   p->info_op = NULL;
1150   done++;
1151
1152   if (done == c_master_peers + c_slave_peers)
1153   {
1154                 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1155                                 "Retrieved all peer ID, connect to ATS\n");
1156                 state.connected_CORE_service = GNUNET_YES;
1157                 GNUNET_SCHEDULER_add_now (&do_connect_ats, NULL);
1158   }
1159 }
1160
1161
1162 /**
1163  * Signature of a main function for a testcase.
1164  *
1165  * @param cls closure
1166  * @param num_peers number of peers in 'peers'
1167  * @param peers_ handle to peers run in the testbed
1168  * @param links_succeeded the number of overlay link connection attempts that
1169  *          succeeded
1170  * @param links_failed the number of overlay link connection attempts that
1171  *          failed
1172  */
1173 static void
1174 test_main (void *cls,
1175            struct GNUNET_TESTBED_RunHandle *h,
1176            unsigned int num_peers,
1177            struct GNUNET_TESTBED_Peer **peers_,
1178            unsigned int links_succeeded,
1179            unsigned int links_failed)
1180 {
1181   int c_p;
1182
1183   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1184               _("Benchmarking solver `%s' on preference `%s' with %u master and %u slave peers\n"),
1185               solver, preference, c_master_peers, c_slave_peers);
1186
1187   shutdown_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply(TEST_TIMEOUT, c_master_peers + c_slave_peers), &do_shutdown, NULL);
1188
1189   GNUNET_assert (NULL == cls);
1190   GNUNET_assert (c_slave_peers + c_master_peers == num_peers);
1191   GNUNET_assert (NULL != peers_);
1192
1193   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1194               _("Initializing... \n"));
1195
1196   for (c_p = 0; c_p < c_master_peers; c_p++)
1197   {
1198     GNUNET_assert (NULL != peers_[c_p]);
1199     bp_master[c_p].no = c_p;
1200     bp_master[c_p].send_mask = (1 << c_slave_peers) - 1;
1201     bp_master[c_p].master = GNUNET_YES;
1202     bp_master[c_p].peer = peers_[c_p];
1203     bp_master[c_p].info_op = GNUNET_TESTBED_peer_get_information (bp_master[c_p].peer,
1204                                                            GNUNET_TESTBED_PIT_IDENTITY,
1205                                                            &peerinformation_cb, &bp_master[c_p]);
1206
1207     /* Select ATS measurement partner */
1208     bp_master[c_p].destination = &bp_slaves[c_p];
1209   }
1210
1211   for (c_p = 0; c_p < c_slave_peers; c_p++)
1212   {
1213     GNUNET_assert (NULL != peers_[c_p + c_master_peers]);
1214     bp_slaves[c_p].no = c_p + c_master_peers;
1215     bp_slaves[c_p].master = GNUNET_NO;
1216     bp_slaves[c_p].peer = peers_[c_p + c_master_peers];
1217     bp_slaves[c_p].info_op = GNUNET_TESTBED_peer_get_information (bp_slaves[c_p].peer,
1218                                                            GNUNET_TESTBED_PIT_IDENTITY, 
1219                                                            &peerinformation_cb, &bp_slaves[c_p]);
1220   }
1221
1222 }
1223
1224
1225 int
1226 main (int argc, char *argv[])
1227 {
1228   char *tmp;
1229   char *tmp_sep;
1230   char *test_name;
1231   char *conf_name;
1232   char *dotexe;
1233   int c;
1234
1235   result = 0;
1236
1237   /* figure out testname */
1238   tmp = strstr (argv[0], TESTNAME_PREFIX);
1239   if (NULL == tmp)
1240   {
1241         fprintf (stderr, "Unable to parse test name `%s'\n", argv[0]);
1242         return GNUNET_SYSERR;
1243   }
1244   tmp += strlen(TESTNAME_PREFIX);
1245   solver = GNUNET_strdup (tmp);
1246   if (NULL != (dotexe = strstr (solver, ".exe")) &&
1247       dotexe[4] == '\0')
1248     dotexe[0] = '\0';
1249   tmp_sep = strchr (solver, '_');
1250   if (NULL == tmp_sep)
1251   {
1252         fprintf (stderr, "Unable to parse test name `%s'\n", argv[0]);
1253         GNUNET_free (solver);
1254         return GNUNET_SYSERR;
1255   }
1256   tmp_sep[0] = '\0';
1257   preference = GNUNET_strdup(tmp_sep + 1);
1258
1259   GNUNET_asprintf(&conf_name, "%s%s_%s.conf", TESTNAME_PREFIX, solver, preference);
1260   GNUNET_asprintf(&test_name, "%s%s_%s", TESTNAME_PREFIX, solver, preference);
1261
1262   for (c = 0; c < (argc -1); c++)
1263   {
1264         if (0 == strcmp(argv[c], "-s"))
1265                 break;
1266   }
1267   if (c < argc-1)
1268   {
1269     if ((0L != (c_slave_peers = strtol (argv[c + 1], NULL, 10))) && (c_slave_peers >= 1))
1270       fprintf (stderr, "Starting %u slave peers\n", c_slave_peers);
1271     else
1272         c_slave_peers = DEFAULT_SLAVES_NUM;
1273   }
1274   else
1275         c_slave_peers = DEFAULT_SLAVES_NUM;
1276
1277   for (c = 0; c < (argc -1); c++)
1278   {
1279         if (0 == strcmp(argv[c], "-m"))
1280                 break;
1281   }
1282   if (c < argc-1)
1283   {
1284     if ((0L != (c_master_peers = strtol (argv[c + 1], NULL, 10))) && (c_master_peers >= 2))
1285       fprintf (stderr, "Starting %u master peers\n", c_master_peers);
1286     else
1287         c_master_peers = DEFAULT_MASTERS_NUM;
1288   }
1289   else
1290         c_master_peers = DEFAULT_MASTERS_NUM;
1291
1292   bp_slaves = GNUNET_malloc (c_slave_peers * sizeof (struct BenchmarkPeer));
1293   bp_master = GNUNET_malloc (c_master_peers * sizeof (struct BenchmarkPeer));
1294
1295   state.connected_ATS_service = GNUNET_NO;
1296   state.connected_CORE_service = GNUNET_NO;
1297   state.connected_PEERS = GNUNET_NO;
1298
1299   /* Start topology */
1300   uint64_t event_mask;
1301   event_mask = 0;
1302   event_mask |= (1LL << GNUNET_TESTBED_ET_CONNECT);
1303   event_mask |= (1LL << GNUNET_TESTBED_ET_OPERATION_FINISHED);
1304   (void) GNUNET_TESTBED_test_run (test_name,
1305                                   conf_name, c_slave_peers + c_master_peers,
1306                                   event_mask, &controller_event_cb, NULL,
1307                                   &test_main, NULL);
1308
1309   GNUNET_free (solver);
1310   GNUNET_free (preference);
1311   GNUNET_free (conf_name);
1312   GNUNET_free (test_name);
1313   GNUNET_free (bp_slaves);
1314
1315   return result;
1316 }
1317
1318 /* end of file perf_ats.c */