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