GNUnet  0.10.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, strlen(s))); \
89  GNUNET_free(s); \
90  } while (0)
91 
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);
117 
118 #undef WRITE_BENCHMARK_OP
119 
121 
122  GNUNET_asprintf(&s, "%s/gnunet-benchmark-urls-%s-%llu-%llu.txt",
123  benchmark_dir,
124  (pid == tid) ? "main" : "thread",
125  (unsigned long long)pid,
126  (unsigned long long)tid);
127 
128  fh = GNUNET_DISK_file_open(s,
134  GNUNET_assert(NULL != fh);
135  GNUNET_free(s);
136 
137  for (unsigned int i = 0; i < bd->urd_len; i++)
138  {
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",
141  urd->request_url,
142  urd->status,
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);
149  GNUNET_free(s);
150  }
151 
153 }
154 
155 
159 static void
161 {
162  struct BenchmarkData *bd;
163 
164  bd = pthread_getspecific(key);
165  if (NULL != bd)
167 }
168 
169 
175 static void
177 {
178  struct BenchmarkData *bd = cls;
179 
180  // main thread will be handled by atexit
181  if (getpid() == (pid_t)syscall(SYS_gettid))
182  return;
183 
184  GNUNET_assert(NULL != bd);
186 }
187 
188 
192 static void
194 {
195  (void)pthread_key_create(&key, &thread_destructor);
196 }
197 
198 
205 struct BenchmarkData *
207 {
208  struct BenchmarkData *bd;
209 
210  (void)pthread_once(&key_once, &make_key);
211 
212  if (NULL == (bd = pthread_getspecific(key)))
213  {
214  bd = GNUNET_new(struct BenchmarkData);
215  (void)pthread_setspecific(key, bd);
216  if (getpid() == (pid_t)syscall(SYS_gettid))
217  {
218  // We're the main thread!
219  atexit(main_thread_destructor);
220  }
221  }
222  return bd;
223 }
224 
225 
235 struct UrlRequestData *
236 get_url_benchmark_data(char *url, unsigned int status)
237 {
238  char trunc[MAX_BENCHMARK_URL_LEN];
239  struct BenchmarkData *bd;
240 
241  if (NULL == url)
242  {
243  /* Should not happen unless curl barfs */
244  GNUNET_break(0);
245  url = "<empty>";
246  }
247 
248  memcpy(trunc, url, MAX_BENCHMARK_URL_LEN);
249  trunc[MAX_BENCHMARK_URL_LEN - 1] = 0;
250 
251  /* We're not interested in what's after the query string */
252  for (size_t i = 0; i < strlen(trunc); i++)
253  {
254  if (trunc[i] == '?')
255  {
256  trunc[i] = 0;
257  break;
258  }
259  }
260 
261  bd = get_benchmark_data();
262 
263  GNUNET_assert(bd->urd_len <= bd->urd_capacity);
264 
265  for (unsigned int i = 0; i < bd->urd_len; i++)
266  {
267  if ((0 == strcmp(trunc, bd->urd[i].request_url)) &&
268  (bd->urd[i].status == status))
269  return &bd->urd[i];
270  }
271 
272  {
273  struct UrlRequestData urd = { 0 };
274 
275  memcpy(&urd.request_url, trunc, MAX_BENCHMARK_URL_LEN);
276  urd.status = status;
277 
278  if (bd->urd_len == bd->urd_capacity)
279  {
280  bd->urd_capacity = 2 * (bd->urd_capacity + 1);
281  bd->urd = GNUNET_realloc(bd->urd, bd->urd_capacity * sizeof(struct UrlRequestData));
282  }
283 
284  bd->urd[bd->urd_len++] = urd;
285  return &bd->urd[bd->urd_len - 1];
286  }
287 }
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:1339
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:586
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:176
Struct for benchmark data for one URL.
Definition: benchmark.h:61
struct GNUNET_TIME_Relative time_max
Slowest time to response.
Definition: benchmark.h:95
#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:75
int GNUNET_asprintf(char **buf, const char *format,...)
Like asprintf, just portable.
struct UrlRequestData * urd
Definition: benchmark.h:137
#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:110
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:80
struct GNUNET_TIME_Relative time
Total time spent requesting this URL.
Definition: benchmark.h:90
static void main_thread_destructor()
Called when the main thread exits and benchmark data for it was created.
Definition: benchmark.c:160
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:841
unsigned int urd_len
Definition: benchmark.h:139
#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:85
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:193
unsigned int urd_capacity
Definition: benchmark.h:141
benchmarking for various operations
struct UrlRequestData * get_url_benchmark_data(char *url, unsigned int status)
Get benchmark data for a URL.
Definition: benchmark.c:236
static struct GNUNET_PeerIdentity pid
Identity of the peer we transmit to / connect to.
unsigned int status
HTTP status code.
Definition: benchmark.h:70
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:1262
#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:206
Handle used to access files (and pipes).
char request_url[128]
Request URL, truncated (but 0-terminated).
Definition: benchmark.h:65
#define GNUNET_free(ptr)
Wrapper around free.