d55e61e6051717432913dcc75664d532188f8651
[oweals/gnunet.git] / src / nse / gnunet-nse-profiler.c
1 /*
2      This file is part of GNUnet.
3      (C) 2011, 2012 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  * @author Christian Grothoff
28  * @author Nathan Evans
29  * @author Sree Harsha Totakura
30  */
31
32 #include "platform.h"
33 #include "gnunet_testbed_service.h"
34 #include "gnunet_nse_service.h"
35
36 /**
37  * Generic loggins shorthand
38  */
39 #define LOG(kind,...)                                           \
40   GNUNET_log (kind, __VA_ARGS__)
41
42 /**
43  * Debug logging shorthand
44  */
45 #define LOG_DEBUG(...)                          \
46   LOG (GNUNET_ERROR_TYPE_DEBUG, __VA_ARGS__)
47
48
49 /**
50  * Information we track for a peer in the testbed.
51  */
52 struct NSEPeer
53 {
54   /**
55    * Prev reference in DLL.
56    */
57   struct NSEPeer *prev;
58
59   /**
60    * Next reference in DLL.
61    */
62   struct NSEPeer *next;
63
64   /**
65    * Handle with testbed.
66    */
67   struct GNUNET_TESTBED_Peer *daemon;
68
69   /**
70    * Testbed operation to connect to NSE service.
71    */
72   struct GNUNET_TESTBED_Operation *nse_op;
73
74   /**
75    * Testbed operation to connect to statistics service
76    */
77   struct GNUNET_TESTBED_Operation *stat_op;
78
79   /**
80    * Handle to the statistics service
81    */
82   struct GNUNET_STATISTICS_Handle *sh;
83 };
84
85
86 /**
87  * Context for the stats task?
88  */
89 struct StatsContext
90 {
91
92   /**
93    * How many messages have peers received during the test.
94    */
95   unsigned long long total_nse_received_messages;
96
97   /**
98    * How many messages have peers send during the test (should be == received).
99    */
100   unsigned long long total_nse_transmitted_messages;
101
102   /**
103    * How many messages have travelled an edge in both directions.
104    */
105   unsigned long long total_nse_cross;
106
107   /**
108    * How many extra messages per edge (corrections) have been received.
109    */
110   unsigned long long total_nse_extra;
111
112   /**
113    * How many messages have been discarded.
114    */
115   unsigned long long total_discarded;
116 };
117
118
119 /**
120  * Operation map entry
121  */
122 struct OpListEntry
123 {
124   /**
125    * DLL next ptr
126    */
127   struct OpListEntry *next;
128
129   /**
130    * DLL prev ptr
131    */
132   struct OpListEntry *prev;
133
134   /**
135    * The testbed operation
136    */
137   struct GNUNET_TESTBED_Operation *op;
138
139   /**
140    * Depending on whether we start or stop NSE service at the peer set this to 1
141    * or -1
142    */
143   int delta;
144 };
145
146
147 /**
148  * Head of DLL of peers we monitor closely.
149  */
150 static struct NSEPeer *peer_head;
151
152 /**
153  * Tail of DLL of peers we monitor closely.
154  */
155 static struct NSEPeer *peer_tail;
156
157 /**
158  * Return value from 'main' (0 == success)
159  */
160 static int ok;
161
162 /**
163  * Be verbose (configuration option)
164  */
165 static int verbose;
166
167 /**
168  * Name of the file with the hosts to run the test over (configuration option)
169  */ 
170 static char *hosts_file;
171
172 /**
173  * Maximum number of peers in the test.
174  */
175 static unsigned int num_peers;
176
177 /**
178  * Total number of rounds to execute.
179  */
180 static unsigned int num_rounds;
181
182 /**
183  * Current round we are in.
184  */
185 static unsigned int current_round;
186
187 /**
188  * Array of size 'num_rounds' with the requested number of peers in the given round.
189  */
190 static unsigned int *num_peers_in_round;
191
192 /**
193  * How many peers are running right now?
194  */
195 static unsigned int peers_running;
196
197 /**
198  * Specification for the numbers of peers to have in each round.
199  */
200 static char *num_peer_spec;
201
202 /**
203  * Handles to all of the running peers.
204  */
205 static struct GNUNET_TESTBED_Peer **daemons;
206
207 /**
208  * Global configuration file
209  */
210 static struct GNUNET_CONFIGURATION_Handle *testing_cfg;
211
212 /**
213  * The shutdown task
214  */
215 static GNUNET_SCHEDULER_TaskIdentifier shutdown_task_id;
216
217 /**
218  * Maximum number of connections to NSE services.
219  */
220 static unsigned int connection_limit;
221
222 /**
223  * Total number of connections in the whole network.
224  */
225 static unsigned int total_connections;
226
227 /**
228  * File to report results to.
229  */
230 static struct GNUNET_DISK_FileHandle *output_file;
231
232 /**
233  * Filename to log results to.
234  */
235 static char *output_filename;
236
237 /**
238  * File to log connection info, statistics to.
239  */
240 static struct GNUNET_DISK_FileHandle *data_file;
241
242 /**
243  * Filename to log connection info, statistics to.
244  */
245 static char *data_filename;
246
247 /**
248  * How long to wait before triggering next round?
249  * Default: 60 s.
250  */
251 static struct GNUNET_TIME_Relative wait_time = { 60 * 1000 };
252
253 /**
254  * DLL head for operation list
255  */
256 static struct OpListEntry *oplist_head;
257
258 /**
259  * DLL tail for operation list
260  */
261 static struct OpListEntry *oplist_tail;
262
263 /**
264  * Are we shutting down
265  */
266 static int shutting_down;
267
268
269 /**
270  * Clean up all of the monitoring connections to NSE and
271  * STATISTICS that we keep to selected peers.
272  */
273 static void
274 close_monitor_connections ()
275 {
276   struct NSEPeer *pos;
277   struct OpListEntry *oplist_entry;
278
279   while (NULL != (pos = peer_head))
280   {
281     if (NULL != pos->nse_op)
282       GNUNET_TESTBED_operation_done (pos->nse_op);
283     if (NULL != pos->stat_op)
284       GNUNET_TESTBED_operation_done (pos->stat_op);
285     GNUNET_CONTAINER_DLL_remove (peer_head, peer_tail, pos);
286     GNUNET_free (pos);
287   }
288   while (NULL != (oplist_entry = oplist_head))
289   {
290     GNUNET_CONTAINER_DLL_remove (oplist_head, oplist_tail, oplist_entry);
291     GNUNET_TESTBED_operation_done (oplist_entry->op);
292     GNUNET_free (oplist_entry);
293   }
294 }
295
296
297 /**
298  * Task run on shutdown; cleans up everything.
299  *
300  * @param cls unused
301  * @param tc unused
302  */
303 static void
304 shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
305 {
306   shutdown_task_id = GNUNET_SCHEDULER_NO_TASK;
307   if (GNUNET_YES == shutting_down)
308     return;
309   shutting_down = GNUNET_YES;
310   LOG_DEBUG ("Ending test.\n");    
311   close_monitor_connections ();
312   if (NULL != data_file)
313   {
314     GNUNET_DISK_file_close (data_file);
315     data_file = NULL;
316   }
317   if (NULL != output_file)
318   {
319     GNUNET_DISK_file_close (output_file);
320     output_file = NULL;
321   }
322   if (NULL != testing_cfg)
323     GNUNET_CONFIGURATION_destroy (testing_cfg);
324   testing_cfg = NULL;
325 }
326
327
328 /**
329  * Schedules shutdown task to be run now
330  */
331 static void
332 shutdown_now ()
333 {
334   if (GNUNET_SCHEDULER_NO_TASK != shutdown_task_id)
335     GNUNET_SCHEDULER_cancel (shutdown_task_id);
336   shutdown_task_id = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
337 }
338
339
340 /**
341  * Callback to call when network size estimate is updated.
342  *
343  * @param cls closure with the 'struct NSEPeer' providing the update
344  * @param timestamp server timestamp
345  * @param estimate the value of the current network size estimate
346  * @param std_dev standard deviation (rounded down to nearest integer)
347  *                of the size estimation values seen
348  */
349 static void
350 handle_estimate (void *cls, 
351                  struct GNUNET_TIME_Absolute timestamp,
352                  double estimate, double std_dev)
353 {
354   struct NSEPeer *peer = cls;
355   char output_buffer[512];
356   size_t size;
357
358   if (NULL == output_file)
359     {
360       FPRINTF (stderr,
361                "Received network size estimate from peer %p. Size: %f std.dev. %f\n",
362                peer, estimate, std_dev);
363       return;
364     }
365   size = GNUNET_snprintf (output_buffer, 
366                           sizeof (output_buffer),
367                           "%p %llu %llu %f %f %f\n",
368                           peer, peers_running,
369                           timestamp.abs_value,
370                           GNUNET_NSE_log_estimate_to_n (estimate), estimate,
371                           std_dev);
372   if (size != GNUNET_DISK_file_write (output_file, output_buffer, size))
373     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
374                 "Unable to write to file!\n");
375 }
376
377
378 /**
379  * Adapter function called to establish a connection to
380  * NSE service.
381  * 
382  * @param cls closure (the 'struct NSEPeer')
383  * @param cfg configuration of the peer to connect to; will be available until
384  *          GNUNET_TESTBED_operation_done() is called on the operation returned
385  *          from GNUNET_TESTBED_service_connect()
386  * @return service handle to return in 'op_result', NULL on error
387  */
388 static void *
389 nse_connect_adapter (void *cls,
390                      const struct GNUNET_CONFIGURATION_Handle *cfg)
391 {
392   struct NSEPeer *current_peer = cls;
393
394   return GNUNET_NSE_connect (cfg, &handle_estimate, current_peer);
395 }
396
397
398 /**
399  * Adapter function called to destroy a connection to
400  * NSE service.
401  * 
402  * @param cls closure
403  * @param op_result service handle returned from the connect adapter
404  */
405 static void 
406 nse_disconnect_adapter (void *cls,
407                         void *op_result)
408 {
409   GNUNET_NSE_disconnect (op_result);
410 }
411
412
413 /**
414  * Callback function to process statistic values.
415  *
416  * @param cls struct StatsContext
417  * @param subsystem name of subsystem that created the statistic
418  * @param name the name of the datum
419  * @param value the current value
420  * @param is_persistent GNUNET_YES if the value is persistent, GNUNET_NO if not
421  * @return GNUNET_OK to continue, GNUNET_SYSERR to abort iteration
422  */
423 static int
424 stat_iterator (void *cls, const char *subsystem, const char *name,
425                      uint64_t value, int is_persistent)
426 {
427   char *output_buffer;
428   struct GNUNET_TIME_Absolute now;
429   size_t size;
430   unsigned int flag;
431
432   GNUNET_assert (NULL != data_file);
433   now = GNUNET_TIME_absolute_get ();
434   flag = strcasecmp (subsystem, "core");
435   if (0 != flag)
436     flag = 1;
437   size = GNUNET_asprintf (&output_buffer, "%llu %llu %u\n",
438                           now.abs_value/1000, value, flag);
439   if (size != GNUNET_DISK_file_write (data_file, output_buffer, size))
440   {
441     GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Unable to write to file!\n");
442     return GNUNET_SYSERR;
443   }
444   return GNUNET_OK;
445 }
446
447
448 /**
449  * Called to open a connection to the peer's statistics
450  *
451  * @param cls peer context
452  * @param cfg configuration of the peer to connect to; will be available until
453  *          GNUNET_TESTBED_operation_done() is called on the operation returned
454  *          from GNUNET_TESTBED_service_connect()
455  * @return service handle to return in 'op_result', NULL on error
456  */
457 static void *
458 stat_connect_adapter (void *cls,
459                       const struct GNUNET_CONFIGURATION_Handle *cfg)
460 {
461   struct NSEPeer *peer = cls;
462   
463   peer->sh = GNUNET_STATISTICS_create ("nse-profiler", cfg);
464   return peer->sh;
465 }
466
467
468 /**
469  * Called to disconnect from peer's statistics service
470  *
471  * @param cls peer context
472  * @param op_result service handle returned from the connect adapter
473  */
474 static void
475 stat_disconnect_adapter (void *cls, void *op_result)
476 {
477   struct NSEPeer *peer = cls;
478
479   GNUNET_break (GNUNET_OK == GNUNET_STATISTICS_watch_cancel
480                 (peer->sh, "core", "# peers connected",
481                  stat_iterator, peer));
482   GNUNET_break (GNUNET_OK == GNUNET_STATISTICS_watch_cancel
483                 (peer->sh, "nse", "# peers connected",
484                  stat_iterator, peer));
485   GNUNET_STATISTICS_destroy (op_result, GNUNET_NO);
486   peer->sh = NULL;
487 }
488
489
490 /**
491  * Called after successfully opening a connection to a peer's statistics
492  * service; we register statistics monitoring for CORE and NSE here.
493  *
494  * @param cls the callback closure from functions generating an operation
495  * @param op the operation that has been finished
496  * @param ca_result the service handle returned from GNUNET_TESTBED_ConnectAdapter()
497  * @param emsg error message in case the operation has failed; will be NULL if
498  *          operation has executed successfully.
499  */
500 static void
501 stat_comp_cb (void *cls, struct GNUNET_TESTBED_Operation *op,
502               void *ca_result, const char *emsg )
503 {
504   struct GNUNET_STATISTICS_Handle *sh = ca_result;
505   struct NSEPeer *peer = cls;
506
507   if (NULL != emsg)
508   {
509     GNUNET_break (0);
510     return;
511   }
512   GNUNET_break (GNUNET_OK == GNUNET_STATISTICS_watch
513                 (sh, "core", "# peers connected",
514                  stat_iterator, peer));
515   GNUNET_break (GNUNET_OK == GNUNET_STATISTICS_watch
516                 (sh, "nse", "# peers connected",
517                  stat_iterator, peer));
518 }
519
520
521 /**
522  * Task run to connect to the NSE and statistics services to a subset of
523  * all of the running peers.
524  */
525 static void
526 connect_nse_service ()
527 {
528   struct NSEPeer *current_peer;
529   unsigned int i;
530   unsigned int connections;
531
532   if (0 == connection_limit)
533     return;  
534   LOG_DEBUG ("Connecting to nse service of peers\n");
535   connections = 0;
536   for (i = 0; i < num_peers_in_round[current_round]; i++)
537   {
538     if ((num_peers_in_round[current_round] > connection_limit) && 
539         (0 != (i % (num_peers_in_round[current_round] / connection_limit))))
540       continue;
541     LOG_DEBUG ("Connecting to nse service of peer %d\n", i);
542     current_peer = GNUNET_malloc (sizeof (struct NSEPeer));
543     current_peer->daemon = daemons[i];
544     current_peer->nse_op 
545         = GNUNET_TESTBED_service_connect (NULL,
546                                           current_peer->daemon,
547                                           "nse",
548                                           NULL, NULL,
549                                           &nse_connect_adapter,
550                                           &nse_disconnect_adapter,
551                                           current_peer);
552     if (NULL != data_file)
553       current_peer->stat_op
554           = GNUNET_TESTBED_service_connect (NULL,
555                                             current_peer->daemon,
556                                             "statistics",
557                                             stat_comp_cb,
558                                             current_peer,
559                                             &stat_connect_adapter,
560                                             &stat_disconnect_adapter,
561                                             current_peer);
562     GNUNET_CONTAINER_DLL_insert (peer_head, peer_tail, current_peer);
563     if (++connections == connection_limit)
564       break;
565   }
566 }
567
568
569 /**
570  * Task that starts/stops peers to move to the next round.
571  *
572  * @param cls NULL, unused
573  * @param tc scheduler context (unused)
574  */
575 static void
576 next_round (void *cls, 
577             const struct GNUNET_SCHEDULER_TaskContext *tc);
578
579
580 /**
581  * We're at the end of a round.  Stop monitoring, write total
582  * number of connections to log and get full stats.  Then trigger
583  * the next round.
584  *
585  * @param cls unused, NULL
586  * @param tc unused
587  */
588 static void
589 finish_round (void *cls, 
590               const struct GNUNET_SCHEDULER_TaskContext *tc)
591 {
592   if (0 != (GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason))
593     return;
594   LOG (GNUNET_ERROR_TYPE_INFO, "Have %u connections\n", total_connections);
595   close_monitor_connections ();
596   GNUNET_SCHEDULER_add_now (&next_round, NULL);
597 }
598
599
600 /**
601  * We have reached the desired number of peers for the current round.
602  * Run it (by connecting and monitoring a few peers and waiting the
603  * specified delay before finishing the round).
604  */
605 static void
606 run_round ()
607 {
608   LOG_DEBUG ("Running round %u\n", current_round);
609   connect_nse_service ();
610   GNUNET_SCHEDULER_add_delayed (wait_time,
611                                 &finish_round,
612                                 NULL);
613 }
614
615
616 /**
617  * Creates an oplist entry and adds it to the oplist DLL
618  */
619 static struct OpListEntry *
620 make_oplist_entry ()
621 {
622   struct OpListEntry *entry;
623
624   entry = GNUNET_malloc (sizeof (struct OpListEntry));
625   GNUNET_CONTAINER_DLL_insert_tail (oplist_head, oplist_tail, entry);
626   return entry;
627 }
628
629
630 /**
631  * Callback to be called when NSE service is started or stopped at peers
632  *
633  * @param cls NULL
634  * @param op the operation handle
635  * @param emsg NULL on success; otherwise an error description
636  */
637 static void 
638 manage_service_cb (void *cls, struct GNUNET_TESTBED_Operation *op,
639                    const char *emsg)
640 {
641   struct OpListEntry *entry = cls;
642   
643   GNUNET_TESTBED_operation_done (entry->op);
644   if (NULL != emsg)
645   {
646     LOG (GNUNET_ERROR_TYPE_ERROR, "Failed to start/stop NSE at a peer\n");
647     GNUNET_SCHEDULER_shutdown ();
648     return;
649   }
650   GNUNET_assert (0 != entry->delta);
651   peers_running += entry->delta;
652   GNUNET_CONTAINER_DLL_remove (oplist_head, oplist_tail, entry);
653   GNUNET_free (entry);
654   if (num_peers_in_round[current_round] == peers_running)
655     run_round ();
656 }
657
658
659 /**
660  * Adjust the number of running peers to match the required number of running
661  * peers for the round
662  */
663 static void
664 adjust_running_peers ()
665 {
666   struct OpListEntry *entry;
667   unsigned int i;
668
669   /* start peers if we have too few */
670   for (i=peers_running;i<num_peers_in_round[current_round];i++)
671   {
672     entry = make_oplist_entry ();
673     entry->delta = 1;
674     entry->op = GNUNET_TESTBED_peer_manage_service (NULL,
675                                                     daemons[i],
676                                                     "nse",
677                                                     &manage_service_cb,
678                                                     entry,
679                                                     1);
680   }
681   /* stop peers if we have too many */
682   for (i=num_peers_in_round[current_round];i<peers_running;i++)
683   {
684     entry = make_oplist_entry ();
685     entry->delta = -1;
686     entry->op =  GNUNET_TESTBED_peer_manage_service (NULL,
687                                                      daemons[i],
688                                                      "nse",
689                                                      &manage_service_cb,
690                                                      entry,
691                                                      0);
692   }
693 }
694
695
696 /**
697  * Task run at the end of a round.  Disconnect from all monitored
698  * peers; then get statistics from *all* peers.
699  *
700  * @param cls NULL, unused
701  * @param tc unused
702  */
703 static void
704 next_round (void *cls, 
705             const struct GNUNET_SCHEDULER_TaskContext *tc)
706 {
707   if (0 != (GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason))
708     return;
709   LOG_DEBUG ("Disconnecting nse service of peers\n");
710   current_round++;  
711   if (current_round == num_rounds)
712     {
713       /* this was the last round, terminate */
714       ok = 0;
715       GNUNET_SCHEDULER_shutdown ();
716       return;
717     }
718   if (num_peers_in_round[current_round] == peers_running)
719     {
720       /* no need to churn, just run next round */
721       run_round ();
722       return;
723     }
724   adjust_running_peers ();
725 }
726
727
728 /**
729  * Function that will be called whenever something in the
730  * testbed changes.
731  *
732  * @param cls closure, NULL
733  * @param event information on what is happening
734  */
735 static void
736 master_controller_cb (void *cls, 
737                       const struct GNUNET_TESTBED_EventInformation *event)
738 {
739   switch (event->type)
740     {
741     case GNUNET_TESTBED_ET_CONNECT:
742       total_connections++;
743       break;
744     case GNUNET_TESTBED_ET_DISCONNECT:
745       total_connections--;
746       break;
747     default:
748       break;
749     }
750 }
751
752
753 /**
754  * Signature of a main function for a testcase.
755  *
756  * @param cls NULL
757  * @param num_peers_ number of peers in 'peers'
758  * @param peers handle to peers run in the testbed.  NULL upon timeout (see
759  *          GNUNET_TESTBED_test_run()).
760  * @param links_succeeded the number of overlay link connection attempts that
761  *          succeeded
762  * @param links_failed the number of overlay link connection attempts that
763  *          failed
764  */
765 static void 
766 test_master (void *cls,
767              unsigned int num_peers_,
768              struct GNUNET_TESTBED_Peer **peers,
769              unsigned int links_succeeded,
770              unsigned int links_failed)
771 {
772   if (NULL == peers)
773   {
774     shutdown_now ();
775     return;
776   }
777   daemons = peers;
778   GNUNET_break (num_peers_ == num_peers);
779   peers_running = num_peers;
780   if (num_peers_in_round[current_round] == peers_running)
781   {
782     /* no need to churn, just run the starting round */
783     run_round ();
784     return;
785   }
786   adjust_running_peers ();
787 }
788
789
790 /**
791  * Actual main function that runs the emulation.
792  *
793  * @param cls unused
794  * @param args remaining args, unused
795  * @param cfgfile name of the configuration
796  * @param cfg configuration handle
797  */
798 static void
799 run (void *cls, char *const *args, const char *cfgfile,
800      const struct GNUNET_CONFIGURATION_Handle *cfg)
801 {
802   char *tok;
803   uint64_t event_mask;
804   unsigned int num;  
805
806   ok = 1;
807   testing_cfg = GNUNET_CONFIGURATION_dup (cfg);
808   LOG_DEBUG ("Starting daemons.\n");
809   if (NULL == num_peer_spec)
810   {
811     fprintf (stderr, "You need to specify the number of peers to run\n");
812     return;
813   }
814   for (tok = strtok (num_peer_spec, ","); NULL != tok; tok = strtok (NULL, ","))
815     {
816       if (1 != sscanf (tok, "%u", &num))
817         {
818           fprintf (stderr, "You need to specify numbers, not `%s'\n", tok);
819           return;
820         }
821       if (0 == num)
822         {
823           fprintf (stderr, "Refusing to run a round with 0 peers\n");
824           return;
825         }
826       GNUNET_array_append (num_peers_in_round, num_rounds, num);
827       num_peers = GNUNET_MAX (num_peers, num);
828     }
829   if (0 == num_peers)
830     {
831       fprintf (stderr, "Refusing to run a testbed with no rounds\n");
832       return;
833     }
834   if ( (NULL != data_filename) &&
835        (NULL == (data_file = 
836                  GNUNET_DISK_file_open (data_filename,
837                                         GNUNET_DISK_OPEN_READWRITE |
838                                         GNUNET_DISK_OPEN_TRUNCATE |
839                                         GNUNET_DISK_OPEN_CREATE,
840                                         GNUNET_DISK_PERM_USER_READ |
841                                         GNUNET_DISK_PERM_USER_WRITE))) )
842     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
843                               "open",
844                               data_filename);
845
846   if ( (NULL != output_filename) &&
847        (NULL == (output_file =
848                  GNUNET_DISK_file_open (output_filename,
849                                         GNUNET_DISK_OPEN_READWRITE |
850                                         GNUNET_DISK_OPEN_CREATE,
851                                         GNUNET_DISK_PERM_USER_READ |
852                                         GNUNET_DISK_PERM_USER_WRITE))) )
853     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open",
854                               output_filename);
855   event_mask = 0LL;
856   event_mask |= (1LL << GNUNET_TESTBED_ET_PEER_START);
857   event_mask |= (1LL << GNUNET_TESTBED_ET_PEER_STOP);
858   event_mask |= (1LL << GNUNET_TESTBED_ET_CONNECT);
859   event_mask |= (1LL << GNUNET_TESTBED_ET_DISCONNECT);
860   GNUNET_TESTBED_run (hosts_file,
861                       cfg,
862                       num_peers,
863                       event_mask,
864                       master_controller_cb,
865                       NULL,     /* master_controller_cb cls */
866                       &test_master,
867                       NULL);    /* test_master cls */
868   shutdown_task_id = 
869       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
870                                     &shutdown_task, NULL);
871 }
872
873
874 /**
875  * Main function.
876  *
877  * @return 0 on success
878  */
879 int
880 main (int argc, char *const *argv)
881 {
882   static struct GNUNET_GETOPT_CommandLineOption options[] = {
883     {'C', "connections", "COUNT",
884      gettext_noop ("limit to the number of connections to NSE services, 0 for none"),
885      1, &GNUNET_GETOPT_set_uint, &connection_limit},
886     {'d', "details", "FILENAME",
887      gettext_noop ("name of the file for writing connection information and statistics"),
888      1, &GNUNET_GETOPT_set_string, &data_filename},
889     {'H', "hosts", "FILENAME",
890      gettext_noop ("name of the file with the login information for the testbed"),
891      1, &GNUNET_GETOPT_set_string, &hosts_file},
892     {'o', "output", "FILENAME",
893      gettext_noop ("name of the file for writing the main results"),
894      1, &GNUNET_GETOPT_set_string, &output_filename},
895     {'p', "peers", "NETWORKSIZESPEC",
896      gettext_noop ("Number of peers to run in each round, separated by commas"),
897      1, &GNUNET_GETOPT_set_string, &num_peer_spec},
898     {'V', "verbose", NULL,
899      gettext_noop ("be verbose (print progress information)"),
900      0, &GNUNET_GETOPT_increment_value, &verbose},
901     {'w', "wait", "DELAY",
902      gettext_noop ("delay between rounds"),
903      1, &GNUNET_GETOPT_set_relative_time, &wait_time},
904     GNUNET_GETOPT_OPTION_END
905   };
906   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
907     return 2;
908   if (GNUNET_OK !=
909       GNUNET_PROGRAM_run (argc, argv, "nse-profiler",
910                           gettext_noop
911                           ("Measure quality and performance of the NSE service."),
912                           options, &run, NULL))
913     ok = 1;
914   return ok;
915 }
916
917 /* end of nse-profiler.c */