GNUnet  0.11.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 ( \
35  GNUNET_TIME_UNIT_MILLISECONDS, 1)
36 
40 #define DEF_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 1)
41 
42 
50 {
51  RC_SHARED = 0,
56  RC_MAX = 2
57 };
58 
59 
65 struct Request
66 {
70  struct Request *next;
71 
75  struct Request *prev;
76 
81 
87  const char *hostname;
88 
94 
99 
104 };
105 
106 
110 static struct GNUNET_GNS_Handle *gns;
111 
115 static unsigned int lookups[RC_MAX];
116 
120 static unsigned int replies[RC_MAX];
121 
125 static unsigned int failures[RC_MAX];
126 
132 
136 static struct Request *act_head;
137 
141 static struct Request *act_tail;
142 
146 static struct Request *succ_head;
147 
151 static struct Request *succ_tail;
152 
156 static struct Request *todo_head;
157 
161 static struct Request *todo_tail;
162 
166 static struct GNUNET_SCHEDULER_Task *t;
167 
172 
177 
181 static unsigned int active_cnt;
182 
186 static int g2d;
187 
193 static void
194 free_request (struct Request *req)
195 {
196  if (NULL != req->lr)
198  GNUNET_free (req);
199 }
200 
201 
210 static void
211 process_result (void *cls,
212  int gns_tld,
213  uint32_t rd_count,
214  const struct GNUNET_GNSRECORD_Data *rd)
215 {
216  struct Request *req = cls;
217 
218  (void) gns_tld;
219  (void) rd_count;
220  (void) rd;
221  active_cnt--;
223  "Got response for request `%s'\n",
224  req->hostname);
225  req->lr = NULL;
227  GNUNET_CONTAINER_DLL_remove (act_head,
228  act_tail,
229  req);
230  GNUNET_CONTAINER_DLL_insert (succ_head,
231  succ_tail,
232  req);
233  replies[req->cat]++;
234  latency_sum[req->cat]
236  req->latency);
237 }
238 
239 
245 static void
246 process_queue (void *cls)
247 {
248  struct Request *req;
249  struct GNUNET_TIME_Relative duration;
250 
251  (void) cls;
252  t = NULL;
253  /* check for expired requests */
254  while (NULL != (req = act_head))
255  {
257  if (duration.rel_value_us < timeout.rel_value_us)
258  break;
259  GNUNET_CONTAINER_DLL_remove (act_head,
260  act_tail,
261  req);
263  "Failing request `%s' due to timeout\n",
264  req->hostname);
265  failures[req->cat]++;
266  active_cnt--;
267  free_request (req);
268  }
269  if (NULL == (req = todo_head))
270  {
271  struct GNUNET_TIME_Absolute at;
272 
273  if (NULL == (req = act_head))
274  {
276  return;
277  }
279  timeout);
280  t = GNUNET_SCHEDULER_add_at (at,
281  &process_queue,
282  NULL);
283  return;
284  }
285  GNUNET_CONTAINER_DLL_remove (todo_head,
286  todo_tail,
287  req);
289  act_tail,
290  req);
291  lookups[req->cat]++;
292  active_cnt++;
295  "Starting request `%s' (%u in parallel)\n",
296  req->hostname,
297  active_cnt);
298  req->lr = GNUNET_GNS_lookup_with_tld (gns,
299  req->hostname,
300  g2d
305  req);
307  &process_queue,
308  NULL);
309 }
310 
311 
319 static int
320 compare_req (const void *c1,
321  const void *c2)
322 {
323  const struct Request *r1 = *(void **) c1;
324  const struct Request *r2 = *(void **) c2;
325 
327  return -1;
329  return 1;
330  return 0;
331 }
332 
333 
339 static void
340 do_shutdown (void *cls)
341 {
342  struct Request *req;
343  struct Request **ra[RC_MAX];
344  unsigned int rp[RC_MAX];
345 
346  (void) cls;
347  for (enum RequestCategory rc = 0; rc < RC_MAX; rc++)
348  {
349  ra[rc] = GNUNET_new_array (replies[rc],
350  struct Request *);
351  rp[rc] = 0;
352  }
353  for (req = succ_head; NULL != req; req = req->next)
354  {
355  GNUNET_assert (rp[req->cat] < replies[req->cat]);
356  ra[req->cat][rp[req->cat]++] = req;
357  }
358  for (enum RequestCategory rc = 0; rc < RC_MAX; rc++)
359  {
360  unsigned int off;
361 
362  fprintf (stdout,
363  "Category %u\n",
364  rc);
365  fprintf (stdout,
366  "\tlookups: %u replies: %u failures: %u\n",
367  lookups[rc],
368  replies[rc],
369  failures[rc]);
370  if (0 == rp[rc])
371  continue;
372  qsort (ra[rc],
373  rp[rc],
374  sizeof(struct Request *),
375  &compare_req);
377  replies[rc]);
378  fprintf (stdout,
379  "\taverage: %s\n",
381  GNUNET_YES));
382  off = rp[rc] * 50 / 100;
383  fprintf (stdout,
384  "\tmedian(50): %s\n",
386  GNUNET_YES));
387  off = rp[rc] * 75 / 100;
388  fprintf (stdout,
389  "\tquantile(75): %s\n",
391  GNUNET_YES));
392  off = rp[rc] * 90 / 100;
393  fprintf (stdout,
394  "\tquantile(90): %s\n",
396  GNUNET_YES));
397  off = rp[rc] * 99 / 100;
398  fprintf (stdout,
399  "\tquantile(99): %s\n",
401  GNUNET_YES));
402  GNUNET_free (ra[rc]);
403  }
404  if (NULL != t)
405  {
407  t = NULL;
408  }
409  while (NULL != (req = act_head))
410  {
411  GNUNET_CONTAINER_DLL_remove (act_head,
412  act_tail,
413  req);
414  free_request (req);
415  }
416  while (NULL != (req = succ_head))
417  {
418  GNUNET_CONTAINER_DLL_remove (succ_head,
419  succ_tail,
420  req);
421  free_request (req);
422  }
423  while (NULL != (req = todo_head))
424  {
425  GNUNET_CONTAINER_DLL_remove (todo_head,
426  todo_tail,
427  req);
428  free_request (req);
429  }
430  if (NULL != gns)
431  {
432  GNUNET_GNS_disconnect (gns);
433  gns = NULL;
434  }
435 }
436 
437 
444 static void
445 queue (const char *hostname,
446  enum RequestCategory cat)
447 {
448  struct Request *req;
449  const char *dot;
450  size_t hlen;
451 
452  dot = strchr (hostname,
453  (unsigned char) '.');
454  if (NULL == dot)
455  {
457  "Refusing invalid hostname `%s' (lacks '.')\n",
458  hostname);
459  return;
460  }
461  hlen = strlen (hostname) + 1;
462  req = GNUNET_malloc (sizeof(struct Request) + hlen);
463  req->cat = cat;
464  req->hostname = (char *) &req[1];
465  GNUNET_memcpy (&req[1],
466  hostname,
467  hlen);
468  GNUNET_CONTAINER_DLL_insert (todo_head,
469  todo_tail,
470  req);
471 }
472 
473 
479 static void
480 process_stdin (void *cls)
481 {
482  static struct GNUNET_TIME_Absolute last;
483  static uint64_t idot;
484  unsigned int cat;
485  char hn[256];
486  char in[270];
487 
488  (void) cls;
489  t = NULL;
490  while (NULL !=
491  fgets (in,
492  sizeof(in),
493  stdin))
494  {
495  if (strlen (in) > 0)
496  hn[strlen (in) - 1] = '\0'; /* eat newline */
497  if ((2 != sscanf (in,
498  "%u %255s",
499  &cat,
500  hn)) ||
501  (cat >= RC_MAX))
502  {
503  fprintf (stderr,
504  "Malformed input line `%s', skipping\n",
505  in);
506  continue;
507  }
508  if (0 == idot)
509  last = GNUNET_TIME_absolute_get ();
510  idot++;
511  if (0 == idot % 100000)
512  {
513  struct GNUNET_TIME_Relative delta;
514 
515  delta = GNUNET_TIME_absolute_get_duration (last);
516  last = GNUNET_TIME_absolute_get ();
517  fprintf (stderr,
518  "Read 100000 domain names in %s\n",
520  GNUNET_YES));
521  }
522  queue (hn,
523  (enum RequestCategory) cat);
524  }
525  fprintf (stderr,
526  "Done reading %llu domain names\n",
527  (unsigned long long) idot);
529  NULL);
530 }
531 
532 
542 static void
543 run (void *cls,
544  char *const *args,
545  const char *cfgfile,
546  const struct GNUNET_CONFIGURATION_Handle *cfg)
547 {
548  (void) cls;
549  (void) args;
550  (void) cfgfile;
552  NULL);
553  gns = GNUNET_GNS_connect (cfg);
554  if (NULL == gns)
555  {
556  GNUNET_break (0);
558  return;
559  }
561  NULL);
562 }
563 
564 
572 int
573 main (int argc,
574  char *const*argv)
575 {
576  int ret = 0;
577  struct GNUNET_GETOPT_CommandLineOption options[] = {
579  "delay",
580  "RELATIVETIME",
581  gettext_noop (
582  "how long to wait between queries"),
583  &request_delay),
585  "timeout",
586  "RELATIVETIME",
587  gettext_noop (
588  "how long to wait for an answer"),
589  &timeout),
591  "g2d",
592  gettext_noop (
593  "look for GNS2DNS records instead of ANY"),
594  &g2d),
596  };
597 
598  if (GNUNET_OK !=
599  GNUNET_STRINGS_get_utf8_args (argc, argv,
600  &argc, &argv))
601  return 2;
604  if (GNUNET_OK !=
605  GNUNET_PROGRAM_run (argc,
606  argv,
607  "gnunet-gns-benchmark",
608  "resolve GNS names and measure performance",
609  options,
610  &run,
611  NULL))
612  ret = 1;
613  GNUNET_free ((void*) argv);
614  return ret;
615 }
616 
617 
618 /* 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.
static const struct GNUNET_CONFIGURATION_Handle * cfg
Configuration we are using.
Definition: gnunet-abd.c:36
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:1300
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:329
int GNUNET_STRINGS_get_utf8_args(int argc, char *const *argv, int *u8argc, char *const **u8argv)
Returns utf-8 encoded arguments.
Definition: strings.c:1438
#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:241
#define GNUNET_memcpy(dst, src, n)
Call memcpy() but check for n being 0 first.
static int ret
Return value of the commandline.
Definition: gnunet-abd.c:81
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:75
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:395
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:526
#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:1253
static unsigned int replies[RC_MAX]
Number of replies we got per category.
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:1280
void GNUNET_GNS_disconnect(struct GNUNET_GNS_Handle *handle)
Shutdown connection with the GNS service.
Definition: gns_api.c:284
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:687
#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:262
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_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:579
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:375
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:367
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:527
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:77
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:1230
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:966