2 This file is part of GNUnet.
3 Copyright (C) 2018 GNUnet e.V.
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.
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.
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/>.
18 SPDX-License-Identifier: AGPL3.0-or-later
22 * @file util/benchmark.c
23 * @brief benchmarking for various operations
24 * @author Florian Dold <flo@dold.me>
28 #include "gnunet_util_lib.h"
29 #include "benchmark.h"
31 #include <sys/syscall.h>
34 * Thread-local storage key for the benchmark data.
36 static pthread_key_t key;
39 * One-time initialization marker for key.
41 static pthread_once_t key_once = PTHREAD_ONCE_INIT;
45 * Write benchmark data to a file.
47 * @param bd the benchmark data
50 write_benchmark_data (struct BenchmarkData *bd)
52 struct GNUNET_DISK_FileHandle *fh;
53 pid_t pid = getpid ();
54 pid_t tid = syscall (SYS_gettid);
58 benchmark_dir = getenv ("GNUNET_BENCHMARK_DIR");
60 if (NULL == benchmark_dir)
63 if (GNUNET_OK != GNUNET_DISK_directory_create (benchmark_dir))
69 GNUNET_asprintf (&s, "%s/gnunet-benchmark-ops-%s-%llu-%llu.txt",
71 (pid == tid) ? "main" : "thread",
72 (unsigned long long) pid,
73 (unsigned long long) tid);
75 fh = GNUNET_DISK_file_open (s,
76 (GNUNET_DISK_OPEN_WRITE |
77 GNUNET_DISK_OPEN_TRUNCATE |
78 GNUNET_DISK_OPEN_CREATE),
79 (GNUNET_DISK_PERM_USER_READ |
80 GNUNET_DISK_PERM_USER_WRITE));
81 GNUNET_assert (NULL != fh);
84 #define WRITE_BENCHMARK_OP(opname) do { \
85 GNUNET_asprintf (&s, "op " #opname " count %llu time_us %llu\n", \
86 (unsigned long long) bd->opname##_count, \
87 (unsigned long long) bd->opname##_time.rel_value_us); \
88 GNUNET_assert (GNUNET_SYSERR != GNUNET_DISK_file_write_blocking (fh, s, strlen (s))); \
92 WRITE_BENCHMARK_OP (ecc_ecdh);
93 WRITE_BENCHMARK_OP (ecdh_eddsa);
94 WRITE_BENCHMARK_OP (ecdhe_key_create);
95 WRITE_BENCHMARK_OP (ecdhe_key_get_public);
96 WRITE_BENCHMARK_OP (ecdsa_ecdh);
97 WRITE_BENCHMARK_OP (ecdsa_key_create);
98 WRITE_BENCHMARK_OP (ecdsa_key_get_public);
99 WRITE_BENCHMARK_OP (ecdsa_sign);
100 WRITE_BENCHMARK_OP (ecdsa_verify);
101 WRITE_BENCHMARK_OP (eddsa_ecdh);
102 WRITE_BENCHMARK_OP (eddsa_key_create);
103 WRITE_BENCHMARK_OP (eddsa_key_get_public);
104 WRITE_BENCHMARK_OP (eddsa_sign);
105 WRITE_BENCHMARK_OP (eddsa_verify);
106 WRITE_BENCHMARK_OP (hash);
107 WRITE_BENCHMARK_OP (hash_context_finish);
108 WRITE_BENCHMARK_OP (hash_context_read);
109 WRITE_BENCHMARK_OP (hash_context_start);
110 WRITE_BENCHMARK_OP (hkdf);
111 WRITE_BENCHMARK_OP (rsa_blind);
112 WRITE_BENCHMARK_OP (rsa_private_key_create);
113 WRITE_BENCHMARK_OP (rsa_private_key_get_public);
114 WRITE_BENCHMARK_OP (rsa_sign_blinded);
115 WRITE_BENCHMARK_OP (rsa_unblind);
116 WRITE_BENCHMARK_OP (rsa_verify);
118 #undef WRITE_BENCHMARK_OP
120 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
122 GNUNET_asprintf (&s, "%s/gnunet-benchmark-urls-%s-%llu-%llu.txt",
124 (pid == tid) ? "main" : "thread",
125 (unsigned long long) pid,
126 (unsigned long long) tid);
128 fh = GNUNET_DISK_file_open (s,
129 (GNUNET_DISK_OPEN_WRITE |
130 GNUNET_DISK_OPEN_TRUNCATE |
131 GNUNET_DISK_OPEN_CREATE),
132 (GNUNET_DISK_PERM_USER_READ |
133 GNUNET_DISK_PERM_USER_WRITE));
134 GNUNET_assert (NULL != fh);
137 for (unsigned int i = 0; i < bd->urd_len; i++)
139 struct UrlRequestData *urd = &bd->urd[i];
140 GNUNET_asprintf (&s, "url %s status %u count %llu time_us %llu time_us_max %llu bytes_sent %llu bytes_received %llu\n",
143 (unsigned long long) urd->count,
144 (unsigned long long) urd->time.rel_value_us,
145 (unsigned long long) urd->time_max.rel_value_us,
146 (unsigned long long) urd->bytes_sent,
147 (unsigned long long) urd->bytes_received);
148 GNUNET_assert (GNUNET_SYSERR != GNUNET_DISK_file_write_blocking (fh, s, strlen (s)));
152 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
157 * Called when the main thread exits and benchmark data for it was created.
160 main_thread_destructor ()
162 struct BenchmarkData *bd;
164 bd = pthread_getspecific (key);
166 write_benchmark_data (bd);
171 * Called when a thread exits and benchmark data for it was created.
176 thread_destructor (void *cls)
178 struct BenchmarkData *bd = cls;
180 // main thread will be handled by atexit
181 if (getpid () == (pid_t) syscall (SYS_gettid))
184 GNUNET_assert (NULL != bd);
185 write_benchmark_data (bd);
190 * Initialize the thread-local variable key for benchmark data.
195 (void) pthread_key_create (&key, &thread_destructor);
200 * Acquire the benchmark data for the current thread, allocate if necessary.
201 * Installs handler to collect the benchmark data on thread termination.
203 * @return benchmark data for the current thread
205 struct BenchmarkData *
206 get_benchmark_data (void)
208 struct BenchmarkData *bd;
210 (void) pthread_once (&key_once, &make_key);
212 if (NULL == (bd = pthread_getspecific (key)))
214 bd = GNUNET_new (struct BenchmarkData);
215 (void) pthread_setspecific (key, bd);
216 if (getpid () == (pid_t) syscall (SYS_gettid))
218 // We're the main thread!
219 atexit (main_thread_destructor);
227 * Get benchmark data for a URL. If the URL is too long, it's truncated
228 * before looking up the correspoding benchmark data.
230 * Statistics are bucketed by URL and status code.
232 * @param url url to get request data for
233 * @param status http status code
235 struct UrlRequestData *
236 get_url_benchmark_data (char *url, unsigned int status)
238 char trunc[MAX_BENCHMARK_URL_LEN];
239 struct BenchmarkData *bd;
243 /* Should not happen unless curl barfs */
248 memcpy (trunc, url, MAX_BENCHMARK_URL_LEN);
249 trunc[MAX_BENCHMARK_URL_LEN - 1] = 0;
251 /* We're not interested in what's after the query string */
252 for (size_t i = 0; i < strlen (trunc); i++)
261 bd = get_benchmark_data ();
263 GNUNET_assert (bd->urd_len <= bd->urd_capacity);
265 for (unsigned int i = 0; i < bd->urd_len; i++)
267 if ( (0 == strcmp (trunc, bd->urd[i].request_url)) &&
268 (bd->urd[i].status == status) )
273 struct UrlRequestData urd = { 0 };
275 memcpy (&urd.request_url, trunc, MAX_BENCHMARK_URL_LEN);
278 if (bd->urd_len == bd->urd_capacity)
280 bd->urd_capacity = 2 * (bd->urd_capacity + 1);
281 bd->urd = GNUNET_realloc (bd->urd, bd->urd_capacity * sizeof (struct UrlRequestData));
284 bd->urd[bd->urd_len++] = urd;
285 return &bd->urd[bd->urd_len - 1];