78f62a96ebbbcfcef033e8525d9a4ff52238b52f
[oweals/gnunet.git] / src / util / benchmark.c
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
19 /**
20  * @file util/benchmark.c
21  * @brief benchmarking for various operations
22  * @author Florian Dold <flo@dold.me>
23  */
24
25 #include "platform.h"
26 #include "gnunet_util_lib.h"
27 #include "benchmark.h"
28 #include <pthread.h>
29 #include <sys/syscall.h>
30
31 /**
32  * Thread-local storage key for the benchmark data.
33  */
34 static pthread_key_t key;
35
36 /**
37  * One-time initialization marker for key.
38  */
39 static pthread_once_t key_once = PTHREAD_ONCE_INIT;
40
41
42 /**
43  * Write benchmark data to a file.
44  *
45  * @param bd the benchmark data
46  */
47 static void
48 write_benchmark_data (struct BenchmarkData *bd)
49 {
50   struct GNUNET_DISK_FileHandle *fh;
51   pid_t pid = getpid ();
52   pid_t tid = syscall (SYS_gettid);
53   char *s;
54
55   GNUNET_asprintf (&s, "gnunet-benchmark-%llu-%llu.txt",
56                    (unsigned long long) pid,
57                    (unsigned long long) tid);
58
59   fh = GNUNET_DISK_file_open (s,
60                               (GNUNET_DISK_OPEN_WRITE |
61                                GNUNET_DISK_OPEN_TRUNCATE |
62                                GNUNET_DISK_OPEN_CREATE),
63                               (GNUNET_DISK_PERM_USER_READ |
64                                GNUNET_DISK_PERM_USER_WRITE));
65   GNUNET_assert (NULL != fh);
66   GNUNET_free (s);
67
68   GNUNET_asprintf (&s, "eddsa_sign_count %llu",
69                    (unsigned long long) bd->eddsa_sign_count);
70   GNUNET_assert (GNUNET_SYSERR != GNUNET_DISK_file_write_blocking (fh, s, strlen (s)));
71   GNUNET_free (s);
72
73   GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
74
75   GNUNET_asprintf (&s, "gnunet-benchmark-urls-%llu-%llu.txt",
76                    (unsigned long long) pid,
77                    (unsigned long long) tid);
78
79   fh = GNUNET_DISK_file_open (s,
80                               (GNUNET_DISK_OPEN_WRITE |
81                                GNUNET_DISK_OPEN_TRUNCATE |
82                                GNUNET_DISK_OPEN_CREATE),
83                               (GNUNET_DISK_PERM_USER_READ |
84                                GNUNET_DISK_PERM_USER_WRITE));
85   GNUNET_assert (NULL != fh);
86   GNUNET_free (s);
87
88   for (unsigned int i = 0; i < bd->urd_len; i++)
89   {
90     struct UrlRequestData *urd = &bd->urd[i];
91     GNUNET_asprintf (&s, "url %s count %lld time_us %lld\n",
92                      urd->request_url,
93                      (unsigned long long) urd->count,
94                      (unsigned long long) urd->time.rel_value_us);
95     GNUNET_assert (GNUNET_SYSERR != GNUNET_DISK_file_write_blocking (fh, s, strlen (s)));
96     GNUNET_free (s);
97   }
98
99   GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
100 }
101
102
103 /**
104  * Called when the main thread exits and benchmark data for it was created.
105  */
106 static void
107 main_thread_destructor ()
108 {
109   struct BenchmarkData *bd;
110
111   bd = pthread_getspecific (key);
112   if (NULL != bd)
113     write_benchmark_data (bd);
114 }
115
116
117 /**
118  * Called when a thread exits and benchmark data for it was created.
119  *
120  * @param cls closure
121  */
122 static void
123 thread_destructor (void *cls)
124 {
125   struct BenchmarkData *bd = cls;
126
127   // main thread will be handled by atexit
128   if (getpid () == (pid_t) syscall (SYS_gettid))
129     return;
130   
131   GNUNET_assert (NULL != bd);
132 }
133
134
135 /**
136  * Initialize the thread-local variable key for benchmark data.
137  */
138 static void
139 make_key()
140 {
141   (void) pthread_key_create (&key, &thread_destructor);
142 }
143
144
145 /**
146  * Acquire the benchmark data for the current thread, allocate if necessary.
147  * Installs handler to collect the benchmark data on thread termination.
148  *
149  * @return benchmark data for the current thread
150  */
151 struct BenchmarkData *
152 get_benchmark_data (void)
153 {
154   struct BenchmarkData *bd;
155
156   (void) pthread_once (&key_once, &make_key);
157
158   if (NULL == (bd = pthread_getspecific (key)))
159   {
160     bd = GNUNET_new (struct BenchmarkData);
161     (void) pthread_setspecific (key, bd);
162     if (getpid () == (pid_t) syscall (SYS_gettid))
163     {
164       // We're the main thread!
165       atexit (main_thread_destructor);
166     }
167   }
168   return bd;
169 }
170
171
172 /**
173  * Get benchmark data for a URL.  If the URL is too long, it's truncated
174  * before looking up the correspoding benchmark data.
175  *
176  * @param url url to get request data for
177  */
178 struct UrlRequestData *
179 get_url_benchmark_data (char *url)
180 {
181   char trunc[MAX_BENCHMARK_URL_LEN];
182   struct BenchmarkData *bd;
183
184   if (NULL == url)
185   {
186     /* Should not happen unless curl barfs */
187     GNUNET_break (0);
188     url = "<empty>";
189   }
190
191   memcpy (trunc, url, MAX_BENCHMARK_URL_LEN);
192   trunc[MAX_BENCHMARK_URL_LEN - 1] = 0;
193
194   bd = get_benchmark_data ();
195
196   GNUNET_assert (bd->urd_len <= bd->urd_capacity);
197
198   for (unsigned int i = 0; i < bd->urd_len; i++)
199   {
200     if (0 == strcmp (trunc, bd->urd[i].request_url))
201       return &bd->urd[i];
202   }
203
204   {
205     struct UrlRequestData urd = { 0 };
206
207     memcpy (&urd.request_url, trunc, MAX_BENCHMARK_URL_LEN);
208
209     if (bd->urd_len == bd->urd_capacity)
210     {
211       bd->urd_capacity = 2 * (bd->urd_capacity + 1);
212       bd->urd = GNUNET_realloc (bd->urd, bd->urd_capacity * sizeof (struct UrlRequestData));
213     }
214
215     bd->urd[bd->urd_len++] = urd;
216     return &bd->urd[bd->urd_len - 1];
217   }
218 }