adding a GNUNET_memcmp_priv for constant-time comparing of data; fixes #6152 (modulo...
[oweals/gnunet.git] / src / set / gnunet-set-profiler.c
1 /*
2       This file is part of GNUnet
3       Copyright (C) 2013 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  */
20
21 /**
22  * @file set/gnunet-set-profiler.c
23  * @brief profiling tool for set
24  * @author Florian Dold
25  */
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "gnunet_statistics_service.h"
29 #include "gnunet_set_service.h"
30 #include "gnunet_testbed_service.h"
31
32
33 static int ret;
34
35 static unsigned int num_a = 5;
36 static unsigned int num_b = 5;
37 static unsigned int num_c = 20;
38
39 static char *op_str = "union";
40
41 const static struct GNUNET_CONFIGURATION_Handle *config;
42
43 struct SetInfo
44 {
45   char *id;
46   struct GNUNET_SET_Handle *set;
47   struct GNUNET_SET_OperationHandle *oh;
48   struct GNUNET_CONTAINER_MultiHashMap *sent;
49   struct GNUNET_CONTAINER_MultiHashMap *received;
50   int done;
51 } info1, info2;
52
53 static struct GNUNET_CONTAINER_MultiHashMap *common_sent;
54
55 static struct GNUNET_HashCode app_id;
56
57 static struct GNUNET_PeerIdentity local_peer;
58
59 static struct GNUNET_SET_ListenHandle *set_listener;
60
61 static int byzantine;
62 static unsigned int force_delta;
63 static unsigned int force_full;
64 static unsigned int element_size = 32;
65
66 /**
67  * Handle to the statistics service.
68  */
69 static struct GNUNET_STATISTICS_Handle *statistics;
70
71 /**
72  * The profiler will write statistics
73  * for all peers to the file with this name.
74  */
75 static char *statistics_filename;
76
77 /**
78  * The profiler will write statistics
79  * for all peers to this file.
80  */
81 static FILE *statistics_file;
82
83
84 static int
85 map_remove_iterator (void *cls,
86                      const struct GNUNET_HashCode *key,
87                      void *value)
88 {
89   struct GNUNET_CONTAINER_MultiHashMap *m = cls;
90   int ret;
91
92   GNUNET_assert (NULL != key);
93
94   ret = GNUNET_CONTAINER_multihashmap_remove_all (m, key);
95   if (GNUNET_OK != ret)
96     printf ("spurious element\n");
97   return GNUNET_YES;
98 }
99
100
101 /**
102  * Callback function to process statistic values.
103  *
104  * @param cls closure
105  * @param subsystem name of subsystem that created the statistic
106  * @param name the name of the datum
107  * @param value the current value
108  * @param is_persistent #GNUNET_YES if the value is persistent, #GNUNET_NO if not
109  * @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration
110  */
111 static int
112 statistics_result (void *cls,
113                    const char *subsystem,
114                    const char *name,
115                    uint64_t value,
116                    int is_persistent)
117 {
118   if (NULL != statistics_file)
119   {
120     fprintf (statistics_file, "%s\t%s\t%lu\n", subsystem, name, (unsigned
121                                                                  long) value);
122   }
123   return GNUNET_OK;
124 }
125
126
127 static void
128 statistics_done (void *cls,
129                  int success)
130 {
131   GNUNET_assert (GNUNET_YES == success);
132   if (NULL != statistics_file)
133     fclose (statistics_file);
134   GNUNET_SCHEDULER_shutdown ();
135 }
136
137
138 static void
139 check_all_done (void)
140 {
141   if ((info1.done == GNUNET_NO) || (info2.done == GNUNET_NO))
142     return;
143
144   GNUNET_CONTAINER_multihashmap_iterate (info1.received, map_remove_iterator,
145                                          info2.sent);
146   GNUNET_CONTAINER_multihashmap_iterate (info2.received, map_remove_iterator,
147                                          info1.sent);
148
149   printf ("set a: %d missing elements\n", GNUNET_CONTAINER_multihashmap_size (
150             info1.sent));
151   printf ("set b: %d missing elements\n", GNUNET_CONTAINER_multihashmap_size (
152             info2.sent));
153
154   if (NULL == statistics_filename)
155   {
156     GNUNET_SCHEDULER_shutdown ();
157     return;
158   }
159
160   statistics_file = fopen (statistics_filename, "w");
161   GNUNET_STATISTICS_get (statistics, NULL, NULL,
162                          &statistics_done,
163                          &statistics_result, NULL);
164 }
165
166
167 static void
168 set_result_cb (void *cls,
169                const struct GNUNET_SET_Element *element,
170                uint64_t current_size,
171                enum GNUNET_SET_Status status)
172 {
173   struct SetInfo *info = cls;
174   struct GNUNET_HashCode hash;
175
176   GNUNET_assert (GNUNET_NO == info->done);
177   switch (status)
178   {
179   case GNUNET_SET_STATUS_DONE:
180   case GNUNET_SET_STATUS_HALF_DONE:
181     info->done = GNUNET_YES;
182     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "set %s done\n", info->id);
183     check_all_done ();
184     info->oh = NULL;
185     return;
186
187   case GNUNET_SET_STATUS_FAILURE:
188     info->oh = NULL;
189     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "failure\n");
190     GNUNET_SCHEDULER_shutdown ();
191     return;
192
193   case GNUNET_SET_STATUS_ADD_LOCAL:
194     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "set %s: local element\n", info->id);
195     break;
196
197   case GNUNET_SET_STATUS_ADD_REMOTE:
198     GNUNET_CRYPTO_hash (element->data, element->size, &hash);
199     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "set %s: remote element %s\n", info->id,
200                 GNUNET_h2s (&hash));
201     // XXX: record and check
202     return;
203
204   default:
205     GNUNET_assert (0);
206   }
207
208   if (element->size != element_size)
209   {
210     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
211                 "wrong element size: %u, expected %u\n",
212                 element->size,
213                 (unsigned int) sizeof(struct GNUNET_HashCode));
214     GNUNET_assert (0);
215   }
216
217   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "set %s: got element (%s)\n",
218               info->id, GNUNET_h2s (element->data));
219   GNUNET_assert (NULL != element->data);
220   struct GNUNET_HashCode data_hash;
221   GNUNET_CRYPTO_hash (element->data, element_size, &data_hash);
222   GNUNET_CONTAINER_multihashmap_put (info->received,
223                                      &data_hash, NULL,
224                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
225 }
226
227
228 static void
229 set_listen_cb (void *cls,
230                const struct GNUNET_PeerIdentity *other_peer,
231                const struct GNUNET_MessageHeader *context_msg,
232                struct GNUNET_SET_Request *request)
233 {
234   /* max. 2 options plus terminator */
235   struct GNUNET_SET_Option opts[3] = { { 0 } };
236   unsigned int n_opts = 0;
237
238   if (NULL == request)
239   {
240     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
241                 "listener failed\n");
242     return;
243   }
244   GNUNET_assert (NULL == info2.oh);
245   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
246               "set listen cb called\n");
247   if (byzantine)
248   {
249     opts[n_opts++] = (struct GNUNET_SET_Option) { .type =
250                                                     GNUNET_SET_OPTION_BYZANTINE };
251   }
252   GNUNET_assert (! (force_full && force_delta));
253   if (force_full)
254   {
255     opts[n_opts++] = (struct GNUNET_SET_Option) { .type =
256                                                     GNUNET_SET_OPTION_FORCE_FULL };
257   }
258   if (force_delta)
259   {
260     opts[n_opts++] = (struct GNUNET_SET_Option) { .type =
261                                                     GNUNET_SET_OPTION_FORCE_DELTA };
262   }
263
264   opts[n_opts].type = 0;
265   info2.oh = GNUNET_SET_accept (request, GNUNET_SET_RESULT_SYMMETRIC,
266                                 opts,
267                                 set_result_cb, &info2);
268   GNUNET_SET_commit (info2.oh, info2.set);
269 }
270
271
272 static int
273 set_insert_iterator (void *cls,
274                      const struct GNUNET_HashCode *key,
275                      void *value)
276 {
277   struct GNUNET_SET_Handle *set = cls;
278   struct GNUNET_SET_Element el;
279
280   el.element_type = 0;
281   el.data = value;
282   el.size = element_size;
283   GNUNET_SET_add_element (set, &el, NULL, NULL);
284   return GNUNET_YES;
285 }
286
287
288 static void
289 handle_shutdown (void *cls)
290 {
291   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
292               "Shutting down set profiler\n");
293   if (NULL != set_listener)
294   {
295     GNUNET_SET_listen_cancel (set_listener);
296     set_listener = NULL;
297   }
298   if (NULL != info1.oh)
299   {
300     GNUNET_SET_operation_cancel (info1.oh);
301     info1.oh = NULL;
302   }
303   if (NULL != info2.oh)
304   {
305     GNUNET_SET_operation_cancel (info2.oh);
306     info2.oh = NULL;
307   }
308   if (NULL != info1.set)
309   {
310     GNUNET_SET_destroy (info1.set);
311     info1.set = NULL;
312   }
313   if (NULL != info2.set)
314   {
315     GNUNET_SET_destroy (info2.set);
316     info2.set = NULL;
317   }
318   GNUNET_STATISTICS_destroy (statistics, GNUNET_NO);
319 }
320
321
322 static void
323 run (void *cls,
324      const struct GNUNET_CONFIGURATION_Handle *cfg,
325      struct GNUNET_TESTING_Peer *peer)
326 {
327   unsigned int i;
328   struct GNUNET_HashCode hash;
329   /* max. 2 options plus terminator */
330   struct GNUNET_SET_Option opts[3] = { { 0 } };
331   unsigned int n_opts = 0;
332
333   config = cfg;
334
335   GNUNET_assert (element_size > 0);
336
337   if (GNUNET_OK != GNUNET_CRYPTO_get_peer_identity (cfg, &local_peer))
338   {
339     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "could not retrieve host identity\n");
340     ret = 0;
341     return;
342   }
343
344   statistics = GNUNET_STATISTICS_create ("set-profiler", cfg);
345
346   GNUNET_SCHEDULER_add_shutdown (&handle_shutdown, NULL);
347
348   info1.id = "a";
349   info2.id = "b";
350
351   info1.sent = GNUNET_CONTAINER_multihashmap_create (num_a + 1, GNUNET_NO);
352   info2.sent = GNUNET_CONTAINER_multihashmap_create (num_b + 1, GNUNET_NO);
353   common_sent = GNUNET_CONTAINER_multihashmap_create (num_c + 1, GNUNET_NO);
354
355   info1.received = GNUNET_CONTAINER_multihashmap_create (num_a + 1, GNUNET_NO);
356   info2.received = GNUNET_CONTAINER_multihashmap_create (num_b + 1, GNUNET_NO);
357
358   for (i = 0; i < num_a; i++)
359   {
360     char *data = GNUNET_malloc (element_size);
361     GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, data, element_size);
362     GNUNET_CRYPTO_hash (data, element_size, &hash);
363     GNUNET_CONTAINER_multihashmap_put (info1.sent, &hash, data,
364                                        GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
365   }
366
367   for (i = 0; i < num_b; i++)
368   {
369     char *data = GNUNET_malloc (element_size);
370     GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, data, element_size);
371     GNUNET_CRYPTO_hash (data, element_size, &hash);
372     GNUNET_CONTAINER_multihashmap_put (info2.sent, &hash, data,
373                                        GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
374   }
375
376   for (i = 0; i < num_c; i++)
377   {
378     char *data = GNUNET_malloc (element_size);
379     GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, data, element_size);
380     GNUNET_CRYPTO_hash (data, element_size, &hash);
381     GNUNET_CONTAINER_multihashmap_put (common_sent, &hash, data,
382                                        GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
383   }
384
385   GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_STRONG, &app_id);
386
387   /* FIXME: also implement intersection etc. */
388   info1.set = GNUNET_SET_create (config, GNUNET_SET_OPERATION_UNION);
389   info2.set = GNUNET_SET_create (config, GNUNET_SET_OPERATION_UNION);
390
391   GNUNET_CONTAINER_multihashmap_iterate (info1.sent, set_insert_iterator,
392                                          info1.set);
393   GNUNET_CONTAINER_multihashmap_iterate (info2.sent, set_insert_iterator,
394                                          info2.set);
395   GNUNET_CONTAINER_multihashmap_iterate (common_sent, set_insert_iterator,
396                                          info1.set);
397   GNUNET_CONTAINER_multihashmap_iterate (common_sent, set_insert_iterator,
398                                          info2.set);
399
400   set_listener = GNUNET_SET_listen (config, GNUNET_SET_OPERATION_UNION,
401                                     &app_id, set_listen_cb, NULL);
402
403
404   if (byzantine)
405   {
406     opts[n_opts++] = (struct GNUNET_SET_Option) { .type =
407                                                     GNUNET_SET_OPTION_BYZANTINE };
408   }
409   GNUNET_assert (! (force_full && force_delta));
410   if (force_full)
411   {
412     opts[n_opts++] = (struct GNUNET_SET_Option) { .type =
413                                                     GNUNET_SET_OPTION_FORCE_FULL };
414   }
415   if (force_delta)
416   {
417     opts[n_opts++] = (struct GNUNET_SET_Option) { .type =
418                                                     GNUNET_SET_OPTION_FORCE_DELTA };
419   }
420
421   opts[n_opts].type = 0;
422
423   info1.oh = GNUNET_SET_prepare (&local_peer, &app_id, NULL,
424                                  GNUNET_SET_RESULT_SYMMETRIC,
425                                  opts,
426                                  set_result_cb, &info1);
427   GNUNET_SET_commit (info1.oh, info1.set);
428   GNUNET_SET_destroy (info1.set);
429   info1.set = NULL;
430 }
431
432
433 static void
434 pre_run (void *cls, char *const *args, const char *cfgfile,
435          const struct GNUNET_CONFIGURATION_Handle *cfg)
436 {
437   if (0 != GNUNET_TESTING_peer_run ("set-profiler",
438                                     cfgfile,
439                                     &run, NULL))
440     ret = 2;
441 }
442
443
444 int
445 main (int argc, char **argv)
446 {
447   struct GNUNET_GETOPT_CommandLineOption options[] = {
448     GNUNET_GETOPT_option_uint ('A',
449                                "num-first",
450                                NULL,
451                                gettext_noop ("number of values"),
452                                &num_a),
453
454     GNUNET_GETOPT_option_uint ('B',
455                                "num-second",
456                                NULL,
457                                gettext_noop ("number of values"),
458                                &num_b),
459
460     GNUNET_GETOPT_option_flag ('b',
461                                "byzantine",
462                                gettext_noop ("use byzantine mode"),
463                                &byzantine),
464
465     GNUNET_GETOPT_option_uint ('f',
466                                "force-full",
467                                NULL,
468                                gettext_noop ("force sending full set"),
469                                &force_full),
470
471     GNUNET_GETOPT_option_uint ('d',
472                                "force-delta",
473                                NULL,
474                                gettext_noop ("number delta operation"),
475                                &force_delta),
476
477     GNUNET_GETOPT_option_uint ('C',
478                                "num-common",
479                                NULL,
480                                gettext_noop ("number of values"),
481                                &num_c),
482
483     GNUNET_GETOPT_option_string ('x',
484                                  "operation",
485                                  NULL,
486                                  gettext_noop ("operation to execute"),
487                                  &op_str),
488
489     GNUNET_GETOPT_option_uint ('w',
490                                "element-size",
491                                NULL,
492                                gettext_noop ("element size"),
493                                &element_size),
494
495     GNUNET_GETOPT_option_filename ('s',
496                                    "statistics",
497                                    "FILENAME",
498                                    gettext_noop ("write statistics to file"),
499                                    &statistics_filename),
500
501     GNUNET_GETOPT_OPTION_END
502   };
503
504   GNUNET_PROGRAM_run2 (argc, argv, "gnunet-set-profiler",
505                        "help",
506                        options, &pre_run, NULL, GNUNET_YES);
507   return ret;
508 }