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