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