-log
[oweals/gnunet.git] / src / mesh / gnunet-mesh-profiler.c
1 /*
2      This file is part of GNUnet.
3      (C) 2011 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 mesh/gnunet-mesh-profiler.c
22  *
23  * @brief Profiler for mesh experiments.
24  */
25 #include <stdio.h>
26 #include "platform.h"
27 #include "mesh_test_lib.h"
28 #include "gnunet_mesh_service.h"
29 #include "gnunet_statistics_service.h"
30
31
32 #define PING 1
33 #define PONG 2
34
35 /**
36  * How many peers to run
37  */
38 #define TOTAL_PEERS 10
39
40 /**
41  * How many peers do pinging
42  */
43 #define PING_PEERS 1
44
45
46 /**
47  * Duration of each round.
48  */
49 #define ROUND_TIME GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
50
51 /**
52  * Paximum ping period in milliseconds. Real period = rand (0, PING_PERIOD)
53  */
54 #define PING_PERIOD 2000
55
56 /**
57  * How long until we give up on connecting the peers?
58  */
59 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 120)
60
61 /**
62  * Time to wait for stuff that should be rather fast
63  */
64 #define SHORT_TIME GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60)
65
66 static float rounds[] = {0.8, 0.7, 0.6, 0.5, 0.0};
67
68 /**
69  * Message type for pings.
70  */
71 struct MeshPingMessage
72 {
73   /**
74    * Header. Type PING/PONG.
75    */
76   struct GNUNET_MessageHeader header;
77
78   /**
79    * Message number.
80    */
81   uint32_t counter;
82
83   /**
84    * Time the message was sent.
85    */
86   struct GNUNET_TIME_AbsoluteNBO timestamp;
87 };
88
89 /**
90  * Peer description.
91  */
92 struct MeshPeer
93 {
94   /**
95    * Testbed Operation (to get peer id, etc).
96    */
97   struct GNUNET_TESTBED_Operation *op;
98
99   /**
100    * Peer ID.
101    */
102   struct GNUNET_PeerIdentity id;
103
104   /**
105    * Mesh handle for the root peer
106    */
107   struct GNUNET_MESH_Handle *mesh;
108
109   /**
110    * Channel handle for the root peer
111    */
112   struct GNUNET_MESH_Channel *ch;
113
114   /**
115    * Channel handle for the dest peer
116    */
117   struct GNUNET_MESH_Channel *incoming_ch;
118
119   /**
120    * Number of payload packes sent
121    */
122   int data_sent;
123
124   /**
125    * Number of payload packets received
126    */
127   int data_received;
128
129   int up;
130
131   /**
132    * Destinaton to ping.
133    */
134   struct MeshPeer *dest;
135
136   /**
137    * Incoming channel for pings.
138    */
139   struct MeshPeer *incoming;
140
141   /**
142    * Task to do the next ping.
143    */
144   GNUNET_SCHEDULER_TaskIdentifier ping_task;
145 };
146
147 /**
148  * GNUNET_PeerIdentity -> MeshPeer
149  */
150 static struct GNUNET_CONTAINER_MultiPeerMap *ids;
151
152 /**
153  * Testbed peer handles.
154  */
155 static struct GNUNET_TESTBED_Peer **testbed_handles;
156
157 /**
158  * Testbed Operation (to get stats).
159  */
160 static struct GNUNET_TESTBED_Operation *stats_op;
161
162 /**
163  * How many events have happened
164  */
165 static int ok;
166
167 /**
168  * Number of events expected to conclude the test successfully.
169  */
170 static int ok_goal;
171
172 /**
173  * Operation to get peer ids.
174  */
175 struct MeshPeer peers[TOTAL_PEERS];
176
177 /**
178  * Peer ids counter.
179  */
180 static unsigned int p_ids;
181
182 /**
183  * Total number of currently running peers.
184  */
185 static unsigned long long peers_running;
186
187 /**
188  * Test context (to shut down).
189  */
190 static struct GNUNET_MESH_TEST_Context *test_ctx;
191
192 /**
193  * Task called to shutdown test.
194  */
195 static GNUNET_SCHEDULER_TaskIdentifier shutdown_handle;
196
197 /**
198  * Task called to disconnect peers, before shutdown.
199  */
200 static GNUNET_SCHEDULER_TaskIdentifier disconnect_task;
201
202 /**
203  * Task to perform tests
204  */
205 static GNUNET_SCHEDULER_TaskIdentifier test_task;
206
207
208 /**
209  * Flag to notify callbacks not to generate any new traffic anymore.
210  */
211 static int test_finished;
212
213 /**
214  * Calculate a random delay.
215  *
216  * @param max Exclusive maximum, in ms.
217  *
218  * @return A time between 0 a max-1 ms.
219  */
220 static struct GNUNET_TIME_Relative
221 delay_ms_rnd (unsigned int max)
222 {
223   unsigned int rnd;
224
225   rnd = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, max);
226   return GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, rnd);
227 }
228
229
230 /**
231  * Get the index of a peer in the peers array.
232  *
233  * @param peer Peer whose index to get.
234  *
235  * @return Index of peer in peers.
236  */
237 static unsigned int
238 get_index (struct MeshPeer *peer)
239 {
240   return peer - peers;
241 }
242
243
244 /**
245  * Show the results of the test (banwidth acheived) and log them to GAUGER
246  */
247 static void
248 show_end_data (void)
249 {
250 }
251
252
253 /**
254  * Shut down peergroup, clean up.
255  *
256  * @param cls Closure (unused).
257  * @param tc Task Context.
258  */
259 static void
260 shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
261 {
262   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Ending test.\n");
263   shutdown_handle = GNUNET_SCHEDULER_NO_TASK;
264 }
265
266
267 /**
268  * Disconnect from mesh services af all peers, call shutdown.
269  *
270  * @param cls Closure (unused).
271  * @param tc Task Context.
272  */
273 static void
274 disconnect_mesh_peers (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
275 {
276   long line = (long) cls;
277   unsigned int i;
278
279   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
280               "disconnecting mesh service, called from line %ld\n", line);
281   disconnect_task = GNUNET_SCHEDULER_NO_TASK;
282   for (i = 0; i < TOTAL_PEERS; i++)
283   {
284     if (NULL != peers[i].op)
285       GNUNET_TESTBED_operation_done (peers[i].op);
286
287     if (peers[i].up != GNUNET_YES)
288       continue;
289
290     if (NULL != peers[i].ch)
291     {
292       GNUNET_log (GNUNET_ERROR_TYPE_INFO, "%u: channel %p\n", i, peers[i].ch);
293       GNUNET_MESH_channel_destroy (peers[i].ch);
294     }
295     if (NULL != peers[i].incoming_ch)
296     {
297       GNUNET_log (GNUNET_ERROR_TYPE_INFO, "%u: incoming channel %p\n",
298                   i, peers[i].incoming_ch);
299       GNUNET_MESH_channel_destroy (peers[i].incoming_ch);
300     }
301   }
302   GNUNET_MESH_TEST_cleanup (test_ctx);
303   if (GNUNET_SCHEDULER_NO_TASK != shutdown_handle)
304   {
305     GNUNET_SCHEDULER_cancel (shutdown_handle);
306   }
307   shutdown_handle = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
308 }
309
310
311 /**
312  * Finish test normally: schedule disconnect and shutdown
313  *
314  * @param line Line in the code the abort is requested from (__LINE__).
315  */
316 static void
317 abort_test (long line)
318 {
319   if (disconnect_task != GNUNET_SCHEDULER_NO_TASK)
320   {
321     GNUNET_SCHEDULER_cancel (disconnect_task);
322     disconnect_task = GNUNET_SCHEDULER_add_now (&disconnect_mesh_peers,
323                                                 (void *) line);
324   }
325 }
326
327 /**
328  * Stats callback. Finish the stats testbed operation and when all stats have
329  * been iterated, shutdown the test.
330  *
331  * @param cls closure
332  * @param op the operation that has been finished
333  * @param emsg error message in case the operation has failed; will be NULL if
334  *          operation has executed successfully.
335  */
336 static void
337 stats_cont (void *cls, struct GNUNET_TESTBED_Operation *op, const char *emsg)
338 {
339   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "... collecting statistics done.\n");
340   GNUNET_TESTBED_operation_done (stats_op);
341
342   if (GNUNET_SCHEDULER_NO_TASK != disconnect_task)
343     GNUNET_SCHEDULER_cancel (disconnect_task);
344   disconnect_task = GNUNET_SCHEDULER_add_now (&disconnect_mesh_peers,
345                                               (void *) __LINE__);
346
347 }
348
349
350 /**
351  * Process statistic values.
352  *
353  * @param cls closure
354  * @param peer the peer the statistic belong to
355  * @param subsystem name of subsystem that created the statistic
356  * @param name the name of the datum
357  * @param value the current value
358  * @param is_persistent GNUNET_YES if the value is persistent, GNUNET_NO if not
359  * @return GNUNET_OK to continue, GNUNET_SYSERR to abort iteration
360  */
361 static int
362 stats_iterator (void *cls, const struct GNUNET_TESTBED_Peer *peer,
363                 const char *subsystem, const char *name,
364                 uint64_t value, int is_persistent)
365 {
366   uint32_t i;
367
368   i = GNUNET_TESTBED_get_index (peer);
369   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " STATS %u - %s [%s]: %llu\n",
370               i, subsystem, name, value);
371
372   return GNUNET_OK;
373 }
374
375
376 /**
377  * Task check that keepalives were sent and received.
378  *
379  * @param cls Closure (NULL).
380  * @param tc Task Context.
381  */
382 static void
383 collect_stats (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
384 {
385   if ((GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason) != 0)
386     return;
387
388   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Start collecting statistics...\n");
389   stats_op = GNUNET_TESTBED_get_statistics (TOTAL_PEERS, testbed_handles,
390                                             NULL, NULL,
391                                             stats_iterator, stats_cont, NULL);
392 }
393
394
395 /**
396  * @brief Finish profiler normally. Signal finish and start collecting stats.
397  *
398  * @param cls Closure (unused).
399  * @param tc Task context.
400  */
401 static void
402 finish_profiler (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
403 {
404   if ((GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason) != 0)
405     return;
406
407   test_finished = GNUNET_YES;
408   show_end_data();
409   GNUNET_SCHEDULER_add_now (&collect_stats, NULL);
410 }
411
412 /**
413  * Set the total number of running peers.
414  *
415  * @param target Desired number of running peers.
416  */
417 static void
418 adjust_running_peers (unsigned int target)
419 {
420   struct GNUNET_TESTBED_Operation *op;
421   unsigned int delta;
422   unsigned int run;
423   unsigned int i;
424   unsigned int r;
425
426   GNUNET_assert (target <= TOTAL_PEERS);
427
428   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "adjust peers to %u\n", target);
429   if (target > peers_running)
430   {
431     delta = target - peers_running;
432     run = GNUNET_YES;
433   }
434   else
435   {
436     delta = peers_running - target;
437     run = GNUNET_NO;
438   }
439
440   for (i = 0; i < delta; i++)
441   {
442     do {
443       r = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
444                                     TOTAL_PEERS - PING_PEERS);
445       r += PING_PEERS;
446     } while (peers[r].up == run || NULL != peers[r].incoming);
447     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "St%s peer %u: %s\n",
448                 run ? "arting" : "opping", r, GNUNET_i2s (&peers[r].id));
449
450     if (GNUNET_SCHEDULER_NO_TASK != peers[r].ping_task)
451       GNUNET_SCHEDULER_cancel (peers[r].ping_task);
452     peers[r].ping_task = GNUNET_SCHEDULER_NO_TASK;
453
454     peers[r].up = run;
455
456     if (NULL != peers[r].ch)
457       GNUNET_MESH_channel_destroy (peers[r].ch);
458     peers[r].ch = NULL;
459     if (NULL != peers[r].dest)
460     {
461       if (NULL != peers[r].dest->incoming_ch)
462         GNUNET_MESH_channel_destroy (peers[r].dest->incoming_ch);
463       peers[r].dest->incoming_ch = NULL;
464     }
465
466     op = GNUNET_TESTBED_peer_manage_service (&peers[r], testbed_handles[r],
467                                              "mesh", NULL, NULL, run);
468     GNUNET_break (NULL != op);
469     peers_running += run ? 1 : -1;
470     GNUNET_assert (peers_running > 0);
471   }
472 }
473
474
475 /**
476  * @brief Move to next round.
477  *
478  * @param cls Closure (round #).
479  * @param tc Task context.
480  */
481 static void
482 next_rnd (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
483 {
484   long round = (long) cls;
485
486   if ((GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason) != 0)
487     return;
488
489   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "ROUND %ld\n", round);
490   if (0.0 == rounds[round])
491   {
492     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Finishing\n");
493     GNUNET_SCHEDULER_add_now (&finish_profiler, NULL);
494     return;
495   }
496   adjust_running_peers (rounds[round] * TOTAL_PEERS);
497
498   GNUNET_SCHEDULER_add_delayed (ROUND_TIME, &next_rnd, (void *) (round + 1));
499 }
500
501
502 /**
503  * Transmit ping callback.
504  *
505  * @param cls Closure (peer for PING, NULL for PONG).
506  * @param size Size of the tranmist buffer.
507  * @param buf Pointer to the beginning of the buffer.
508  *
509  * @return Number of bytes written to buf.
510  */
511 static size_t
512 tmt_rdy_ping (void *cls, size_t size, void *buf);
513
514
515 /**
516  * Transmit pong callback.
517  *
518  * @param cls Closure (copy of PING message, to be freed).
519  * @param size Size of the buffer we have.
520  * @param buf Buffer to copy data to.
521  */
522 static size_t
523 tmt_rdy_pong (void *cls, size_t size, void *buf)
524 {
525   struct MeshPingMessage *ping = cls;
526   struct MeshPingMessage *pong;
527
528   if (0 == size || NULL == buf)
529   {
530     GNUNET_free (ping);
531     return 0;
532   }
533   pong = (struct MeshPingMessage *) buf;
534   memcpy (pong, ping, sizeof (*ping));
535   pong->header.type = htons (PONG);
536
537   GNUNET_free (ping);
538   return sizeof (*ping);
539 }
540
541
542 /**
543  * @brief Send a ping to destination
544  *
545  * @param cls Closure (peer).
546  * @param tc Task context.
547  */
548 static void
549 ping (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
550 {
551   struct MeshPeer *peer = (struct MeshPeer *) cls;
552
553   peer->ping_task = GNUNET_SCHEDULER_NO_TASK;
554
555   if ((GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason) != 0
556       || GNUNET_YES == test_finished)
557     return;
558
559   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "%u -> %u (%u)\n",
560               get_index (peer), get_index (peer->dest), peer->data_sent);
561
562   GNUNET_MESH_notify_transmit_ready (peer->ch, GNUNET_NO,
563                                      GNUNET_TIME_UNIT_FOREVER_REL,
564                                      sizeof (struct MeshPingMessage),
565                                      &tmt_rdy_ping, peer);
566 }
567
568 /**
569  * @brief Reply with a pong to origin.
570  *
571  * @param cls Closure (peer).
572  * @param tc Task context.
573  */
574 static void
575 pong (struct GNUNET_MESH_Channel *channel, const struct MeshPingMessage *ping)
576 {
577   struct MeshPingMessage *copy;
578
579   copy = GNUNET_new (struct MeshPingMessage);
580   memcpy (copy, ping, sizeof (*ping));
581   GNUNET_MESH_notify_transmit_ready (channel, GNUNET_NO,
582                                      GNUNET_TIME_UNIT_FOREVER_REL,
583                                      sizeof (struct MeshPingMessage),
584                                      &tmt_rdy_pong, copy);
585 }
586
587
588 /**
589  * Transmit ping callback
590  *
591  * @param cls Closure (peer).
592  * @param size Size of the buffer we have.
593  * @param buf Buffer to copy data to.
594  */
595 static size_t
596 tmt_rdy_ping (void *cls, size_t size, void *buf)
597 {
598   struct MeshPeer *peer = (struct MeshPeer *) cls;
599   struct MeshPingMessage *msg = buf;
600
601   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "tmt_rdy called, filling buffer\n");
602   if (size < sizeof (struct MeshPingMessage) || NULL == buf)
603   {
604     GNUNET_break (0);
605     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
606                 "size %u, buf %p, data_sent %u, data_received %u\n",
607                 size, buf, peer->data_sent, peer->data_received);
608
609     return 0;
610   }
611   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending: msg %d\n", peer->data_sent);
612   msg->header.size = htons (size);
613   msg->header.type = htons (PING);
614   msg->counter = htonl (peer->data_sent++);
615   msg->timestamp = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ());
616   peer->ping_task = GNUNET_SCHEDULER_add_delayed (delay_ms_rnd (PING_PERIOD),
617                                                   &ping, peer);
618
619   return sizeof (struct MeshPingMessage);
620 }
621
622
623 /**
624  * Function is called whenever a PING message is received.
625  *
626  * @param cls closure (peer #, set from GNUNET_MESH_connect)
627  * @param channel connection to the other end
628  * @param channel_ctx place to store local state associated with the channel
629  * @param message the actual message
630  * @return GNUNET_OK to keep the connection open,
631  *         GNUNET_SYSERR to close it (signal serious error)
632  */
633 int
634 ping_handler (void *cls, struct GNUNET_MESH_Channel *channel,
635               void **channel_ctx,
636               const struct GNUNET_MessageHeader *message)
637 {
638   long n = (long) cls;
639
640   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "%u got PING\n", n);
641   GNUNET_MESH_receive_done (channel);
642   if (GNUNET_NO == test_finished)
643     pong (channel, (struct MeshPingMessage *) message);
644
645   return GNUNET_OK;
646 }
647
648
649 /**
650  * Function is called whenever a PONG message is received.
651  *
652  * @param cls closure (peer #, set from GNUNET_MESH_connect)
653  * @param channel connection to the other end
654  * @param channel_ctx place to store local state associated with the channel
655  * @param message the actual message
656  * @return GNUNET_OK to keep the connection open,
657  *         GNUNET_SYSERR to close it (signal serious error)
658  */
659 int
660 pong_handler (void *cls, struct GNUNET_MESH_Channel *channel,
661               void **channel_ctx,
662               const struct GNUNET_MessageHeader *message)
663 {
664   long n = (long) cls;
665   struct MeshPeer *peer;
666   struct MeshPingMessage *msg;
667   struct GNUNET_TIME_Absolute send_time;
668   struct GNUNET_TIME_Relative latency;
669
670   GNUNET_MESH_receive_done (channel);
671   peer = &peers[n];
672
673   msg = (struct MeshPingMessage *) message;
674
675   send_time = GNUNET_TIME_absolute_ntoh (msg->timestamp);
676   latency = GNUNET_TIME_absolute_get_duration (send_time);
677   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "%u <- %u (%u) latency: %s\n",
678               get_index (peer), get_index (peer->dest), ntohl (msg->counter),
679               GNUNET_STRINGS_relative_time_to_string (latency, GNUNET_NO));
680
681   return GNUNET_OK;
682 }
683
684
685 /**
686  * Handlers, for diverse services
687  */
688 static struct GNUNET_MESH_MessageHandler handlers[] = {
689   {&ping_handler, PING, sizeof (struct MeshPingMessage)},
690   {&pong_handler, PONG, sizeof (struct MeshPingMessage)},
691   {NULL, 0, 0}
692 };
693
694
695 /**
696  * Method called whenever another peer has added us to a channel
697  * the other peer initiated.
698  *
699  * @param cls Closure.
700  * @param channel New handle to the channel.
701  * @param initiator Peer that started the channel.
702  * @param port Port this channel is connected to.
703  * @param options channel option flags
704  * @return Initial channel context for the channel
705  *         (can be NULL -- that's not an error).
706  */
707 static void *
708 incoming_channel (void *cls, struct GNUNET_MESH_Channel *channel,
709                  const struct GNUNET_PeerIdentity *initiator,
710                  uint32_t port, enum GNUNET_MESH_ChannelOption options)
711 {
712   long n = (long) cls;
713   struct MeshPeer *peer;
714
715   peer = GNUNET_CONTAINER_multipeermap_get (ids, initiator);
716   GNUNET_assert (NULL != peer);
717   GNUNET_assert (peer == peers[n].incoming);
718   GNUNET_assert (peer->dest == &peers[n]);
719   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "%u <= %u %p\n",
720               n, get_index (peer), channel);
721   peers[n].incoming_ch = channel;
722
723   return NULL;
724 }
725
726 /**
727  * Function called whenever an inbound channel is destroyed.  Should clean up
728  * any associated state.
729  *
730  * @param cls closure (set from GNUNET_MESH_connect)
731  * @param channel connection to the other end (henceforth invalid)
732  * @param channel_ctx place where local state associated
733  *                   with the channel is stored
734  */
735 static void
736 channel_cleaner (void *cls, const struct GNUNET_MESH_Channel *channel,
737                  void *channel_ctx)
738 {
739   long n = (long) cls;
740   struct MeshPeer *peer = &peers[n];
741
742   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
743               "Channel %p disconnected at peer %ld\n", channel, n);
744   if (peer->ch == channel)
745     peer->ch = NULL;
746 }
747
748
749 static struct MeshPeer *
750 select_random_peer (struct MeshPeer *peer)
751 {
752   unsigned int r;
753
754   do
755   {
756     r = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, TOTAL_PEERS);
757   } while (NULL != peers[r].incoming);
758   peers[r].incoming = peer;
759
760   return &peers[r];
761 }
762
763 /**
764  * START THE TEST ITSELF, AS WE ARE CONNECTED TO THE MESH SERVICES.
765  *
766  * Testcase continues when the root receives confirmation of connected peers,
767  * on callback funtion ch.
768  *
769  * @param cls Closure (unsued).
770  * @param tc Task Context.
771  */
772 static void
773 start_test (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
774 {
775   enum GNUNET_MESH_ChannelOption flags;
776   unsigned long i;
777
778   if ((GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason) != 0)
779     return;
780
781   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Start profiler\n");
782
783   flags = GNUNET_MESH_OPTION_DEFAULT;
784   for (i = 0; i < PING_PEERS; i++)
785   {
786
787     peers[i].dest = select_random_peer (&peers[i]);
788     peers[i].ch = GNUNET_MESH_channel_create (peers[i].mesh, NULL,
789                                               &peers[i].dest->id,
790                                               1, flags);
791     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "%u => %u %p\n",
792                 i, get_index (peers[i].dest), peers[i].ch);
793     peers[i].ping_task = GNUNET_SCHEDULER_add_delayed (delay_ms_rnd (2000),
794                                                        &ping, &peers[i]);
795   }
796   peers_running = TOTAL_PEERS;
797   GNUNET_SCHEDULER_add_delayed (ROUND_TIME, &next_rnd, NULL);
798 }
799
800
801 /**
802  * Callback to be called when the requested peer information is available
803  *
804  * @param cls the closure from GNUNET_TESTBED_peer_get_information()
805  * @param op the operation this callback corresponds to
806  * @param pinfo the result; will be NULL if the operation has failed
807  * @param emsg error message if the operation has failed;
808  *             NULL if the operation is successfull
809  */
810 static void
811 peer_id_cb (void *cls,
812        struct GNUNET_TESTBED_Operation *op,
813        const struct GNUNET_TESTBED_PeerInformation *pinfo,
814        const char *emsg)
815 {
816   long n = (long) cls;
817
818   if (NULL == pinfo || NULL != emsg)
819   {
820     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "pi_cb: %s\n", emsg);
821     abort_test (__LINE__);
822     return;
823   }
824   peers[n].id = *(pinfo->result.id);
825   GNUNET_log (GNUNET_ERROR_TYPE_INFO, " %u  id: %s\n",
826               n, GNUNET_i2s (&peers[n].id));
827   GNUNET_break (GNUNET_OK ==
828                 GNUNET_CONTAINER_multipeermap_put (ids, &peers[n].id, &peers[n],
829                                                    GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST));
830
831   GNUNET_TESTBED_operation_done (peers[n].op);
832   peers[n].op = NULL;
833
834   p_ids++;
835   if (p_ids < TOTAL_PEERS)
836     return;
837   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Got all IDs, starting profiler\n");
838   test_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
839                                             &start_test, NULL);
840 }
841
842 /**
843  * test main: start test when all peers are connected
844  *
845  * @param cls Closure.
846  * @param ctx Argument to give to GNUNET_MESH_TEST_cleanup on test end.
847  * @param num_peers Number of peers that are running.
848  * @param testbed_peers Array of peers.
849  * @param meshes Handle to each of the MESHs of the peers.
850  */
851 static void
852 tmain (void *cls,
853        struct GNUNET_MESH_TEST_Context *ctx,
854        unsigned int num_peers,
855        struct GNUNET_TESTBED_Peer **testbed_peers,
856        struct GNUNET_MESH_Handle **meshes)
857 {
858   unsigned long i;
859
860   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test main\n");
861   ok = 0;
862   test_ctx = ctx;
863   GNUNET_assert (TOTAL_PEERS > 2 * PING_PEERS);
864   GNUNET_assert (TOTAL_PEERS == num_peers);
865   peers_running = num_peers;
866   testbed_handles = testbed_peers;
867   disconnect_task = GNUNET_SCHEDULER_add_delayed (SHORT_TIME,
868                                                   &disconnect_mesh_peers,
869                                                   (void *) __LINE__);
870   shutdown_handle = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
871                                                   &shutdown_task, NULL);
872   for (i = 0; i < TOTAL_PEERS; i++)
873   {
874     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "requesting id %ld\n", i);
875     peers[i].up = GNUNET_YES;
876     peers[i].mesh = meshes[i];
877     peers[i].op =
878       GNUNET_TESTBED_peer_get_information (testbed_handles[i],
879                                            GNUNET_TESTBED_PIT_IDENTITY,
880                                            &peer_id_cb, (void *) i);
881   }
882   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "requested peer ids\n");
883   /* Continues from pi_cb -> do_test */
884 }
885
886
887 /**
888  * Main: start profiler.
889  */
890 int
891 main (int argc, char *argv[])
892 {
893   static uint32_t ports[2];
894   const char *config_file;
895
896   config_file = "test_mesh.conf";
897
898   ids = GNUNET_CONTAINER_multipeermap_create (2 * TOTAL_PEERS, GNUNET_YES);
899   GNUNET_assert (NULL != ids);
900   p_ids = 0;
901   test_finished = GNUNET_NO;
902   ports[0] = 1;
903   ports[1] = 0;
904   GNUNET_MESH_TEST_run ("mesh-profiler", config_file, TOTAL_PEERS,
905                         &tmain, NULL, /* tmain cls */
906                         &incoming_channel, &channel_cleaner,
907                         handlers, ports);
908
909   if (ok_goal > ok)
910   {
911     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
912                 "FAILED! (%d/%d)\n", ok, ok_goal);
913     return 1;
914   }
915   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "success\n");
916   return 0;
917 }
918
919 /* end of gnunet-mesh-profiler.c */
920