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