gnunet-set-profiler.c 14 KB

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