4144c3da5cb38249fbe746368fdfaa146c9b5c21
[oweals/gnunet.git] / src / testbed / gnunet-testbed-profiler.c
1 /*
2      This file is part of GNUnet.
3      (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * @file testbed/gnunet-testbed-profiler.c
23  * @brief Profiling driver for the testbed.
24  * @author Sree Harsha Totakura <sreeharsha@totakura.in> 
25  */
26
27 #include "platform.h"
28 #include "gnunet_common.h"
29 #include "gnunet_testbed_service.h"
30 #include "testbed_api_hosts.h"
31
32 /**
33  * Generic loggins shorthand
34  */
35 #define LOG(kind,...)                                           \
36   GNUNET_log_from (kind, "testbed-api-testbed", __VA_ARGS__)
37
38 /**
39  * DLL of operations
40  */
41 struct DLLOperation
42 {
43   /**
44    * The testbed operation handle
45    */
46   struct GNUNET_TESTBED_Operation *op;
47
48   /**
49    * Closure
50    */
51   void *cls;
52
53   /**
54    * The next pointer for DLL
55    */
56   struct DLLOperation *next;
57
58   /**
59    * The prev pointer for DLL
60    */
61   struct DLLOperation *prev;
62 };
63
64
65 /**
66  * Availanle states during profiling
67  */
68 enum State
69 {
70   /**
71    * Initial state
72    */
73   STATE_INIT = 0,
74
75   /**
76    * Starting slaves
77    */
78   STATE_SLAVES_STARTING,
79
80   /**
81    * Creating peers
82    */
83   STATE_PEERS_CREATING,
84
85   /**
86    * Starting peers
87    */
88   STATE_PEERS_STARTING
89 };
90
91
92 /**
93  * An array of hosts loaded from the hostkeys file
94  */
95 static struct GNUNET_TESTBED_Host **hosts;
96
97 /**
98  * The array of peers; we fill this as the peers are given to us by the testbed
99  */
100 static struct GNUNET_TESTBED_Peer **peers;
101
102 /* /\** */
103 /*  * Operation handle */
104 /*  *\/ */
105 /* static struct GNUNET_TESTBED_Operation *op; */
106
107 /**
108  * Host registration handle
109  */
110 static struct GNUNET_TESTBED_HostRegistrationHandle *reg_handle;
111
112 /**
113  * Handle to the master controller process
114  */
115 struct GNUNET_TESTBED_ControllerProc *mc_proc;
116
117 /**
118  * Handle to the master controller
119  */
120 struct GNUNET_TESTBED_Controller *mc;
121
122 /**
123  * Handle to global configuration
124  */
125 struct GNUNET_CONFIGURATION_Handle *cfg;
126
127 /**
128  * Head of the operations list
129  */
130 struct DLLOperation *dll_op_head;
131
132 /**
133  * Tail of the operations list
134  */
135 struct DLLOperation *dll_op_tail;
136
137 /**
138  * Abort task identifier
139  */
140 static GNUNET_SCHEDULER_TaskIdentifier abort_task;
141
142 /**
143  * Host registration task identifier
144  */
145 static GNUNET_SCHEDULER_TaskIdentifier register_hosts_task;
146
147 /**
148  * Global event mask for all testbed events
149  */
150 uint64_t event_mask;
151
152 /**
153  * The starting time of a profiling step
154  */
155 struct GNUNET_TIME_Absolute prof_start_time;
156
157 /**
158  * Duration profiling step has taken
159  */
160 struct GNUNET_TIME_Relative prof_time;
161
162 /**
163  * Current peer id
164  */
165 unsigned int peer_id;
166
167 /**
168  * Number of peers to be started by the profiler
169  */
170 static unsigned int num_peers;
171
172 /**
173  * Number of hosts in the hosts array
174  */
175 static unsigned int num_hosts;
176
177 /**
178  * Global testing status
179  */
180 static int result;
181
182 /**
183  * current state of profiling
184  */
185 enum State state;
186
187
188 /**
189  * Shutdown nicely
190  *
191  * @param cls NULL
192  * @param tc the task context
193  */
194 static void
195 do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
196 {
197   struct DLLOperation *dll_op;
198   unsigned int nhost;
199
200   if (GNUNET_SCHEDULER_NO_TASK != abort_task)
201     GNUNET_SCHEDULER_cancel (abort_task);
202   if (GNUNET_SCHEDULER_NO_TASK != register_hosts_task)
203     GNUNET_SCHEDULER_cancel (register_hosts_task);
204   if (NULL != reg_handle)
205     GNUNET_TESTBED_cancel_registration (reg_handle);
206   for (nhost = 0; nhost < num_hosts; nhost++)
207     if (NULL != hosts[nhost])
208       GNUNET_TESTBED_host_destroy (hosts[nhost]);
209   GNUNET_free_non_null (hosts);
210   while (NULL != (dll_op = dll_op_head))
211   {
212     GNUNET_TESTBED_operation_cancel (dll_op->op);
213     GNUNET_CONTAINER_DLL_remove (dll_op_head, dll_op_tail, dll_op);
214     GNUNET_free (dll_op);
215   }
216   if (NULL != mc)
217     GNUNET_TESTBED_controller_disconnect (mc);
218   if (NULL != mc_proc)
219     GNUNET_TESTBED_controller_stop (mc_proc);
220   if (NULL != cfg)
221     GNUNET_CONFIGURATION_destroy (cfg);
222   GNUNET_SCHEDULER_shutdown (); /* Stop scheduler to shutdown testbed run */
223 }
224
225
226 /**
227  * abort task to run on test timed out
228  *
229  * @param cls NULL
230  * @param tc the task context
231  */
232 static void
233 do_abort (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
234 {
235   GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Aborting\n");
236   abort_task = GNUNET_SCHEDULER_NO_TASK;
237   result = GNUNET_SYSERR;
238   GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
239 }
240
241
242 /**
243  * Functions of this signature are called when a peer has been successfully
244  * created
245  *
246  * @param cls the closure from GNUNET_TESTBED_peer_create()
247  * @param peer the handle for the created peer; NULL on any error during
248  *          creation
249  * @param emsg NULL if peer is not NULL; else MAY contain the error description
250  */
251 static void 
252 peer_create_cb (void *cls, struct GNUNET_TESTBED_Peer *peer, const char *emsg)
253 {
254   struct DLLOperation *dll_op = cls;
255   struct GNUNET_TESTBED_Peer **peer_ptr;
256   static unsigned int created_peers;
257   unsigned int peer_cnt;
258
259   if (NULL != emsg)
260   {
261     LOG (GNUNET_ERROR_TYPE_WARNING,
262          _("Creating a peer failed. Error: %s\n"), emsg);
263     GNUNET_TESTBED_operation_done (dll_op->op);
264     GNUNET_CONTAINER_DLL_remove (dll_op_head, dll_op_tail, dll_op);
265     GNUNET_free (dll_op);
266     GNUNET_SCHEDULER_cancel (abort_task);
267     abort_task = GNUNET_SCHEDULER_add_now (&do_abort, NULL);
268     return;
269   }
270   peer_ptr = dll_op->cls;
271   GNUNET_assert (NULL == *peer_ptr);
272   *peer_ptr = peer;
273   GNUNET_TESTBED_operation_done (dll_op->op);
274   GNUNET_CONTAINER_DLL_remove (dll_op_head, dll_op_tail, dll_op);
275   GNUNET_free (dll_op);
276   if (++created_peers == num_peers)
277   {
278     prof_time = GNUNET_TIME_absolute_get_duration (prof_start_time);    
279     printf ("All peers created successfully in %.2f seconds\n",
280             ((double) prof_time.rel_value) / 1000.00);
281     /* Now peers are to be started */
282     state = STATE_PEERS_STARTING;
283     for (peer_cnt = 0; peer_cnt < num_peers; peer_cnt++)
284     {
285       dll_op = GNUNET_malloc (sizeof (struct DLLOperation));
286       dll_op->op = GNUNET_TESTBED_peer_start (NULL, peers[peer_cnt], NULL, NULL);
287       GNUNET_CONTAINER_DLL_insert_tail (dll_op_head, dll_op_tail, dll_op);
288     }
289   }
290 }
291
292
293 /**
294  * Controller event callback
295  *
296  * @param cls NULL
297  * @param event the controller event
298  */
299 static void
300 controller_event_cb (void *cls,
301                      const struct GNUNET_TESTBED_EventInformation *event)
302 {
303   struct DLLOperation *dll_op;
304   struct GNUNET_TESTBED_Operation *op;
305
306   switch (state)
307   {
308   case STATE_SLAVES_STARTING:
309     switch (event->type)
310     {
311     case GNUNET_TESTBED_ET_OPERATION_FINISHED:
312       {
313         static unsigned int slaves_started;
314         unsigned int peer_cnt;
315         
316         dll_op = event->details.operation_finished.op_cls;
317         GNUNET_CONTAINER_DLL_remove (dll_op_head, dll_op_tail, dll_op);
318         GNUNET_free (dll_op);
319         op = event->details.operation_finished.operation;
320         if (NULL != event->details.operation_finished.emsg)
321         {
322           LOG (GNUNET_ERROR_TYPE_WARNING,
323                _("An operation has failed while starting slaves\n"));
324           GNUNET_TESTBED_operation_done (op);
325           GNUNET_SCHEDULER_cancel (abort_task);
326           abort_task = GNUNET_SCHEDULER_add_now (&do_abort, NULL);
327           return;
328         }
329         GNUNET_TESTBED_operation_done (op);
330         /* Proceed to start peers */
331         if (++slaves_started == num_hosts - 1)
332         {
333           printf ("All slaves started successfully\n");
334           state = STATE_PEERS_CREATING;
335           prof_start_time = GNUNET_TIME_absolute_get ();
336           peers = GNUNET_malloc (sizeof (struct GNUNET_TESTBED_Peer *)
337                                  * num_peers);
338           for (peer_cnt = 0; peer_cnt < num_peers; peer_cnt++)
339           {
340             dll_op = GNUNET_malloc (sizeof (struct DLLOperation));
341             dll_op->cls = &peers[peer_cnt];
342             dll_op->op = GNUNET_TESTBED_peer_create (mc,
343                                                      hosts
344                                                      [peer_cnt % num_hosts],
345                                                      cfg,
346                                                      &peer_create_cb,
347                                                      dll_op);
348             GNUNET_CONTAINER_DLL_insert_tail (dll_op_head, dll_op_tail, dll_op);
349           }
350         }
351       }
352       break;
353     default:
354       GNUNET_assert (0);
355     }
356     break;
357   case STATE_PEERS_STARTING:
358     switch (event->type)
359     {
360     case GNUNET_TESTBED_ET_OPERATION_FINISHED:
361       /* Control reaches here when peer start fails */
362       GNUNET_break (0);
363       break;
364     case GNUNET_TESTBED_ET_PEER_START:
365       GNUNET_break (0);
366       break;
367     default:
368       GNUNET_assert (0);
369     }
370     break;
371   default:
372     GNUNET_assert (0);
373   }
374 }
375
376
377 /**
378  * Task to register all hosts available in the global host list
379  *
380  * @param cls NULL
381  * @param tc the scheduler task context
382  */
383 static void
384 register_hosts (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
385
386
387 /**
388  * Callback which will be called to after a host registration succeeded or failed
389  *
390  * @param cls the closure
391  * @param emsg the error message; NULL if host registration is successful
392  */
393 static void
394 host_registration_completion (void *cls, const char *emsg)
395 {
396   reg_handle = NULL;
397   if (NULL != emsg)
398   {
399     LOG (GNUNET_ERROR_TYPE_WARNING,
400          _("Host registration failed for a host. Error: %s\n"), emsg);
401     GNUNET_SCHEDULER_cancel (abort_task);
402     abort_task = GNUNET_SCHEDULER_add_now (&do_abort, NULL);
403     return;
404   }
405   register_hosts_task = GNUNET_SCHEDULER_add_now (&register_hosts, NULL);
406 }
407
408
409 /**
410  * Task to register all hosts available in the global host list
411  *
412  * @param cls NULL
413  * @param tc the scheduler task context
414  */
415 static void
416 register_hosts (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
417 {
418   struct DLLOperation *dll_op;
419   static unsigned int reg_host;
420   unsigned int slave;
421
422   register_hosts_task = GNUNET_SCHEDULER_NO_TASK;  
423   if (reg_host == num_hosts - 1)
424   {
425     LOG (GNUNET_ERROR_TYPE_DEBUG,
426          "All hosts successfully registered\n");
427     /* Start slaves */
428     state = STATE_SLAVES_STARTING;
429     for (slave = 1; slave < num_hosts; slave++)
430     {
431       dll_op = GNUNET_malloc (sizeof (struct DLLOperation));
432       dll_op->op = GNUNET_TESTBED_controller_link (dll_op,
433                                                    mc,
434                                                    hosts[slave],
435                                                    hosts[0],
436                                                    cfg,
437                                                    GNUNET_YES);
438       GNUNET_CONTAINER_DLL_insert_tail (dll_op_head, dll_op_tail, dll_op);
439     }
440     return;
441   }
442   reg_handle = GNUNET_TESTBED_register_host (mc, hosts[++reg_host],
443                                              host_registration_completion,
444                                              NULL);
445 }
446
447
448 /**
449  * Callback to signal successfull startup of the controller process
450  *
451  * @param cls the closure from GNUNET_TESTBED_controller_start()
452  * @param cfg the configuration with which the controller has been started;
453  *          NULL if status is not GNUNET_OK
454  * @param status GNUNET_OK if the startup is successfull; GNUNET_SYSERR if not,
455  *          GNUNET_TESTBED_controller_stop() shouldn't be called in this case
456  */
457 static void
458 status_cb (void *cls, const struct GNUNET_CONFIGURATION_Handle *config, int status)
459 {
460   GNUNET_SCHEDULER_cancel (abort_task);
461   if (GNUNET_OK != status)
462   {
463     mc_proc = NULL;
464     abort_task = GNUNET_SCHEDULER_add_now (&do_abort, NULL);
465     return;
466   }
467   event_mask = 0;
468   event_mask |= (1LL << GNUNET_TESTBED_ET_PEER_START);
469   event_mask |= (1LL << GNUNET_TESTBED_ET_PEER_STOP);
470   event_mask |= (1LL << GNUNET_TESTBED_ET_CONNECT);
471   event_mask |= (1LL << GNUNET_TESTBED_ET_DISCONNECT);
472   event_mask |= (1LL << GNUNET_TESTBED_ET_OPERATION_FINISHED);
473   mc = GNUNET_TESTBED_controller_connect (config, hosts[0], event_mask,
474                                           &controller_event_cb, NULL);
475   if (NULL == mc)
476   {
477     LOG (GNUNET_ERROR_TYPE_WARNING,
478          _("Unable to connect to master controller -- Check config\n"));
479     abort_task = GNUNET_SCHEDULER_add_now (&do_abort, NULL);
480     return;
481   }
482   register_hosts_task = GNUNET_SCHEDULER_add_now (&register_hosts, NULL);
483   abort_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
484                                              &do_abort, NULL);
485 }
486
487
488 /**
489  * Main function that will be run by the scheduler.
490  *
491  * @param cls closure
492  * @param args remaining command-line arguments
493  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
494  * @param config configuration
495  */
496 static void
497 run (void *cls, char *const *args, const char *cfgfile,
498      const struct GNUNET_CONFIGURATION_Handle *config)
499 {
500   unsigned int nhost;
501
502   if (NULL == args[0])
503   {
504     fprintf (stderr, _("No hosts-file specified on command line\n"));
505     return;
506   }
507   if (0 == num_peers)
508   {
509     result = GNUNET_OK;
510     return;
511   }
512   num_hosts = GNUNET_TESTBED_hosts_load_from_file (args[0], &hosts);
513   if (0 == num_hosts)
514   {
515     fprintf (stderr, _("No hosts loaded. Need atleast one host\n"));
516     return;
517   }
518   for (nhost = 0; nhost < num_hosts; nhost++)
519   {
520     if (GNUNET_YES != GNUNET_TESTBED_is_host_habitable (hosts[nhost]))
521     {
522       fprintf (stderr, _("Host %s cannot start testbed\n"),
523                          GNUNET_TESTBED_host_get_hostname_ (hosts[nhost]));
524       break;
525     }
526   }
527   if (num_hosts != nhost)
528   {
529     fprintf (stderr, _("Exiting\n"));
530     GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
531     return;
532   }
533   cfg = GNUNET_CONFIGURATION_dup (config);
534   mc_proc = 
535       GNUNET_TESTBED_controller_start (GNUNET_TESTBED_host_get_hostname_ 
536                                        (hosts[0]),
537                                        hosts[0],
538                                        cfg,
539                                        status_cb,
540                                        NULL);
541   abort_task =
542       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
543                                     (GNUNET_TIME_UNIT_SECONDS, 5), &do_abort,
544                                     NULL);
545 }
546
547
548 /**
549  * Main function.
550  *
551  * @return 0 on success
552  */
553 int
554 main (int argc, char *const *argv)
555 {
556   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
557     { 'n', "num-peers", "COUNT",
558       gettext_noop ("create COUNT number of peers"),
559       GNUNET_YES, &GNUNET_GETOPT_set_uint, &num_peers },
560     { 'n', "num-peers", "COUNT",
561       gettext_noop ("create COUNT number of peers"),
562       GNUNET_YES, &GNUNET_GETOPT_set_uint, &num_peers },
563     GNUNET_GETOPT_OPTION_END
564   };
565   int ret;
566
567   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
568     return 2;
569   
570   result = GNUNET_SYSERR;
571   ret =
572       GNUNET_PROGRAM_run (argc, argv, "gnunet-testbed-profiler [OPTIONS] hosts-file",
573                           _("Profiler for testbed"),
574                           options, &run, NULL);
575   if (GNUNET_OK != ret)
576     return ret;
577   if (GNUNET_OK != result)
578     return 1;
579   return 0;
580 }