80f30d9f0f7288dfacc3e28f03547b1d3a40b8df
[oweals/gnunet.git] / src / ats-tests / ats-testing-experiment.c
1 /*
2  This file is part of GNUnet.
3  (C) 2010-2013 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  * @file ats-tests/ats-testing-experiment.c
22  * @brief ats benchmark: controlled experiment execution
23  * @author Christian Grothoff
24  * @author Matthias Wachs
25  */
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "ats-testing.h"
29
30 const char *
31 print_op (enum OperationType op)
32 {
33   switch (op) {
34     case START_SEND:
35       return "START_SEND";
36     case STOP_SEND:
37       return "STOP_SEND";
38     case SET_RATE:
39       return "SET_RATE";
40     case SET_PREFERENCE:
41       return "SET_PREFERENCE";
42     default:
43       break;
44   }
45   return "";
46 }
47
48
49 static struct Experiment *
50 create_experiment ()
51 {
52   struct Experiment *e;
53   e = GNUNET_new (struct Experiment);
54   e->name = NULL;
55   e->num_masters = 0;
56   e->num_slaves = 0;
57   e->start = NULL;
58   e->total_duration = GNUNET_TIME_UNIT_ZERO;
59   return e;
60 }
61
62 static void
63 free_experiment (struct Experiment *e)
64 {
65   struct Episode *cur;
66   struct Episode *next;
67   struct Operation *cur_o;
68   struct Operation *next_o;
69
70   next = e->start;
71   for (cur = next; NULL != cur; cur = next)
72   {
73     next = cur->next;
74
75     next_o = cur->head;
76     for (cur_o = next_o; NULL != cur_o; cur_o = next_o)
77     {
78       next_o = cur_o->next;
79       GNUNET_free (cur_o);
80     }
81     GNUNET_free (cur);
82   }
83
84   GNUNET_free_non_null (e->name);
85   GNUNET_free_non_null (e->cfg_file);
86   GNUNET_free (e);
87 }
88
89 static int
90 load_episode (struct Experiment *e, struct Episode *cur,
91     struct GNUNET_CONFIGURATION_Handle *cfg)
92 {
93   struct Operation *o;
94   char *sec_name;
95   char *op_name;
96   char *op;
97   char *type;
98   int op_counter = 0;
99   fprintf (stderr, "Parsing episode %u\n",cur->id);
100   GNUNET_asprintf(&sec_name, "episode-%u", cur->id);
101
102   while (1)
103   {
104     /* Load operation */
105     GNUNET_asprintf(&op_name, "op-%u-operation", op_counter);
106     if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_string(cfg,
107         sec_name, op_name, &op))
108     {
109       break;
110     }
111     o = GNUNET_new (struct Operation);
112     /* operations = set_rate, start_send, stop_send, set_preference */
113     if (0 == strcmp (op, "start_send"))
114     {
115       o->type = START_SEND;
116     }
117     else if (0 == strcmp (op, "stop_send"))
118     {
119       o->type = STOP_SEND;
120     }
121     else if (0 == strcmp (op, "set_rate"))
122     {
123       o->type = SET_RATE;
124     }
125     else if (0 == strcmp (op, "set_preference"))
126     {
127       o->type = SET_PREFERENCE;
128     }
129     else
130     {
131       fprintf (stderr, "Invalid operation %u `%s' in episode %u\n",
132           op_counter, op, cur->id);
133       GNUNET_free (op);
134       GNUNET_free (op_name);
135       return GNUNET_SYSERR;
136     }
137     GNUNET_free (op_name);
138
139     /* Get source */
140     GNUNET_asprintf(&op_name, "op-%u-src", op_counter);
141     if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_number (cfg,
142         sec_name, op_name, &o->src_id))
143     {
144       fprintf (stderr, "Missing src in operation %u `%s' in episode %u\n",
145           op_counter, op, cur->id);
146       GNUNET_free (op);
147       GNUNET_free (op_name);
148       return GNUNET_SYSERR;
149     }
150     if (o->src_id > e->num_masters)
151     {
152       fprintf (stderr, "Invalid src %llu in operation %u `%s' in episode %u\n",
153           o->src_id, op_counter, op, cur->id);
154       GNUNET_free (op);
155       GNUNET_free (op_name);
156       return GNUNET_SYSERR;
157     }
158     GNUNET_free (op_name);
159
160     /* Get destination */
161     GNUNET_asprintf(&op_name, "op-%u-dest", op_counter);
162     if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_number (cfg,
163         sec_name, op_name, &o->dest_id))
164     {
165       fprintf (stderr, "Missing src in operation %u `%s' in episode %u\n",
166           op_counter, op, cur->id);
167       GNUNET_free (op);
168       GNUNET_free (op_name);
169       return GNUNET_SYSERR;
170     }
171     if (o->dest_id > e->num_slaves)
172     {
173       fprintf (stderr, "Invalid destination %llu in operation %u `%s' in episode %u\n",
174           o->dest_id, op_counter, op, cur->id);
175       GNUNET_free (op);
176       GNUNET_free (op_name);
177       return GNUNET_SYSERR;
178     }
179     GNUNET_free (op_name);
180
181     GNUNET_asprintf(&op_name, "op-%u-type", op_counter);
182     if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_string(cfg,
183         sec_name, op_name, &type))
184     {
185       break;
186     }
187
188     if (STOP_SEND != o->type)
189     {
190       /* Load arguments for set_rate, start_send, set_preference */
191       if (0 == strcmp (type, "constant"))
192       {
193         o->tg_type = GNUNET_ATS_TEST_TG_CONSTANT;
194       }
195       else if (0 == strcmp (type, "linear"))
196       {
197         o->tg_type = GNUNET_ATS_TEST_TG_LINEAR;
198       }
199       else if (0 == strcmp (type, "sinus"))
200       {
201         o->tg_type = GNUNET_ATS_TEST_TG_SINUS;
202       }
203       else if (0 == strcmp (type, "random"))
204       {
205         o->tg_type = GNUNET_ATS_TEST_TG_RANDOM;
206       }
207       else
208       {
209         fprintf (stderr, "Invalid type %u `%s' in episode %u\n",
210             op_counter, op, cur->id);
211         GNUNET_free (op);
212         GNUNET_free (op_name);
213         return GNUNET_SYSERR;
214       }
215       GNUNET_free (op_name);
216
217       /* Get base rate */
218       GNUNET_asprintf(&op_name, "op-%u-base-rate", op_counter);
219       if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_number (cfg,
220           sec_name, op_name, &o->base_rate))
221       {
222         fprintf (stderr, "Missing base rate in operation %u `%s' in episode %u\n",
223             op_counter, op, cur->id);
224         GNUNET_free (op);
225         GNUNET_free (op_name);
226         return GNUNET_SYSERR;
227       }
228       GNUNET_free (op_name);
229
230       /* Get max rate */
231       GNUNET_asprintf(&op_name, "op-%u-max-rate", op_counter);
232       if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_number (cfg,
233           sec_name, op_name, &o->max_rate))
234       {
235         if ((GNUNET_ATS_TEST_TG_LINEAR == o->tg_type) ||
236             (GNUNET_ATS_TEST_TG_RANDOM == o->tg_type) ||
237             (GNUNET_ATS_TEST_TG_SINUS == o->tg_type))
238         {
239           fprintf (stderr, "Missing max rate in operation %u `%s' in episode %u\n",
240               op_counter, op, cur->id);
241           GNUNET_free (op);
242           return GNUNET_SYSERR;
243         }
244       }
245       GNUNET_free (op_name);
246
247       {
248         /* Get period */
249         GNUNET_asprintf(&op_name, "op-%u-period", op_counter);
250         if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_time (cfg,
251             sec_name, op_name, &o->period))
252         {
253           o->period = cur->duration;
254         }
255         GNUNET_free (op_name);
256       }
257     }
258
259     /* Safety checks */
260     if ((GNUNET_ATS_TEST_TG_LINEAR == o->tg_type) ||
261         (GNUNET_ATS_TEST_TG_SINUS == o->tg_type))
262     {
263       if ((o->max_rate - o->base_rate) > o->base_rate)
264       {
265         /* This will cause an underflow */
266         GNUNET_break (0);
267       }
268       fprintf (stderr, "Selected max rate and base rate cannot be used for desired traffic form!\n");
269     }
270
271     fprintf (stderr, "Found operation %u in episode %u: %s [%llu]->[%llu] == %s, %llu -> %llu in %s\n",
272         op_counter, cur->id, print_op (o->type), o->src_id,
273         o->dest_id, type, o->base_rate, o->max_rate,
274         GNUNET_STRINGS_relative_time_to_string (o->period, GNUNET_YES));
275
276     GNUNET_CONTAINER_DLL_insert (cur->head,cur->tail, o);
277     op_counter++;
278   }
279   GNUNET_free (sec_name);
280
281   return GNUNET_OK;
282 }
283
284 static int
285 load_episodes (struct Experiment *e, struct GNUNET_CONFIGURATION_Handle *cfg)
286 {
287   int e_counter = 0;
288   char *sec_name;
289   struct GNUNET_TIME_Relative e_duration;
290   struct Episode *cur;
291   struct Episode *last;
292
293   e_counter = 0;
294   last = NULL;
295   while (1)
296   {
297     GNUNET_asprintf(&sec_name, "episode-%u", e_counter);
298     if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_time(cfg,
299         sec_name, "duration", &e_duration))
300     {
301       GNUNET_free (sec_name);
302       break;
303     }
304
305     cur = GNUNET_new (struct Episode);
306     cur->duration = e_duration;
307     cur->id = e_counter;
308
309     if (GNUNET_OK != load_episode (e, cur, cfg))
310     {
311       GNUNET_free (sec_name);
312       GNUNET_free (cur);
313       return GNUNET_SYSERR;
314     }
315
316     fprintf (stderr, "Found episode %u with duration %s \n",
317         e_counter,
318         GNUNET_STRINGS_relative_time_to_string(cur->duration, GNUNET_YES));
319
320     /* Update experiment */
321     e->num_episodes ++;
322     e->total_duration = GNUNET_TIME_relative_add(e->total_duration, cur->duration);
323     /* Put in linked list */
324     if (NULL == last)
325       e->start = cur;
326     else
327     last->next = cur;
328
329     GNUNET_free (sec_name);
330     e_counter ++;
331     last = cur;
332   }
333   return e_counter;
334 }
335
336 static void
337 timeout_experiment (void *cls, const struct GNUNET_SCHEDULER_TaskContext* tc)
338 {
339   struct Experiment *e = cls;
340   e->experiment_timeout_task = GNUNET_SCHEDULER_NO_TASK;
341   fprintf (stderr, "Experiment timeout!\n");
342
343   if (GNUNET_SCHEDULER_NO_TASK != e->episode_timeout_task)
344   {
345     GNUNET_SCHEDULER_cancel (e->episode_timeout_task);
346     e->episode_timeout_task = GNUNET_SCHEDULER_NO_TASK;
347   }
348
349   e->e_done_cb (e, GNUNET_TIME_absolute_get_duration(e->start_time),
350       GNUNET_SYSERR);
351 }
352
353 static void
354 enforce_start_send (struct Operation *op)
355 {
356   GNUNET_break (0);
357 }
358
359 static void
360 enforce_stop_send (struct Operation *op)
361 {
362   GNUNET_break (0);
363 }
364
365 static void
366 enforce_set_rate (struct Operation *op)
367 {
368   GNUNET_break (0);
369 }
370
371 static void
372 enforce_set_preference (struct Operation *op)
373 {
374   GNUNET_break (0);
375 }
376
377 static void enforce_episode (struct Episode *ep)
378 {
379   struct Operation *cur;
380   for (cur = ep->head; NULL != cur; cur = cur->next)
381   {
382
383     fprintf (stderr, "Enforcing operation: %s [%llu]->[%llu] == %llu\n",
384         print_op (cur->type), cur->src_id, cur->dest_id, cur->base_rate);
385     switch (cur->type) {
386       case START_SEND:
387         enforce_start_send (cur);
388         break;
389       case STOP_SEND:
390         enforce_stop_send (cur);
391         break;
392       case SET_RATE:
393         enforce_set_rate (cur);
394         break;
395       case SET_PREFERENCE:
396         enforce_set_preference (cur);
397         break;
398       default:
399         break;
400     }
401   }
402 }
403
404 static void
405 timeout_episode (void *cls, const struct GNUNET_SCHEDULER_TaskContext* tc)
406 {
407   struct Experiment *e = cls;
408   e->episode_timeout_task = GNUNET_SCHEDULER_NO_TASK;
409   if (NULL != e->ep_done_cb)
410     e->ep_done_cb (e->cur);
411
412   /* Scheduling next */
413   e->cur = e->cur->next;
414   if (NULL == e->cur)
415   {
416     /* done */
417     fprintf (stderr, "Last episode done!\n");
418     if (GNUNET_SCHEDULER_NO_TASK != e->experiment_timeout_task)
419     {
420       GNUNET_SCHEDULER_cancel (e->experiment_timeout_task);
421       e->experiment_timeout_task = GNUNET_SCHEDULER_NO_TASK;
422     }
423     e->e_done_cb (e, GNUNET_TIME_absolute_get_duration(e->start_time), GNUNET_OK);
424     return;
425   }
426
427   fprintf (stderr, "Running episode %u with timeout %s\n",
428       e->cur->id,
429       GNUNET_STRINGS_relative_time_to_string(e->cur->duration, GNUNET_YES));
430   enforce_episode(e->cur);
431
432   e->episode_timeout_task = GNUNET_SCHEDULER_add_delayed (e->cur->duration,
433       &timeout_episode, e);
434 }
435
436
437 void
438 GNUNET_ATS_TEST_experimentation_run (struct Experiment *e,
439     GNUNET_ATS_TESTING_EpisodeDoneCallback ep_done_cb,
440     GNUNET_ATS_TESTING_ExperimentDoneCallback e_done_cb)
441 {
442   fprintf (stderr, "Running experiment `%s'  with timeout %s\n", e->name,
443       GNUNET_STRINGS_relative_time_to_string(e->max_duration, GNUNET_YES));
444   e->e_done_cb = e_done_cb;
445   e->ep_done_cb = ep_done_cb;
446   e->start_time = GNUNET_TIME_absolute_get();
447
448   /* Start total time out */
449   e->experiment_timeout_task = GNUNET_SCHEDULER_add_delayed (e->max_duration,
450       &timeout_experiment, e);
451
452   /* Start */
453   e->cur = e->start;
454   fprintf (stderr, "Running episode %u with timeout %s\n",
455       e->cur->id,
456       GNUNET_STRINGS_relative_time_to_string(e->cur->duration, GNUNET_YES));
457   enforce_episode(e->cur);
458   e->episode_timeout_task = GNUNET_SCHEDULER_add_delayed (e->cur->duration,
459       &timeout_episode, e);
460
461
462 }
463
464
465 struct Experiment *
466 GNUNET_ATS_TEST_experimentation_load (char *filename)
467 {
468   struct Experiment *e;
469   struct GNUNET_CONFIGURATION_Handle *cfg;
470   e = NULL;
471
472   cfg = GNUNET_CONFIGURATION_create();
473   if (GNUNET_SYSERR == GNUNET_CONFIGURATION_load (cfg, filename))
474   {
475     fprintf (stderr, "Failed to load `%s'\n", filename);
476     GNUNET_CONFIGURATION_destroy (cfg);
477     return NULL;
478   }
479
480   e = create_experiment ();
481
482   if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_string(cfg, "experiment",
483       "name", &e->name))
484   {
485     fprintf (stderr, "Invalid %s", "name");
486     free_experiment (e);
487     return NULL;
488   }
489   else
490     fprintf (stderr, "Experiment name: `%s'\n", e->name);
491
492   if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_filename (cfg, "experiment",
493       "cfg_file", &e->cfg_file))
494   {
495     fprintf (stderr, "Invalid %s", "cfg_file");
496     free_experiment (e);
497     return NULL;
498   }
499   else
500     fprintf (stderr, "Experiment name: `%s'\n", e->cfg_file);
501
502   if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_number(cfg, "experiment",
503       "masters", &e->num_masters))
504   {
505     fprintf (stderr, "Invalid %s", "masters");
506     free_experiment (e);
507     return NULL;
508   }
509   else
510     fprintf (stderr, "Experiment masters: `%llu'\n",
511         e->num_masters);
512
513   if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_number(cfg, "experiment",
514       "slaves", &e->num_slaves))
515   {
516     fprintf (stderr, "Invalid %s", "slaves");
517     free_experiment (e);
518     return NULL;
519   }
520   else
521     fprintf (stderr, "Experiment slaves: `%llu'\n",
522         e->num_slaves);
523
524   if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_time(cfg, "experiment",
525       "max_duration", &e->max_duration))
526   {
527     fprintf (stderr, "Invalid %s", "max_duration");
528     free_experiment (e);
529     return NULL;
530   }
531   else
532     fprintf (stderr, "Experiment duration: `%s'\n",
533         GNUNET_STRINGS_relative_time_to_string (e->max_duration, GNUNET_YES));
534
535   load_episodes (e, cfg);
536   fprintf (stderr, "Loaded %u episodes with total duration %s\n",
537       e->num_episodes,
538       GNUNET_STRINGS_relative_time_to_string (e->total_duration, GNUNET_YES));
539
540   GNUNET_CONFIGURATION_destroy (cfg);
541   return e;
542 }
543
544 void
545 GNUNET_ATS_TEST_experimentation_stop (struct Experiment *e)
546 {
547   if (GNUNET_SCHEDULER_NO_TASK != e->experiment_timeout_task)
548   {
549     GNUNET_SCHEDULER_cancel (e->experiment_timeout_task);
550     e->experiment_timeout_task = GNUNET_SCHEDULER_NO_TASK;
551   }
552   free_experiment (e);
553 }
554
555 /* end of file ats-testing-experiment.c*/
556