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