paragraph for gnunet devs that don't know how to use the web
[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 /**
19  * @file nse/gnunet-nse-profiler.c
20  *
21  * @brief Profiling driver for the network size estimation service.
22  *        Generally, the profiler starts a given number of peers,
23  *        then churns some off, waits a certain amount of time, then
24  *        churns again, and repeats.
25  * @author Christian Grothoff
26  * @author Nathan Evans
27  * @author Sree Harsha Totakura
28  */
29
30 #include "platform.h"
31 #include "gnunet_testbed_service.h"
32 #include "gnunet_nse_service.h"
33
34 /**
35  * Generic loggins shorthand
36  */
37 #define LOG(kind,...)                                           \
38   GNUNET_log (kind, __VA_ARGS__)
39
40 /**
41  * Debug logging shorthand
42  */
43 #define LOG_DEBUG(...)                          \
44   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, double std_dev)
303 {
304   struct NSEPeer *peer = cls;
305   char output_buffer[512];
306   size_t size;
307
308   if (NULL == output_file)
309     {
310       FPRINTF (stderr,
311                "Received network size estimate from peer %p. Size: %f std.dev. %f\n",
312                peer, estimate, std_dev);
313       return;
314     }
315   size = GNUNET_snprintf (output_buffer,
316                           sizeof (output_buffer),
317                           "%p %llu %llu %f %f %f\n",
318                           peer, peers_running,
319                           (unsigned long long) timestamp.abs_value_us,
320                           GNUNET_NSE_log_estimate_to_n (estimate), estimate,
321                           std_dev);
322   if (size != GNUNET_DISK_file_write (output_file, output_buffer, size))
323     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
324                 "Unable to write to file!\n");
325 }
326
327
328 /**
329  * Adapter function called to establish a connection to
330  * NSE service.
331  *
332  * @param cls closure (the 'struct NSEPeer')
333  * @param cfg configuration of the peer to connect to; will be available until
334  *          GNUNET_TESTBED_operation_done() is called on the operation returned
335  *          from GNUNET_TESTBED_service_connect()
336  * @return service handle to return in 'op_result', NULL on error
337  */
338 static void *
339 nse_connect_adapter (void *cls,
340                      const struct GNUNET_CONFIGURATION_Handle *cfg)
341 {
342   struct NSEPeer *current_peer = cls;
343
344   return GNUNET_NSE_connect (cfg, &handle_estimate, current_peer);
345 }
346
347
348 /**
349  * Adapter function called to destroy a connection to
350  * NSE service.
351  *
352  * @param cls closure
353  * @param op_result service handle returned from the connect adapter
354  */
355 static void
356 nse_disconnect_adapter (void *cls,
357                         void *op_result)
358 {
359   GNUNET_NSE_disconnect (op_result);
360 }
361
362
363 /**
364  * Callback function to process statistic values.
365  *
366  * @param cls `struct NSEPeer`
367  * @param subsystem name of subsystem that created the statistic
368  * @param name the name of the datum
369  * @param value the current value
370  * @param is_persistent #GNUNET_YES if the value is persistent, #GNUNET_NO if not
371  * @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration
372  */
373 static int
374 stat_iterator (void *cls,
375                const char *subsystem,
376                const char *name,
377                uint64_t value,
378                int is_persistent)
379 {
380   char *output_buffer;
381   struct GNUNET_TIME_Absolute now;
382   int size;
383   unsigned int flag;
384
385   GNUNET_assert (NULL != data_file);
386   now = GNUNET_TIME_absolute_get ();
387   flag = strcasecmp (subsystem, "core");
388   if (0 != flag)
389     flag = 1;
390   size = GNUNET_asprintf (&output_buffer, "%llu %llu %u\n",
391                           now.abs_value_us / 1000LL / 1000LL,
392                           value, flag);
393   if (0 > size)
394   {
395     GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Error formatting output buffer.\n");
396     GNUNET_free (output_buffer);
397     return GNUNET_SYSERR;
398   }
399   if (size != GNUNET_DISK_file_write (data_file, output_buffer, (size_t) size))
400   {
401     GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Unable to write to file!\n");
402     GNUNET_free (output_buffer);
403     return GNUNET_SYSERR;
404   }
405   GNUNET_free (output_buffer);
406   return GNUNET_OK;
407 }
408
409
410 /**
411  * Called to open a connection to the peer's statistics
412  *
413  * @param cls peer context
414  * @param cfg configuration of the peer to connect to; will be available until
415  *          GNUNET_TESTBED_operation_done() is called on the operation returned
416  *          from GNUNET_TESTBED_service_connect()
417  * @return service handle to return in 'op_result', NULL on error
418  */
419 static void *
420 stat_connect_adapter (void *cls,
421                       const struct GNUNET_CONFIGURATION_Handle *cfg)
422 {
423   struct NSEPeer *peer = cls;
424
425   peer->sh = GNUNET_STATISTICS_create ("nse-profiler", cfg);
426   return peer->sh;
427 }
428
429
430 /**
431  * Called to disconnect from peer's statistics service
432  *
433  * @param cls peer context
434  * @param op_result service handle returned from the connect adapter
435  */
436 static void
437 stat_disconnect_adapter (void *cls, void *op_result)
438 {
439   struct NSEPeer *peer = cls;
440
441   GNUNET_break (GNUNET_OK == GNUNET_STATISTICS_watch_cancel
442                 (peer->sh, "core", "# peers connected",
443                  stat_iterator, peer));
444   GNUNET_break (GNUNET_OK == GNUNET_STATISTICS_watch_cancel
445                 (peer->sh, "nse", "# peers connected",
446                  stat_iterator, peer));
447   GNUNET_STATISTICS_destroy (op_result, GNUNET_NO);
448   peer->sh = NULL;
449 }
450
451
452 /**
453  * Called after successfully opening a connection to a peer's statistics
454  * service; we register statistics monitoring for CORE and NSE here.
455  *
456  * @param cls the callback closure from functions generating an operation
457  * @param op the operation that has been finished
458  * @param ca_result the service handle returned from GNUNET_TESTBED_ConnectAdapter()
459  * @param emsg error message in case the operation has failed; will be NULL if
460  *          operation has executed successfully.
461  */
462 static void
463 stat_comp_cb (void *cls, struct GNUNET_TESTBED_Operation *op,
464               void *ca_result, const char *emsg )
465 {
466   struct GNUNET_STATISTICS_Handle *sh = ca_result;
467   struct NSEPeer *peer = cls;
468
469   if (NULL != emsg)
470   {
471     GNUNET_break (0);
472     return;
473   }
474   GNUNET_break (GNUNET_OK == GNUNET_STATISTICS_watch
475                 (sh, "core", "# peers connected",
476                  stat_iterator, peer));
477   GNUNET_break (GNUNET_OK == GNUNET_STATISTICS_watch
478                 (sh, "nse", "# peers connected",
479                  stat_iterator, peer));
480 }
481
482
483 /**
484  * Task run to connect to the NSE and statistics services to a subset of
485  * all of the running peers.
486  */
487 static void
488 connect_nse_service ()
489 {
490   struct NSEPeer *current_peer;
491   unsigned int i;
492   unsigned int connections;
493
494   if (0 == connection_limit)
495     return;
496   LOG_DEBUG ("Connecting to nse service of peers\n");
497   connections = 0;
498   for (i = 0; i < num_peers_in_round[current_round]; i++)
499   {
500     if ((num_peers_in_round[current_round] > connection_limit) &&
501         (0 != (i % (num_peers_in_round[current_round] / connection_limit))))
502       continue;
503     LOG_DEBUG ("Connecting to nse service of peer %d\n", i);
504     current_peer = GNUNET_new (struct NSEPeer);
505     current_peer->daemon = daemons[i];
506     current_peer->nse_op
507         = GNUNET_TESTBED_service_connect (NULL,
508                                           current_peer->daemon,
509                                           "nse",
510                                           NULL, NULL,
511                                           &nse_connect_adapter,
512                                           &nse_disconnect_adapter,
513                                           current_peer);
514     if (NULL != data_file)
515       current_peer->stat_op
516           = GNUNET_TESTBED_service_connect (NULL,
517                                             current_peer->daemon,
518                                             "statistics",
519                                             stat_comp_cb,
520                                             current_peer,
521                                             &stat_connect_adapter,
522                                             &stat_disconnect_adapter,
523                                             current_peer);
524     GNUNET_CONTAINER_DLL_insert (peer_head, peer_tail, current_peer);
525     if (++connections == connection_limit)
526       break;
527   }
528 }
529
530
531 /**
532  * Task that starts/stops peers to move to the next round.
533  *
534  * @param cls NULL, unused
535  */
536 static void
537 next_round (void *cls);
538
539
540 /**
541  * We're at the end of a round.  Stop monitoring, write total
542  * number of connections to log and get full stats.  Then trigger
543  * the next round.
544  *
545  * @param cls unused, NULL
546  */
547 static void
548 finish_round (void *cls)
549 {
550   LOG (GNUNET_ERROR_TYPE_INFO,
551        "Have %u connections\n",
552        total_connections);
553   close_monitor_connections ();
554   round_task = GNUNET_SCHEDULER_add_now (&next_round, NULL);
555 }
556
557
558 /**
559  * We have reached the desired number of peers for the current round.
560  * Run it (by connecting and monitoring a few peers and waiting the
561  * specified delay before finishing the round).
562  */
563 static void
564 run_round ()
565 {
566   LOG_DEBUG ("Running round %u\n",
567              current_round);
568   connect_nse_service ();
569   GNUNET_SCHEDULER_add_delayed (wait_time,
570                                 &finish_round,
571                                 NULL);
572 }
573
574
575 /**
576  * Creates an oplist entry and adds it to the oplist DLL
577  */
578 static struct OpListEntry *
579 make_oplist_entry ()
580 {
581   struct OpListEntry *entry;
582
583   entry = GNUNET_new (struct OpListEntry);
584   GNUNET_CONTAINER_DLL_insert_tail (oplist_head,
585                                     oplist_tail,
586                                     entry);
587   return entry;
588 }
589
590
591 /**
592  * Callback to be called when NSE service is started or stopped at peers
593  *
594  * @param cls NULL
595  * @param op the operation handle
596  * @param emsg NULL on success; otherwise an error description
597  */
598 static void
599 manage_service_cb (void *cls,
600                    struct GNUNET_TESTBED_Operation *op,
601                    const char *emsg)
602 {
603   struct OpListEntry *entry = cls;
604
605   GNUNET_TESTBED_operation_done (entry->op);
606   if (NULL != emsg)
607   {
608     LOG (GNUNET_ERROR_TYPE_ERROR, "Failed to start/stop NSE at a peer\n");
609     GNUNET_SCHEDULER_shutdown ();
610     return;
611   }
612   GNUNET_assert (0 != entry->delta);
613   peers_running += entry->delta;
614   GNUNET_CONTAINER_DLL_remove (oplist_head,
615                                oplist_tail,
616                                entry);
617   GNUNET_free (entry);
618   if (num_peers_in_round[current_round] == peers_running)
619     run_round ();
620 }
621
622
623 /**
624  * Adjust the number of running peers to match the required number of running
625  * peers for the round
626  */
627 static void
628 adjust_running_peers ()
629 {
630   struct OpListEntry *entry;
631   unsigned int i;
632
633   /* start peers if we have too few */
634   for (i=peers_running;i<num_peers_in_round[current_round];i++)
635   {
636     entry = make_oplist_entry ();
637     entry->delta = 1;
638     entry->op = GNUNET_TESTBED_peer_manage_service (NULL,
639                                                     daemons[i],
640                                                     "nse",
641                                                     &manage_service_cb,
642                                                     entry,
643                                                     1);
644   }
645   /* stop peers if we have too many */
646   for (i=num_peers_in_round[current_round];i<peers_running;i++)
647   {
648     entry = make_oplist_entry ();
649     entry->delta = -1;
650     entry->op =  GNUNET_TESTBED_peer_manage_service (NULL,
651                                                      daemons[i],
652                                                      "nse",
653                                                      &manage_service_cb,
654                                                      entry,
655                                                      0);
656   }
657 }
658
659
660 /**
661  * Task run at the end of a round.  Disconnect from all monitored
662  * peers; then get statistics from *all* peers.
663  *
664  * @param cls NULL, unused
665  */
666 static void
667 next_round (void *cls)
668 {
669   round_task = NULL;
670   LOG_DEBUG ("Disconnecting nse service of peers\n");
671   current_round++;
672   if (current_round == num_rounds)
673   {
674     /* this was the last round, terminate */
675     ok = 0;
676     GNUNET_SCHEDULER_shutdown ();
677     return;
678   }
679   if (num_peers_in_round[current_round] == peers_running)
680   {
681     /* no need to churn, just run next round */
682     run_round ();
683     return;
684   }
685   adjust_running_peers ();
686 }
687
688
689 /**
690  * Function that will be called whenever something in the
691  * testbed changes.
692  *
693  * @param cls closure, NULL
694  * @param event information on what is happening
695  */
696 static void
697 master_controller_cb (void *cls,
698                       const struct GNUNET_TESTBED_EventInformation *event)
699 {
700   switch (event->type)
701     {
702     case GNUNET_TESTBED_ET_CONNECT:
703       total_connections++;
704       break;
705     case GNUNET_TESTBED_ET_DISCONNECT:
706       total_connections--;
707       break;
708     default:
709       break;
710     }
711 }
712
713
714 /**
715  * Signature of a main function for a testcase.
716  *
717  * @param cls NULL
718  * @param h the run handle
719  * @param num_peers_ number of peers in 'peers'
720  * @param peers handle to peers run in the testbed.  NULL upon timeout (see
721  *          GNUNET_TESTBED_test_run()).
722  * @param links_succeeded the number of overlay link connection attempts that
723  *          succeeded
724  * @param links_failed the number of overlay link connection attempts that
725  *          failed
726  */
727 static void
728 test_master (void *cls,
729              struct GNUNET_TESTBED_RunHandle *h,
730              unsigned int num_peers_,
731              struct GNUNET_TESTBED_Peer **peers,
732              unsigned int links_succeeded,
733              unsigned int links_failed)
734 {
735   if (NULL == peers)
736   {
737     GNUNET_SCHEDULER_shutdown ();
738     return;
739   }
740   daemons = peers;
741   GNUNET_break (num_peers_ == num_peers);
742   peers_running = num_peers;
743   if (num_peers_in_round[current_round] == peers_running)
744   {
745     /* no need to churn, just run the starting round */
746     run_round ();
747     return;
748   }
749   adjust_running_peers ();
750 }
751
752
753 /**
754  * Actual main function that runs the emulation.
755  *
756  * @param cls unused
757  * @param args remaining args, unused
758  * @param cfgfile name of the configuration
759  * @param cfg configuration handle
760  */
761 static void
762 run (void *cls, char *const *args, const char *cfgfile,
763      const struct GNUNET_CONFIGURATION_Handle *cfg)
764 {
765   char *tok;
766   uint64_t event_mask;
767   unsigned int num;
768
769   ok = 1;
770   testing_cfg = GNUNET_CONFIGURATION_dup (cfg);
771   LOG_DEBUG ("Starting daemons.\n");
772   if (NULL == num_peer_spec)
773   {
774     fprintf (stderr, "You need to specify the number of peers to run\n");
775     return;
776   }
777   for (tok = strtok (num_peer_spec, ","); NULL != tok; tok = strtok (NULL, ","))
778     {
779       if (1 != sscanf (tok, "%u", &num))
780         {
781           fprintf (stderr, "You need to specify numbers, not `%s'\n", tok);
782           return;
783         }
784       if (0 == num)
785         {
786           fprintf (stderr, "Refusing to run a round with 0 peers\n");
787           return;
788         }
789       GNUNET_array_append (num_peers_in_round, num_rounds, num);
790       num_peers = GNUNET_MAX (num_peers, num);
791     }
792   if (0 == num_peers)
793     {
794       fprintf (stderr, "Refusing to run a testbed with no rounds\n");
795       return;
796     }
797   if ( (NULL != data_filename) &&
798        (NULL == (data_file =
799                  GNUNET_DISK_file_open (data_filename,
800                                         GNUNET_DISK_OPEN_READWRITE |
801                                         GNUNET_DISK_OPEN_TRUNCATE |
802                                         GNUNET_DISK_OPEN_CREATE,
803                                         GNUNET_DISK_PERM_USER_READ |
804                                         GNUNET_DISK_PERM_USER_WRITE))) )
805     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
806                               "open",
807                               data_filename);
808
809   if ( (NULL != output_filename) &&
810        (NULL == (output_file =
811                  GNUNET_DISK_file_open (output_filename,
812                                         GNUNET_DISK_OPEN_READWRITE |
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",
817                               output_filename);
818   event_mask = 0LL;
819   event_mask |= (1LL << GNUNET_TESTBED_ET_PEER_START);
820   event_mask |= (1LL << GNUNET_TESTBED_ET_PEER_STOP);
821   event_mask |= (1LL << GNUNET_TESTBED_ET_CONNECT);
822   event_mask |= (1LL << GNUNET_TESTBED_ET_DISCONNECT);
823   GNUNET_TESTBED_run (hosts_file,
824                       cfg,
825                       num_peers,
826                       event_mask,
827                       master_controller_cb,
828                       NULL,     /* master_controller_cb cls */
829                       &test_master,
830                       NULL);    /* test_master cls */
831   GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL);
832 }
833
834
835 /**
836  * Main function.
837  *
838  * @return 0 on success
839  */
840 int
841 main (int argc, char *const *argv)
842 {
843   struct GNUNET_GETOPT_CommandLineOption options[] = {
844     GNUNET_GETOPT_option_uint ('C',
845                                    "connections",
846                                    "COUNT",
847                                    gettext_noop ("limit to the number of connections to NSE services, 0 for none"),
848                                    &connection_limit),
849     GNUNET_GETOPT_option_string ('d',
850                                  "details",
851                                  "FILENAME",
852                                  gettext_noop ("name of the file for writing connection information and statistics"),
853                                  &data_filename),
854
855     GNUNET_GETOPT_option_string ('H',
856                                  "hosts",
857                                  "FILENAME",
858                                  gettext_noop ("name of the file with the login information for the testbed"),
859                                  &hosts_file),
860
861     GNUNET_GETOPT_option_string ('o',
862                                  "output",
863                                  "FILENAME",
864                                  gettext_noop ("name of the file for writing the main results"),
865                                  &output_filename),
866
867
868     GNUNET_GETOPT_option_string ('p',
869                                  "peers",
870                                  "NETWORKSIZESPEC",
871                                  gettext_noop ("Number of peers to run in each round, separated by commas"),
872                                  &num_peer_spec),
873
874     GNUNET_GETOPT_option_increment_uint ('V',
875                                           "verbose",
876                                           gettext_noop ("be verbose (print progress information)"),
877                                           &verbose),
878
879     GNUNET_GETOPT_option_relative_time ('w',
880                                             "wait",
881                                             "DELAY",
882                                             gettext_noop ("delay between rounds"),
883                                             &wait_time),
884     GNUNET_GETOPT_OPTION_END
885   };
886   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
887     return 2;
888   if (GNUNET_OK !=
889       GNUNET_PROGRAM_run (argc, argv, "nse-profiler",
890                           gettext_noop
891                           ("Measure quality and performance of the NSE service."),
892                           options, &run, NULL))
893     ok = 1;
894   return ok;
895 }
896
897 /* end of nse-profiler.c */