add function conv param string
[oweals/gnunet.git] / src / ats-tests / ats-testing-traffic.c
1 /*
2  This file is part of GNUnet.
3  Copyright (C) 2010-2013 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 ats-tests/ats-testing-traffic.c
22  * @brief ats benchmark: traffic generator
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 static struct TrafficGenerator *tg_head;
31 static struct TrafficGenerator *tg_tail;
32
33 extern struct GNUNET_ATS_TEST_Topology *top;
34
35 static struct GNUNET_TIME_Relative
36 get_delay (struct TrafficGenerator *tg)
37 {
38   struct GNUNET_TIME_Relative delay;
39   struct GNUNET_TIME_Relative time_delta;
40   long long int cur_rate;
41   long long int delta_rate;
42
43   delay.rel_value_us = 0;
44
45   /* Calculate the current transmission rate based on the type of traffic */
46   switch (tg->type) {
47     case GNUNET_ATS_TEST_TG_CONSTANT:
48       if (UINT32_MAX == tg->base_rate)
49         return GNUNET_TIME_UNIT_ZERO;
50       cur_rate = tg->base_rate;
51       break;
52     case GNUNET_ATS_TEST_TG_LINEAR:
53       time_delta = GNUNET_TIME_absolute_get_duration(tg->time_start);
54       /* Calculate point of time in the current period */
55       time_delta.rel_value_us = time_delta.rel_value_us % tg->duration_period.rel_value_us;
56       delta_rate = ((double) time_delta.rel_value_us  / tg->duration_period.rel_value_us) *
57           (tg->max_rate - tg->base_rate);
58       if ((tg->max_rate < tg->base_rate) && ((tg->max_rate - tg->base_rate) > tg->base_rate))
59       {
60         /* This will cause an underflow */
61         GNUNET_break (0);
62       }
63       cur_rate = tg->base_rate + delta_rate;
64       break;
65     case GNUNET_ATS_TEST_TG_RANDOM:
66       cur_rate = tg->base_rate + GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
67           tg->max_rate - tg->base_rate);
68       break;
69     case GNUNET_ATS_TEST_TG_SINUS:
70       time_delta = GNUNET_TIME_absolute_get_duration(tg->time_start);
71       /* Calculate point of time in the current period */
72       time_delta.rel_value_us = time_delta.rel_value_us % tg->duration_period.rel_value_us;
73       if ((tg->max_rate - tg->base_rate) > tg->base_rate)
74       {
75         /* This will cause an underflow for second half of sinus period,
76          * will be detected in general when experiments are loaded */
77         GNUNET_break (0);
78       }
79       delta_rate = (tg->max_rate - tg->base_rate) *
80           sin ( (2 * M_PI) / ((double) tg->duration_period.rel_value_us) * time_delta.rel_value_us);
81       cur_rate = tg->base_rate + delta_rate;
82       break;
83     default:
84       return delay;
85       break;
86   }
87
88   if (cur_rate < 0)
89   {
90     cur_rate = 1;
91   }
92   /* Calculate the delay for the next message based on the current delay  */
93   delay.rel_value_us =  GNUNET_TIME_UNIT_SECONDS.rel_value_us * TEST_MESSAGE_SIZE / cur_rate;
94
95   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
96               "Current rate is %lld, calculated delay is %llu\n",
97               cur_rate,
98               (unsigned long long) delay.rel_value_us);
99   return delay;
100 }
101
102
103 static size_t
104 send_ping_ready_cb (void *cls, size_t size, void *buf)
105 {
106   struct BenchmarkPartner *p = cls;
107   static char msgbuf[TEST_MESSAGE_SIZE];
108   struct GNUNET_MessageHeader *msg;
109   struct GNUNET_TIME_Relative delay;
110
111   if (NULL == buf)
112   {
113     GNUNET_break (0);
114     return 0;
115   }
116   if (size < TEST_MESSAGE_SIZE)
117   {
118     GNUNET_break (0);
119     return 0;
120   }
121
122   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
123              "Master [%u]: Sending PING to [%u]\n",
124              p->me->no, p->dest->no);
125   if (top->test_core)
126   {
127     if (NULL == p->cth)
128     {
129       GNUNET_break (0);
130     }
131     p->cth = NULL;
132   }
133   else
134   {
135     if (NULL == p->tth)
136     {
137       GNUNET_break (0);
138     }
139     p->tth = NULL;
140   }
141
142   msg = (struct GNUNET_MessageHeader *) &msgbuf;
143   memset (&msgbuf, 'a', TEST_MESSAGE_SIZE);
144   msg->type = htons (TEST_MESSAGE_TYPE_PING);
145   msg->size = htons (TEST_MESSAGE_SIZE);
146   memcpy (buf, msg, TEST_MESSAGE_SIZE);
147
148   p->messages_sent++;
149   p->bytes_sent += TEST_MESSAGE_SIZE;
150   p->me->total_messages_sent++;
151   p->me->total_bytes_sent += TEST_MESSAGE_SIZE;
152
153   if (NULL == p->tg)
154   {
155     GNUNET_break (0);
156     return TEST_MESSAGE_SIZE;
157   }
158   delay = get_delay (p->tg);
159
160   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Delay for next transmission %llu ms\n",
161       (long long unsigned int) delay.rel_value_us / 1000);
162   p->tg->next_ping_transmission = GNUNET_TIME_absolute_add(GNUNET_TIME_absolute_get(),
163       delay);
164
165   return TEST_MESSAGE_SIZE;
166 }
167
168
169 static void
170 comm_schedule_send (void *cls)
171 {
172   struct BenchmarkPartner *p = cls;
173
174   p->tg->send_task = NULL;
175   p->last_message_sent = GNUNET_TIME_absolute_get();
176   if (GNUNET_YES == top->test_core)
177   {
178     p->cth = GNUNET_CORE_notify_transmit_ready (p->me->ch, GNUNET_NO,
179                                                 GNUNET_CORE_PRIO_BEST_EFFORT,
180                                                 GNUNET_TIME_UNIT_MINUTES,
181                                                 &p->dest->id,
182                                                 TEST_MESSAGE_SIZE,
183                                                 &send_ping_ready_cb, p);
184   }
185   else
186   {
187     p->tth = GNUNET_TRANSPORT_notify_transmit_ready (p->me->th,
188                                                      &p->dest->id,
189                                                      TEST_MESSAGE_SIZE,
190                                                      GNUNET_TIME_UNIT_MINUTES,
191                                                      &send_ping_ready_cb, p);
192   }
193 }
194
195
196 static size_t
197 comm_send_pong_ready (void *cls, size_t size, void *buf)
198 {
199   static char msgbuf[TEST_MESSAGE_SIZE];
200   struct BenchmarkPartner *p = cls;
201   struct GNUNET_MessageHeader *msg;
202
203   if (GNUNET_YES == top->test_core)
204     p->cth = NULL;
205   else
206     p->tth = NULL;
207
208   p->messages_sent++;
209   p->bytes_sent += TEST_MESSAGE_SIZE;
210   p->me->total_messages_sent++;
211   p->me->total_bytes_sent += TEST_MESSAGE_SIZE;
212
213   msg = (struct GNUNET_MessageHeader *) &msgbuf;
214   memset (&msgbuf, 'a', TEST_MESSAGE_SIZE);
215   msg->type = htons (TEST_MESSAGE_TYPE_PONG);
216   msg->size = htons (TEST_MESSAGE_SIZE);
217   memcpy (buf, msg, TEST_MESSAGE_SIZE);
218
219   return TEST_MESSAGE_SIZE;
220 }
221
222
223 void
224 GNUNET_ATS_TEST_traffic_handle_ping (struct BenchmarkPartner *p)
225 {
226   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
227       "Slave [%u]: Received PING from [%u], sending PONG\n", p->me->no,
228       p->dest->no);
229
230   p->messages_received++;
231   p->bytes_received += TEST_MESSAGE_SIZE;
232   p->me->total_messages_received++;
233   p->me->total_bytes_received += TEST_MESSAGE_SIZE;
234
235   if (GNUNET_YES == top->test_core)
236   {
237     GNUNET_assert (NULL == p->cth);
238
239     p->cth
240       = GNUNET_CORE_notify_transmit_ready (p->me->ch, GNUNET_NO,
241                                            GNUNET_CORE_PRIO_BEST_EFFORT,
242                                            GNUNET_TIME_UNIT_MINUTES,
243                                            &p->dest->id, TEST_MESSAGE_SIZE,
244                                            &comm_send_pong_ready, p);
245   }
246   else
247   {
248     GNUNET_assert (NULL == p->tth);
249     p->tth = GNUNET_TRANSPORT_notify_transmit_ready (p->me->th, &p->dest->id,
250         TEST_MESSAGE_SIZE, GNUNET_TIME_UNIT_MINUTES, &comm_send_pong_ready,
251         p);
252   }
253 }
254
255
256 void
257 GNUNET_ATS_TEST_traffic_handle_pong (struct BenchmarkPartner *p)
258 {
259   struct GNUNET_TIME_Relative left;
260   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
261       "Master [%u]: Received PONG from [%u], next message\n", p->me->no,
262       p->dest->no);
263
264   p->messages_received++;
265   p->bytes_received += TEST_MESSAGE_SIZE;
266   p->me->total_messages_received++;
267   p->me->total_bytes_received += TEST_MESSAGE_SIZE;
268   p->total_app_rtt += GNUNET_TIME_absolute_get_difference(p->last_message_sent,
269       GNUNET_TIME_absolute_get()).rel_value_us;
270
271   /* Schedule next send event */
272   if (NULL == p->tg)
273     return;
274
275   left = GNUNET_TIME_absolute_get_remaining(p->tg->next_ping_transmission);
276   if (UINT32_MAX == p->tg->base_rate)
277   {
278     p->tg->send_task = GNUNET_SCHEDULER_add_now (&comm_schedule_send, p);
279   }
280   else if (0 == left.rel_value_us)
281   {
282     p->tg->send_task = GNUNET_SCHEDULER_add_now (&comm_schedule_send, p);
283   }
284   else
285   {
286     /* Enforce minimum transmission rate 1 msg / sec */
287     if (GNUNET_TIME_UNIT_SECONDS.rel_value_us == (left = GNUNET_TIME_relative_min (left, GNUNET_TIME_UNIT_SECONDS)).rel_value_us)
288       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
289           "Enforcing minimum send rate between master [%u] and slave [%u]\n",
290           p->me->no, p->dest->no);
291     p->tg->send_task = GNUNET_SCHEDULER_add_delayed (left,
292         &comm_schedule_send, p);
293   }
294 }
295
296
297 /**
298  * Generate between the source master and the partner and send traffic with a
299  * maximum rate.
300  *
301  * @param src traffic source
302  * @param dest traffic partner
303  * @param type type of traffic to generate
304  * @param base_rate traffic base rate to send data with
305  * @param max_rate  traffic maximum rate to send data with
306  * @param period duration of a period of traffic generation (~ 1/frequency)
307  * @param duration how long to generate traffic
308  * @return the traffic generator
309  */
310 struct TrafficGenerator *
311 GNUNET_ATS_TEST_generate_traffic_start (struct BenchmarkPeer *src,
312                                         struct BenchmarkPartner *dest,
313                                         enum GeneratorType type,
314                                         unsigned int base_rate,
315                                         unsigned int max_rate,
316                                         struct GNUNET_TIME_Relative period,
317                                         struct GNUNET_TIME_Relative duration)
318 {
319   struct TrafficGenerator *tg;
320
321   if (NULL != dest->tg)
322   {
323     GNUNET_break (0);
324     return NULL;
325   }
326
327   tg = GNUNET_new (struct TrafficGenerator);
328   GNUNET_CONTAINER_DLL_insert (tg_head, tg_tail, tg);
329   tg->type = type;
330   tg->src = src;
331   tg->dest = dest;
332   tg->base_rate = base_rate;
333   tg->max_rate = max_rate;
334   tg->duration_period = period;
335   tg->time_start = GNUNET_TIME_absolute_get();
336   tg->next_ping_transmission = GNUNET_TIME_UNIT_FOREVER_ABS;
337
338   switch (type) {
339     case GNUNET_ATS_TEST_TG_CONSTANT:
340       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
341                   "Setting up constant traffic generator master[%u] `%s' and slave [%u] `%s' max %u Bips\n",
342                   dest->me->no, GNUNET_i2s (&dest->me->id),
343                   dest->dest->no, GNUNET_i2s (&dest->dest->id),
344                   base_rate);
345       break;
346     case GNUNET_ATS_TEST_TG_LINEAR:
347       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
348                   "Setting up linear traffic generator master[%u] `%s' and slave [%u] `%s' min %u Bips max %u Bips\n",
349                   dest->me->no, GNUNET_i2s (&dest->me->id),
350                   dest->dest->no, GNUNET_i2s (&dest->dest->id),
351                   base_rate,
352                   max_rate);
353       break;
354     case GNUNET_ATS_TEST_TG_SINUS:
355       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
356                   "Setting up sinus traffic generator master[%u] `%s' and slave [%u] `%s' baserate %u Bips, amplitude %u Bps\n",
357                   dest->me->no, GNUNET_i2s (&dest->me->id),
358                   dest->dest->no, GNUNET_i2s (&dest->dest->id),
359                   base_rate, max_rate);
360       break;
361     case GNUNET_ATS_TEST_TG_RANDOM:
362       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
363                   "Setting up random traffic generator master[%u] `%s' and slave [%u] `%s' min %u Bips max %u Bps\n",
364                   dest->me->no, GNUNET_i2s (&dest->me->id),
365                   dest->dest->no, GNUNET_i2s (&dest->dest->id),
366                   base_rate, max_rate);
367       break;
368     default:
369       break;
370   }
371
372   if ( ((GNUNET_YES == top->test_core) && (NULL != dest->cth)) ||
373        ((GNUNET_NO == top->test_core) && (NULL != dest->tth)) )
374   {
375         GNUNET_break (0);
376         GNUNET_CONTAINER_DLL_remove (tg_head, tg_tail, tg);
377         GNUNET_free (tg);
378         return NULL;
379   }
380
381   dest->tg = tg;
382   tg->send_task = GNUNET_SCHEDULER_add_now (&comm_schedule_send, dest);
383   return tg;
384 }
385
386
387 void
388 GNUNET_ATS_TEST_generate_traffic_stop (struct TrafficGenerator *tg)
389 {
390   GNUNET_CONTAINER_DLL_remove (tg_head, tg_tail, tg);
391   tg->dest->tg = NULL;
392
393   if (NULL != tg->send_task)
394   {
395     GNUNET_SCHEDULER_cancel (tg->send_task);
396     tg->send_task = NULL;
397   }
398   if (top->test_core)
399   {
400     if (NULL != tg->dest->cth)
401     {
402       GNUNET_CORE_notify_transmit_ready_cancel (tg->dest->cth);
403       tg->dest->cth = NULL;
404     }
405   }
406   else
407   {
408     if (NULL != tg->dest->tth)
409     {
410       GNUNET_TRANSPORT_notify_transmit_ready_cancel (tg->dest->tth);
411       tg->dest->tth = NULL;
412     }
413   }
414   GNUNET_free (tg);
415 }
416
417
418 /**
419  * Stop all traffic generators
420  */
421 void
422 GNUNET_ATS_TEST_generate_traffic_stop_all ()
423 {
424   struct TrafficGenerator *cur;
425   struct TrafficGenerator *next;
426   next = tg_head;
427   for (cur = next; NULL != cur; cur = next)
428   {
429       next = cur->next;
430       GNUNET_ATS_TEST_generate_traffic_stop(cur);
431   }
432 }
433
434 /* end of file ats-testing-traffic.c */