-avoid calling memcpy() with NULL argument, even if len is 0
[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
6       it under the terms of the GNU General Public License as published
7       by the Free Software Foundation; either version 3, or (at your
8       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       General Public License for more details.
14
15       You should have received a copy of the GNU General Public License
16       along with GNUnet; see the file COPYING.  If not, write to the
17       Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18       Boston, MA 02110-1301, USA.
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 /**
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 (m, key, NULL);
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                enum GNUNET_SET_Status status)
162 {
163   struct SetInfo *info = cls;
164   struct GNUNET_HashCode hash;
165
166   GNUNET_assert (GNUNET_NO == info->done);
167   switch (status)
168   {
169     case GNUNET_SET_STATUS_DONE:
170     case GNUNET_SET_STATUS_HALF_DONE:
171       info->done = GNUNET_YES;
172       GNUNET_log (GNUNET_ERROR_TYPE_INFO, "set %s done\n", info->id);
173       check_all_done ();
174       info->oh = NULL;
175       return;
176     case GNUNET_SET_STATUS_FAILURE:
177       info->oh = NULL;
178       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "failure\n");
179       GNUNET_SCHEDULER_shutdown ();
180       return;
181     case GNUNET_SET_STATUS_ADD_LOCAL:
182       GNUNET_log (GNUNET_ERROR_TYPE_INFO, "set %s: local element\n", info->id);
183       break;
184     case GNUNET_SET_STATUS_ADD_REMOTE:
185       GNUNET_CRYPTO_hash (element->data, element->size, &hash);
186       GNUNET_log (GNUNET_ERROR_TYPE_INFO, "set %s: remote element %s\n", info->id,
187                   GNUNET_h2s (&hash));
188       // XXX: record and check
189       return;
190     default:
191       GNUNET_assert (0);
192   }
193
194   if (element->size != sizeof (struct GNUNET_HashCode))
195   {
196     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
197                 "wrong element size: %u, expected %u\n",
198                 element->size,
199                 (unsigned int) sizeof (struct GNUNET_HashCode));
200     GNUNET_assert (0);
201   }
202
203   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "set %s: got element (%s)\n",
204               info->id, GNUNET_h2s (element->data));
205   GNUNET_assert (NULL != element->data);
206   GNUNET_CONTAINER_multihashmap_put (info->received,
207                                      element->data, NULL,
208                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
209 }
210
211
212 static void
213 set_listen_cb (void *cls,
214                const struct GNUNET_PeerIdentity *other_peer,
215                const struct GNUNET_MessageHeader *context_msg,
216                struct GNUNET_SET_Request *request)
217 {
218   if (NULL == request)
219   {
220     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
221                 "listener failed\n");
222     return;
223   }
224   GNUNET_assert (NULL == info2.oh);
225   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
226               "set listen cb called\n");
227   info2.oh = GNUNET_SET_accept (request, GNUNET_SET_RESULT_SYMMETRIC,
228                                set_result_cb, &info2);
229   GNUNET_SET_commit (info2.oh, info2.set);
230 }
231
232
233 static int
234 set_insert_iterator (void *cls,
235                      const struct GNUNET_HashCode *key,
236                      void *value)
237 {
238   struct GNUNET_SET_Handle *set = cls;
239   struct GNUNET_SET_Element *el;
240
241   el = GNUNET_malloc (sizeof (struct GNUNET_SET_Element) +
242                       sizeof (struct GNUNET_HashCode));
243   el->element_type = 0;
244   GNUNET_memcpy (&el[1], key, sizeof *key);
245   el->data = &el[1];
246   el->size = sizeof *key;
247   GNUNET_SET_add_element (set, el, NULL, NULL);
248   GNUNET_free (el);
249   return GNUNET_YES;
250 }
251
252
253 static void
254 handle_shutdown (void *cls)
255 {
256   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
257               "Shutting down set profiler\n");
258   if (NULL != set_listener)
259   {
260     GNUNET_SET_listen_cancel (set_listener);
261     set_listener = NULL;
262   }
263   if (NULL != info1.oh)
264   {
265     GNUNET_SET_operation_cancel (info1.oh);
266     info1.oh = NULL;
267   }
268   if (NULL != info2.oh)
269   {
270     GNUNET_SET_operation_cancel (info2.oh);
271     info2.oh = NULL;
272   }
273   if (NULL != info1.set)
274   {
275     GNUNET_SET_destroy (info1.set);
276     info1.set = NULL;
277   }
278   if (NULL != info2.set)
279   {
280     GNUNET_SET_destroy (info2.set);
281     info2.set = NULL;
282   }
283   GNUNET_STATISTICS_destroy (statistics, GNUNET_NO);
284 }
285
286
287 static void
288 run (void *cls,
289      const struct GNUNET_CONFIGURATION_Handle *cfg,
290      struct GNUNET_TESTING_Peer *peer)
291 {
292   unsigned int i;
293   struct GNUNET_HashCode hash;
294
295   config = cfg;
296
297   if (GNUNET_OK != GNUNET_CRYPTO_get_peer_identity (cfg, &local_peer))
298   {
299     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "could not retrieve host identity\n");
300     ret = 0;
301     return;
302   }
303
304   statistics = GNUNET_STATISTICS_create ("set-profiler", cfg);
305
306   GNUNET_SCHEDULER_add_shutdown (&handle_shutdown, NULL);
307
308   info1.id = "a";
309   info2.id = "b";
310
311   info1.sent = GNUNET_CONTAINER_multihashmap_create (num_a+1, GNUNET_NO);
312   info2.sent = GNUNET_CONTAINER_multihashmap_create (num_b+1, GNUNET_NO);
313   common_sent = GNUNET_CONTAINER_multihashmap_create (num_c+1, GNUNET_NO);
314
315   info1.received = GNUNET_CONTAINER_multihashmap_create (num_a+1, GNUNET_NO);
316   info2.received = GNUNET_CONTAINER_multihashmap_create (num_b+1, GNUNET_NO);
317
318   for (i = 0; i < num_a; i++)
319   {
320     GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_STRONG, &hash);
321     GNUNET_CONTAINER_multihashmap_put (info1.sent, &hash, NULL,
322                                        GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
323   }
324
325   for (i = 0; i < num_b; i++)
326   {
327     GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_STRONG, &hash);
328     GNUNET_CONTAINER_multihashmap_put (info2.sent, &hash, NULL,
329                                        GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
330   }
331
332   for (i = 0; i < num_c; i++)
333   {
334     GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_STRONG, &hash);
335     GNUNET_CONTAINER_multihashmap_put (common_sent, &hash, NULL,
336                                        GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
337   }
338
339   GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_STRONG, &app_id);
340
341   /* FIXME: also implement intersection etc. */
342   info1.set = GNUNET_SET_create (config, GNUNET_SET_OPERATION_UNION);
343   info2.set = GNUNET_SET_create (config, GNUNET_SET_OPERATION_UNION);
344
345   GNUNET_CONTAINER_multihashmap_iterate (info1.sent, set_insert_iterator, info1.set);
346   GNUNET_CONTAINER_multihashmap_iterate (info2.sent, set_insert_iterator, info2.set);
347   GNUNET_CONTAINER_multihashmap_iterate (common_sent, set_insert_iterator, info1.set);
348   GNUNET_CONTAINER_multihashmap_iterate (common_sent, set_insert_iterator, info2.set);
349
350   set_listener = GNUNET_SET_listen (config, GNUNET_SET_OPERATION_UNION,
351                                     &app_id, set_listen_cb, NULL);
352
353   info1.oh = GNUNET_SET_prepare (&local_peer, &app_id, NULL,
354                                  GNUNET_SET_RESULT_SYMMETRIC,
355                                  set_result_cb, &info1);
356   GNUNET_SET_commit (info1.oh, info1.set);
357   GNUNET_SET_destroy (info1.set);
358   info1.set = NULL;
359 }
360
361
362 static void
363 pre_run (void *cls, char *const *args, const char *cfgfile,
364          const struct GNUNET_CONFIGURATION_Handle *cfg)
365 {
366   if (0 != GNUNET_TESTING_peer_run ("set-profiler",
367                                     cfgfile,
368                                     &run, NULL))
369     ret = 2;
370 }
371
372
373 int
374 main (int argc, char **argv)
375 {
376    static const struct GNUNET_GETOPT_CommandLineOption options[] = {
377       { 'A', "num-first", NULL,
378         gettext_noop ("number of values"),
379         GNUNET_YES, &GNUNET_GETOPT_set_uint, &num_a },
380       { 'B', "num-second", NULL,
381         gettext_noop ("number of values"),
382         GNUNET_YES, &GNUNET_GETOPT_set_uint, &num_b },
383       { 'C', "num-common", NULL,
384         gettext_noop ("number of values"),
385         GNUNET_YES, &GNUNET_GETOPT_set_uint, &num_c },
386       { 'x', "operation", NULL,
387         gettext_noop ("operation to execute"),
388         GNUNET_YES, &GNUNET_GETOPT_set_string, &op_str },
389       { 's', "statistics", NULL,
390         gettext_noop ("write statistics to file"),
391         GNUNET_YES, &GNUNET_GETOPT_set_filename, &statistics_filename },
392       GNUNET_GETOPT_OPTION_END
393   };
394   GNUNET_PROGRAM_run2 (argc, argv, "gnunet-set-profiler",
395                       "help",
396                       options, &pre_run, NULL, GNUNET_YES);
397   return ret;
398 }