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