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