2 This file is part of GNUnet.
3 Copyright (C) 2012 Christian Grothoff
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU 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.
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.
17 * @file fs/gnunet-daemon-fsprofiler.c
18 * @brief daemon that publishes and downloads (random) files
19 * @author Christian Grothoff
22 * - how to signal driver that we're done?
25 #include "gnunet_fs_service.h"
26 #include "gnunet_statistics_service.h"
29 * We use 'patterns' of the form (x,y,t) to specify desired download/publish
30 * activities of a peer. They are stored in a DLL.
45 * Execution context for the pattern (FS-handle to the operation).
50 * Secondary execution context for the pattern (FS-handle to the operation).
55 * When did the operation start?
57 struct GNUNET_TIME_Absolute start_time;
60 * With how much delay should this operation be started?
62 struct GNUNET_TIME_Relative delay;
65 * Task to run the operation.
67 struct GNUNET_SCHEDULER_Task * task;
70 * Secondary task to run the operation.
72 struct GNUNET_SCHEDULER_Task * stask;
87 * Return value from 'main'.
89 static int global_ret;
92 * Configuration we use.
94 static const struct GNUNET_CONFIGURATION_Handle *cfg;
97 * Handle to the statistics service.
99 static struct GNUNET_STATISTICS_Handle *stats_handle;
104 static struct GNUNET_FS_Handle *fs_handle;
107 * Unique number for this peer in the testbed.
109 static unsigned long long my_peerid;
112 * Desired anonymity level.
114 static unsigned long long anonymity_level;
117 * Desired replication level.
119 static unsigned long long replication_level;
122 * String describing which publishing operations this peer should
123 * perform. The format is "(SIZE,SEED,TIME)*", for example:
124 * "(1,5,0)(7,3,13)" means to publish a file with 1 byte and
125 * seed/keyword 5 immediately and another file with 7 bytes and
126 * seed/keyword 3 after 13 ms.
128 static char *publish_pattern;
131 * Head of the DLL of publish patterns.
133 static struct Pattern *publish_head;
136 * Tail of the DLL of publish patterns.
138 static struct Pattern *publish_tail;
141 * String describing which download operations this peer should
142 * perform. The format is "(KEYWORD,SIZE,DELAY)*"; for example,
143 * "(1,7,3)(3,8,8)" means to download one file of 7 bytes under
144 * keyword "1" starting the search after 3 ms; and another one of 8
145 * bytes under keyword '3' starting after 8 ms. The file size is
146 * used to determine which search result(s) should be used or ignored.
148 static char *download_pattern;
151 * Head of the DLL of publish patterns.
153 static struct Pattern *download_head;
156 * Tail of the DLL of publish patterns.
158 static struct Pattern *download_tail;
162 * Parse a pattern string and store the corresponding
163 * 'struct Pattern' in the given head/tail.
165 * @param head where to store the head
166 * @param tail where to store the tail
167 * @param pattern pattern to parse
168 * @return GNUNET_OK on success
171 parse_pattern (struct Pattern **head,
172 struct Pattern **tail,
176 unsigned long long x;
177 unsigned long long y;
178 unsigned long long t;
180 while (3 == sscanf (pattern,
184 p = GNUNET_new (struct Pattern);
187 p->delay.rel_value_us = (uint64_t) t;
188 GNUNET_CONTAINER_DLL_insert (*head, *tail, p);
189 pattern = strstr (pattern, ")");
190 GNUNET_assert (NULL != pattern);
193 return (0 == strlen (pattern)) ? GNUNET_OK : GNUNET_SYSERR;
198 * Create a KSK URI from a number.
200 * @param kval the number
201 * @return corresponding KSK URI
203 static struct GNUNET_FS_Uri *
204 make_keywords (uint64_t kval)
208 GNUNET_snprintf (kw, sizeof (kw),
209 "%llu", (unsigned long long) kval);
210 return GNUNET_FS_uri_ksk_create (kw, NULL);
215 * Create a file of the given length with a deterministic amount
216 * of data to be published under keyword 'kval'.
218 * @param length number of bytes in the file
219 * @param kval keyword value and seed for the data of the file
220 * @param ctx context to pass to 'fi'
221 * @return file information handle for the file
223 static struct GNUNET_FS_FileInformation *
224 make_file (uint64_t length,
228 struct GNUNET_FS_FileInformation *fi;
229 struct GNUNET_FS_BlockOptions bo;
231 struct GNUNET_FS_Uri *keywords;
232 unsigned long long i;
235 data = NULL; /* to make compilers happy */
236 if ( (0 != length) &&
237 (NULL == (data = GNUNET_malloc_large ((size_t) length))) )
239 /* initialize data with 'unique' data only depending on 'kval' and 'size',
240 making sure that blocks do not repeat */
241 for (i=0;i<length; i+=8)
243 xor = length ^ kval ^ (uint64_t) (i / 32 / 1024);
244 GNUNET_memcpy (&data[i], &xor, GNUNET_MIN (length - i, sizeof (uint64_t)));
246 bo.expiration_time = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_DAYS);
247 bo.anonymity_level = (uint32_t) anonymity_level;
248 bo.content_priority = 128;
249 bo.replication_level = (uint32_t) replication_level;
250 keywords = make_keywords (kval);
251 fi = GNUNET_FS_file_information_create_from_data (fs_handle,
255 NULL, GNUNET_NO, &bo);
256 GNUNET_FS_uri_destroy (keywords);
262 * Task run during shutdown.
267 shutdown_task (void *cls)
271 while (NULL != (p = publish_head))
274 GNUNET_SCHEDULER_cancel (p->task);
276 GNUNET_FS_publish_stop (p->ctx);
277 GNUNET_CONTAINER_DLL_remove (publish_head, publish_tail, p);
280 while (NULL != (p = download_head))
283 GNUNET_SCHEDULER_cancel (p->task);
284 if (NULL != p->stask)
285 GNUNET_SCHEDULER_cancel (p->stask);
287 GNUNET_FS_download_stop (p->ctx, GNUNET_YES);
289 GNUNET_FS_search_stop (p->sctx);
290 GNUNET_CONTAINER_DLL_remove (download_head, download_tail, p);
293 if (NULL != fs_handle)
295 GNUNET_FS_stop (fs_handle);
298 if (NULL != stats_handle)
300 GNUNET_STATISTICS_destroy (stats_handle, GNUNET_YES);
307 * Task run when a publish operation should be stopped.
309 * @param cls the 'struct Pattern' of the publish operation to stop
312 publish_stop_task (void *cls)
314 struct Pattern *p = cls;
317 GNUNET_FS_publish_stop (p->ctx);
322 * Task run when a download operation should be stopped.
324 * @param cls the 'struct Pattern' of the download operation to stop
327 download_stop_task (void *cls)
329 struct Pattern *p = cls;
332 GNUNET_FS_download_stop (p->ctx, GNUNET_YES);
337 * Task run when a download operation should be stopped.
339 * @param cls the 'struct Pattern' of the download operation to stop
342 search_stop_task (void *cls)
344 struct Pattern *p = cls;
347 GNUNET_FS_search_stop (p->sctx);
352 * Notification of FS to a client about the progress of an
353 * operation. Callbacks of this type will be used for uploads,
354 * downloads and searches. Some of the arguments depend a bit
355 * in their meaning on the context in which the callback is used.
358 * @param info details about the event, specifying the event type
359 * and various bits about the event
360 * @return client-context (for the next progress call
361 * for this operation; should be set to NULL for
362 * SUSPEND and STOPPED events). The value returned
363 * will be passed to future callbacks in the respective
364 * field in the GNUNET_FS_ProgressInfo struct.
367 progress_cb (void *cls,
368 const struct GNUNET_FS_ProgressInfo *info)
371 const struct GNUNET_FS_Uri *uri;
373 switch (info->status)
375 case GNUNET_FS_STATUS_PUBLISH_START:
376 case GNUNET_FS_STATUS_PUBLISH_PROGRESS:
377 p = info->value.publish.cctx;
379 case GNUNET_FS_STATUS_PUBLISH_PROGRESS_DIRECTORY:
380 p = info->value.publish.cctx;
382 case GNUNET_FS_STATUS_PUBLISH_ERROR:
383 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
384 "Publishing failed\n");
385 GNUNET_STATISTICS_update (stats_handle,
386 "# failed publish operations", 1, GNUNET_NO);
387 p = info->value.publish.cctx;
388 p->task = GNUNET_SCHEDULER_add_now (&publish_stop_task, p);
390 case GNUNET_FS_STATUS_PUBLISH_COMPLETED:
391 p = info->value.publish.cctx;
392 GNUNET_STATISTICS_update (stats_handle,
393 "# publishing time (ms)",
394 (long long) GNUNET_TIME_absolute_get_duration (p->start_time).rel_value_us / 1000LL,
396 p->task = GNUNET_SCHEDULER_add_now (&publish_stop_task, p);
398 case GNUNET_FS_STATUS_PUBLISH_STOPPED:
399 p = info->value.publish.cctx;
401 GNUNET_CONTAINER_DLL_remove (publish_head, publish_tail, p);
404 case GNUNET_FS_STATUS_DOWNLOAD_START:
405 case GNUNET_FS_STATUS_DOWNLOAD_PROGRESS:
406 case GNUNET_FS_STATUS_DOWNLOAD_ACTIVE:
407 case GNUNET_FS_STATUS_DOWNLOAD_INACTIVE:
408 p = info->value.download.cctx;
410 case GNUNET_FS_STATUS_DOWNLOAD_ERROR:
411 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
412 "Download failed\n");
413 GNUNET_STATISTICS_update (stats_handle,
414 "# failed downloads", 1, GNUNET_NO);
415 p = info->value.download.cctx;
416 p->task = GNUNET_SCHEDULER_add_now (&download_stop_task, p);
418 case GNUNET_FS_STATUS_DOWNLOAD_COMPLETED:
419 p = info->value.download.cctx;
420 GNUNET_STATISTICS_update (stats_handle,
421 "# download time (ms)",
422 (long long) GNUNET_TIME_absolute_get_duration (p->start_time).rel_value_us / 1000LL,
424 p->task = GNUNET_SCHEDULER_add_now (&download_stop_task, p);
426 case GNUNET_FS_STATUS_DOWNLOAD_STOPPED:
427 p = info->value.download.cctx;
431 GNUNET_CONTAINER_DLL_remove (download_head, download_tail, p);
435 case GNUNET_FS_STATUS_SEARCH_START:
436 case GNUNET_FS_STATUS_SEARCH_RESULT_NAMESPACE:
437 p = info->value.search.cctx;
439 case GNUNET_FS_STATUS_SEARCH_RESULT:
440 p = info->value.search.cctx;
441 uri = info->value.search.specifics.result.uri;
442 if (GNUNET_YES != GNUNET_FS_uri_test_chk (uri))
443 return NULL; /* not what we want */
444 if (p->y != GNUNET_FS_uri_chk_get_file_size (uri))
445 return NULL; /* not what we want */
446 GNUNET_STATISTICS_update (stats_handle,
447 "# search time (ms)",
448 (long long) GNUNET_TIME_absolute_get_duration (p->start_time).rel_value_us / 1000LL,
450 p->start_time = GNUNET_TIME_absolute_get ();
451 p->ctx = GNUNET_FS_download_start (fs_handle, uri,
453 0, GNUNET_FS_uri_chk_get_file_size (uri),
455 GNUNET_FS_DOWNLOAD_NO_TEMPORARIES,
458 p->stask = GNUNET_SCHEDULER_add_now (&search_stop_task, p);
460 case GNUNET_FS_STATUS_SEARCH_UPDATE:
461 case GNUNET_FS_STATUS_SEARCH_RESULT_STOPPED:
462 return NULL; /* don't care */
463 case GNUNET_FS_STATUS_SEARCH_ERROR:
464 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
466 GNUNET_STATISTICS_update (stats_handle,
467 "# failed searches", 1, GNUNET_NO);
468 p = info->value.search.cctx;
469 p->stask = GNUNET_SCHEDULER_add_now (&search_stop_task, p);
471 case GNUNET_FS_STATUS_SEARCH_STOPPED:
472 p = info->value.search.cctx;
476 GNUNET_CONTAINER_DLL_remove (download_head, download_tail, p);
481 /* unexpected event during profiling */
489 * Start publish operation.
491 * @param cls the 'struct Pattern' specifying the operation to perform
494 start_publish (void *cls)
496 struct Pattern *p = cls;
497 struct GNUNET_FS_FileInformation *fi;
500 fi = make_file (p->x, p->y, p);
501 p->start_time = GNUNET_TIME_absolute_get ();
502 p->ctx = GNUNET_FS_publish_start (fs_handle,
505 GNUNET_FS_PUBLISH_OPTION_NONE);
510 * Start download operation.
512 * @param cls the 'struct Pattern' specifying the operation to perform
515 start_download (void *cls)
517 struct Pattern *p = cls;
518 struct GNUNET_FS_Uri *keywords;
521 keywords = make_keywords (p->x);
522 p->start_time = GNUNET_TIME_absolute_get ();
523 p->sctx = GNUNET_FS_search_start (fs_handle, keywords,
525 GNUNET_FS_SEARCH_OPTION_NONE,
531 * @brief Main function that will be run by the scheduler.
534 * @param args remaining command-line arguments
535 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
536 * @param cfg_ configuration
539 run (void *cls, char *const *args GNUNET_UNUSED,
540 const char *cfgfile GNUNET_UNUSED,
541 const struct GNUNET_CONFIGURATION_Handle *cfg_)
547 /* Scheduled the task to clean up when shutdown is called */
548 GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
552 GNUNET_CONFIGURATION_get_value_number (cfg,
556 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
557 "TESTBED", "PEERID");
558 global_ret = GNUNET_SYSERR;
559 GNUNET_SCHEDULER_shutdown ();
563 GNUNET_CONFIGURATION_get_value_number (cfg,
564 "FSPROFILER", "ANONYMITY_LEVEL",
568 GNUNET_CONFIGURATION_get_value_number (cfg,
569 "FSPROFILER", "REPLICATION_LEVEL",
571 replication_level = 1;
572 GNUNET_snprintf (myoptname, sizeof (myoptname),
573 "DOWNLOAD-PATTERN-%u", my_peerid);
575 GNUNET_CONFIGURATION_get_value_string (cfg,
576 "FSPROFILER", myoptname,
578 download_pattern = GNUNET_strdup ("");
579 GNUNET_snprintf (myoptname, sizeof (myoptname),
580 "PUBLISH-PATTERN-%u", my_peerid);
582 GNUNET_CONFIGURATION_get_value_string (cfg,
583 "FSPROFILER", myoptname,
585 publish_pattern = GNUNET_strdup ("");
587 parse_pattern (&download_head,
589 download_pattern)) ||
591 parse_pattern (&publish_head,
595 GNUNET_SCHEDULER_shutdown ();
599 stats_handle = GNUNET_STATISTICS_create ("fsprofiler", cfg);
601 GNUNET_FS_start (cfg,
604 GNUNET_FS_FLAGS_NONE,
605 GNUNET_FS_OPTIONS_DOWNLOAD_PARALLELISM, 1,
606 GNUNET_FS_OPTIONS_REQUEST_PARALLELISM, 1,
607 GNUNET_FS_OPTIONS_END);
608 if (NULL == fs_handle)
610 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not acquire FS handle. Exiting.\n");
611 global_ret = GNUNET_SYSERR;
612 GNUNET_SCHEDULER_shutdown ();
615 for (p = publish_head; NULL != p; p = p->next)
616 p->task = GNUNET_SCHEDULER_add_delayed (p->delay,
618 for (p = download_head; NULL != p; p = p->next)
619 p->task = GNUNET_SCHEDULER_add_delayed (p->delay,
625 * Program that performs various "random" FS activities.
627 * @param argc number of arguments from the command line
628 * @param argv command line arguments
629 * @return 0 ok, 1 on error
632 main (int argc, char *const *argv)
634 static const struct GNUNET_GETOPT_CommandLineOption options[] = {
635 GNUNET_GETOPT_OPTION_END
638 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
641 GNUNET_PROGRAM_run (argc, argv, "gnunet-daemon-fsprofiler",
643 ("Daemon to use file-sharing to measure its performance."),
644 options, &run, NULL)) ? global_ret : 1;
647 /* end of gnunet-daemon-fsprofiler.c */