aboutsummaryrefslogtreecommitdiff
path: root/src/service/fs/gnunet-daemon-fsprofiler.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/service/fs/gnunet-daemon-fsprofiler.c')
-rw-r--r--src/service/fs/gnunet-daemon-fsprofiler.c673
1 files changed, 673 insertions, 0 deletions
diff --git a/src/service/fs/gnunet-daemon-fsprofiler.c b/src/service/fs/gnunet-daemon-fsprofiler.c
new file mode 100644
index 000000000..b99933cfa
--- /dev/null
+++ b/src/service/fs/gnunet-daemon-fsprofiler.c
@@ -0,0 +1,673 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012 Christian Grothoff
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 fs/gnunet-daemon-fsprofiler.c
23 * @brief daemon that publishes and downloads (random) files
24 * @author Christian Grothoff
25 *
26 * TODO:
27 * - how to signal driver that we're done?
28 */
29#include "platform.h"
30
31#include "gnunet_fs_service.h"
32#include "gnunet_statistics_service.h"
33
34/**
35 * We use 'patterns' of the form (x,y,t) to specify desired download/publish
36 * activities of a peer. They are stored in a DLL.
37 */
38struct Pattern
39{
40 /**
41 * Kept in a DLL.
42 */
43 struct Pattern *next;
44
45 /**
46 * Kept in a DLL.
47 */
48 struct Pattern *prev;
49
50 /**
51 * Execution context for the pattern (FS-handle to the operation).
52 */
53 void *ctx;
54
55 /**
56 * Secondary execution context for the pattern (FS-handle to the operation).
57 */
58 void *sctx;
59
60 /**
61 * When did the operation start?
62 */
63 struct GNUNET_TIME_Absolute start_time;
64
65 /**
66 * With how much delay should this operation be started?
67 */
68 struct GNUNET_TIME_Relative delay;
69
70 /**
71 * Task to run the operation.
72 */
73 struct GNUNET_SCHEDULER_Task *task;
74
75 /**
76 * Secondary task to run the operation.
77 */
78 struct GNUNET_SCHEDULER_Task *stask;
79
80 /**
81 * X-value.
82 */
83 unsigned long long x;
84
85 /**
86 * Y-value.
87 */
88 unsigned long long y;
89};
90
91
92/**
93 * Return value from 'main'.
94 */
95static int global_ret;
96
97/**
98 * Configuration we use.
99 */
100static const struct GNUNET_CONFIGURATION_Handle *cfg;
101
102/**
103 * Handle to the statistics service.
104 */
105static struct GNUNET_STATISTICS_Handle *stats_handle;
106
107/**
108 * Peer's FS handle.
109 */
110static struct GNUNET_FS_Handle *fs_handle;
111
112/**
113 * Unique number for this peer in the testbed.
114 */
115static unsigned long long my_peerid;
116
117/**
118 * Desired anonymity level.
119 */
120static unsigned long long anonymity_level;
121
122/**
123 * Desired replication level.
124 */
125static unsigned long long replication_level;
126
127/**
128 * String describing which publishing operations this peer should
129 * perform. The format is "(SIZE,SEED,TIME)*", for example:
130 * "(1,5,0)(7,3,13)" means to publish a file with 1 byte and
131 * seed/keyword 5 immediately and another file with 7 bytes and
132 * seed/keyword 3 after 13 ms.
133 */
134static char *publish_pattern;
135
136/**
137 * Head of the DLL of publish patterns.
138 */
139static struct Pattern *publish_head;
140
141/**
142 * Tail of the DLL of publish patterns.
143 */
144static struct Pattern *publish_tail;
145
146/**
147 * String describing which download operations this peer should
148 * perform. The format is "(KEYWORD,SIZE,DELAY)*"; for example,
149 * "(1,7,3)(3,8,8)" means to download one file of 7 bytes under
150 * keyword "1" starting the search after 3 ms; and another one of 8
151 * bytes under keyword '3' starting after 8 ms. The file size is
152 * used to determine which search result(s) should be used or ignored.
153 */
154static char *download_pattern;
155
156/**
157 * Head of the DLL of publish patterns.
158 */
159static struct Pattern *download_head;
160
161/**
162 * Tail of the DLL of publish patterns.
163 */
164static struct Pattern *download_tail;
165
166
167/**
168 * Parse a pattern string and store the corresponding
169 * 'struct Pattern' in the given head/tail.
170 *
171 * @param head where to store the head
172 * @param tail where to store the tail
173 * @param pattern pattern to parse
174 * @return GNUNET_OK on success
175 */
176static int
177parse_pattern (struct Pattern **head,
178 struct Pattern **tail,
179 const char *pattern)
180{
181 struct Pattern *p;
182 unsigned long long x;
183 unsigned long long y;
184 unsigned long long t;
185
186 while (3 == sscanf (pattern,
187 "(%llu,%llu,%llu)",
188 &x, &y, &t))
189 {
190 p = GNUNET_new (struct Pattern);
191 p->x = x;
192 p->y = y;
193 p->delay.rel_value_us = (uint64_t) t;
194 GNUNET_CONTAINER_DLL_insert (*head, *tail, p);
195 pattern = strstr (pattern, ")");
196 GNUNET_assert (NULL != pattern);
197 pattern++;
198 }
199 return (0 == strlen (pattern)) ? GNUNET_OK : GNUNET_SYSERR;
200}
201
202
203/**
204 * Create a KSK URI from a number.
205 *
206 * @param kval the number
207 * @return corresponding KSK URI
208 */
209static struct GNUNET_FS_Uri *
210make_keywords (uint64_t kval)
211{
212 char kw[128];
213
214 GNUNET_snprintf (kw, sizeof(kw),
215 "%llu", (unsigned long long) kval);
216 return GNUNET_FS_uri_ksk_create (kw, NULL);
217}
218
219
220/**
221 * Create a file of the given length with a deterministic amount
222 * of data to be published under keyword 'kval'.
223 *
224 * @param length number of bytes in the file
225 * @param kval keyword value and seed for the data of the file
226 * @param ctx context to pass to 'fi'
227 * @return file information handle for the file
228 */
229static struct GNUNET_FS_FileInformation *
230make_file (uint64_t length,
231 uint64_t kval,
232 void *ctx)
233{
234 struct GNUNET_FS_FileInformation *fi;
235 struct GNUNET_FS_BlockOptions bo;
236 char *data;
237 struct GNUNET_FS_Uri *keywords;
238 unsigned long long i;
239 uint64_t xor;
240
241 data = NULL; /* to make compilers happy */
242 if ((0 != length) &&
243 (NULL == (data = GNUNET_malloc_large ((size_t) length))))
244 return NULL;
245 /* initialize data with 'unique' data only depending on 'kval' and 'size',
246 making sure that blocks do not repeat */
247 for (i = 0; i < length; i += 8)
248 {
249 xor = length ^ kval ^ (uint64_t) (i / 32 / 1024);
250 GNUNET_memcpy (&data[i], &xor, GNUNET_MIN (length - i, sizeof(uint64_t)));
251 }
252 bo.expiration_time = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_DAYS);
253 bo.anonymity_level = (uint32_t) anonymity_level;
254 bo.content_priority = 128;
255 bo.replication_level = (uint32_t) replication_level;
256 keywords = make_keywords (kval);
257 fi = GNUNET_FS_file_information_create_from_data (fs_handle,
258 ctx,
259 length,
260 data, keywords,
261 NULL, GNUNET_NO, &bo);
262 GNUNET_FS_uri_destroy (keywords);
263 return fi;
264}
265
266
267/**
268 * Task run during shutdown.
269 *
270 * @param cls unused
271 */
272static void
273shutdown_task (void *cls)
274{
275 struct Pattern *p;
276
277 while (NULL != (p = publish_head))
278 {
279 if (NULL != p->task)
280 GNUNET_SCHEDULER_cancel (p->task);
281 if (NULL != p->ctx)
282 GNUNET_FS_publish_stop (p->ctx);
283 GNUNET_CONTAINER_DLL_remove (publish_head, publish_tail, p);
284 GNUNET_free (p);
285 }
286 while (NULL != (p = download_head))
287 {
288 if (NULL != p->task)
289 GNUNET_SCHEDULER_cancel (p->task);
290 if (NULL != p->stask)
291 GNUNET_SCHEDULER_cancel (p->stask);
292 if (NULL != p->ctx)
293 GNUNET_FS_download_stop (p->ctx, GNUNET_YES);
294 if (NULL != p->sctx)
295 GNUNET_FS_search_stop (p->sctx);
296 GNUNET_CONTAINER_DLL_remove (download_head, download_tail, p);
297 GNUNET_free (p);
298 }
299 if (NULL != fs_handle)
300 {
301 GNUNET_FS_stop (fs_handle);
302 fs_handle = NULL;
303 }
304 if (NULL != stats_handle)
305 {
306 GNUNET_STATISTICS_destroy (stats_handle, GNUNET_YES);
307 stats_handle = NULL;
308 }
309}
310
311
312/**
313 * Task run when a publish operation should be stopped.
314 *
315 * @param cls the 'struct Pattern' of the publish operation to stop
316 */
317static void
318publish_stop_task (void *cls)
319{
320 struct Pattern *p = cls;
321
322 p->task = NULL;
323 GNUNET_FS_publish_stop (p->ctx);
324}
325
326
327/**
328 * Task run when a download operation should be stopped.
329 *
330 * @param cls the 'struct Pattern' of the download operation to stop
331 */
332static void
333download_stop_task (void *cls)
334{
335 struct Pattern *p = cls;
336
337 p->task = NULL;
338 GNUNET_FS_download_stop (p->ctx, GNUNET_YES);
339}
340
341
342/**
343 * Task run when a download operation should be stopped.
344 *
345 * @param cls the 'struct Pattern' of the download operation to stop
346 */
347static void
348search_stop_task (void *cls)
349{
350 struct Pattern *p = cls;
351
352 p->stask = NULL;
353 GNUNET_FS_search_stop (p->sctx);
354}
355
356
357/**
358 * Notification of FS to a client about the progress of an
359 * operation. Callbacks of this type will be used for uploads,
360 * downloads and searches. Some of the arguments depend a bit
361 * in their meaning on the context in which the callback is used.
362 *
363 * @param cls closure
364 * @param info details about the event, specifying the event type
365 * and various bits about the event
366 * @return client-context (for the next progress call
367 * for this operation; should be set to NULL for
368 * SUSPEND and STOPPED events). The value returned
369 * will be passed to future callbacks in the respective
370 * field in the GNUNET_FS_ProgressInfo struct.
371 */
372static void *
373progress_cb (void *cls,
374 const struct GNUNET_FS_ProgressInfo *info)
375{
376 struct Pattern *p;
377 const struct GNUNET_FS_Uri *uri;
378
379 switch (info->status)
380 {
381 case GNUNET_FS_STATUS_PUBLISH_START:
382 case GNUNET_FS_STATUS_PUBLISH_PROGRESS:
383 p = info->value.publish.cctx;
384 return p;
385
386 case GNUNET_FS_STATUS_PUBLISH_PROGRESS_DIRECTORY:
387 p = info->value.publish.cctx;
388 return p;
389
390 case GNUNET_FS_STATUS_PUBLISH_ERROR:
391 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
392 "Publishing failed\n");
393 GNUNET_STATISTICS_update (stats_handle,
394 "# failed publish operations", 1, GNUNET_NO);
395 p = info->value.publish.cctx;
396 p->task = GNUNET_SCHEDULER_add_now (&publish_stop_task, p);
397 return p;
398
399 case GNUNET_FS_STATUS_PUBLISH_COMPLETED:
400 p = info->value.publish.cctx;
401 GNUNET_STATISTICS_update (stats_handle,
402 "# publishing time (ms)",
403 (long long) GNUNET_TIME_absolute_get_duration (
404 p->start_time).rel_value_us / 1000LL,
405 GNUNET_NO);
406 p->task = GNUNET_SCHEDULER_add_now (&publish_stop_task, p);
407 return p;
408
409 case GNUNET_FS_STATUS_PUBLISH_STOPPED:
410 p = info->value.publish.cctx;
411 p->ctx = NULL;
412 GNUNET_CONTAINER_DLL_remove (publish_head, publish_tail, p);
413 GNUNET_free (p);
414 return NULL;
415
416 case GNUNET_FS_STATUS_DOWNLOAD_START:
417 case GNUNET_FS_STATUS_DOWNLOAD_PROGRESS:
418 case GNUNET_FS_STATUS_DOWNLOAD_ACTIVE:
419 case GNUNET_FS_STATUS_DOWNLOAD_INACTIVE:
420 p = info->value.download.cctx;
421 return p;
422
423 case GNUNET_FS_STATUS_DOWNLOAD_ERROR:
424 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
425 "Download failed\n");
426 GNUNET_STATISTICS_update (stats_handle,
427 "# failed downloads", 1, GNUNET_NO);
428 p = info->value.download.cctx;
429 p->task = GNUNET_SCHEDULER_add_now (&download_stop_task, p);
430 return p;
431
432 case GNUNET_FS_STATUS_DOWNLOAD_COMPLETED:
433 p = info->value.download.cctx;
434 GNUNET_STATISTICS_update (stats_handle,
435 "# download time (ms)",
436 (long long) GNUNET_TIME_absolute_get_duration (
437 p->start_time).rel_value_us / 1000LL,
438 GNUNET_NO);
439 p->task = GNUNET_SCHEDULER_add_now (&download_stop_task, p);
440 return p;
441
442 case GNUNET_FS_STATUS_DOWNLOAD_STOPPED:
443 p = info->value.download.cctx;
444 p->ctx = NULL;
445 if (NULL == p->sctx)
446 {
447 GNUNET_CONTAINER_DLL_remove (download_head, download_tail, p);
448 GNUNET_free (p);
449 }
450 return NULL;
451
452 case GNUNET_FS_STATUS_SEARCH_START:
453 case GNUNET_FS_STATUS_SEARCH_RESULT_NAMESPACE:
454 p = info->value.search.cctx;
455 return p;
456
457 case GNUNET_FS_STATUS_SEARCH_RESULT:
458 p = info->value.search.cctx;
459 uri = info->value.search.specifics.result.uri;
460 if (GNUNET_YES != GNUNET_FS_uri_test_chk (uri))
461 return NULL; /* not what we want */
462 if (p->y != GNUNET_FS_uri_chk_get_file_size (uri))
463 return NULL; /* not what we want */
464 GNUNET_STATISTICS_update (stats_handle,
465 "# search time (ms)",
466 (long long) GNUNET_TIME_absolute_get_duration (
467 p->start_time).rel_value_us / 1000LL,
468 GNUNET_NO);
469 p->start_time = GNUNET_TIME_absolute_get ();
470 p->ctx = GNUNET_FS_download_start (fs_handle, uri,
471 NULL, NULL, NULL,
472 0, GNUNET_FS_uri_chk_get_file_size (uri),
473 anonymity_level,
474 GNUNET_FS_DOWNLOAD_NO_TEMPORARIES,
475 p,
476 NULL);
477 p->stask = GNUNET_SCHEDULER_add_now (&search_stop_task, p);
478 return NULL;
479
480 case GNUNET_FS_STATUS_SEARCH_UPDATE:
481 case GNUNET_FS_STATUS_SEARCH_RESULT_STOPPED:
482 return NULL; /* don't care */
483
484 case GNUNET_FS_STATUS_SEARCH_ERROR:
485 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
486 "Search failed\n");
487 GNUNET_STATISTICS_update (stats_handle,
488 "# failed searches", 1, GNUNET_NO);
489 p = info->value.search.cctx;
490 p->stask = GNUNET_SCHEDULER_add_now (&search_stop_task, p);
491 return p;
492
493 case GNUNET_FS_STATUS_SEARCH_STOPPED:
494 p = info->value.search.cctx;
495 p->sctx = NULL;
496 if (NULL == p->ctx)
497 {
498 GNUNET_CONTAINER_DLL_remove (download_head, download_tail, p);
499 GNUNET_free (p);
500 }
501 return NULL;
502
503 default:
504 /* unexpected event during profiling */
505 GNUNET_break (0);
506 return NULL;
507 }
508}
509
510
511/**
512 * Start publish operation.
513 *
514 * @param cls the 'struct Pattern' specifying the operation to perform
515 */
516static void
517start_publish (void *cls)
518{
519 struct Pattern *p = cls;
520 struct GNUNET_FS_FileInformation *fi;
521
522 p->task = NULL;
523 fi = make_file (p->x, p->y, p);
524 p->start_time = GNUNET_TIME_absolute_get ();
525 p->ctx = GNUNET_FS_publish_start (fs_handle,
526 fi,
527 NULL, NULL, NULL,
528 GNUNET_FS_PUBLISH_OPTION_NONE);
529}
530
531
532/**
533 * Start download operation.
534 *
535 * @param cls the 'struct Pattern' specifying the operation to perform
536 */
537static void
538start_download (void *cls)
539{
540 struct Pattern *p = cls;
541 struct GNUNET_FS_Uri *keywords;
542
543 p->task = NULL;
544 keywords = make_keywords (p->x);
545 p->start_time = GNUNET_TIME_absolute_get ();
546 p->sctx = GNUNET_FS_search_start (fs_handle, keywords,
547 anonymity_level,
548 GNUNET_FS_SEARCH_OPTION_NONE,
549 p);
550}
551
552
553/**
554 * @brief Main function that will be run by the scheduler.
555 *
556 * @param cls closure
557 * @param args remaining command-line arguments
558 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
559 * @param cfg_ configuration
560 */
561static void
562run (void *cls, char *const *args GNUNET_UNUSED,
563 const char *cfgfile GNUNET_UNUSED,
564 const struct GNUNET_CONFIGURATION_Handle *cfg_)
565{
566 char myoptname[128];
567 struct Pattern *p;
568
569 cfg = cfg_;
570 /* Scheduled the task to clean up when shutdown is called */
571 GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
572 NULL);
573
574 if (GNUNET_OK !=
575 GNUNET_CONFIGURATION_get_value_number (cfg,
576 "TESTBED", "PEERID",
577 &my_peerid))
578 {
579 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
580 "TESTBED", "PEERID");
581 global_ret = GNUNET_SYSERR;
582 GNUNET_SCHEDULER_shutdown ();
583 return;
584 }
585 if (GNUNET_OK !=
586 GNUNET_CONFIGURATION_get_value_number (cfg,
587 "FSPROFILER", "ANONYMITY_LEVEL",
588 &anonymity_level))
589 anonymity_level = 1;
590 if (GNUNET_OK !=
591 GNUNET_CONFIGURATION_get_value_number (cfg,
592 "FSPROFILER", "REPLICATION_LEVEL",
593 &replication_level))
594 replication_level = 1;
595 GNUNET_snprintf (myoptname, sizeof(myoptname),
596 "DOWNLOAD-PATTERN-%llu", my_peerid);
597 if (GNUNET_OK !=
598 GNUNET_CONFIGURATION_get_value_string (cfg,
599 "FSPROFILER", myoptname,
600 &download_pattern))
601 download_pattern = GNUNET_strdup ("");
602 GNUNET_snprintf (myoptname, sizeof(myoptname),
603 "PUBLISH-PATTERN-%llu", my_peerid);
604 if (GNUNET_OK !=
605 GNUNET_CONFIGURATION_get_value_string (cfg,
606 "FSPROFILER", myoptname,
607 &publish_pattern))
608 publish_pattern = GNUNET_strdup ("");
609 if ((GNUNET_OK !=
610 parse_pattern (&download_head,
611 &download_tail,
612 download_pattern)) ||
613 (GNUNET_OK !=
614 parse_pattern (&publish_head,
615 &publish_tail,
616 publish_pattern)))
617 {
618 GNUNET_SCHEDULER_shutdown ();
619 return;
620 }
621
622 stats_handle = GNUNET_STATISTICS_create ("fsprofiler", cfg);
623 fs_handle =
624 GNUNET_FS_start (cfg,
625 "fsprofiler",
626 &progress_cb, NULL,
627 GNUNET_FS_FLAGS_NONE,
628 GNUNET_FS_OPTIONS_DOWNLOAD_PARALLELISM, 1,
629 GNUNET_FS_OPTIONS_REQUEST_PARALLELISM, 1,
630 GNUNET_FS_OPTIONS_END);
631 if (NULL == fs_handle)
632 {
633 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
634 "Could not acquire FS handle. Exiting.\n");
635 global_ret = GNUNET_SYSERR;
636 GNUNET_SCHEDULER_shutdown ();
637 return;
638 }
639 for (p = publish_head; NULL != p; p = p->next)
640 p->task = GNUNET_SCHEDULER_add_delayed (p->delay,
641 &start_publish, p);
642 for (p = download_head; NULL != p; p = p->next)
643 p->task = GNUNET_SCHEDULER_add_delayed (p->delay,
644 &start_download, p);
645}
646
647
648/**
649 * Program that performs various "random" FS activities.
650 *
651 * @param argc number of arguments from the command line
652 * @param argv command line arguments
653 * @return 0 ok, 1 on error
654 */
655int
656main (int argc, char *const *argv)
657{
658 static const struct GNUNET_GETOPT_CommandLineOption options[] = {
659 GNUNET_GETOPT_OPTION_END
660 };
661
662 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
663 return 2;
664 return (GNUNET_OK ==
665 GNUNET_PROGRAM_run (argc, argv, "gnunet-daemon-fsprofiler",
666 gettext_noop
667 (
668 "Daemon to use file-sharing to measure its performance."),
669 options, &run, NULL)) ? global_ret : 1;
670}
671
672
673/* end of gnunet-daemon-fsprofiler.c */