gnunet-seti-profiler.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480
  1. /*
  2. This file is part of GNUnet
  3. Copyright (C) 2013, 2020 GNUnet e.V.
  4. GNUnet is free software: you can redistribute it and/or modify it
  5. under the terms of the GNU Affero General Public License as published
  6. by the Free Software Foundation, either version 3 of the License,
  7. or (at your option) any later version.
  8. GNUnet is distributed in the hope that it will be useful, but
  9. WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. Affero General Public License for more details.
  12. You should have received a copy of the GNU Affero General Public License
  13. along with this program. If not, see <http://www.gnu.org/licenses/>.
  14. SPDX-License-Identifier: AGPL3.0-or-later
  15. */
  16. /**
  17. * @file set/gnunet-seti-profiler.c
  18. * @brief profiling tool for set intersection
  19. * @author Florian Dold
  20. */
  21. #include "platform.h"
  22. #include "gnunet_util_lib.h"
  23. #include "gnunet_statistics_service.h"
  24. #include "gnunet_seti_service.h"
  25. #include "gnunet_testbed_service.h"
  26. static int ret;
  27. static unsigned int num_a = 5;
  28. static unsigned int num_b = 5;
  29. static unsigned int num_c = 20;
  30. const static struct GNUNET_CONFIGURATION_Handle *config;
  31. struct SetInfo
  32. {
  33. char *id;
  34. struct GNUNET_SETI_Handle *set;
  35. struct GNUNET_SETI_OperationHandle *oh;
  36. struct GNUNET_CONTAINER_MultiHashMap *sent;
  37. struct GNUNET_CONTAINER_MultiHashMap *received;
  38. int done;
  39. } info1, info2;
  40. static struct GNUNET_CONTAINER_MultiHashMap *common_sent;
  41. static struct GNUNET_HashCode app_id;
  42. static struct GNUNET_PeerIdentity local_peer;
  43. static struct GNUNET_SETI_ListenHandle *set_listener;
  44. static unsigned int use_intersection;
  45. static unsigned int element_size = 32;
  46. /**
  47. * Handle to the statistics service.
  48. */
  49. static struct GNUNET_STATISTICS_Handle *statistics;
  50. /**
  51. * The profiler will write statistics
  52. * for all peers to the file with this name.
  53. */
  54. static char *statistics_filename;
  55. /**
  56. * The profiler will write statistics
  57. * for all peers to this file.
  58. */
  59. static FILE *statistics_file;
  60. static int
  61. map_remove_iterator (void *cls,
  62. const struct GNUNET_HashCode *key,
  63. void *value)
  64. {
  65. struct GNUNET_CONTAINER_MultiHashMap *m = cls;
  66. int ret;
  67. GNUNET_assert (NULL != key);
  68. ret = GNUNET_CONTAINER_multihashmap_remove_all (m, key);
  69. if (GNUNET_OK != ret)
  70. printf ("spurious element\n");
  71. return GNUNET_YES;
  72. }
  73. /**
  74. * Callback function to process statistic values.
  75. *
  76. * @param cls closure
  77. * @param subsystem name of subsystem that created the statistic
  78. * @param name the name of the datum
  79. * @param value the current value
  80. * @param is_persistent #GNUNET_YES if the value is persistent, #GNUNET_NO if not
  81. * @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration
  82. */
  83. static int
  84. statistics_result (void *cls,
  85. const char *subsystem,
  86. const char *name,
  87. uint64_t value,
  88. int is_persistent)
  89. {
  90. if (NULL != statistics_file)
  91. {
  92. fprintf (statistics_file, "%s\t%s\t%lu\n", subsystem, name, (unsigned
  93. long) value);
  94. }
  95. return GNUNET_OK;
  96. }
  97. static void
  98. statistics_done (void *cls,
  99. int success)
  100. {
  101. GNUNET_assert (GNUNET_YES == success);
  102. if (NULL != statistics_file)
  103. fclose (statistics_file);
  104. GNUNET_SCHEDULER_shutdown ();
  105. }
  106. static void
  107. check_all_done (void)
  108. {
  109. if ((info1.done == GNUNET_NO) || (info2.done == GNUNET_NO))
  110. return;
  111. GNUNET_CONTAINER_multihashmap_iterate (info1.received, map_remove_iterator,
  112. info2.sent);
  113. GNUNET_CONTAINER_multihashmap_iterate (info2.received, map_remove_iterator,
  114. info1.sent);
  115. printf ("set a: %d missing elements\n", GNUNET_CONTAINER_multihashmap_size (
  116. info1.sent));
  117. printf ("set b: %d missing elements\n", GNUNET_CONTAINER_multihashmap_size (
  118. info2.sent));
  119. if (NULL == statistics_filename)
  120. {
  121. GNUNET_SCHEDULER_shutdown ();
  122. return;
  123. }
  124. statistics_file = fopen (statistics_filename, "w");
  125. GNUNET_STATISTICS_get (statistics, NULL, NULL,
  126. &statistics_done,
  127. &statistics_result, NULL);
  128. }
  129. static void
  130. set_result_cb (void *cls,
  131. const struct GNUNET_SETI_Element *element,
  132. uint64_t current_size,
  133. enum GNUNET_SETI_Status status)
  134. {
  135. struct SetInfo *info = cls;
  136. struct GNUNET_HashCode hash;
  137. GNUNET_assert (GNUNET_NO == info->done);
  138. switch (status)
  139. {
  140. case GNUNET_SETI_STATUS_DONE:
  141. info->done = GNUNET_YES;
  142. GNUNET_log (GNUNET_ERROR_TYPE_INFO,
  143. "set intersection done\n");
  144. check_all_done ();
  145. info->oh = NULL;
  146. return;
  147. case GNUNET_SETI_STATUS_FAILURE:
  148. info->oh = NULL;
  149. GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
  150. "failure\n");
  151. GNUNET_SCHEDULER_shutdown ();
  152. return;
  153. case GNUNET_SETI_STATUS_ADD_LOCAL:
  154. GNUNET_CRYPTO_hash (element->data,
  155. element->size,
  156. &hash);
  157. GNUNET_log (GNUNET_ERROR_TYPE_INFO,
  158. "set %s: keep element %s\n",
  159. info->id,
  160. GNUNET_h2s (&hash));
  161. break;
  162. case GNUNET_SETI_STATUS_DEL_LOCAL:
  163. GNUNET_CRYPTO_hash (element->data,
  164. element->size,
  165. &hash);
  166. GNUNET_log (GNUNET_ERROR_TYPE_INFO,
  167. "set %s: remove element %s\n",
  168. info->id,
  169. GNUNET_h2s (&hash));
  170. return;
  171. default:
  172. GNUNET_assert (0);
  173. }
  174. if (element->size != element_size)
  175. {
  176. GNUNET_log (GNUNET_ERROR_TYPE_INFO,
  177. "wrong element size: %u, expected %u\n",
  178. element->size,
  179. (unsigned int) sizeof(struct GNUNET_HashCode));
  180. GNUNET_assert (0);
  181. }
  182. GNUNET_log (GNUNET_ERROR_TYPE_INFO,
  183. "set %s: got element (%s)\n",
  184. info->id, GNUNET_h2s (element->data));
  185. GNUNET_assert (NULL != element->data);
  186. {
  187. struct GNUNET_HashCode data_hash;
  188. GNUNET_CRYPTO_hash (element->data,
  189. element_size,
  190. &data_hash);
  191. GNUNET_CONTAINER_multihashmap_put (info->received,
  192. &data_hash,
  193. NULL,
  194. GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
  195. }
  196. }
  197. static void
  198. set_listen_cb (void *cls,
  199. const struct GNUNET_PeerIdentity *other_peer,
  200. const struct GNUNET_MessageHeader *context_msg,
  201. struct GNUNET_SETI_Request *request)
  202. {
  203. /* max. 1 option plus terminator */
  204. struct GNUNET_SETI_Option opts[2] = { { 0 } };
  205. unsigned int n_opts = 0;
  206. if (NULL == request)
  207. {
  208. GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
  209. "listener failed\n");
  210. return;
  211. }
  212. GNUNET_assert (NULL == info2.oh);
  213. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
  214. "set listen cb called\n");
  215. if (use_intersection)
  216. {
  217. opts[n_opts++] = (struct GNUNET_SETI_Option) { .type =
  218. GNUNET_SETI_OPTION_RETURN_INTERSECTION };
  219. }
  220. opts[n_opts].type = GNUNET_SETI_OPTION_END;
  221. info2.oh = GNUNET_SETI_accept (request,
  222. opts,
  223. &set_result_cb,
  224. &info2);
  225. GNUNET_SETI_commit (info2.oh,
  226. info2.set);
  227. }
  228. static int
  229. set_insert_iterator (void *cls,
  230. const struct GNUNET_HashCode *key,
  231. void *value)
  232. {
  233. struct GNUNET_SETI_Handle *set = cls;
  234. struct GNUNET_SETI_Element el;
  235. el.element_type = 0;
  236. el.data = value;
  237. el.size = element_size;
  238. GNUNET_SETI_add_element (set, &el, NULL, NULL);
  239. return GNUNET_YES;
  240. }
  241. static void
  242. handle_shutdown (void *cls)
  243. {
  244. GNUNET_log (GNUNET_ERROR_TYPE_INFO,
  245. "Shutting down set profiler\n");
  246. if (NULL != set_listener)
  247. {
  248. GNUNET_SETI_listen_cancel (set_listener);
  249. set_listener = NULL;
  250. }
  251. if (NULL != info1.oh)
  252. {
  253. GNUNET_SETI_operation_cancel (info1.oh);
  254. info1.oh = NULL;
  255. }
  256. if (NULL != info2.oh)
  257. {
  258. GNUNET_SETI_operation_cancel (info2.oh);
  259. info2.oh = NULL;
  260. }
  261. if (NULL != info1.set)
  262. {
  263. GNUNET_SETI_destroy (info1.set);
  264. info1.set = NULL;
  265. }
  266. if (NULL != info2.set)
  267. {
  268. GNUNET_SETI_destroy (info2.set);
  269. info2.set = NULL;
  270. }
  271. GNUNET_STATISTICS_destroy (statistics, GNUNET_NO);
  272. }
  273. static void
  274. run (void *cls,
  275. const struct GNUNET_CONFIGURATION_Handle *cfg,
  276. struct GNUNET_TESTING_Peer *peer)
  277. {
  278. unsigned int i;
  279. struct GNUNET_HashCode hash;
  280. /* max. 1 option plus terminator */
  281. struct GNUNET_SETI_Option opts[2] = { { 0 } };
  282. unsigned int n_opts = 0;
  283. config = cfg;
  284. GNUNET_assert (element_size > 0);
  285. if (GNUNET_OK != GNUNET_CRYPTO_get_peer_identity (cfg, &local_peer))
  286. {
  287. GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
  288. "could not retrieve host identity\n");
  289. ret = 0;
  290. return;
  291. }
  292. statistics = GNUNET_STATISTICS_create ("set-profiler", cfg);
  293. GNUNET_SCHEDULER_add_shutdown (&handle_shutdown, NULL);
  294. info1.id = "a";
  295. info2.id = "b";
  296. info1.sent = GNUNET_CONTAINER_multihashmap_create (num_a + 1, GNUNET_NO);
  297. info2.sent = GNUNET_CONTAINER_multihashmap_create (num_b + 1, GNUNET_NO);
  298. common_sent = GNUNET_CONTAINER_multihashmap_create (num_c + 1, GNUNET_NO);
  299. info1.received = GNUNET_CONTAINER_multihashmap_create (num_a + 1, GNUNET_NO);
  300. info2.received = GNUNET_CONTAINER_multihashmap_create (num_b + 1, GNUNET_NO);
  301. for (i = 0; i < num_a; i++)
  302. {
  303. char *data = GNUNET_malloc (element_size);
  304. GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, data, element_size);
  305. GNUNET_CRYPTO_hash (data, element_size, &hash);
  306. GNUNET_CONTAINER_multihashmap_put (info1.sent, &hash, data,
  307. GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
  308. }
  309. for (i = 0; i < num_b; i++)
  310. {
  311. char *data = GNUNET_malloc (element_size);
  312. GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, data, element_size);
  313. GNUNET_CRYPTO_hash (data, element_size, &hash);
  314. GNUNET_CONTAINER_multihashmap_put (info2.sent, &hash, data,
  315. GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
  316. }
  317. for (i = 0; i < num_c; i++)
  318. {
  319. char *data = GNUNET_malloc (element_size);
  320. GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, data, element_size);
  321. GNUNET_CRYPTO_hash (data, element_size, &hash);
  322. GNUNET_CONTAINER_multihashmap_put (common_sent, &hash, data,
  323. GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
  324. }
  325. GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_STRONG, &app_id);
  326. info1.set = GNUNET_SETI_create (config);
  327. info2.set = GNUNET_SETI_create (config);
  328. GNUNET_CONTAINER_multihashmap_iterate (info1.sent,
  329. &set_insert_iterator,
  330. info1.set);
  331. GNUNET_CONTAINER_multihashmap_iterate (info2.sent,
  332. &set_insert_iterator,
  333. info2.set);
  334. GNUNET_CONTAINER_multihashmap_iterate (common_sent,
  335. &set_insert_iterator,
  336. info1.set);
  337. GNUNET_CONTAINER_multihashmap_iterate (common_sent,
  338. &set_insert_iterator,
  339. info2.set);
  340. set_listener = GNUNET_SETI_listen (config,
  341. &app_id,
  342. &set_listen_cb,
  343. NULL);
  344. if (use_intersection)
  345. {
  346. opts[n_opts++] = (struct GNUNET_SETI_Option) { .type =
  347. GNUNET_SETI_OPTION_RETURN_INTERSECTION };
  348. }
  349. opts[n_opts].type = GNUNET_SETI_OPTION_END;
  350. info1.oh = GNUNET_SETI_prepare (&local_peer,
  351. &app_id,
  352. NULL,
  353. opts,
  354. set_result_cb,
  355. &info1);
  356. GNUNET_SETI_commit (info1.oh,
  357. info1.set);
  358. GNUNET_SETI_destroy (info1.set);
  359. info1.set = NULL;
  360. }
  361. static void
  362. pre_run (void *cls,
  363. char *const *args,
  364. const char *cfgfile,
  365. const struct GNUNET_CONFIGURATION_Handle *cfg)
  366. {
  367. if (0 != GNUNET_TESTING_peer_run ("set-profiler",
  368. cfgfile,
  369. &run, NULL))
  370. ret = 2;
  371. }
  372. int
  373. main (int argc, char **argv)
  374. {
  375. struct GNUNET_GETOPT_CommandLineOption options[] = {
  376. GNUNET_GETOPT_option_uint ('A',
  377. "num-first",
  378. NULL,
  379. gettext_noop ("number of values"),
  380. &num_a),
  381. GNUNET_GETOPT_option_uint ('B',
  382. "num-second",
  383. NULL,
  384. gettext_noop ("number of values"),
  385. &num_b),
  386. GNUNET_GETOPT_option_uint ('C',
  387. "num-common",
  388. NULL,
  389. gettext_noop ("number of values"),
  390. &num_c),
  391. GNUNET_GETOPT_option_uint ('i',
  392. "use-intersection",
  393. NULL,
  394. gettext_noop (
  395. "return intersection instead of delta"),
  396. &use_intersection),
  397. GNUNET_GETOPT_option_uint ('w',
  398. "element-size",
  399. NULL,
  400. gettext_noop ("element size"),
  401. &element_size),
  402. GNUNET_GETOPT_option_filename ('s',
  403. "statistics",
  404. "FILENAME",
  405. gettext_noop ("write statistics to file"),
  406. &statistics_filename),
  407. GNUNET_GETOPT_OPTION_END
  408. };
  409. GNUNET_PROGRAM_run2 (argc, argv,
  410. "gnunet-seti-profiler",
  411. "help",
  412. options,
  413. &pre_run,
  414. NULL,
  415. GNUNET_YES);
  416. return ret;
  417. }