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