- Fix contidion for next round
[oweals/gnunet.git] / src / nse / gnunet-nse-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 nse/gnunet-nse-profiler.c
22  *
23  * @brief Profiling driver for the network size estimation service.
24  *        Generally, the profiler starts a given number of peers,
25  *        then churns some off, waits a certain amount of time, then
26  *        churns again, and repeats.
27  */
28 #include "platform.h"
29 #include "gnunet_testing_lib.h"
30 #include "gnunet_nse_service.h"
31
32 #define VERBOSE 3
33
34 struct NSEPeer
35 {
36   struct NSEPeer *prev;
37
38   struct NSEPeer *next;
39
40   struct GNUNET_TESTING_Daemon *daemon;
41
42   struct GNUNET_NSE_Handle *nse_handle;
43
44   struct GNUNET_STATISTICS_Handle *stats;
45   
46   GNUNET_SCHEDULER_TaskIdentifier stats_task;
47 };
48
49
50 struct StatsContext
51 {
52   /**
53    * Whether or not shoutdown after finishing.
54    */
55   int shutdown;
56
57   /**
58    * How many messages have peers received during the test.
59    */
60   unsigned long long total_nse_received_messages;
61
62   /**
63    * How many messages have peers send during the test (should be == received).
64    */
65   unsigned long long total_nse_transmitted_messages;
66
67   /**
68    * How many messages have travelled an edge in both directions.
69    */
70   unsigned long long total_nse_cross;
71
72   /**
73    * How many extra messages per edge (corrections) have been received.
74    */
75   unsigned long long total_nse_extra;
76
77   /**
78    * How many messages have been discarded.
79    */
80   unsigned long long total_discarded;
81 };
82
83
84 static struct NSEPeer *peer_head;
85
86 static struct NSEPeer *peer_tail;
87
88 /**
89  * How long until we give up on connecting the peers?
90  */
91 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1500)
92
93 static int ok;
94
95 /**
96  * Be verbose
97  */
98 static int verbose;
99
100 /**
101  * Total number of peers in the test.
102  */
103 static unsigned long long num_peers;
104
105 /**
106  * Global configuration file
107  */
108 static struct GNUNET_CONFIGURATION_Handle *testing_cfg;
109
110 /**
111  * Total number of currently running peers.
112  */
113 static unsigned long long peers_running;
114
115 /**
116  * Current round we are in.
117  */
118 static unsigned long long current_round;
119
120 /**
121  * Peers desired in the next round.
122  */
123 static unsigned long long peers_next_round;
124
125 /**
126  * Maximum number of connections to NSE services.
127  */
128 static unsigned long long connection_limit;
129
130 /**
131  * Total number of connections in the whole network.
132  */
133 static unsigned int total_connections;
134
135 /**
136  * The currently running peer group.
137  */
138 static struct GNUNET_TESTING_PeerGroup *pg;
139
140 /**
141  * File to report results to.
142  */
143 static struct GNUNET_DISK_FileHandle *output_file;
144
145 /**
146  * File to log connection info, statistics to.
147  */
148 static struct GNUNET_DISK_FileHandle *data_file;
149
150 /**
151  * How many data points to capture before triggering next round?
152  */
153 static struct GNUNET_TIME_Relative wait_time;
154
155 /**
156  * NSE interval.
157  */
158 static struct GNUNET_TIME_Relative interval;
159
160 /**
161  * Task called to disconnect peers.
162  */
163 static GNUNET_SCHEDULER_TaskIdentifier disconnect_task;
164
165 /**
166  * Task called to shutdown test.
167  */
168 static GNUNET_SCHEDULER_TaskIdentifier shutdown_handle;
169
170 /**
171  * Task used to churn the network.
172  */
173 static GNUNET_SCHEDULER_TaskIdentifier churn_task;
174
175 static char *topology_file;
176
177 /**
178  * Check whether peers successfully shut down.
179  */
180 static void
181 shutdown_callback (void *cls, const char *emsg)
182 {
183   if (emsg != NULL)
184   {
185 #if VERBOSE
186     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Shutdown of peers failed!\n");
187 #endif
188     if (ok == 0)
189       ok = 666;
190   }
191   else
192   {
193 #if VERBOSE
194     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "All peers successfully shut down!\n");
195 #endif
196     ok = 0;
197   }
198 }
199
200
201 static void
202 shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
203 {
204   struct NSEPeer *pos;
205
206 #if VERBOSE
207   FPRINTF (stderr, "%s",  "Ending test.\n");
208 #endif
209
210   if (disconnect_task != GNUNET_SCHEDULER_NO_TASK)
211   {
212     GNUNET_SCHEDULER_cancel (disconnect_task);
213     disconnect_task = GNUNET_SCHEDULER_NO_TASK;
214   }
215   while (NULL != (pos = peer_head))
216   {
217     if (pos->nse_handle != NULL)
218       GNUNET_NSE_disconnect (pos->nse_handle);
219     GNUNET_CONTAINER_DLL_remove (peer_head, peer_tail, pos);
220     if (GNUNET_SCHEDULER_NO_TASK != pos->stats_task)
221     {
222       GNUNET_SCHEDULER_cancel (pos->stats_task);
223       if (NULL != pos->stats)
224         GNUNET_STATISTICS_destroy(pos->stats, GNUNET_NO);
225     }
226     GNUNET_free (pos);
227   }
228
229   if (data_file != NULL)
230     GNUNET_DISK_file_close (data_file);
231   GNUNET_TESTING_daemons_stop (pg, TIMEOUT, &shutdown_callback, NULL);
232 }
233
234
235 /**
236  * Callback to call when network size estimate is updated.
237  *
238  * @param cls closure
239  * @param timestamp server timestamp
240  * @param estimate the value of the current network size estimate
241  * @param std_dev standard deviation (rounded down to nearest integer)
242  *                of the size estimation values seen
243  *
244  */
245 static void
246 handle_estimate (void *cls, struct GNUNET_TIME_Absolute timestamp,
247                  double estimate, double std_dev)
248 {
249   struct NSEPeer *peer = cls;
250   char *output_buffer;
251   size_t size;
252
253   if (output_file != NULL)
254   {
255     size =
256         GNUNET_asprintf (&output_buffer, "%s %llu %llu %f %f %f\n",
257                          GNUNET_i2s (&peer->daemon->id), peers_running,
258                          timestamp.abs_value,
259                          GNUNET_NSE_log_estimate_to_n (estimate), estimate,
260                          std_dev);
261     if (size != GNUNET_DISK_file_write (output_file, output_buffer, size))
262       GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Unable to write to file!\n");
263     GNUNET_free (output_buffer);
264   }
265   else
266     FPRINTF (stderr,
267              "Received network size estimate from peer %s. Size: %f std.dev. %f\n",
268              GNUNET_i2s (&peer->daemon->id), estimate, std_dev);
269
270 }
271
272 /**
273  * Process core statistic values.
274  *
275  * @param cls closure
276  * @param subsystem name of subsystem that created the statistic
277  * @param name the name of the datum
278  * @param value the current value
279  * @param is_persistent GNUNET_YES if the value is persistent, GNUNET_NO if not
280  * @return GNUNET_OK to continue, GNUNET_SYSERR to abort iteration
281  */
282 static int
283 core_stats_iterator (void *cls, const char *subsystem, const char *name,
284                      uint64_t value, int is_persistent)
285 {
286   struct NSEPeer *peer = cls;
287   char *output_buffer;
288   size_t size;
289
290   if (output_file != NULL)
291   {
292     size =
293         GNUNET_asprintf (&output_buffer, "%s [%s] %s %llu\n",
294                           GNUNET_i2s (&peer->daemon->id),
295                          subsystem, name, value);
296     if (size != GNUNET_DISK_file_write (output_file, output_buffer, size))
297       GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Unable to write to file!\n");
298     GNUNET_free (output_buffer);
299   }
300   else
301     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
302                 "%s -> %s [%s]: %llu\n",
303                 GNUNET_i2s (&peer->daemon->id), subsystem, name, value);
304
305   return GNUNET_OK;
306 }
307
308 /**
309  * Continuation called by "get_stats" function.
310  *
311  * @param cls closure
312  * @param success GNUNET_OK if statistics were
313  *        successfully obtained, GNUNET_SYSERR if not.
314  */
315 static void
316 core_stats_cont (void *cls, int success);
317
318 static void
319 core_get_stats (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
320 {
321   struct NSEPeer *peer = cls;
322
323   if ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0)
324   {
325     GNUNET_STATISTICS_destroy(peer->stats, GNUNET_NO);
326     peer->stats = NULL;
327     return;
328   }
329   else
330   {
331     GNUNET_STATISTICS_get(peer->stats, "core", NULL,
332                           GNUNET_TIME_UNIT_FOREVER_REL,
333                           &core_stats_cont, &core_stats_iterator, peer);
334     GNUNET_STATISTICS_get(peer->stats, "transport", NULL,
335                           GNUNET_TIME_UNIT_FOREVER_REL,
336                           NULL, &core_stats_iterator, peer);
337     GNUNET_STATISTICS_get(peer->stats, "nse", NULL,
338                           GNUNET_TIME_UNIT_FOREVER_REL,
339                           NULL, &core_stats_iterator, peer);
340   }
341   peer->stats_task = GNUNET_SCHEDULER_NO_TASK;
342 }
343
344 /**
345  * Continuation called by "get_stats" function.
346  *
347  * @param cls closure
348  * @param success GNUNET_OK if statistics were
349  *        successfully obtained, GNUNET_SYSERR if not.
350  */
351 static void
352 core_stats_cont (void *cls, int success)
353 {
354   struct NSEPeer *peer = cls;
355   peer->stats_task = GNUNET_SCHEDULER_add_delayed (interval, &core_get_stats,
356                                                    peer);
357 }
358
359
360 /**
361  *
362  */
363 static void
364 connect_nse_service (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
365 {
366   struct NSEPeer *current_peer;
367   unsigned int i;
368
369 #if VERBOSE
370   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting to nse service of peers\n");
371 #endif
372   for (i = 0; i < num_peers; i++)
373   {
374     if ((connection_limit > 0) && (i % (num_peers / connection_limit) != 0))
375       continue;
376 #if VERBOSE
377     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
378                 "nse-profiler: connecting to nse service of peer %d\n", i);
379 #endif
380     current_peer = GNUNET_malloc (sizeof (struct NSEPeer));
381     current_peer->daemon = GNUNET_TESTING_daemon_get (pg, i);
382     if (GNUNET_YES ==
383         GNUNET_TESTING_test_daemon_running (GNUNET_TESTING_daemon_get (pg, i)))
384     {
385       current_peer->nse_handle =
386           GNUNET_NSE_connect (current_peer->daemon->cfg, &handle_estimate,
387                               current_peer);
388       GNUNET_assert (current_peer->nse_handle != NULL);
389     }
390     current_peer->stats = GNUNET_STATISTICS_create("profiler", current_peer->daemon->cfg);
391     GNUNET_STATISTICS_get(current_peer->stats, "core", NULL,
392                           GNUNET_TIME_UNIT_FOREVER_REL,
393                           &core_stats_cont, &core_stats_iterator, current_peer);
394     GNUNET_STATISTICS_get(current_peer->stats, "transport", NULL,
395                           GNUNET_TIME_UNIT_FOREVER_REL,
396                           NULL, &core_stats_iterator, current_peer);
397     GNUNET_STATISTICS_get(current_peer->stats, "nse", NULL,
398                           GNUNET_TIME_UNIT_FOREVER_REL,
399                           NULL, &core_stats_iterator, current_peer);
400     GNUNET_CONTAINER_DLL_insert (peer_head, peer_tail, current_peer);
401   }
402 }
403
404
405 static void
406 churn_peers (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
407
408
409 /**
410  * Continuation called by the "get_all" and "get" functions.
411  *
412  * @param cls struct StatsContext
413  * @param success GNUNET_OK if statistics were
414  *        successfully obtained, GNUNET_SYSERR if not.
415  */
416 static void
417 stats_finished_callback (void *cls, int success)
418 {
419   struct StatsContext *stats_context = cls;
420   char *buf;
421   int buf_len;
422
423   if ((GNUNET_OK == success) && (data_file != NULL))
424   {
425     /* Stats lookup successful, write out data */
426     buf = NULL;
427     buf_len =
428         GNUNET_asprintf (&buf, "TOTAL_NSE_RECEIVED_MESSAGES_%d: %u \n",
429                          stats_context->shutdown, 
430                          stats_context->total_nse_received_messages);
431     if (buf_len > 0)
432     {
433       GNUNET_DISK_file_write (data_file, buf, buf_len);
434     }
435     GNUNET_free_non_null (buf);
436
437     buf = NULL;
438     buf_len =
439         GNUNET_asprintf (&buf, "TOTAL_NSE_TRANSMITTED_MESSAGES_%d: %u\n",
440                          stats_context->shutdown, 
441                          stats_context->total_nse_transmitted_messages);
442     if (buf_len > 0)
443     {
444       GNUNET_DISK_file_write (data_file, buf, buf_len);
445     }
446     GNUNET_free_non_null (buf);
447
448     buf = NULL;
449     buf_len =
450         GNUNET_asprintf (&buf, "TOTAL_NSE_CROSS_%d: %u \n",
451                          stats_context->shutdown, 
452                          stats_context->total_nse_cross);
453     if (buf_len > 0)
454     {
455       GNUNET_DISK_file_write (data_file, buf, buf_len);
456     }
457     GNUNET_free_non_null (buf);
458
459     buf = NULL;
460     buf_len =
461         GNUNET_asprintf (&buf, "TOTAL_NSE_EXTRA_%d: %u \n",
462                          stats_context->shutdown, 
463                          stats_context->total_nse_extra);
464     if (buf_len > 0)
465     {
466       GNUNET_DISK_file_write (data_file, buf, buf_len);
467     }
468     GNUNET_free_non_null (buf);
469
470     buf = NULL;
471     buf_len =
472         GNUNET_asprintf (&buf, "TOTAL_NSE_DISCARDED_%d: %u \n",
473                          stats_context->shutdown, 
474                          stats_context->total_discarded);
475     if (buf_len > 0)
476     {
477       GNUNET_DISK_file_write (data_file, buf, buf_len);
478     }
479     GNUNET_free_non_null (buf);
480
481   }
482
483   if (GNUNET_YES == stats_context->shutdown)
484   {
485     GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == shutdown_handle);
486     shutdown_handle = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
487   }
488   else
489   {
490     GNUNET_assert (churn_task == GNUNET_SCHEDULER_NO_TASK);
491     churn_task = GNUNET_SCHEDULER_add_now (&churn_peers, NULL);
492   }
493   GNUNET_free (stats_context);
494 }
495
496
497 /**
498  * Callback function to process statistic values.
499  *
500  * @param cls struct StatsContext
501  * @param peer the peer the statistics belong to
502  * @param subsystem name of subsystem that created the statistic
503  * @param name the name of the datum
504  * @param value the current value
505  * @param is_persistent GNUNET_YES if the value is persistent, GNUNET_NO if not
506  * @return GNUNET_OK to continue, GNUNET_SYSERR to abort iteration
507  */
508 static int
509 statistics_iterator (void *cls, const struct GNUNET_PeerIdentity *peer,
510                      const char *subsystem, const char *name, uint64_t value,
511                      int is_persistent)
512 {
513   struct StatsContext *stats_context = cls;
514
515   if (0 == strcmp (subsystem, "nse"))
516   {
517     if (0 == strcmp (name, "# flood messages received"))
518     {
519       stats_context->total_nse_received_messages += value;
520 #if VERBOSE
521       if (data_file != NULL)
522       {
523         char *buf;
524         int buf_len;
525
526         buf = NULL;
527         buf_len =
528             GNUNET_asprintf (&buf, "%s %u RECEIVED\n", GNUNET_i2s(peer), value);
529         if (buf_len > 0)
530         {
531           GNUNET_DISK_file_write (data_file, buf, buf_len);
532         }
533         GNUNET_free_non_null (buf);
534       }
535 #endif
536     }
537     if (0 == strcmp (name, "# flood messages transmitted"))
538     {
539       stats_context->total_nse_transmitted_messages += value;
540 #if VERBOSE
541       if (data_file != NULL)
542       {
543         char *buf;
544         int buf_len;
545
546         buf = NULL;
547         buf_len =
548             GNUNET_asprintf (&buf, "%s %u TRANSMITTED\n", 
549                              GNUNET_i2s(peer), value);
550         if (buf_len > 0)
551         {
552           GNUNET_DISK_file_write (data_file, buf, buf_len);
553         }
554         GNUNET_free_non_null (buf);
555       }
556 #endif
557     }
558     if (0 == strcmp (name, "# cross messages"))
559     {
560       stats_context->total_nse_cross += value;
561     }
562     if (0 == strcmp (name, "# extra messages"))
563     {
564       stats_context->total_nse_extra += value;
565     }
566     if (0 == strcmp (name, "# flood messages discarded (clock skew too large)"))
567     {
568       stats_context->total_discarded += value;
569     }
570   }
571   return GNUNET_OK;
572 }
573
574
575 static void
576 disconnect_nse_peers (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
577 {
578   struct NSEPeer *pos;
579   char *buf;
580   struct StatsContext *stats_context;
581
582   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "disconnecting nse service of peers\n");
583   disconnect_task = GNUNET_SCHEDULER_NO_TASK;
584   while (NULL != (pos = peer_head))
585   {
586     if (pos->nse_handle != NULL)
587     {
588       GNUNET_NSE_disconnect (pos->nse_handle);
589       pos->nse_handle = NULL;
590     }
591     GNUNET_CONTAINER_DLL_remove (peer_head, peer_tail, pos);
592     if (NULL != pos->stats)
593       GNUNET_STATISTICS_destroy(pos->stats, GNUNET_NO);
594     if (GNUNET_SCHEDULER_NO_TASK != pos->stats_task)
595       GNUNET_SCHEDULER_cancel (pos->stats_task);
596     GNUNET_free (pos);
597   }
598
599   GNUNET_asprintf (&buf, "round%llu", current_round);
600   if (GNUNET_OK ==
601       GNUNET_CONFIGURATION_get_value_number (testing_cfg, "nse-profiler", buf,
602                                              &peers_next_round))
603   {
604     current_round++;
605     if (current_round == 1)
606     {
607       stats_context = GNUNET_malloc (sizeof (struct StatsContext));
608       stats_context->shutdown = GNUNET_NO;
609       GNUNET_TESTING_get_statistics (pg, &stats_finished_callback,
610                                     &statistics_iterator, stats_context);
611     }
612     else
613     {
614       GNUNET_assert (churn_task == GNUNET_SCHEDULER_NO_TASK);
615       churn_task = GNUNET_SCHEDULER_add_now (&churn_peers, NULL);
616     }
617   }
618   else                          /* No more rounds, let's shut it down! */
619   {
620     stats_context = GNUNET_malloc (sizeof (struct StatsContext));
621     stats_context->shutdown = GNUNET_YES;
622     GNUNET_SCHEDULER_cancel (shutdown_handle);
623     shutdown_handle = GNUNET_SCHEDULER_NO_TASK;
624     GNUNET_TESTING_get_statistics (pg, &stats_finished_callback,
625                                    &statistics_iterator, stats_context);
626   }
627   GNUNET_free (buf);
628 }
629
630
631 /**
632  * FIXME.
633  *
634  * @param cls unused
635  * @param emsg NULL on success
636  */
637 static void
638 topology_output_callback (void *cls, const char *emsg)
639 {
640   disconnect_task =
641       GNUNET_SCHEDULER_add_delayed (wait_time, &disconnect_nse_peers, NULL);
642   GNUNET_SCHEDULER_add_now (&connect_nse_service, NULL);
643 }
644
645
646 /**
647  * FIXME.
648  *
649  * @param cls closure
650  * @param emsg NULL on success
651  */
652 static void
653 churn_callback (void *cls, const char *emsg)
654 {
655   char *temp_output_file;
656
657   if (emsg == NULL)             /* Everything is okay! */
658   {
659     peers_running = peers_next_round;
660     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
661                 "Round %llu, churn finished successfully.\n", current_round);
662     GNUNET_assert (disconnect_task == GNUNET_SCHEDULER_NO_TASK);
663     GNUNET_asprintf (&temp_output_file, "%s_%llu.dot", topology_file,
664                      current_round);
665     GNUNET_TESTING_peergroup_topology_to_file (pg, temp_output_file,
666                                                &topology_output_callback, NULL);
667     GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Writing topology to file %s\n",
668                 temp_output_file);
669     GNUNET_free (temp_output_file);
670   }
671   else
672   {
673     GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Round %llu, churn FAILED!!\n",
674                 current_round);
675     GNUNET_SCHEDULER_cancel (shutdown_handle);
676     shutdown_handle = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
677   }
678 }
679
680
681 static void
682 churn_peers (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
683 {
684   /* peers_running = GNUNET_TESTING_daemons_running(pg); */
685   churn_task = GNUNET_SCHEDULER_NO_TASK;
686   if (peers_next_round == peers_running)
687   {
688     /* Nothing to do... */
689     GNUNET_SCHEDULER_add_now (&connect_nse_service, NULL);
690     GNUNET_assert (disconnect_task == GNUNET_SCHEDULER_NO_TASK);
691     disconnect_task =
692         GNUNET_SCHEDULER_add_delayed (wait_time, &disconnect_nse_peers, NULL);
693     GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Round %lu, doing nothing!\n",
694                 current_round);
695   }
696   else
697   {
698     if (peers_next_round > num_peers)
699     {
700       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
701                   "Asked to turn on more peers than we have!!\n");
702       GNUNET_SCHEDULER_cancel (shutdown_handle);
703       GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
704     }
705     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
706                 "Round %llu, turning off %llu peers, turning on %llu peers!\n",
707                 current_round,
708                 (peers_running >
709                  peers_next_round) ? peers_running - peers_next_round : 0,
710                 (peers_next_round >
711                  peers_running) ? peers_next_round - peers_running : 0);
712     GNUNET_TESTING_daemons_churn (pg, "nse",
713                                   (peers_running >
714                                    peers_next_round) ? peers_running -
715                                   peers_next_round : 0,
716                                   (peers_next_round >
717                                    peers_running) ? peers_next_round -
718                                   peers_running : 0, wait_time, &churn_callback,
719                                   NULL);
720   }
721 }
722
723
724 static void
725 nse_started_cb (void *cls, const char *emsg)
726 {
727   GNUNET_SCHEDULER_add_now (&connect_nse_service, NULL);
728   disconnect_task =
729       GNUNET_SCHEDULER_add_delayed (wait_time, &disconnect_nse_peers, NULL);
730 }
731
732
733 static void
734 my_cb (void *cls, const char *emsg)
735 {
736   char *buf;
737   int buf_len;
738
739   if (emsg != NULL)
740   {
741     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
742                 "Peergroup callback called with error, aborting test!\n");
743     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Error from testing: `%s'\n");
744     ok = 1;
745     GNUNET_TESTING_daemons_stop (pg, TIMEOUT, &shutdown_callback, NULL);
746     return;
747   }
748 #if VERBOSE
749   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
750               "Peer Group started successfully, connecting to NSE service for each peer!\n");
751 #endif
752   GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Have %u connections\n",
753               total_connections);
754   if (data_file != NULL)
755   {
756     buf = NULL;
757     buf_len = GNUNET_asprintf (&buf, "CONNECTIONS_0: %u\n", total_connections);
758     if (buf_len > 0)
759       GNUNET_DISK_file_write (data_file, buf, buf_len);
760     GNUNET_free (buf);
761   }
762   peers_running = GNUNET_TESTING_daemons_running (pg);
763   GNUNET_TESTING_daemons_start_service (pg, "nse", wait_time, &nse_started_cb,
764                                         NULL);
765
766 }
767
768
769 /**
770  * Function that will be called whenever two daemons are connected by
771  * the testing library.
772  *
773  * @param cls closure
774  * @param first peer id for first daemon
775  * @param second peer id for the second daemon
776  * @param distance distance between the connected peers
777  * @param first_cfg config for the first daemon
778  * @param second_cfg config for the second daemon
779  * @param first_daemon handle for the first daemon
780  * @param second_daemon handle for the second daemon
781  * @param emsg error message (NULL on success)
782  */
783 static void
784 connect_cb (void *cls, const struct GNUNET_PeerIdentity *first,
785             const struct GNUNET_PeerIdentity *second, uint32_t distance,
786             const struct GNUNET_CONFIGURATION_Handle *first_cfg,
787             const struct GNUNET_CONFIGURATION_Handle *second_cfg,
788             struct GNUNET_TESTING_Daemon *first_daemon,
789             struct GNUNET_TESTING_Daemon *second_daemon, const char *emsg)
790 {
791   if (emsg == NULL)
792     total_connections++;
793 }
794
795
796 static void
797 run (void *cls, char *const *args, const char *cfgfile,
798      const struct GNUNET_CONFIGURATION_Handle *cfg)
799 {
800   char *temp_str;
801   struct GNUNET_TESTING_Host *hosts;
802   char *data_filename;
803
804   ok = 1;
805   //testing_cfg = GNUNET_CONFIGURATION_create ();
806   testing_cfg = GNUNET_CONFIGURATION_dup (cfg);
807 #if VERBOSE
808   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Starting daemons.\n");
809   GNUNET_CONFIGURATION_set_value_string (testing_cfg, "testing",
810                                          "use_progressbars", "YES");
811 #endif
812   if (GNUNET_OK !=
813       GNUNET_CONFIGURATION_get_value_number (testing_cfg, "testing",
814                                              "num_peers", &num_peers))
815   {
816     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
817                 "Option TESTING:NUM_PEERS is required!\n");
818     return;
819   }
820
821   if (GNUNET_OK !=
822       GNUNET_CONFIGURATION_get_value_time (testing_cfg, "nse-profiler",
823                                            "WAIT_TIME", &wait_time))
824   {
825     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
826                 "Option nse-profiler:wait_time is required!\n");
827     return;
828   }
829
830   if (GNUNET_OK !=
831       GNUNET_CONFIGURATION_get_value_time (testing_cfg, "nse",
832                                            "INTERVAL", &interval))
833   {
834     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
835                 "Option nse:interval is required!\n");
836     return;
837   }
838
839   if (GNUNET_OK !=
840       GNUNET_CONFIGURATION_get_value_number (testing_cfg, "nse-profiler",
841                                              "connection_limit",
842                                              &connection_limit))
843   {
844     connection_limit = 0;
845   }
846
847   if (GNUNET_OK !=
848       GNUNET_CONFIGURATION_get_value_string (testing_cfg, "nse-profiler",
849                                              "topology_output_file",
850                                              &topology_file))
851   {
852     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
853                 "Option nse-profiler:topology_output_file is required!\n");
854     return;
855   }
856
857   if (GNUNET_OK ==
858       GNUNET_CONFIGURATION_get_value_string (testing_cfg, "nse-profiler",
859                                              "data_output_file",
860                                              &data_filename))
861   {
862     data_file =
863         GNUNET_DISK_file_open (data_filename,
864                                GNUNET_DISK_OPEN_READWRITE |
865                                GNUNET_DISK_OPEN_TRUNCATE |
866                                GNUNET_DISK_OPEN_CREATE,
867                                GNUNET_DISK_PERM_USER_READ |
868                                GNUNET_DISK_PERM_USER_WRITE);
869     if (data_file == NULL)
870       GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Failed to open %s for output!\n",
871                   data_filename);
872     GNUNET_free (data_filename);
873   }
874
875   if (GNUNET_YES ==
876       GNUNET_CONFIGURATION_get_value_string (cfg, "nse-profiler", "output_file",
877                                              &temp_str))
878   {
879     output_file =
880         GNUNET_DISK_file_open (temp_str,
881                                GNUNET_DISK_OPEN_READWRITE |
882                                GNUNET_DISK_OPEN_CREATE,
883                                GNUNET_DISK_PERM_USER_READ |
884                                GNUNET_DISK_PERM_USER_WRITE);
885     if (output_file == NULL)
886       GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Failed to open %s for output!\n",
887                   temp_str);
888   }
889   GNUNET_free_non_null (temp_str);
890
891   hosts = GNUNET_TESTING_hosts_load (testing_cfg);
892
893   pg = GNUNET_TESTING_peergroup_start (testing_cfg, num_peers, TIMEOUT,
894                                        &connect_cb, &my_cb, NULL, hosts);
895   GNUNET_assert (pg != NULL);
896   shutdown_handle =
897       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_get_forever (),
898                                     &shutdown_task, NULL);
899 }
900
901
902
903 /**
904  * nse-profiler command line options
905  */
906 static struct GNUNET_GETOPT_CommandLineOption options[] = {
907   {'V', "verbose", NULL,
908    gettext_noop ("be verbose (print progress information)"),
909    0, &GNUNET_GETOPT_set_one, &verbose},
910   GNUNET_GETOPT_OPTION_END
911 };
912
913
914 int
915 main (int argc, char *argv[])
916 {
917   GNUNET_log_setup ("nse-profiler",
918 #if VERBOSE
919                     "DEBUG",
920 #else
921                     "WARNING",
922 #endif
923                     NULL);
924   GNUNET_PROGRAM_run (argc, argv, "nse-profiler",
925                       gettext_noop
926                       ("Measure quality and performance of the NSE service."),
927                       options, &run, NULL);
928 #if REMOVE_DIR
929   GNUNET_DISK_directory_remove ("/tmp/nse-profiler");
930 #endif
931   return ok;
932 }
933
934 /* end of nse-profiler.c */