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