GNUnet  0.10.x
gnunet-gns-benchmark.c
Go to the documentation of this file.
1 /*
2  This file is part of GNUnet
3  Copyright (C) 2018 GNUnet e.V.
4 
5  GNUnet is free software: you can redistribute it and/or modify it
6  under the terms of the GNU Affero General Public License as published
7  by the Free Software Foundation, either version 3 of the License,
8  or (at your 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  Affero General Public License for more details.
14 
15  You should have received a copy of the GNU Affero General Public License
16  along with this program. If not, see <http://www.gnu.org/licenses/>.
17 
18  SPDX-License-Identifier: AGPL3.0-or-later
19 */
25 #include "platform.h"
26 #include <gnunet_util_lib.h>
27 #include <gnunet_gnsrecord_lib.h>
28 #include <gnunet_gns_service.h>
29 
30 
34 #define DEF_REQUEST_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 1)
35 
39 #define DEF_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 1)
40 
41 
49 {
50  RC_SHARED = 0,
55  RC_MAX = 2
56 };
57 
58 
64 struct Request
65 {
66 
70  struct Request *next;
71 
75  struct Request *prev;
76 
81 
87  const char *hostname;
88 
94 
99 
104 
105 };
106 
107 
111 static struct GNUNET_GNS_Handle *gns;
112 
116 static unsigned int lookups[RC_MAX];
117 
121 static unsigned int replies[RC_MAX];
122 
126 static unsigned int failures[RC_MAX];
127 
133 
137 static struct Request *act_head;
138 
142 static struct Request *act_tail;
143 
147 static struct Request *succ_head;
148 
152 static struct Request *succ_tail;
153 
157 static struct Request *todo_head;
158 
162 static struct Request *todo_tail;
163 
167 static struct GNUNET_SCHEDULER_Task *t;
168 
173 
178 
182 static unsigned int active_cnt;
183 
187 static int g2d;
188 
194 static void
195 free_request (struct Request *req)
196 {
197  if (NULL != req->lr)
199  GNUNET_free (req);
200 }
201 
202 
211 static void
212 process_result (void *cls,
213  int gns_tld,
214  uint32_t rd_count,
215  const struct GNUNET_GNSRECORD_Data *rd)
216 {
217  struct Request *req = cls;
218 
219  (void) gns_tld;
220  (void) rd_count;
221  (void) rd;
222  active_cnt--;
224  "Got response for request `%s'\n",
225  req->hostname);
226  req->lr = NULL;
228  GNUNET_CONTAINER_DLL_remove (act_head,
229  act_tail,
230  req);
231  GNUNET_CONTAINER_DLL_insert (succ_head,
232  succ_tail,
233  req);
234  replies[req->cat]++;
235  latency_sum[req->cat]
237  req->latency);
238 }
239 
240 
246 static void
247 process_queue (void *cls)
248 {
249  struct Request *req;
250  struct GNUNET_TIME_Relative duration;
251 
252  (void) cls;
253  t = NULL;
254  /* check for expired requests */
255  while (NULL != (req = act_head))
256  {
258  if (duration.rel_value_us < timeout.rel_value_us)
259  break;
260  GNUNET_CONTAINER_DLL_remove (act_head,
261  act_tail,
262  req);
264  "Failing request `%s' due to timeout\n",
265  req->hostname);
266  failures[req->cat]++;
267  active_cnt--;
268  free_request (req);
269  }
270  if (NULL == (req = todo_head))
271  {
272  struct GNUNET_TIME_Absolute at;
273 
274  if (NULL == (req = act_head))
275  {
277  return;
278  }
280  timeout);
281  t = GNUNET_SCHEDULER_add_at (at,
282  &process_queue,
283  NULL);
284  return;
285  }
286  GNUNET_CONTAINER_DLL_remove (todo_head,
287  todo_tail,
288  req);
290  act_tail,
291  req);
292  lookups[req->cat]++;
293  active_cnt++;
296  "Starting request `%s' (%u in parallel)\n",
297  req->hostname,
298  active_cnt);
299  req->lr = GNUNET_GNS_lookup_with_tld (gns,
300  req->hostname,
301  g2d
306  req);
308  &process_queue,
309  NULL);
310 }
311 
312 
320 static int
321 compare_req (const void *c1,
322  const void *c2)
323 {
324  const struct Request *r1 = *(void **) c1;
325  const struct Request *r2 = *(void **) c2;
326 
328  return -1;
330  return 1;
331  return 0;
332 }
333 
334 
340 static void
341 do_shutdown (void *cls)
342 {
343  struct Request *req;
344  struct Request **ra[RC_MAX];
345  unsigned int rp[RC_MAX];
346 
347  (void) cls;
348  for (enum RequestCategory rc = 0;rc < RC_MAX;rc++)
349  {
350  ra[rc] = GNUNET_new_array (replies[rc],
351  struct Request *);
352  rp[rc] = 0;
353  }
354  for (req = succ_head;NULL != req; req = req->next)
355  {
356  GNUNET_assert (rp[req->cat] < replies[req->cat]);
357  ra[req->cat][rp[req->cat]++] = req;
358  }
359  for (enum RequestCategory rc = 0;rc < RC_MAX;rc++)
360  {
361  unsigned int off;
362 
363  fprintf (stdout,
364  "Category %u\n",
365  rc);
366  fprintf (stdout,
367  "\tlookups: %u replies: %u failures: %u\n",
368  lookups[rc],
369  replies[rc],
370  failures[rc]);
371  if (0 == rp[rc])
372  continue;
373  qsort (ra[rc],
374  rp[rc],
375  sizeof (struct Request *),
376  &compare_req);
378  replies[rc]);
379  fprintf (stdout,
380  "\taverage: %s\n",
382  GNUNET_YES));
383  off = rp[rc] * 50 / 100;
384  fprintf (stdout,
385  "\tmedian(50): %s\n",
387  GNUNET_YES));
388  off = rp[rc] * 75 / 100;
389  fprintf (stdout,
390  "\tquantile(75): %s\n",
392  GNUNET_YES));
393  off = rp[rc] * 90 / 100;
394  fprintf (stdout,
395  "\tquantile(90): %s\n",
397  GNUNET_YES));
398  off = rp[rc] * 99 / 100;
399  fprintf (stdout,
400  "\tquantile(99): %s\n",
402  GNUNET_YES));
403  GNUNET_free (ra[rc]);
404  }
405  if (NULL != t)
406  {
408  t = NULL;
409  }
410  while (NULL != (req = act_head))
411  {
412  GNUNET_CONTAINER_DLL_remove (act_head,
413  act_tail,
414  req);
415  free_request (req);
416  }
417  while (NULL != (req = succ_head))
418  {
419  GNUNET_CONTAINER_DLL_remove (succ_head,
420  succ_tail,
421  req);
422  free_request (req);
423  }
424  while (NULL != (req = todo_head))
425  {
426  GNUNET_CONTAINER_DLL_remove (todo_head,
427  todo_tail,
428  req);
429  free_request (req);
430  }
431  if (NULL != gns)
432  {
433  GNUNET_GNS_disconnect (gns);
434  gns = NULL;
435  }
436 }
437 
438 
445 static void
446 queue (const char *hostname,
447  enum RequestCategory cat)
448 {
449  struct Request *req;
450  const char *dot;
451  size_t hlen;
452 
453  dot = strchr (hostname,
454  (unsigned char) '.');
455  if (NULL == dot)
456  {
458  "Refusing invalid hostname `%s' (lacks '.')\n",
459  hostname);
460  return;
461  }
462  hlen = strlen (hostname) + 1;
463  req = GNUNET_malloc (sizeof (struct Request) + hlen);
464  req->cat = cat;
465  req->hostname = (char *) &req[1];
466  GNUNET_memcpy (&req[1],
467  hostname,
468  hlen);
469  GNUNET_CONTAINER_DLL_insert (todo_head,
470  todo_tail,
471  req);
472 }
473 
474 
480 static void
481 process_stdin (void *cls)
482 {
483  static struct GNUNET_TIME_Absolute last;
484  static uint64_t idot;
485  unsigned int cat;
486  char hn[256];
487  char in[270];
488 
489  (void) cls;
490  t = NULL;
491  while (NULL !=
492  fgets (in,
493  sizeof (in),
494  stdin))
495  {
496  if (strlen(in) > 0)
497  hn[strlen(in)-1] = '\0'; /* eat newline */
498  if ( (2 != sscanf (in,
499  "%u %255s",
500  &cat,
501  hn)) ||
502  (cat >= RC_MAX) )
503  {
504  fprintf (stderr,
505  "Malformed input line `%s', skipping\n",
506  in);
507  continue;
508  }
509  if (0 == idot)
510  last = GNUNET_TIME_absolute_get ();
511  idot++;
512  if (0 == idot % 100000)
513  {
514  struct GNUNET_TIME_Relative delta;
515 
516  delta = GNUNET_TIME_absolute_get_duration (last);
517  last = GNUNET_TIME_absolute_get ();
518  fprintf (stderr,
519  "Read 100000 domain names in %s\n",
521  GNUNET_YES));
522  }
523  queue (hn,
524  (enum RequestCategory) cat);
525  }
526  fprintf (stderr,
527  "Done reading %llu domain names\n",
528  (unsigned long long) idot);
530  NULL);
531 }
532 
533 
543 static void
544 run (void *cls,
545  char *const *args,
546  const char *cfgfile,
547  const struct GNUNET_CONFIGURATION_Handle *cfg)
548 {
549  (void) cls;
550  (void) args;
551  (void) cfgfile;
553  NULL);
554  gns = GNUNET_GNS_connect (cfg);
555  if (NULL == gns)
556  {
557  GNUNET_break (0);
559  return;
560  }
562  NULL);
563 }
564 
565 
573 int
574 main (int argc,
575  char *const*argv)
576 {
577  int ret = 0;
578  struct GNUNET_GETOPT_CommandLineOption options[] = {
580  "delay",
581  "RELATIVETIME",
582  gettext_noop ("how long to wait between queries"),
583  &request_delay),
585  "timeout",
586  "RELATIVETIME",
587  gettext_noop ("how long to wait for an answer"),
588  &timeout),
590  "g2d",
591  gettext_noop ("look for GNS2DNS records instead of ANY"),
592  &g2d),
594  };
595 
596  if (GNUNET_OK !=
597  GNUNET_STRINGS_get_utf8_args (argc, argv,
598  &argc, &argv))
599  return 2;
602  if (GNUNET_OK !=
603  GNUNET_PROGRAM_run (argc,
604  argv,
605  "gnunet-gns-benchmark",
606  "resolve GNS names and measure performance",
607  options,
608  &run,
609  NULL))
610  ret = 1;
611  GNUNET_free ((void*) argv);
612  return ret;
613 }
614 
615 /* end of gnunet-gns-benchmark.c */
struct GNUNET_GETOPT_CommandLineOption GNUNET_GETOPT_option_relative_time(char shortName, const char *name, const char *argumentHelp, const char *description, struct GNUNET_TIME_Relative *val)
Allow user to specify a struct GNUNET_TIME_Relative (using human-readable "fancy" time)...
#define GNUNET_CONTAINER_DLL_remove(head, tail, element)
Remove an element from a DLL.
Connection to the GNS service.
Definition: gns_api.h:35
static void free_request(struct Request *req)
Free req and data structures reachable from it.
static struct Request * todo_head
Yet to be started requests are kept in a DLL.
static int compare_req(const void *c1, const void *c2)
Compare two requests by latency for qsort().
static void run(void *cls, char *const *args, const char *cfgfile, const struct GNUNET_CONFIGURATION_Handle *cfg)
Process requests from the queue, then if the queue is not empty, try again.
uint64_t rel_value_us
The actual value.
#define GNUNET_CONTAINER_DLL_insert(head, tail, element)
Insert an element at the head of a DLL.
char * hostname
Hostname we are resolving.
const char * hostname
Hostname we are resolving, allocated at the end of this struct (optimizing memory consumption by redu...
struct GNUNET_SCHEDULER_Task * GNUNET_SCHEDULER_add_shutdown(GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
Schedule a new task to be run on shutdown, that is when a CTRL-C signal is received, or when GNUNET_SCHEDULER_shutdown() is being invoked.
Definition: scheduler.c:1293
static void process_result(void *cls, int gns_tld, uint32_t rd_count, const struct GNUNET_GNSRECORD_Data *rd)
Function called with the result of a GNS resolution.
void * GNUNET_GNS_lookup_with_tld_cancel(struct GNUNET_GNS_LookupWithTldRequest *ltr)
Cancel pending lookup request.
Definition: gns_tld_api.c:330
int GNUNET_STRINGS_get_utf8_args(int argc, char *const *argv, int *u8argc, char *const **u8argv)
Returns utf-8 encoded arguments.
Definition: strings.c:1521
#define GNUNET_assert(cond)
Use this for fatal errors that cannot be handled.
#define GNUNET_GNSRECORD_TYPE_ANY
Record type indicating any record/&#39;*&#39;.
static struct GNUNET_TIME_Relative request_delay
Delay between requests.
static struct Request * todo_tail
Yet to be started requests are kept in a DLL.
struct GNUNET_GNS_LookupWithTldRequest * GNUNET_GNS_lookup_with_tld(struct GNUNET_GNS_Handle *handle, const char *name, uint32_t type, enum GNUNET_GNS_LocalOptions options, GNUNET_GNS_LookupResultProcessor2 proc, void *proc_cls)
Perform an asynchronous lookup operation on the GNS, determining the zone using the TLD of the given ...
Definition: gns_tld_api.c:242
static unsigned int failures[RC_MAX]
Number of replies we got per category.
#define GNUNET_OK
Named constants for return values.
Definition: gnunet_common.h:78
struct GNUNET_TIME_Absolute GNUNET_TIME_absolute_add(struct GNUNET_TIME_Absolute start, struct GNUNET_TIME_Relative duration)
Add a given relative duration to the given start time.
Definition: time.c:393
Definition of a command line option.
enum RequestCategory cat
Category of the request.
void GNUNET_SCHEDULER_shutdown(void)
Request the shutdown of a scheduler.
Definition: scheduler.c:524
static int ret
Final status code.
Definition: gnunet-arm.c:89
#define GNUNET_break(cond)
Use this for internal assertion violations that are not fatal (can be handled) but should not occur...
struct GNUNET_GETOPT_CommandLineOption GNUNET_GETOPT_OPTION_END
Definition: 002.c:13
static struct GNUNET_GNS_Handle * gns
GNS handle.
struct GNUNET_SCHEDULER_Task * GNUNET_SCHEDULER_add_delayed(struct GNUNET_TIME_Relative delay, GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
Schedule a new task to be run with a specified delay.
Definition: scheduler.c:1246
static unsigned int replies[RC_MAX]
Number of replies we got per category.
#define GNUNET_memcpy(dst, src, n)
struct GNUNET_SCHEDULER_Task * GNUNET_SCHEDULER_add_now(GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
Schedule a new task to be run as soon as possible.
Definition: scheduler.c:1273
void GNUNET_GNS_disconnect(struct GNUNET_GNS_Handle *handle)
Shutdown connection with the GNS service.
Definition: gns_api.c:285
static void process_stdin(void *cls)
Begin processing hostnames from stdin.
struct GNUNET_CONTAINER_HeapNode * hn
Requests are kept in a heap while waiting to be resolved.
#define GNUNET_GNSRECORD_TYPE_GNS2DNS
Record type for delegation to DNS.
const char * GNUNET_STRINGS_relative_time_to_string(struct GNUNET_TIME_Relative delta, int do_round)
Give relative time in human-readable fancy format.
Definition: strings.c:727
#define GNUNET_new_array(n, type)
Allocate a size n array with structs or unions of the given type.
#define DEF_REQUEST_DELAY
How long do we wait at least between requests by default?
static void do_shutdown(void *cls)
Output statistics, then clean up and terminate the process.
int main(int argc, char *const *argv)
Call with list of names with numeric category to query.
static struct Request * succ_head
Completed successful requests are kept in a DLL.
struct GNUNET_GNS_Handle * GNUNET_GNS_connect(const struct GNUNET_CONFIGURATION_Handle *cfg)
Initialize the connection with the GNS service.
Definition: gns_api.c:263
static void queue(const char *hostname, enum RequestCategory cat)
Add hostname to the list of requests to be made.
static struct GNUNET_TIME_Relative latency_sum[RC_MAX]
Sum of the observed latencies of successful queries, per category.
static void process_queue(void *cls)
Process request from the queue.
struct GNUNET_TIME_Absolute GNUNET_TIME_absolute_get(void)
Get the current time.
Definition: time.c:118
static unsigned int lookups[RC_MAX]
Number of lookups we performed overall per category.
static struct GNUNET_CONFIGURATION_Handle * cfg
Our configuration.
Definition: gnunet-arm.c:104
static struct GNUNET_SCHEDULER_Task * t
Main task.
struct Request * prev
Requests are kept in a DLL.
static struct GNUNET_TIME_Relative timeout
Timeout for requests.
#define GNUNET_CONTAINER_DLL_insert_tail(head, tail, element)
Insert an element at the tail of a DLL.
Request we should make.
struct GNUNET_TIME_Relative GNUNET_TIME_relative_add(struct GNUNET_TIME_Relative a1, struct GNUNET_TIME_Relative a2)
Add relative times together.
Definition: time.c:576
struct GNUNET_GNS_LookupWithTldRequest * lr
Socket used to make the request, NULL if not active.
struct GNUNET_TIME_Absolute op_start_time
While we are fetching the record, the value is set to the starting time of the GNS operation...
RequestCategory
We distinguish between different categories of requests, for which we track statistics separately...
configuration data
Definition: configuration.c:85
static struct Request * act_tail
Active requests are kept in a DLL.
struct GNUNET_TIME_Relative GNUNET_TIME_absolute_get_duration(struct GNUNET_TIME_Absolute whence)
Get the duration of an operation as the difference of the current time and the given start time "henc...
Definition: time.c:373
struct GNUNET_TIME_Relative latency
Observed latency, set once we got a reply.
struct GNUNET_GETOPT_CommandLineOption GNUNET_GETOPT_option_flag(char shortName, const char *name, const char *description, int *val)
Allow user to specify a flag (which internally means setting an integer to 1/GNUNET_YES/GNUNET_OK.
#define GNUNET_log(kind,...)
Entry in list of pending tasks.
Definition: scheduler.c:134
struct Request * next
Requests are kept in a DLL.
Must be last and match number of categories.
static char * rp
Relying party.
#define DEF_TIMEOUT
How long do we wait until we consider a request failed by default?
int GNUNET_PROGRAM_run(int argc, char *const *argv, const char *binaryName, const char *binaryHelp, const struct GNUNET_GETOPT_CommandLineOption *options, GNUNET_PROGRAM_Main task, void *task_cls)
Run a standard GNUnet command startup sequence (initialize loggers and configuration, parse options).
Definition: program.c:361
static int g2d
Look for GNS2DNS records specifically?
struct GNUNET_TIME_Relative GNUNET_TIME_relative_divide(struct GNUNET_TIME_Relative rel, unsigned long long factor)
Divide relative time by a given factor.
Definition: time.c:525
Time for absolute times used by GNUnet, in microseconds.
static unsigned int active_cnt
Number of requests we have concurrently active.
#define GNUNET_YES
Definition: gnunet_common.h:80
Defaults, look in cache, then in DHT.
static struct Request * act_head
Active requests are kept in a DLL.
struct GNUNET_SCHEDULER_Task * GNUNET_SCHEDULER_add_at(struct GNUNET_TIME_Absolute at, GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
Schedule a new task to be run at the specified time.
Definition: scheduler.c:1223
static struct Request * succ_tail
Completed successful requests are kept in a DLL.
Handle to a lookup request.
Definition: gns_tld_api.c:44
#define GNUNET_malloc(size)
Wrapper around malloc.
#define GNUNET_free(ptr)
Wrapper around free.
Time for relative time used by GNUnet, in microseconds.
#define gettext_noop(String)
Definition: gettext.h:69
void * GNUNET_SCHEDULER_cancel(struct GNUNET_SCHEDULER_Task *task)
Cancel the task with the specified identifier.
Definition: scheduler.c:965