basic gnuplot script creation
[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                           (unsigned long long) timestamp.abs_value_us,
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_us / 1000LL / 1000LL, 
439                           value, flag);
440   if (size != GNUNET_DISK_file_write (data_file, output_buffer, size))
441   {
442     GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Unable to write to file!\n");
443     GNUNET_free (output_buffer);
444     return GNUNET_SYSERR;
445   }
446   GNUNET_free (output_buffer);
447   return GNUNET_OK;
448 }
449
450
451 /**
452  * Called to open a connection to the peer's statistics
453  *
454  * @param cls peer context
455  * @param cfg configuration of the peer to connect to; will be available until
456  *          GNUNET_TESTBED_operation_done() is called on the operation returned
457  *          from GNUNET_TESTBED_service_connect()
458  * @return service handle to return in 'op_result', NULL on error
459  */
460 static void *
461 stat_connect_adapter (void *cls,
462                       const struct GNUNET_CONFIGURATION_Handle *cfg)
463 {
464   struct NSEPeer *peer = cls;
465   
466   peer->sh = GNUNET_STATISTICS_create ("nse-profiler", cfg);
467   return peer->sh;
468 }
469
470
471 /**
472  * Called to disconnect from peer's statistics service
473  *
474  * @param cls peer context
475  * @param op_result service handle returned from the connect adapter
476  */
477 static void
478 stat_disconnect_adapter (void *cls, void *op_result)
479 {
480   struct NSEPeer *peer = cls;
481
482   GNUNET_break (GNUNET_OK == GNUNET_STATISTICS_watch_cancel
483                 (peer->sh, "core", "# peers connected",
484                  stat_iterator, peer));
485   GNUNET_break (GNUNET_OK == GNUNET_STATISTICS_watch_cancel
486                 (peer->sh, "nse", "# peers connected",
487                  stat_iterator, peer));
488   GNUNET_STATISTICS_destroy (op_result, GNUNET_NO);
489   peer->sh = NULL;
490 }
491
492
493 /**
494  * Called after successfully opening a connection to a peer's statistics
495  * service; we register statistics monitoring for CORE and NSE here.
496  *
497  * @param cls the callback closure from functions generating an operation
498  * @param op the operation that has been finished
499  * @param ca_result the service handle returned from GNUNET_TESTBED_ConnectAdapter()
500  * @param emsg error message in case the operation has failed; will be NULL if
501  *          operation has executed successfully.
502  */
503 static void
504 stat_comp_cb (void *cls, struct GNUNET_TESTBED_Operation *op,
505               void *ca_result, const char *emsg )
506 {
507   struct GNUNET_STATISTICS_Handle *sh = ca_result;
508   struct NSEPeer *peer = cls;
509
510   if (NULL != emsg)
511   {
512     GNUNET_break (0);
513     return;
514   }
515   GNUNET_break (GNUNET_OK == GNUNET_STATISTICS_watch
516                 (sh, "core", "# peers connected",
517                  stat_iterator, peer));
518   GNUNET_break (GNUNET_OK == GNUNET_STATISTICS_watch
519                 (sh, "nse", "# peers connected",
520                  stat_iterator, peer));
521 }
522
523
524 /**
525  * Task run to connect to the NSE and statistics services to a subset of
526  * all of the running peers.
527  */
528 static void
529 connect_nse_service ()
530 {
531   struct NSEPeer *current_peer;
532   unsigned int i;
533   unsigned int connections;
534
535   if (0 == connection_limit)
536     return;  
537   LOG_DEBUG ("Connecting to nse service of peers\n");
538   connections = 0;
539   for (i = 0; i < num_peers_in_round[current_round]; i++)
540   {
541     if ((num_peers_in_round[current_round] > connection_limit) && 
542         (0 != (i % (num_peers_in_round[current_round] / connection_limit))))
543       continue;
544     LOG_DEBUG ("Connecting to nse service of peer %d\n", i);
545     current_peer = GNUNET_malloc (sizeof (struct NSEPeer));
546     current_peer->daemon = daemons[i];
547     current_peer->nse_op 
548         = GNUNET_TESTBED_service_connect (NULL,
549                                           current_peer->daemon,
550                                           "nse",
551                                           NULL, NULL,
552                                           &nse_connect_adapter,
553                                           &nse_disconnect_adapter,
554                                           current_peer);
555     if (NULL != data_file)
556       current_peer->stat_op
557           = GNUNET_TESTBED_service_connect (NULL,
558                                             current_peer->daemon,
559                                             "statistics",
560                                             stat_comp_cb,
561                                             current_peer,
562                                             &stat_connect_adapter,
563                                             &stat_disconnect_adapter,
564                                             current_peer);
565     GNUNET_CONTAINER_DLL_insert (peer_head, peer_tail, current_peer);
566     if (++connections == connection_limit)
567       break;
568   }
569 }
570
571
572 /**
573  * Task that starts/stops peers to move to the next round.
574  *
575  * @param cls NULL, unused
576  * @param tc scheduler context (unused)
577  */
578 static void
579 next_round (void *cls, 
580             const struct GNUNET_SCHEDULER_TaskContext *tc);
581
582
583 /**
584  * We're at the end of a round.  Stop monitoring, write total
585  * number of connections to log and get full stats.  Then trigger
586  * the next round.
587  *
588  * @param cls unused, NULL
589  * @param tc unused
590  */
591 static void
592 finish_round (void *cls, 
593               const struct GNUNET_SCHEDULER_TaskContext *tc)
594 {
595   if (0 != (GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason))
596     return;
597   LOG (GNUNET_ERROR_TYPE_INFO, "Have %u connections\n", total_connections);
598   close_monitor_connections ();
599   GNUNET_SCHEDULER_add_now (&next_round, NULL);
600 }
601
602
603 /**
604  * We have reached the desired number of peers for the current round.
605  * Run it (by connecting and monitoring a few peers and waiting the
606  * specified delay before finishing the round).
607  */
608 static void
609 run_round ()
610 {
611   LOG_DEBUG ("Running round %u\n", current_round);
612   connect_nse_service ();
613   GNUNET_SCHEDULER_add_delayed (wait_time,
614                                 &finish_round,
615                                 NULL);
616 }
617
618
619 /**
620  * Creates an oplist entry and adds it to the oplist DLL
621  */
622 static struct OpListEntry *
623 make_oplist_entry ()
624 {
625   struct OpListEntry *entry;
626
627   entry = GNUNET_malloc (sizeof (struct OpListEntry));
628   GNUNET_CONTAINER_DLL_insert_tail (oplist_head, oplist_tail, entry);
629   return entry;
630 }
631
632
633 /**
634  * Callback to be called when NSE service is started or stopped at peers
635  *
636  * @param cls NULL
637  * @param op the operation handle
638  * @param emsg NULL on success; otherwise an error description
639  */
640 static void 
641 manage_service_cb (void *cls, struct GNUNET_TESTBED_Operation *op,
642                    const char *emsg)
643 {
644   struct OpListEntry *entry = cls;
645   
646   GNUNET_TESTBED_operation_done (entry->op);
647   if (NULL != emsg)
648   {
649     LOG (GNUNET_ERROR_TYPE_ERROR, "Failed to start/stop NSE at a peer\n");
650     GNUNET_SCHEDULER_shutdown ();
651     return;
652   }
653   GNUNET_assert (0 != entry->delta);
654   peers_running += entry->delta;
655   GNUNET_CONTAINER_DLL_remove (oplist_head, oplist_tail, entry);
656   GNUNET_free (entry);
657   if (num_peers_in_round[current_round] == peers_running)
658     run_round ();
659 }
660
661
662 /**
663  * Adjust the number of running peers to match the required number of running
664  * peers for the round
665  */
666 static void
667 adjust_running_peers ()
668 {
669   struct OpListEntry *entry;
670   unsigned int i;
671
672   /* start peers if we have too few */
673   for (i=peers_running;i<num_peers_in_round[current_round];i++)
674   {
675     entry = make_oplist_entry ();
676     entry->delta = 1;
677     entry->op = GNUNET_TESTBED_peer_manage_service (NULL,
678                                                     daemons[i],
679                                                     "nse",
680                                                     &manage_service_cb,
681                                                     entry,
682                                                     1);
683   }
684   /* stop peers if we have too many */
685   for (i=num_peers_in_round[current_round];i<peers_running;i++)
686   {
687     entry = make_oplist_entry ();
688     entry->delta = -1;
689     entry->op =  GNUNET_TESTBED_peer_manage_service (NULL,
690                                                      daemons[i],
691                                                      "nse",
692                                                      &manage_service_cb,
693                                                      entry,
694                                                      0);
695   }
696 }
697
698
699 /**
700  * Task run at the end of a round.  Disconnect from all monitored
701  * peers; then get statistics from *all* peers.
702  *
703  * @param cls NULL, unused
704  * @param tc unused
705  */
706 static void
707 next_round (void *cls, 
708             const struct GNUNET_SCHEDULER_TaskContext *tc)
709 {
710   if (0 != (GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason))
711     return;
712   LOG_DEBUG ("Disconnecting nse service of peers\n");
713   current_round++;  
714   if (current_round == num_rounds)
715     {
716       /* this was the last round, terminate */
717       ok = 0;
718       GNUNET_SCHEDULER_shutdown ();
719       return;
720     }
721   if (num_peers_in_round[current_round] == peers_running)
722     {
723       /* no need to churn, just run next round */
724       run_round ();
725       return;
726     }
727   adjust_running_peers ();
728 }
729
730
731 /**
732  * Function that will be called whenever something in the
733  * testbed changes.
734  *
735  * @param cls closure, NULL
736  * @param event information on what is happening
737  */
738 static void
739 master_controller_cb (void *cls, 
740                       const struct GNUNET_TESTBED_EventInformation *event)
741 {
742   switch (event->type)
743     {
744     case GNUNET_TESTBED_ET_CONNECT:
745       total_connections++;
746       break;
747     case GNUNET_TESTBED_ET_DISCONNECT:
748       total_connections--;
749       break;
750     default:
751       break;
752     }
753 }
754
755
756 /**
757  * Signature of a main function for a testcase.
758  *
759  * @param cls NULL
760  * @param h the run handle
761  * @param num_peers_ number of peers in 'peers'
762  * @param peers handle to peers run in the testbed.  NULL upon timeout (see
763  *          GNUNET_TESTBED_test_run()).
764  * @param links_succeeded the number of overlay link connection attempts that
765  *          succeeded
766  * @param links_failed the number of overlay link connection attempts that
767  *          failed
768  */
769 static void 
770 test_master (void *cls,
771              struct GNUNET_TESTBED_RunHandle *h,
772              unsigned int num_peers_,
773              struct GNUNET_TESTBED_Peer **peers,
774              unsigned int links_succeeded,
775              unsigned int links_failed)
776 {
777   if (NULL == peers)
778   {
779     shutdown_now ();
780     return;
781   }
782   daemons = peers;
783   GNUNET_break (num_peers_ == num_peers);
784   peers_running = num_peers;
785   if (num_peers_in_round[current_round] == peers_running)
786   {
787     /* no need to churn, just run the starting round */
788     run_round ();
789     return;
790   }
791   adjust_running_peers ();
792 }
793
794
795 /**
796  * Actual main function that runs the emulation.
797  *
798  * @param cls unused
799  * @param args remaining args, unused
800  * @param cfgfile name of the configuration
801  * @param cfg configuration handle
802  */
803 static void
804 run (void *cls, char *const *args, const char *cfgfile,
805      const struct GNUNET_CONFIGURATION_Handle *cfg)
806 {
807   char *tok;
808   uint64_t event_mask;
809   unsigned int num;  
810
811   ok = 1;
812   testing_cfg = GNUNET_CONFIGURATION_dup (cfg);
813   LOG_DEBUG ("Starting daemons.\n");
814   if (NULL == num_peer_spec)
815   {
816     fprintf (stderr, "You need to specify the number of peers to run\n");
817     return;
818   }
819   for (tok = strtok (num_peer_spec, ","); NULL != tok; tok = strtok (NULL, ","))
820     {
821       if (1 != sscanf (tok, "%u", &num))
822         {
823           fprintf (stderr, "You need to specify numbers, not `%s'\n", tok);
824           return;
825         }
826       if (0 == num)
827         {
828           fprintf (stderr, "Refusing to run a round with 0 peers\n");
829           return;
830         }
831       GNUNET_array_append (num_peers_in_round, num_rounds, num);
832       num_peers = GNUNET_MAX (num_peers, num);
833     }
834   if (0 == num_peers)
835     {
836       fprintf (stderr, "Refusing to run a testbed with no rounds\n");
837       return;
838     }
839   if ( (NULL != data_filename) &&
840        (NULL == (data_file = 
841                  GNUNET_DISK_file_open (data_filename,
842                                         GNUNET_DISK_OPEN_READWRITE |
843                                         GNUNET_DISK_OPEN_TRUNCATE |
844                                         GNUNET_DISK_OPEN_CREATE,
845                                         GNUNET_DISK_PERM_USER_READ |
846                                         GNUNET_DISK_PERM_USER_WRITE))) )
847     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
848                               "open",
849                               data_filename);
850
851   if ( (NULL != output_filename) &&
852        (NULL == (output_file =
853                  GNUNET_DISK_file_open (output_filename,
854                                         GNUNET_DISK_OPEN_READWRITE |
855                                         GNUNET_DISK_OPEN_CREATE,
856                                         GNUNET_DISK_PERM_USER_READ |
857                                         GNUNET_DISK_PERM_USER_WRITE))) )
858     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open",
859                               output_filename);
860   event_mask = 0LL;
861   event_mask |= (1LL << GNUNET_TESTBED_ET_PEER_START);
862   event_mask |= (1LL << GNUNET_TESTBED_ET_PEER_STOP);
863   event_mask |= (1LL << GNUNET_TESTBED_ET_CONNECT);
864   event_mask |= (1LL << GNUNET_TESTBED_ET_DISCONNECT);
865   GNUNET_TESTBED_run (hosts_file,
866                       cfg,
867                       num_peers,
868                       event_mask,
869                       master_controller_cb,
870                       NULL,     /* master_controller_cb cls */
871                       &test_master,
872                       NULL);    /* test_master cls */
873   shutdown_task_id = 
874       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
875                                     &shutdown_task, NULL);
876 }
877
878
879 /**
880  * Main function.
881  *
882  * @return 0 on success
883  */
884 int
885 main (int argc, char *const *argv)
886 {
887   static struct GNUNET_GETOPT_CommandLineOption options[] = {
888     {'C', "connections", "COUNT",
889      gettext_noop ("limit to the number of connections to NSE services, 0 for none"),
890      1, &GNUNET_GETOPT_set_uint, &connection_limit},
891     {'d', "details", "FILENAME",
892      gettext_noop ("name of the file for writing connection information and statistics"),
893      1, &GNUNET_GETOPT_set_string, &data_filename},
894     {'H', "hosts", "FILENAME",
895      gettext_noop ("name of the file with the login information for the testbed"),
896      1, &GNUNET_GETOPT_set_string, &hosts_file},
897     {'o', "output", "FILENAME",
898      gettext_noop ("name of the file for writing the main results"),
899      1, &GNUNET_GETOPT_set_string, &output_filename},
900     {'p', "peers", "NETWORKSIZESPEC",
901      gettext_noop ("Number of peers to run in each round, separated by commas"),
902      1, &GNUNET_GETOPT_set_string, &num_peer_spec},
903     {'V', "verbose", NULL,
904      gettext_noop ("be verbose (print progress information)"),
905      0, &GNUNET_GETOPT_increment_value, &verbose},
906     {'w', "wait", "DELAY",
907      gettext_noop ("delay between rounds"),
908      1, &GNUNET_GETOPT_set_relative_time, &wait_time},
909     GNUNET_GETOPT_OPTION_END
910   };
911   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
912     return 2;
913   if (GNUNET_OK !=
914       GNUNET_PROGRAM_run (argc, argv, "nse-profiler",
915                           gettext_noop
916                           ("Measure quality and performance of the NSE service."),
917                           options, &run, NULL))
918     ok = 1;
919   return ok;
920 }
921
922 /* end of nse-profiler.c */