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  RC_SHARED = 0,
54  RC_MAX = 2
55 };
56 
57 
63 struct Request {
67  struct Request *next;
68 
72  struct Request *prev;
73 
78 
84  const char *hostname;
85 
91 
96 
101 };
102 
103 
107 static struct GNUNET_GNS_Handle *gns;
108 
112 static unsigned int lookups[RC_MAX];
113 
117 static unsigned int replies[RC_MAX];
118 
122 static unsigned int failures[RC_MAX];
123 
129 
133 static struct Request *act_head;
134 
138 static struct Request *act_tail;
139 
143 static struct Request *succ_head;
144 
148 static struct Request *succ_tail;
149 
153 static struct Request *todo_head;
154 
158 static struct Request *todo_tail;
159 
163 static struct GNUNET_SCHEDULER_Task *t;
164 
169 
174 
178 static unsigned int active_cnt;
179 
183 static int g2d;
184 
190 static void
191 free_request(struct Request *req)
192 {
193  if (NULL != req->lr)
195  GNUNET_free(req);
196 }
197 
198 
207 static void
208 process_result(void *cls,
209  int gns_tld,
210  uint32_t rd_count,
211  const struct GNUNET_GNSRECORD_Data *rd)
212 {
213  struct Request *req = cls;
214 
215  (void)gns_tld;
216  (void)rd_count;
217  (void)rd;
218  active_cnt--;
220  "Got response for request `%s'\n",
221  req->hostname);
222  req->lr = NULL;
225  act_tail,
226  req);
227  GNUNET_CONTAINER_DLL_insert(succ_head,
228  succ_tail,
229  req);
230  replies[req->cat]++;
231  latency_sum[req->cat]
233  req->latency);
234 }
235 
236 
242 static void
243 process_queue(void *cls)
244 {
245  struct Request *req;
246  struct GNUNET_TIME_Relative duration;
247 
248  (void)cls;
249  t = NULL;
250  /* check for expired requests */
251  while (NULL != (req = act_head))
252  {
254  if (duration.rel_value_us < timeout.rel_value_us)
255  break;
257  act_tail,
258  req);
260  "Failing request `%s' due to timeout\n",
261  req->hostname);
262  failures[req->cat]++;
263  active_cnt--;
264  free_request(req);
265  }
266  if (NULL == (req = todo_head))
267  {
268  struct GNUNET_TIME_Absolute at;
269 
270  if (NULL == (req = act_head))
271  {
273  return;
274  }
276  timeout);
278  &process_queue,
279  NULL);
280  return;
281  }
282  GNUNET_CONTAINER_DLL_remove(todo_head,
283  todo_tail,
284  req);
286  act_tail,
287  req);
288  lookups[req->cat]++;
289  active_cnt++;
292  "Starting request `%s' (%u in parallel)\n",
293  req->hostname,
294  active_cnt);
295  req->lr = GNUNET_GNS_lookup_with_tld(gns,
296  req->hostname,
297  g2d
302  req);
304  &process_queue,
305  NULL);
306 }
307 
308 
316 static int
317 compare_req(const void *c1,
318  const void *c2)
319 {
320  const struct Request *r1 = *(void **)c1;
321  const struct Request *r2 = *(void **)c2;
322 
324  return -1;
326  return 1;
327  return 0;
328 }
329 
330 
336 static void
337 do_shutdown(void *cls)
338 {
339  struct Request *req;
340  struct Request **ra[RC_MAX];
341  unsigned int rp[RC_MAX];
342 
343  (void)cls;
344  for (enum RequestCategory rc = 0; rc < RC_MAX; rc++)
345  {
346  ra[rc] = GNUNET_new_array(replies[rc],
347  struct Request *);
348  rp[rc] = 0;
349  }
350  for (req = succ_head; NULL != req; req = req->next)
351  {
352  GNUNET_assert(rp[req->cat] < replies[req->cat]);
353  ra[req->cat][rp[req->cat]++] = req;
354  }
355  for (enum RequestCategory rc = 0; rc < RC_MAX; rc++)
356  {
357  unsigned int off;
358 
359  fprintf(stdout,
360  "Category %u\n",
361  rc);
362  fprintf(stdout,
363  "\tlookups: %u replies: %u failures: %u\n",
364  lookups[rc],
365  replies[rc],
366  failures[rc]);
367  if (0 == rp[rc])
368  continue;
369  qsort(ra[rc],
370  rp[rc],
371  sizeof(struct Request *),
372  &compare_req);
374  replies[rc]);
375  fprintf(stdout,
376  "\taverage: %s\n",
378  GNUNET_YES));
379  off = rp[rc] * 50 / 100;
380  fprintf(stdout,
381  "\tmedian(50): %s\n",
383  GNUNET_YES));
384  off = rp[rc] * 75 / 100;
385  fprintf(stdout,
386  "\tquantile(75): %s\n",
388  GNUNET_YES));
389  off = rp[rc] * 90 / 100;
390  fprintf(stdout,
391  "\tquantile(90): %s\n",
393  GNUNET_YES));
394  off = rp[rc] * 99 / 100;
395  fprintf(stdout,
396  "\tquantile(99): %s\n",
398  GNUNET_YES));
399  GNUNET_free(ra[rc]);
400  }
401  if (NULL != t)
402  {
404  t = NULL;
405  }
406  while (NULL != (req = act_head))
407  {
409  act_tail,
410  req);
411  free_request(req);
412  }
413  while (NULL != (req = succ_head))
414  {
415  GNUNET_CONTAINER_DLL_remove(succ_head,
416  succ_tail,
417  req);
418  free_request(req);
419  }
420  while (NULL != (req = todo_head))
421  {
422  GNUNET_CONTAINER_DLL_remove(todo_head,
423  todo_tail,
424  req);
425  free_request(req);
426  }
427  if (NULL != gns)
428  {
430  gns = NULL;
431  }
432 }
433 
434 
441 static void
442 queue(const char *hostname,
443  enum RequestCategory cat)
444 {
445  struct Request *req;
446  const char *dot;
447  size_t hlen;
448 
449  dot = strchr(hostname,
450  (unsigned char)'.');
451  if (NULL == dot)
452  {
454  "Refusing invalid hostname `%s' (lacks '.')\n",
455  hostname);
456  return;
457  }
458  hlen = strlen(hostname) + 1;
459  req = GNUNET_malloc(sizeof(struct Request) + hlen);
460  req->cat = cat;
461  req->hostname = (char *)&req[1];
462  GNUNET_memcpy(&req[1],
463  hostname,
464  hlen);
465  GNUNET_CONTAINER_DLL_insert(todo_head,
466  todo_tail,
467  req);
468 }
469 
470 
476 static void
477 process_stdin(void *cls)
478 {
479  static struct GNUNET_TIME_Absolute last;
480  static uint64_t idot;
481  unsigned int cat;
482  char hn[256];
483  char in[270];
484 
485  (void)cls;
486  t = NULL;
487  while (NULL !=
488  fgets(in,
489  sizeof(in),
490  stdin))
491  {
492  if (strlen(in) > 0)
493  hn[strlen(in) - 1] = '\0'; /* eat newline */
494  if ((2 != sscanf(in,
495  "%u %255s",
496  &cat,
497  hn)) ||
498  (cat >= RC_MAX))
499  {
500  fprintf(stderr,
501  "Malformed input line `%s', skipping\n",
502  in);
503  continue;
504  }
505  if (0 == idot)
506  last = GNUNET_TIME_absolute_get();
507  idot++;
508  if (0 == idot % 100000)
509  {
510  struct GNUNET_TIME_Relative delta;
511 
512  delta = GNUNET_TIME_absolute_get_duration(last);
513  last = GNUNET_TIME_absolute_get();
514  fprintf(stderr,
515  "Read 100000 domain names in %s\n",
517  GNUNET_YES));
518  }
519  queue(hn,
520  (enum RequestCategory)cat);
521  }
522  fprintf(stderr,
523  "Done reading %llu domain names\n",
524  (unsigned long long)idot);
526  NULL);
527 }
528 
529 
539 static void
540 run(void *cls,
541  char *const *args,
542  const char *cfgfile,
543  const struct GNUNET_CONFIGURATION_Handle *cfg)
544 {
545  (void)cls;
546  (void)args;
547  (void)cfgfile;
549  NULL);
550  gns = GNUNET_GNS_connect(cfg);
551  if (NULL == gns)
552  {
553  GNUNET_break(0);
555  return;
556  }
558  NULL);
559 }
560 
561 
569 int
570 main(int argc,
571  char *const*argv)
572 {
573  int ret = 0;
574  struct GNUNET_GETOPT_CommandLineOption options[] = {
576  "delay",
577  "RELATIVETIME",
578  gettext_noop("how long to wait between queries"),
579  &request_delay),
581  "timeout",
582  "RELATIVETIME",
583  gettext_noop("how long to wait for an answer"),
584  &timeout),
586  "g2d",
587  gettext_noop("look for GNS2DNS records instead of ANY"),
588  &g2d),
590  };
591 
592  if (GNUNET_OK !=
593  GNUNET_STRINGS_get_utf8_args(argc, argv,
594  &argc, &argv))
595  return 2;
598  if (GNUNET_OK !=
599  GNUNET_PROGRAM_run(argc,
600  argv,
601  "gnunet-gns-benchmark",
602  "resolve GNS names and measure performance",
603  options,
604  &run,
605  NULL))
606  ret = 1;
607  GNUNET_free((void*)argv);
608  return ret;
609 }
610 
611 /* 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:1284
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:328
int GNUNET_STRINGS_get_utf8_args(int argc, char *const *argv, int *u8argc, char *const **u8argv)
Returns utf-8 encoded arguments.
Definition: strings.c:1439
#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:240
#define GNUNET_memcpy(dst, src, n)
Call memcpy() but check for n being 0 first.
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: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:517
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:1237
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:1264
void GNUNET_GNS_disconnect(struct GNUNET_GNS_Handle *handle)
Shutdown connection with the GNS service.
Definition: gns_api.c:282
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:686
#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:260
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:577
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:83
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:131
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: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: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:1214
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:956