more scaffolding
[oweals/gnunet.git] / src / stream / perf_stream_api.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 stream/perf_stream_api.c
23  * @brief performance benchmarks for stream api
24  * @author Sree Harsha Totakura
25  */
26
27 /****************************************************************************************/
28 /* Test is setup into the following major steps:                                        */
29 /*    1. Measurements over loopback (1 hop). i.e. we use only one peer and open         */
30 /*       stream connections over loopback. Messages will go through                     */
31 /*       STREAM_API->MESH_API->MESH_SERVICE->MESH_API->STREAM_API.                      */
32 /*    2. Measurements over 2 peers (2 hops). We use testbed to create 2 peers,          */
33 /*       connect them and then create stream connections. Messages will go through      */
34 /*       STREAM_API->MESH_API->MESH_SERVICE->CORE1.....CORE2->MESH_API->STREAM_API      */
35 /*    3. Measurements over 3 peers (3 hops). We use testbed to create 3 peers,          */
36 /*       connect them in a line topology: peer1->peer2->peer3. Messages will go         */
37 /*       through                                                                        */
38 /*       STREAM_API->MESH_API->MESH_SERVICE->CORE1..CORE2..CORE3->MESH_API->STREAM_API. */
39 /****************************************************************************************/
40
41 #include "platform.h"
42 #include "gnunet_common.h"
43 #include "gnunet_util_lib.h"
44 #include "gnunet_testing_lib.h"
45 #include "gnunet_testbed_service.h"
46 #include "gnunet_stream_lib.h"
47
48
49   
50 /**
51  * Simple struct to keep track of progress, and print a
52  * nice little percentage meter for long running tasks.
53  */
54 struct ProgressMeter
55 {
56   unsigned int total;
57
58   unsigned int modnum;
59
60   unsigned int dotnum;
61
62   unsigned int completed;
63
64   int print;
65
66   char *startup_string;
67 };
68
69
70 /**
71  * Steps in testing
72  */
73 enum TestStep
74 {
75   /**
76    * Single hop loopback testing
77    */
78   TEST_STEP_1_HOP,
79
80   /**
81    * Testing with 2 peers
82    */
83   TEST_STEP_2_HOP,
84
85   /**
86    * Testing with 3 peers
87    */
88   TEST_STEP_3_HOP
89 };
90
91 /**
92  * Structure for holding peer's sockets and IO Handles
93  */
94 struct PeerData
95 {
96   /**
97    * Peer's stream socket
98    */
99   struct GNUNET_STREAM_Socket *socket;
100
101   struct GNUNET_PeerIdentity self;
102
103   /**
104    * Peer's io write handle
105    */
106   struct GNUNET_STREAM_IOWriteHandle *io_write_handle;
107
108   /**
109    * Peer's io read handle
110    */
111   struct GNUNET_STREAM_IOReadHandle *io_read_handle;
112
113   /**
114    * Bytes the peer has written
115    */
116   unsigned int bytes_wrote;
117
118   /**
119    * Byte the peer has read
120    */
121   unsigned int bytes_read;
122 };
123
124
125 /**
126  * Maximum size of the data which we will transfer during tests
127  */
128 #define DATA_SIZE 65536      /* 64KB */
129
130 /**
131  * Listen socket of peer2
132  */
133 struct GNUNET_STREAM_ListenSocket *peer2_listen_socket;
134
135 /**
136  * Handle to configuration during TEST_STEP_1_HOP
137  */
138 const struct GNUNET_CONFIGURATION_Handle *config;
139
140 /**
141  * Placeholder for peer data
142  */
143 static struct PeerData peer_data[3];
144
145 /**
146  * Task ID for abort task
147  */
148 static GNUNET_SCHEDULER_TaskIdentifier abort_task;
149
150 /**
151  * Random data block. Should generate data first
152  */
153 static uint32_t data[DATA_SIZE / 4];     /* 64KB array */
154
155 /**
156  * Payload sizes to test each major test with
157  */
158 static uint16_t payload_size[] = 
159 { 20, 500, 2000, 7000, 13000, 25000, 56000, 64000 };
160
161 /**
162  * Handle for the progress meter
163  */
164 static struct ProgressMeter *meter;
165
166 /**
167  * Current step of testing
168  */
169 static enum TestStep test_step;
170
171 /**
172  * Index for choosing payload size
173  */
174 unsigned int payload_size_index;
175
176 /**
177  * Create a meter to keep track of the progress of some task.
178  *
179  * @param total the total number of items to complete
180  * @param start_string a string to prefix the meter with (if printing)
181  * @param print GNUNET_YES to print the meter, GNUNET_NO to count
182  *              internally only
183  *
184  * @return the progress meter
185  */
186 static struct ProgressMeter *
187 create_meter (unsigned int total, char *start_string, int print)
188 {
189   struct ProgressMeter *ret;
190
191   ret = GNUNET_malloc (sizeof (struct ProgressMeter));
192   ret->print = print;
193   ret->total = total;
194   ret->modnum = total / 4;
195   if (ret->modnum == 0)         /* Divide by zero check */
196     ret->modnum = 1;
197   ret->dotnum = (total / 50) + 1;
198   if (start_string != NULL)
199     ret->startup_string = GNUNET_strdup (start_string);
200   else
201     ret->startup_string = GNUNET_strdup ("");
202
203   return ret;
204 }
205
206
207 /**
208  * Update progress meter (increment by one).
209  *
210  * @param meter the meter to update and print info for
211  *
212  * @return GNUNET_YES if called the total requested,
213  *         GNUNET_NO if more items expected
214  */
215 static int
216 update_meter (struct ProgressMeter *meter)
217 {
218   if (meter->print == GNUNET_YES)
219   {
220     if (meter->completed % meter->modnum == 0)
221     {
222       if (meter->completed == 0)
223       {
224         FPRINTF (stdout, "%sProgress: [0%%", meter->startup_string);
225       }
226       else
227         FPRINTF (stdout, "%d%%",
228                  (int) (((float) meter->completed / meter->total) * 100));
229     }
230     else if (meter->completed % meter->dotnum == 0)
231       FPRINTF (stdout, "%s",  ".");
232
233     if (meter->completed + 1 == meter->total)
234       FPRINTF (stdout, "%d%%]\n", 100);
235     fflush (stdout);
236   }
237   meter->completed++;
238
239   if (meter->completed == meter->total)
240     return GNUNET_YES;
241   if (meter->completed > meter->total)
242     GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Progress meter overflow!!\n");
243   return GNUNET_NO;
244 }
245
246
247 /**
248  * Reset progress meter.
249  *
250  * @param meter the meter to reset
251  *
252  * @return GNUNET_YES if meter reset,
253  *         GNUNET_SYSERR on error
254  */
255 static int
256 reset_meter (struct ProgressMeter *meter)
257 {
258   if (meter == NULL)
259     return GNUNET_SYSERR;
260
261   meter->completed = 0;
262   return GNUNET_YES;
263 }
264
265
266 /**
267  * Release resources for meter
268  *
269  * @param meter the meter to free
270  */
271 static void
272 free_meter (struct ProgressMeter *meter)
273 {
274   GNUNET_free_non_null (meter->startup_string);
275   GNUNET_free (meter);
276 }
277
278
279 /**
280  * Something went wrong and timed out. Kill everything and set error flag
281  */
282 static void
283 do_abort (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
284 {
285   GNUNET_break (0);
286 }
287
288
289 /**
290  * Functions of this type are called upon new stream connection from other peers
291  *
292  * @param cls the closure from GNUNET_STREAM_listen
293  * @param socket the socket representing the stream
294  * @param initiator the identity of the peer who wants to establish a stream
295  *            with us
296  * @return GNUNET_OK to keep the socket open, GNUNET_SYSERR to close the
297  *             stream (the socket will be invalid after the call)
298  */
299 static int
300 stream_listen_cb (void *cls, struct GNUNET_STREAM_Socket *socket,
301                   const struct GNUNET_PeerIdentity *initiator)
302 {
303   GNUNET_break (0);
304   return GNUNET_OK;
305 }
306
307
308 /**
309  * Listen success callback; connects a peer to stream as client
310  */
311 static void
312 stream_connect (void)
313 {
314   GNUNET_break (0);
315 }
316
317
318 /**
319  * Initialize framework and start test
320  *
321  * @param cls closure
322  * @param cfg configuration of the peer that was started
323  * @param peer identity of the peer that was created
324  */
325 static void
326 run (void *cls, 
327      const struct GNUNET_CONFIGURATION_Handle *cfg,
328      struct GNUNET_TESTING_Peer *peer)
329 {
330   struct GNUNET_PeerIdentity self;
331
332   GNUNET_TESTING_peer_get_identity (peer, &self);
333   config = cfg;
334   peer2_listen_socket = 
335       GNUNET_STREAM_listen (config, 10, /* App port */ &stream_listen_cb, NULL,
336                             GNUNET_STREAM_OPTION_SIGNAL_LISTEN_SUCCESS,
337                             &stream_connect, GNUNET_STREAM_OPTION_END);
338   GNUNET_assert (NULL != peer2_listen_socket);
339   peer_data[1].self = self;
340   peer_data[2].self = self;
341   abort_task =
342     GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
343                                   (GNUNET_TIME_UNIT_SECONDS, 60), &do_abort,
344                                   NULL);
345 }
346
347
348 /**
349  * Main function
350  */
351 int main (int argc, char **argv)
352 {
353   char *pmsg;
354   unsigned int count;
355   int ret;
356
357   meter = create_meter ((sizeof (data) / 4), "Generating random data\n", GNUNET_YES);
358   for (count=0; count < (sizeof (data) / 4); count++)
359   {
360     data[count] = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
361                                             UINT32_MAX);
362     update_meter (meter);
363   }
364   reset_meter (meter);
365   free_meter (meter);
366   test_step = TEST_STEP_1_HOP;
367   for (payload_size_index = 0; 
368        payload_size_index < (sizeof (payload_size) / sizeof (uint16_t));
369        payload_size_index++)
370   {
371     GNUNET_asprintf (&pmsg, "Testing over loopback with payload size %hu\n",
372                      payload_size[payload_size_index]);
373     meter = create_meter ((sizeof (data) / 4), pmsg, GNUNET_YES);
374     GNUNET_free (pmsg);
375     ret = GNUNET_TESTING_peer_run ("test_stream_big", "test_stream_local.conf",
376                                    &run, NULL);
377     free_meter (meter);
378     if (0 != ret)
379       break;
380   }
381   test_step = TEST_STEP_2_HOP;
382   for (payload_size_index = 0; 
383        payload_size_index < (sizeof (payload_size) / sizeof (uint16_t));
384        payload_size_index++)
385   {
386     /* Initialize testbed here */
387   }
388   test_step = TEST_STEP_3_HOP;
389   for (payload_size_index = 0; 
390        payload_size_index < (sizeof (payload_size) / sizeof (uint16_t));
391        payload_size_index++)
392   {
393     /* Initialize testbed here */
394   }
395   return ret;
396 }