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