GNUnet  0.11.x
benchmark.c
Go to the documentation of this file.
1 /*
2  This file is part of GNUnet.
3  Copyright (C) 2018 GNUnet e.V.
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 
27 #include "platform.h"
28 #include "gnunet_util_lib.h"
29 #include "benchmark.h"
30 #include <pthread.h>
31 #include <sys/syscall.h>
32 
36 static pthread_key_t key;
37 
41 static pthread_once_t key_once = PTHREAD_ONCE_INIT;
42 
43 
49 static void
51 {
52  struct GNUNET_DISK_FileHandle *fh;
53  pid_t pid = getpid ();
54  pid_t tid = syscall (SYS_gettid);
55  char *benchmark_dir;
56  char *s;
57 
58  benchmark_dir = getenv ("GNUNET_BENCHMARK_DIR");
59 
60  if (NULL == benchmark_dir)
61  return;
62 
63  if (GNUNET_OK != GNUNET_DISK_directory_create (benchmark_dir))
64  {
65  GNUNET_break (0);
66  return;
67  }
68 
69  GNUNET_asprintf (&s, "%s/gnunet-benchmark-ops-%s-%llu-%llu.txt",
70  benchmark_dir,
71  (pid == tid) ? "main" : "thread",
72  (unsigned long long) pid,
73  (unsigned long long) tid);
74 
75  fh = GNUNET_DISK_file_open (s,
81  GNUNET_assert (NULL != fh);
82  GNUNET_free (s);
83 
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, \
89  strlen ( \
90  s))); \
91  GNUNET_free (s); \
92 } while (0)
93 
94  WRITE_BENCHMARK_OP (ecc_ecdh);
95  WRITE_BENCHMARK_OP (ecdh_eddsa);
96  WRITE_BENCHMARK_OP (ecdhe_key_create);
97  WRITE_BENCHMARK_OP (ecdhe_key_get_public);
98  WRITE_BENCHMARK_OP (ecdsa_ecdh);
99  WRITE_BENCHMARK_OP (ecdsa_key_create);
100  WRITE_BENCHMARK_OP (ecdsa_key_get_public);
101  WRITE_BENCHMARK_OP (ecdsa_sign);
102  WRITE_BENCHMARK_OP (ecdsa_verify);
103  WRITE_BENCHMARK_OP (eddsa_ecdh);
104  WRITE_BENCHMARK_OP (eddsa_key_create);
105  WRITE_BENCHMARK_OP (eddsa_key_get_public);
106  WRITE_BENCHMARK_OP (eddsa_sign);
107  WRITE_BENCHMARK_OP (eddsa_verify);
108  WRITE_BENCHMARK_OP (hash);
109  WRITE_BENCHMARK_OP (hash_context_finish);
110  WRITE_BENCHMARK_OP (hash_context_read);
111  WRITE_BENCHMARK_OP (hash_context_start);
112  WRITE_BENCHMARK_OP (hkdf);
113  WRITE_BENCHMARK_OP (rsa_blind);
114  WRITE_BENCHMARK_OP (rsa_private_key_create);
115  WRITE_BENCHMARK_OP (rsa_private_key_get_public);
116  WRITE_BENCHMARK_OP (rsa_sign_blinded);
117  WRITE_BENCHMARK_OP (rsa_unblind);
118  WRITE_BENCHMARK_OP (rsa_verify);
119 
120 #undef WRITE_BENCHMARK_OP
121 
123 
124  GNUNET_asprintf (&s, "%s/gnunet-benchmark-urls-%s-%llu-%llu.txt",
125  benchmark_dir,
126  (pid == tid) ? "main" : "thread",
127  (unsigned long long) pid,
128  (unsigned long long) tid);
129 
130  fh = GNUNET_DISK_file_open (s,
136  GNUNET_assert (NULL != fh);
137  GNUNET_free (s);
138 
139  for (unsigned int i = 0; i < bd->urd_len; i++)
140  {
141  struct UrlRequestData *urd = &bd->urd[i];
142  GNUNET_asprintf (&s,
143  "url %s status %u count %llu time_us %llu time_us_max %llu bytes_sent %llu bytes_received %llu\n",
144  urd->request_url,
145  urd->status,
146  (unsigned long long) urd->count,
147  (unsigned long long) urd->time.rel_value_us,
148  (unsigned long long) urd->time_max.rel_value_us,
149  (unsigned long long) urd->bytes_sent,
150  (unsigned long long) urd->bytes_received);
152  strlen (
153  s)));
154  GNUNET_free (s);
155  }
156 
158 }
159 
160 
164 static void
166 {
167  struct BenchmarkData *bd;
168 
169  bd = pthread_getspecific (key);
170  if (NULL != bd)
172 }
173 
174 
180 static void
181 thread_destructor (void *cls)
182 {
183  struct BenchmarkData *bd = cls;
184 
185  // main thread will be handled by atexit
186  if (getpid () == (pid_t) syscall (SYS_gettid))
187  return;
188 
189  GNUNET_assert (NULL != bd);
191 }
192 
193 
197 static void
199 {
200  (void) pthread_key_create (&key, &thread_destructor);
201 }
202 
203 
210 struct BenchmarkData *
212 {
213  struct BenchmarkData *bd;
214 
215  (void) pthread_once (&key_once, &make_key);
216 
217  if (NULL == (bd = pthread_getspecific (key)))
218  {
219  bd = GNUNET_new (struct BenchmarkData);
220  (void) pthread_setspecific (key, bd);
221  if (getpid () == (pid_t) syscall (SYS_gettid))
222  {
223  // We're the main thread!
224  atexit (main_thread_destructor);
225  }
226  }
227  return bd;
228 }
229 
230 
240 struct UrlRequestData *
241 get_url_benchmark_data (char *url, unsigned int status)
242 {
243  char trunc[MAX_BENCHMARK_URL_LEN];
244  struct BenchmarkData *bd;
245 
246  if (NULL == url)
247  {
248  /* Should not happen unless curl barfs */
249  GNUNET_break (0);
250  url = "<empty>";
251  }
252 
253  memcpy (trunc, url, MAX_BENCHMARK_URL_LEN);
254  trunc[MAX_BENCHMARK_URL_LEN - 1] = 0;
255 
256  /* We're not interested in what's after the query string */
257  for (size_t i = 0; i < strlen (trunc); i++)
258  {
259  if (trunc[i] == '?')
260  {
261  trunc[i] = 0;
262  break;
263  }
264  }
265 
266  bd = get_benchmark_data ();
267 
268  GNUNET_assert (bd->urd_len <= bd->urd_capacity);
269 
270  for (unsigned int i = 0; i < bd->urd_len; i++)
271  {
272  if ((0 == strcmp (trunc, bd->urd[i].request_url)) &&
273  (bd->urd[i].status == status))
274  return &bd->urd[i];
275  }
276 
277  {
278  struct UrlRequestData urd = { 0 };
279 
280  memcpy (&urd.request_url, trunc, MAX_BENCHMARK_URL_LEN);
281  urd.status = status;
282 
283  if (bd->urd_len == bd->urd_capacity)
284  {
285  bd->urd_capacity = 2 * (bd->urd_capacity + 1);
286  bd->urd = GNUNET_realloc (bd->urd, bd->urd_capacity * sizeof(struct
287  UrlRequestData));
288  }
289 
290  bd->urd[bd->urd_len++] = urd;
291  return &bd->urd[bd->urd_len - 1];
292  }
293 }
Create file if it doesn&#39;t exist.
static pthread_once_t key_once
One-time initialization marker for key.
Definition: benchmark.c:41
int GNUNET_DISK_file_close(struct GNUNET_DISK_FileHandle *h)
Close an open file.
Definition: disk.c:1345
uint64_t rel_value_us
The actual value.
static pthread_key_t key
Thread-local storage key for the benchmark data.
Definition: benchmark.c:36
int GNUNET_DISK_directory_create(const char *dir)
Implementation of "mkdir -p".
Definition: disk.c:589
static void write_benchmark_data(struct BenchmarkData *bd)
Write benchmark data to a file.
Definition: benchmark.c:50
#define GNUNET_assert(cond)
Use this for fatal errors that cannot be handled.
#define GNUNET_OK
Named constants for return values.
Definition: gnunet_common.h:75
#define GNUNET_new(type)
Allocate a struct or union of the given type.
static void thread_destructor(void *cls)
Called when a thread exits and benchmark data for it was created.
Definition: benchmark.c:181
Struct for benchmark data for one URL.
Definition: benchmark.h:65
struct GNUNET_TIME_Relative time_max
Slowest time to response.
Definition: benchmark.h:100
#define GNUNET_break(cond)
Use this for internal assertion violations that are not fatal (can be handled) but should not occur...
uint64_t count
How often was the URL requested?
Definition: benchmark.h:80
int GNUNET_asprintf(char **buf, const char *format,...)
Like asprintf, just portable.
struct UrlRequestData * urd
Definition: benchmark.h:143
#define GNUNET_realloc(ptr, size)
Wrapper around realloc.
Truncate file if it exists.
static struct GNUNET_SCHEDULER_Task * tid
Task identifier of the task that waits for stdin.
uint16_t status
See PRISM_STATUS_*-constants.
Thread-local struct for benchmarking data.
Definition: benchmark.h:115
static int fh
Handle to the unique file.
uint64_t bytes_sent
How many bytes were sent in total to request the URL.
Definition: benchmark.h:85
struct GNUNET_TIME_Relative time
Total time spent requesting this URL.
Definition: benchmark.h:95
static void main_thread_destructor()
Called when the main thread exits and benchmark data for it was created.
Definition: benchmark.c:165
ssize_t GNUNET_DISK_file_write_blocking(const struct GNUNET_DISK_FileHandle *h, const void *buffer, size_t n)
Write a buffer to a file, blocking, if necessary.
Definition: disk.c:844
unsigned int urd_len
Definition: benchmark.h:145
#define GNUNET_SYSERR
Definition: gnunet_common.h:76
uint64_t bytes_received
How many bytes were received in total as response to requesting this URL.
Definition: benchmark.h:90
Open the file for writing.
char * getenv()
#define MAX_BENCHMARK_URL_LEN
Maximum length of URLs considered for benchmarking.
Definition: benchmark.h:36
static void make_key()
Initialize the thread-local variable key for benchmark data.
Definition: benchmark.c:198
unsigned int urd_capacity
Definition: benchmark.h:147
benchmarking for various operations
struct UrlRequestData * get_url_benchmark_data(char *url, unsigned int status)
Get benchmark data for a URL.
Definition: benchmark.c:241
static struct GNUNET_PeerIdentity pid
Identity of the peer we transmit to / connect to.
unsigned int status
HTTP status code.
Definition: benchmark.h:75
struct GNUNET_DISK_FileHandle * GNUNET_DISK_file_open(const char *fn, enum GNUNET_DISK_OpenFlags flags, enum GNUNET_DISK_AccessPermissions perm)
Open a file.
Definition: disk.c:1268
#define WRITE_BENCHMARK_OP(opname)
struct BenchmarkData * get_benchmark_data(void)
Acquire the benchmark data for the current thread, allocate if necessary.
Definition: benchmark.c:211
Handle used to access files (and pipes).
char request_url[128]
Request URL, truncated (but 0-terminated).
Definition: benchmark.h:70
#define GNUNET_free(ptr)
Wrapper around free.