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