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