d55adc2d74e5d9ceb07c37fa1292c8902967af9a
[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   int ep_counter = 0;
98   fprintf (stderr, "Parsing episode %u\n",cur->id);
99   GNUNET_asprintf(&sec_name, "episode-%u", cur->id);
100
101   while (1)
102   {
103
104     GNUNET_asprintf(&op_name, "op-%u-operation", ep_counter);
105     if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_string(cfg,
106         sec_name, op_name, &op))
107     {
108       break;
109     }
110     o = GNUNET_new (struct Operation);
111     /* operations = set_rate, start_send, stop_send, set_preference */
112     if (0 == strcmp (op, "start_send"))
113     {
114       o->type = START_SEND;
115     }
116     else if (0 == strcmp (op, "stop_send"))
117     {
118       o->type = STOP_SEND;
119     }
120     else if (0 == strcmp (op, "set_rate"))
121     {
122       o->type = SET_RATE;
123     }
124     else if (0 == strcmp (op, "set_preference"))
125     {
126       o->type = SET_PREFERENCE;
127     }
128     else
129     {
130       fprintf (stderr, "Invalid operation %u `%s' in episode %u\n",
131           ep_counter, op, cur->id);
132       GNUNET_free (op);
133       return GNUNET_SYSERR;
134     }
135
136     GNUNET_free (op_name);
137     GNUNET_asprintf(&op_name, "op-%u-src", ep_counter);
138     if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_number (cfg,
139         sec_name, op_name, &o->src_id))
140     {
141       fprintf (stderr, "Missing src in operation %u `%s' in episode %u\n",
142           ep_counter, op, cur->id);
143       GNUNET_free (op);
144       return GNUNET_SYSERR;
145     }
146     if (o->src_id > e->num_masters)
147     {
148       fprintf (stderr, "Invalid src %llu in operation %u `%s' in episode %u\n",
149           o->src_id, ep_counter, op, cur->id);
150       GNUNET_free (op);
151       return GNUNET_SYSERR;
152     }
153
154     GNUNET_free (op_name);
155     GNUNET_asprintf(&op_name, "op-%u-dest", ep_counter);
156     if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_number (cfg,
157         sec_name, op_name, &o->dest_id))
158     {
159       fprintf (stderr, "Missing src in operation %u `%s' in episode %u\n",
160           ep_counter, op, cur->id);
161       GNUNET_free (op);
162       return GNUNET_SYSERR;
163     }
164     if (o->dest_id > e->num_slaves)
165     {
166       fprintf (stderr, "Invalid destination %llu in operation %u `%s' in episode %u\n",
167           o->dest_id, ep_counter, op, cur->id);
168       GNUNET_free (op);
169       return GNUNET_SYSERR;
170     }
171
172
173     GNUNET_free (op_name);
174     GNUNET_asprintf(&op_name, "op-%u-value", ep_counter);
175     if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_number (cfg,
176         sec_name, op_name, &o->value))
177     {
178       fprintf (stderr, "Missing value in operation %u `%s' in episode %u\n",
179           ep_counter, op, cur->id);
180       GNUNET_free (op);
181       return GNUNET_SYSERR;
182     }
183     if (o->dest_id > e->num_slaves)
184     {
185       fprintf (stderr, "Invalid destination %llu in operation %u `%s' in episode %u\n",
186           o->dest_id, ep_counter, op, cur->id);
187       GNUNET_free (op);
188       return GNUNET_SYSERR;
189     }
190
191     fprintf (stderr, "Found operation %u in episode %u: %s [%llu]->[%llu] == %llu\n",
192         ep_counter, cur->id, print_op (o->type), o->src_id, o->dest_id, o->value);
193
194     GNUNET_CONTAINER_DLL_insert (cur->head,cur->tail, o);
195     GNUNET_free (op_name);
196     ep_counter++;
197   }
198   GNUNET_free (sec_name);
199
200   return GNUNET_OK;
201 }
202
203 static int
204 load_episodes (struct Experiment *e, struct GNUNET_CONFIGURATION_Handle *cfg)
205 {
206   int e_counter = 0;
207   char *sec_name;
208   struct GNUNET_TIME_Relative e_duration;
209   struct Episode *cur;
210   struct Episode *last;
211
212   e_counter = 0;
213   last = NULL;
214   while (1)
215   {
216     GNUNET_asprintf(&sec_name, "episode-%u", e_counter);
217     if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_time(cfg,
218         sec_name, "duration", &e_duration))
219     {
220       GNUNET_free (sec_name);
221       break;
222     }
223
224     cur = GNUNET_new (struct Episode);
225     cur->duration = e_duration;
226     cur->id = e_counter;
227
228     if (GNUNET_OK != load_episode (e, cur, cfg))
229     {
230       GNUNET_free (sec_name);
231       GNUNET_free (cur);
232       return GNUNET_SYSERR;
233     }
234
235     fprintf (stderr, "Found episode %u with duration %s \n",
236         e_counter,
237         GNUNET_STRINGS_relative_time_to_string(cur->duration, GNUNET_YES));
238
239     /* Update experiment */
240     e->num_episodes ++;
241     e->total_duration = GNUNET_TIME_relative_add(e->total_duration, cur->duration);
242     /* Put in linked list */
243     if (NULL == last)
244       e->start = cur;
245     else
246     last->next = cur;
247
248     GNUNET_free (sec_name);
249     e_counter ++;
250     last = cur;
251   }
252   return e_counter;
253 }
254
255 static void
256 timeout_experiment (void *cls, const struct GNUNET_SCHEDULER_TaskContext* tc)
257 {
258   struct Experiment *e = cls;
259   e->experiment_timeout_task = GNUNET_SCHEDULER_NO_TASK;
260   fprintf (stderr, "Experiment timeout!\n");
261
262   if (GNUNET_SCHEDULER_NO_TASK != e->episode_timeout_task)
263   {
264     e->episode_timeout_task = GNUNET_SCHEDULER_NO_TASK;
265     GNUNET_SCHEDULER_cancel (e->episode_timeout_task);
266   }
267
268   e->e_done_cb (e, GNUNET_TIME_absolute_get_duration(e->start_time),
269       GNUNET_SYSERR);
270 }
271
272 static void
273 enforce_start_send (struct Operation *op)
274 {
275   GNUNET_break (0);
276 }
277
278 static void
279 enforce_stop_send (struct Operation *op)
280 {
281   GNUNET_break (0);
282 }
283
284 static void
285 enforce_set_rate (struct Operation *op)
286 {
287   GNUNET_break (0);
288 }
289
290 static void
291 enforce_set_preference (struct Operation *op)
292 {
293   GNUNET_break (0);
294 }
295
296 static void enforce_episode (struct Episode *ep)
297 {
298   struct Operation *cur;
299   for (cur = ep->head; NULL != cur; cur = cur->next)
300   {
301
302     fprintf (stderr, "Enforcing operation: %s [%llu]->[%llu] == %llu\n",
303         print_op (cur->type), cur->src_id, cur->dest_id, cur->value);
304     switch (cur->type) {
305       case START_SEND:
306         enforce_start_send (cur);
307         break;
308       case STOP_SEND:
309         enforce_stop_send (cur);
310         break;
311       case SET_RATE:
312         enforce_set_rate (cur);
313         break;
314       case SET_PREFERENCE:
315         enforce_set_preference (cur);
316         break;
317       default:
318         break;
319     }
320   }
321 }
322
323 static void
324 timeout_episode (void *cls, const struct GNUNET_SCHEDULER_TaskContext* tc)
325 {
326   struct Experiment *e = cls;
327   e->episode_timeout_task = GNUNET_SCHEDULER_NO_TASK;
328   if (NULL != e->ep_done_cb)
329     e->ep_done_cb (e->cur);
330
331   /* Scheduling next */
332   e->cur = e->cur->next;
333   if (NULL == e->cur)
334   {
335     /* done */
336     fprintf (stderr, "Last episode done!\n");
337     if (GNUNET_SCHEDULER_NO_TASK != e->experiment_timeout_task)
338     {
339       GNUNET_SCHEDULER_cancel (e->experiment_timeout_task);
340       e->experiment_timeout_task = GNUNET_SCHEDULER_NO_TASK;
341     }
342     e->e_done_cb (e, GNUNET_TIME_absolute_get_duration(e->start_time), GNUNET_OK);
343     return;
344   }
345
346   fprintf (stderr, "Running episode %u with timeout %s\n",
347       e->cur->id,
348       GNUNET_STRINGS_relative_time_to_string(e->cur->duration, GNUNET_YES));
349   enforce_episode(e->cur);
350
351   e->episode_timeout_task = GNUNET_SCHEDULER_add_delayed (e->cur->duration,
352       &timeout_episode, e);
353 }
354
355
356 void
357 GNUNET_ATS_TEST_experimentation_run (struct Experiment *e,
358     GNUNET_ATS_TESTING_EpisodeDoneCallback ep_done_cb,
359     GNUNET_ATS_TESTING_ExperimentDoneCallback e_done_cb)
360 {
361   fprintf (stderr, "Running experiment `%s'  with timeout %s\n", e->name,
362       GNUNET_STRINGS_relative_time_to_string(e->max_duration, GNUNET_YES));
363   e->e_done_cb = e_done_cb;
364   e->ep_done_cb = ep_done_cb;
365   e->start_time = GNUNET_TIME_absolute_get();
366
367   /* Start total time out */
368   e->experiment_timeout_task = GNUNET_SCHEDULER_add_delayed (e->max_duration,
369       &timeout_experiment, e);
370
371   /* Start */
372   e->cur = e->start;
373   fprintf (stderr, "Running episode %u with timeout %s\n",
374       e->cur->id,
375       GNUNET_STRINGS_relative_time_to_string(e->cur->duration, GNUNET_YES));
376   enforce_episode(e->cur);
377   e->episode_timeout_task = GNUNET_SCHEDULER_add_delayed (e->cur->duration,
378       &timeout_episode, e);
379
380
381 }
382
383
384 struct Experiment *
385 GNUNET_ATS_TEST_experimentation_load (char *filename)
386 {
387   struct Experiment *e;
388   struct GNUNET_CONFIGURATION_Handle *cfg;
389   e = NULL;
390
391   cfg = GNUNET_CONFIGURATION_create();
392   if (GNUNET_SYSERR == GNUNET_CONFIGURATION_load (cfg, filename))
393   {
394     fprintf (stderr, "Failed to load `%s'\n", filename);
395     GNUNET_CONFIGURATION_destroy (cfg);
396     return NULL;
397   }
398
399   e = create_experiment ();
400
401   if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_string(cfg, "experiment",
402       "name", &e->name))
403   {
404     fprintf (stderr, "Invalid %s", "name");
405     free_experiment (e);
406     return NULL;
407   }
408   else
409     fprintf (stderr, "Experiment name: `%s'\n", e->name);
410
411   if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_filename (cfg, "experiment",
412       "cfg_file", &e->cfg_file))
413   {
414     fprintf (stderr, "Invalid %s", "cfg_file");
415     free_experiment (e);
416     return NULL;
417   }
418   else
419     fprintf (stderr, "Experiment name: `%s'\n", e->cfg_file);
420
421   if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_number(cfg, "experiment",
422       "masters", &e->num_masters))
423   {
424     fprintf (stderr, "Invalid %s", "masters");
425     free_experiment (e);
426     return NULL;
427   }
428   else
429     fprintf (stderr, "Experiment masters: `%llu'\n",
430         e->num_masters);
431
432   if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_number(cfg, "experiment",
433       "slaves", &e->num_slaves))
434   {
435     fprintf (stderr, "Invalid %s", "slaves");
436     free_experiment (e);
437     return NULL;
438   }
439   else
440     fprintf (stderr, "Experiment slaves: `%llu'\n",
441         e->num_slaves);
442
443   if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_time(cfg, "experiment",
444       "max_duration", &e->max_duration))
445   {
446     fprintf (stderr, "Invalid %s", "max_duration");
447     free_experiment (e);
448     return NULL;
449   }
450   else
451     fprintf (stderr, "Experiment duration: `%s'\n",
452         GNUNET_STRINGS_relative_time_to_string (e->max_duration, GNUNET_YES));
453
454   load_episodes (e, cfg);
455   fprintf (stderr, "Loaded %u episodes with total duration %s\n",
456       e->num_episodes,
457       GNUNET_STRINGS_relative_time_to_string (e->total_duration, GNUNET_YES));
458
459   GNUNET_CONFIGURATION_destroy (cfg);
460   return e;
461 }
462
463 void
464 GNUNET_ATS_TEST_experimentation_stop (struct Experiment *e)
465 {
466   if (GNUNET_SCHEDULER_NO_TASK != e->experiment_timeout_task)
467   {
468     GNUNET_SCHEDULER_cancel (e->experiment_timeout_task);
469     e->experiment_timeout_task = GNUNET_SCHEDULER_NO_TASK;
470   }
471   free_experiment (e);
472 }
473
474 /* end of file ats-testing-experiment.c*/
475