profiler done
[oweals/gnunet.git] / src / transport / gnunet-transport-profiler.c
1 /*
2  This file is part of GNUnet.
3  (C) 2011-2014 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 src/transport/gnunet-transport.c
23  * @brief Tool to help configure, measure and control the transport subsystem.
24  * @author Christian Grothoff
25  * @author Nathan Evans
26  *
27  * This utility can be used to test if a transport mechanism for
28  * GNUnet is properly configured.
29  */
30 #include "platform.h"
31 #include "gnunet_util_lib.h"
32 #include "gnunet_protocols.h"
33 #include "gnunet_transport_service.h"
34
35 struct Iteration
36 {
37   struct Iteration *next;
38   struct Iteration *prev;
39   struct GNUNET_TIME_Absolute start;
40   struct GNUNET_TIME_Absolute end;
41
42   struct GNUNET_TIME_Relative dur;
43
44   unsigned int msgs_sent;
45 };
46
47
48 /**
49  * Timeout for a connections
50  */
51 #define CONNECT_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
52
53 /**
54  * Benchmarking block size in bye
55  */
56 #define DEFAULT_MESSAGE_SIZE 1024
57
58 /**
59  * Benchmarking message count
60  */
61 #define DEFAULT_MESSAGE_COUNT 1024
62
63 /**
64  * Benchmarking iteration count
65  */
66 #define DEFAULT_ITERATION_COUNT 1
67
68 /**
69  * Option -s.
70  */
71 static int benchmark_send;
72
73 /**
74  * Option -b.
75  */
76 static int benchmark_receive;
77
78 /**
79  * Option -n.
80  */
81 static unsigned int benchmark_count;
82
83 /**
84  * Option -i.
85  */
86 static unsigned int benchmark_iterations;
87
88 /**
89  * Option -m.
90  */
91 static unsigned int benchmark_size;
92
93 /**
94  * Benchmark running
95  */
96 static unsigned int benchmark_running;
97
98 /**
99  * Which peer should we connect to?
100  */
101 static char *cpid;
102
103 /**
104  * Handle to transport service.
105  */
106 static struct GNUNET_TRANSPORT_Handle *handle;
107
108 /**
109  * Configuration handle
110  */
111 static struct GNUNET_CONFIGURATION_Handle *cfg;
112
113 /**
114  * Try_connect handle
115  */
116 struct GNUNET_TRANSPORT_TryConnectHandle *tc_handle;
117
118
119 struct Iteration *ihead;
120 struct Iteration *itail;
121
122 /**
123  * Global return value (0 success).
124  */
125 static int ret;
126 /**
127  * Handle for current transmission request.
128  */
129 static struct GNUNET_TRANSPORT_TransmitHandle *th;
130
131 struct GNUNET_TRANSPORT_Blacklist *bl_handle;
132
133 /**
134  * Identity of the peer we transmit to / connect to.
135  * (equivalent to 'cpid' string).
136  */
137 static struct GNUNET_PeerIdentity pid;
138
139 /**
140  * Task scheduled for cleanup / termination of the process.
141  */
142 static GNUNET_SCHEDULER_TaskIdentifier end;
143
144 /**
145  * Selected level of verbosity.
146  */
147 static int verbosity;
148
149 /**
150  * Task run in monitor mode when the user presses CTRL-C to abort.
151  * Stops monitoring activity.
152  *
153  * @param cls NULL
154  * @param tc scheduler context
155  */
156 static void
157 shutdown_task (void *cls,
158                const struct GNUNET_SCHEDULER_TaskContext *tc)
159 {
160   struct Iteration *icur;
161   struct Iteration *inext;
162
163   if (NULL != tc_handle)
164   {
165     GNUNET_TRANSPORT_try_connect_cancel (tc_handle);
166     tc_handle = NULL;
167   }
168   if (NULL != th)
169   {
170     GNUNET_TRANSPORT_notify_transmit_ready_cancel (th);
171     th = NULL;
172   }
173
174   if (NULL != bl_handle )
175   {
176     GNUNET_TRANSPORT_blacklist_cancel (bl_handle);
177     bl_handle = NULL;
178   }
179
180   if (NULL != handle)
181   {
182     GNUNET_TRANSPORT_disconnect (handle);
183     handle = NULL;
184   }
185
186   if (benchmark_send)
187   {
188     inext = ihead;
189     while (NULL != (icur = inext))
190     {
191       inext = icur->next;
192       if (verbosity > 0)
193         FPRINTF (stdout, _("%llu B in %llu ms == %.2f KB/s!\n"),
194             ((long long unsigned int) benchmark_count * benchmark_size),
195             ((long long unsigned int) itail->dur.rel_value_us / 1000),
196             (float) ((benchmark_count * benchmark_size) / 1024)/ ((float) itail->dur.rel_value_us / (1000 * 1000)));
197     }
198     FPRINTF (stdout, _("%u;%u"),benchmark_count, benchmark_size);
199     inext = ihead;
200     while (NULL != (icur = inext))
201     {
202       inext = icur->next;
203       GNUNET_CONTAINER_DLL_remove (ihead, itail, icur);
204
205       FPRINTF (stdout, _(";%llu"),
206       (long long unsigned int) icur->dur.rel_value_us);
207
208       GNUNET_free (icur);
209     }
210   }
211 #if 0
212   if (benchmark_receive)
213   {
214     duration = GNUNET_TIME_absolute_get_duration (start_time);
215     FPRINTF (stdout,
216              _("Received %llu bytes/s (%llu bytes in %s)\n"),
217              1000LL * 1000LL * traffic_received / (1 + duration.rel_value_us),
218              traffic_received,
219              GNUNET_STRINGS_relative_time_to_string (duration, GNUNET_YES));
220   }
221 #endif
222   FPRINTF (stdout, _("\n"));
223 }
224
225 static void
226 iteration_done ();
227
228 /**
229  * Function called to notify a client about the socket
230  * begin ready to queue more data.  @a buf will be
231  * NULL and @a size zero if the socket was closed for
232  * writing in the meantime.
233  *
234  * @param cls closure
235  * @param size number of bytes available in @a buf
236  * @param buf where the callee should write the message
237  * @return number of bytes written to @a buf
238  */
239 static size_t
240 transmit_data (void *cls,
241                size_t size,
242                void *buf)
243 {
244   struct GNUNET_MessageHeader *m = buf;
245
246   th = NULL;
247   if ((NULL == buf) || (0 == size))
248   {
249     th = NULL;
250     return 0;
251   }
252
253   itail->msgs_sent ++;
254
255   GNUNET_assert(size >= sizeof(struct GNUNET_MessageHeader));
256   GNUNET_assert(size < GNUNET_SERVER_MAX_MESSAGE_SIZE);
257   m->size = ntohs (size);
258   m->type = ntohs (GNUNET_MESSAGE_TYPE_DUMMY);
259   memset (&m[1], 52, size - sizeof(struct GNUNET_MessageHeader));
260
261   if (itail->msgs_sent < benchmark_count)
262   {
263     th = GNUNET_TRANSPORT_notify_transmit_ready (handle, &pid, benchmark_size,
264         GNUNET_TIME_UNIT_FOREVER_REL, &transmit_data, NULL );
265   }
266   else
267   {
268
269     iteration_done();
270     return size;
271   }
272   if (verbosity > 0)
273     if (itail->msgs_sent % 10 == 0 )
274     FPRINTF (stdout, _("."));
275   return size;
276 }
277
278
279 static void
280 iteration_start ()
281 {
282   struct Iteration *icur;
283   ret = 0;
284
285   if (benchmark_send)
286   {
287     benchmark_running = GNUNET_YES;
288     icur = GNUNET_new (struct Iteration);
289     GNUNET_CONTAINER_DLL_insert_tail (ihead, itail, icur);
290     icur->start = GNUNET_TIME_absolute_get();
291
292     if (verbosity > 0)
293       FPRINTF (stdout,
294           _("\nStarting benchmark to `%s', starting to send %u messages in %u byte blocks\n"),
295           GNUNET_i2s (&pid), benchmark_count, benchmark_size);
296     if (NULL == th)
297       th = GNUNET_TRANSPORT_notify_transmit_ready (handle, &pid, benchmark_size,
298           GNUNET_TIME_UNIT_FOREVER_REL, &transmit_data, NULL );
299     else
300       GNUNET_break(0);
301     return;
302   }
303 }
304
305 static void
306 iteration_done ()
307 {
308   static int it_count = 0;
309   it_count ++;
310
311   itail->dur = GNUNET_TIME_absolute_get_duration (itail->start);
312   if (it_count == benchmark_iterations)
313   {
314     benchmark_running = GNUNET_NO;
315     if (GNUNET_SCHEDULER_NO_TASK != end)
316       GNUNET_SCHEDULER_cancel (end);
317     end = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
318     return;
319   }
320   else
321   {
322     iteration_start ();
323   }
324 }
325
326
327
328 /**
329  * Function called to notify transport users that another
330  * peer connected to us.
331  *
332  * @param cls closure
333  * @param peer the peer that connected
334  */
335 static void
336 notify_connect (void *cls,
337                 const struct GNUNET_PeerIdentity *peer)
338 {
339   if (0 != memcmp (&pid, peer, sizeof(struct GNUNET_PeerIdentity)))
340   {
341     FPRINTF (stdout,
342         _("Connected to different peer `%s'\n"), GNUNET_i2s (&pid));
343     return;
344   }
345
346   if (verbosity > 0)
347     FPRINTF (stdout,
348         _("Successfully connected to `%s'\n"),
349         GNUNET_i2s (&pid));
350
351   if (NULL != tc_handle)
352   {
353     GNUNET_TRANSPORT_try_connect_cancel (tc_handle);
354     tc_handle = NULL;
355   }
356
357   iteration_start ();
358 }
359
360
361 /**
362  * Function called to notify transport users that another
363  * peer disconnected from us.
364  *
365  * @param cls closure
366  * @param peer the peer that disconnected
367  */
368 static void
369 notify_disconnect (void *cls,
370                    const struct GNUNET_PeerIdentity *peer)
371 {
372   if (0 != memcmp (&pid, peer, sizeof(struct GNUNET_PeerIdentity)))
373     return;
374   if (GNUNET_YES == benchmark_running)
375   {
376     FPRINTF (stdout, _("Disconnected from peer `%s' while benchmarking\n"),
377         GNUNET_i2s (&pid));
378     return;
379   }
380 }
381
382 /**
383  * Function called by the transport for each received message.
384  *
385  * @param cls closure
386  * @param peer (claimed) identity of the other peer
387  * @param message the message
388  */
389 static void
390 notify_receive (void *cls,
391                 const struct GNUNET_PeerIdentity *peer,
392                 const struct GNUNET_MessageHeader *message)
393 {
394   if (benchmark_receive)
395   {
396     if (GNUNET_MESSAGE_TYPE_DUMMY != ntohs (message->type))
397       return;
398     if (verbosity > 0)
399       FPRINTF (stdout,
400                _("Received %u bytes from %s\n"),
401                (unsigned int) ntohs (message->size),
402                GNUNET_i2s (peer));
403     return;
404   }
405 }
406
407
408
409 static void
410 try_connect_cb (void *cls,
411                 const int result)
412 {
413   static int retries = 0;
414
415   if (GNUNET_OK == result)
416   {
417     tc_handle = NULL;
418     return;
419   }
420
421   retries++;
422   if (retries < 10)
423   {
424     if (verbosity > 0)
425       FPRINTF (stdout, _("Retrying to connect to `%s'\n"), GNUNET_i2s (&pid));
426
427     tc_handle = GNUNET_TRANSPORT_try_connect (handle, &pid, try_connect_cb,
428         NULL);
429   }
430   else
431   {
432     FPRINTF (stderr,
433              "%s",
434              _("Failed to send connect request to transport service\n"));
435     if (GNUNET_SCHEDULER_NO_TASK != end)
436       GNUNET_SCHEDULER_cancel (end);
437     end = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
438     ret = 1;
439     return;
440   }
441 }
442
443 static int
444 blacklist_cb (void *cls, const struct GNUNET_PeerIdentity *peer)
445 {
446   if (0 != memcmp (&pid, peer, sizeof(struct GNUNET_PeerIdentity)))
447   {
448     if (verbosity > 0)
449       FPRINTF (stdout,
450           _("Denying connection to `%s'\n"), GNUNET_i2s (peer));
451     return GNUNET_SYSERR;
452   }
453
454   return GNUNET_OK;
455 }
456
457
458
459 /**
460  * Function called with the result of the check if the 'transport'
461  * service is running.
462  *
463  * @param cls closure with our configuration
464  * @param result #GNUNET_YES if transport is running
465  */
466 static void
467 testservice_task (void *cls, int result)
468 {
469   ret = 1;
470
471   if (GNUNET_YES != result)
472   {
473     FPRINTF (stderr, _("Service `%s' is not running\n"), "transport");
474     return;
475   }
476
477   if (GNUNET_SERVER_MAX_MESSAGE_SIZE <= benchmark_size)
478   {
479     FPRINTF (stderr, _("Message size too big!\n"));
480     return;
481   }
482
483   if (NULL == cpid)
484   {
485     FPRINTF (stderr, _("No peer identity given\n"));
486     return;
487   }
488   if ((GNUNET_OK != GNUNET_CRYPTO_eddsa_public_key_from_string (cpid, strlen (cpid),
489               &pid.public_key)))
490   {
491     FPRINTF (stderr, _("Failed to parse peer identity `%s'\n"), cpid);
492     return;
493   }
494
495
496   if (1 == benchmark_send)
497   {
498     if (verbosity > 0)
499       FPRINTF (stderr,
500         _("Trying to send %u messages with size %u to peer `%s'\n"),
501           benchmark_count, benchmark_size, GNUNET_i2s (&pid));
502   }
503   else if (1 == benchmark_receive)
504   {
505     FPRINTF (stderr,
506         _("Trying to receive messages from peer `%s'\n"),
507         GNUNET_i2s (&pid));
508   }
509   else
510   {
511     FPRINTF (stderr, _("No operation given\n"));
512     return;
513   }
514
515   handle = GNUNET_TRANSPORT_connect (cfg, NULL, NULL, &notify_receive,
516       &notify_connect, &notify_disconnect);
517
518   if (NULL == handle)
519   {
520     FPRINTF (stderr, "%s", _("Failed to connect to transport service\n"));
521     ret = 1;
522     return;
523   }
524
525   bl_handle = GNUNET_TRANSPORT_blacklist (cfg, blacklist_cb, NULL);
526   tc_handle = GNUNET_TRANSPORT_try_connect(handle, &pid, try_connect_cb, NULL);
527
528   end = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
529                                       &shutdown_task,
530                                       NULL);
531 }
532
533
534 /**
535  * Main function that will be run by the scheduler.
536  *
537  * @param cls closure
538  * @param args remaining command-line arguments
539  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
540  * @param mycfg configuration
541  */
542 static void
543 run (void *cls,
544      char * const *args,
545      const char *cfgfile,
546      const struct GNUNET_CONFIGURATION_Handle *mycfg)
547 {
548   cfg = (struct GNUNET_CONFIGURATION_Handle *) mycfg;
549   GNUNET_CLIENT_service_test ("transport", cfg, GNUNET_TIME_UNIT_SECONDS,
550       &testservice_task, (void *) cfg);
551 }
552
553 int
554 main (int argc, char * const *argv)
555 {
556   int res;
557   benchmark_count = DEFAULT_MESSAGE_COUNT;
558   benchmark_size = DEFAULT_MESSAGE_SIZE;
559   benchmark_iterations = DEFAULT_ITERATION_COUNT;
560   benchmark_running = GNUNET_NO;
561
562   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
563
564     { 's', "send", NULL,
565       gettext_noop ("send data to peer"),
566       0, &GNUNET_GETOPT_set_one, &benchmark_send},
567     { 'r', "receive", NULL, gettext_noop
568       ("receive data from peer"), 0,
569       &GNUNET_GETOPT_set_one, &benchmark_receive},
570     { 'i', "iterations", NULL, gettext_noop
571       ("iterations"), 1,
572       &GNUNET_GETOPT_set_uint, &benchmark_iterations},
573     { 'n', "number", NULL, gettext_noop
574       ("number of messages to send"), 1,
575       &GNUNET_GETOPT_set_uint, &benchmark_count},
576     { 'm', "messagesize", NULL, gettext_noop
577       ("message size to use"), 1,
578       &GNUNET_GETOPT_set_uint, &benchmark_size},
579     { 'p', "peer", "PEER",
580       gettext_noop ("peer identity"), 1, &GNUNET_GETOPT_set_string,
581       &cpid },
582     GNUNET_GETOPT_OPTION_VERBOSE (&verbosity),
583     GNUNET_GETOPT_OPTION_END
584   };
585
586   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
587     return 2;
588
589   res = GNUNET_PROGRAM_run (argc, argv,
590                             "gnunet-transport",
591                             gettext_noop ("Direct access to transport service."),
592                             options,
593                             &run, NULL);
594   GNUNET_free((void *) argv);
595   if (GNUNET_OK == res)
596     return ret;
597   return 1;
598 }
599
600 /* end of gnunet-transport.c */