123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293 |
- /*
- This file is part of GNUnet.
- Copyright (C) 2018 GNUnet e.V.
- GNUnet is free software: you can redistribute it and/or modify it
- under the terms of the GNU Affero General Public License as published
- by the Free Software Foundation, either version 3 of the License,
- or (at your option) any later version.
- GNUnet is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Affero General Public License for more details.
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- SPDX-License-Identifier: AGPL3.0-or-later
- */
- /**
- * @file util/benchmark.c
- * @brief benchmarking for various operations
- * @author Florian Dold <flo@dold.me>
- */
- #include "platform.h"
- #include "gnunet_util_lib.h"
- #include "benchmark.h"
- #include <pthread.h>
- #include <sys/syscall.h>
- /**
- * Thread-local storage key for the benchmark data.
- */
- static pthread_key_t key;
- /**
- * One-time initialization marker for key.
- */
- static pthread_once_t key_once = PTHREAD_ONCE_INIT;
- /**
- * Write benchmark data to a file.
- *
- * @param bd the benchmark data
- */
- static void
- write_benchmark_data (struct BenchmarkData *bd)
- {
- struct GNUNET_DISK_FileHandle *fh;
- pid_t pid = getpid ();
- pid_t tid = syscall (SYS_gettid);
- char *benchmark_dir;
- char *s;
- benchmark_dir = getenv ("GNUNET_BENCHMARK_DIR");
- if (NULL == benchmark_dir)
- return;
- if (GNUNET_OK != GNUNET_DISK_directory_create (benchmark_dir))
- {
- GNUNET_break (0);
- return;
- }
- GNUNET_asprintf (&s, "%s/gnunet-benchmark-ops-%s-%llu-%llu.txt",
- benchmark_dir,
- (pid == tid) ? "main" : "thread",
- (unsigned long long) pid,
- (unsigned long long) tid);
- fh = GNUNET_DISK_file_open (s,
- (GNUNET_DISK_OPEN_WRITE
- | GNUNET_DISK_OPEN_TRUNCATE
- | GNUNET_DISK_OPEN_CREATE),
- (GNUNET_DISK_PERM_USER_READ
- | GNUNET_DISK_PERM_USER_WRITE));
- GNUNET_assert (NULL != fh);
- GNUNET_free (s);
- #define WRITE_BENCHMARK_OP(opname) do { \
- GNUNET_asprintf (&s, "op " #opname " count %llu time_us %llu\n", \
- (unsigned long long) bd->opname ## _count, \
- (unsigned long long) bd->opname ## _time.rel_value_us); \
- GNUNET_assert (GNUNET_SYSERR != GNUNET_DISK_file_write_blocking (fh, s, \
- strlen ( \
- s))); \
- GNUNET_free (s); \
- } while (0)
- WRITE_BENCHMARK_OP (ecc_ecdh);
- WRITE_BENCHMARK_OP (ecdh_eddsa);
- WRITE_BENCHMARK_OP (ecdhe_key_create);
- WRITE_BENCHMARK_OP (ecdhe_key_get_public);
- WRITE_BENCHMARK_OP (ecdsa_ecdh);
- WRITE_BENCHMARK_OP (ecdsa_key_create);
- WRITE_BENCHMARK_OP (ecdsa_key_get_public);
- WRITE_BENCHMARK_OP (ecdsa_sign);
- WRITE_BENCHMARK_OP (ecdsa_verify);
- WRITE_BENCHMARK_OP (eddsa_ecdh);
- WRITE_BENCHMARK_OP (eddsa_key_create);
- WRITE_BENCHMARK_OP (eddsa_key_get_public);
- WRITE_BENCHMARK_OP (eddsa_sign);
- WRITE_BENCHMARK_OP (eddsa_verify);
- WRITE_BENCHMARK_OP (hash);
- WRITE_BENCHMARK_OP (hash_context_finish);
- WRITE_BENCHMARK_OP (hash_context_read);
- WRITE_BENCHMARK_OP (hash_context_start);
- WRITE_BENCHMARK_OP (hkdf);
- WRITE_BENCHMARK_OP (rsa_blind);
- WRITE_BENCHMARK_OP (rsa_private_key_create);
- WRITE_BENCHMARK_OP (rsa_private_key_get_public);
- WRITE_BENCHMARK_OP (rsa_sign_blinded);
- WRITE_BENCHMARK_OP (rsa_unblind);
- WRITE_BENCHMARK_OP (rsa_verify);
- #undef WRITE_BENCHMARK_OP
- GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
- GNUNET_asprintf (&s, "%s/gnunet-benchmark-urls-%s-%llu-%llu.txt",
- benchmark_dir,
- (pid == tid) ? "main" : "thread",
- (unsigned long long) pid,
- (unsigned long long) tid);
- fh = GNUNET_DISK_file_open (s,
- (GNUNET_DISK_OPEN_WRITE
- | GNUNET_DISK_OPEN_TRUNCATE
- | GNUNET_DISK_OPEN_CREATE),
- (GNUNET_DISK_PERM_USER_READ
- | GNUNET_DISK_PERM_USER_WRITE));
- GNUNET_assert (NULL != fh);
- GNUNET_free (s);
- for (unsigned int i = 0; i < bd->urd_len; i++)
- {
- struct UrlRequestData *urd = &bd->urd[i];
- GNUNET_asprintf (&s,
- "url %s status %u count %llu time_us %llu time_us_max %llu bytes_sent %llu bytes_received %llu\n",
- urd->request_url,
- urd->status,
- (unsigned long long) urd->count,
- (unsigned long long) urd->time.rel_value_us,
- (unsigned long long) urd->time_max.rel_value_us,
- (unsigned long long) urd->bytes_sent,
- (unsigned long long) urd->bytes_received);
- GNUNET_assert (GNUNET_SYSERR != GNUNET_DISK_file_write_blocking (fh, s,
- strlen (
- s)));
- GNUNET_free (s);
- }
- GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
- }
- /**
- * Called when the main thread exits and benchmark data for it was created.
- */
- static void
- main_thread_destructor ()
- {
- struct BenchmarkData *bd;
- bd = pthread_getspecific (key);
- if (NULL != bd)
- write_benchmark_data (bd);
- }
- /**
- * Called when a thread exits and benchmark data for it was created.
- *
- * @param cls closure
- */
- static void
- thread_destructor (void *cls)
- {
- struct BenchmarkData *bd = cls;
- // main thread will be handled by atexit
- if (getpid () == (pid_t) syscall (SYS_gettid))
- return;
- GNUNET_assert (NULL != bd);
- write_benchmark_data (bd);
- }
- /**
- * Initialize the thread-local variable key for benchmark data.
- */
- static void
- make_key ()
- {
- (void) pthread_key_create (&key, &thread_destructor);
- }
- /**
- * Acquire the benchmark data for the current thread, allocate if necessary.
- * Installs handler to collect the benchmark data on thread termination.
- *
- * @return benchmark data for the current thread
- */
- struct BenchmarkData *
- get_benchmark_data (void)
- {
- struct BenchmarkData *bd;
- (void) pthread_once (&key_once, &make_key);
- if (NULL == (bd = pthread_getspecific (key)))
- {
- bd = GNUNET_new (struct BenchmarkData);
- (void) pthread_setspecific (key, bd);
- if (getpid () == (pid_t) syscall (SYS_gettid))
- {
- // We're the main thread!
- atexit (main_thread_destructor);
- }
- }
- return bd;
- }
- /**
- * Get benchmark data for a URL. If the URL is too long, it's truncated
- * before looking up the correspoding benchmark data.
- *
- * Statistics are bucketed by URL and status code.
- *
- * @param url url to get request data for
- * @param status http status code
- */
- struct UrlRequestData *
- get_url_benchmark_data (char *url, unsigned int status)
- {
- char trunc[MAX_BENCHMARK_URL_LEN];
- struct BenchmarkData *bd;
- if (NULL == url)
- {
- /* Should not happen unless curl barfs */
- GNUNET_break (0);
- url = "<empty>";
- }
- memcpy (trunc, url, MAX_BENCHMARK_URL_LEN);
- trunc[MAX_BENCHMARK_URL_LEN - 1] = 0;
- /* We're not interested in what's after the query string */
- for (size_t i = 0; i < strlen (trunc); i++)
- {
- if (trunc[i] == '?')
- {
- trunc[i] = 0;
- break;
- }
- }
- bd = get_benchmark_data ();
- GNUNET_assert (bd->urd_len <= bd->urd_capacity);
- for (unsigned int i = 0; i < bd->urd_len; i++)
- {
- if ((0 == strcmp (trunc, bd->urd[i].request_url)) &&
- (bd->urd[i].status == status))
- return &bd->urd[i];
- }
- {
- struct UrlRequestData urd = { 0 };
- memcpy (&urd.request_url, trunc, MAX_BENCHMARK_URL_LEN);
- urd.status = status;
- if (bd->urd_len == bd->urd_capacity)
- {
- bd->urd_capacity = 2 * (bd->urd_capacity + 1);
- bd->urd = GNUNET_realloc (bd->urd, bd->urd_capacity * sizeof(struct
- UrlRequestData));
- }
- bd->urd[bd->urd_len++] = urd;
- return &bd->urd[bd->urd_len - 1];
- }
- }
|