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