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