OpenVPN
perf.c
Go to the documentation of this file.
1/*
2 * OpenVPN -- An application to securely tunnel IP networks
3 * over a single TCP/UDP port, with support for SSL/TLS-based
4 * session authentication and key exchange,
5 * packet encryption, packet authentication, and
6 * packet compression.
7 *
8 * Copyright (C) 2002-2024 OpenVPN Inc <sales@openvpn.net>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2
12 * as published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 */
23
24#ifdef HAVE_CONFIG_H
25#include "config.h"
26#endif
27
28#include "syshead.h"
29
30#include "perf.h"
31
32#ifdef ENABLE_PERFORMANCE_METRICS
33
34#include "error.h"
35#include "otime.h"
36
37#include "memdbg.h"
38
39static const char *metric_names[] = {
40 "PERF_BIO_READ_PLAINTEXT",
41 "PERF_BIO_WRITE_PLAINTEXT",
42 "PERF_BIO_READ_CIPHERTEXT",
43 "PERF_BIO_WRITE_CIPHERTEXT",
44 "PERF_TLS_MULTI_PROCESS",
45 "PERF_IO_WAIT",
46 "PERF_EVENT_LOOP",
47 "PERF_MULTI_CREATE_INSTANCE",
48 "PERF_MULTI_CLOSE_INSTANCE",
49 "PERF_MULTI_SHOW_STATS",
50 "PERF_MULTI_BCAST",
51 "PERF_MULTI_MCAST",
52 "PERF_SCRIPT",
53 "PERF_READ_IN_LINK",
54 "PERF_PROC_IN_LINK",
55 "PERF_READ_IN_TUN",
56 "PERF_PROC_IN_TUN",
57 "PERF_PROC_OUT_LINK",
58 "PERF_PROC_OUT_TUN",
59 "PERF_PROC_OUT_TUN_MTCP"
60};
61
62struct perf
63{
64#define PS_INITIAL 0
65#define PS_METER_RUNNING 1
66#define PS_METER_INTERRUPTED 2
67 int state;
68
69 struct timeval start;
70 double sofar;
71 double sum;
72 double max;
73 double count;
74};
75
76struct perf_set
77{
78 int stack_len;
79 int stack[STACK_N];
80 struct perf perf[PERF_N];
81};
82
83static struct perf_set perf_set;
84
85static void perf_print_state(int lev);
86
87static inline int
88get_stack_index(int sdelta)
89{
90 const int sindex = perf_set.stack_len + sdelta;
91 if (sindex >= 0 && sindex < STACK_N)
92 {
93 return sindex;
94 }
95 else
96 {
97 return -1;
98 }
99}
100
101static int
102get_perf_index(int sdelta)
103{
104 const int sindex = get_stack_index(sdelta);
105 if (sindex >= 0)
106 {
107 const int pindex = perf_set.stack[sindex];
108 if (pindex >= 0 && pindex < PERF_N)
109 {
110 return pindex;
111 }
112 else
113 {
114 return -1;
115 }
116 }
117 else
118 {
119 return -1;
120 }
121}
122
123static struct perf *
124get_perf(int sdelta)
125{
126 const int pindex = get_perf_index(sdelta);
127 if (pindex >= 0)
128 {
129 return &perf_set.perf[pindex];
130 }
131 else
132 {
133 return NULL;
134 }
135}
136
137static void
138push_perf_index(int pindex)
139{
140 const int sindex = get_stack_index(0);
141 const int newlen = get_stack_index(1);
142 if (sindex >= 0 && newlen >= 0
143 && pindex >= 0 && pindex < PERF_N)
144 {
145 int i;
146 for (i = 0; i < sindex; ++i)
147 {
148 if (perf_set.stack[i] == pindex)
149 {
150 perf_print_state(M_INFO);
151 msg(M_FATAL, "PERF: push_perf_index %s failed",
152 metric_names [pindex]);
153 }
154 }
155
156 perf_set.stack[sindex] = pindex;
157 perf_set.stack_len = newlen;
158 }
159 else
160 {
161 msg(M_FATAL, "PERF: push_perf_index: stack push error");
162 }
163}
164
165static void
166pop_perf_index(void)
167{
168 const int newlen = get_stack_index(-1);
169 if (newlen >= 0)
170 {
171 perf_set.stack_len = newlen;
172 }
173 else
174 {
175 msg(M_FATAL, "PERF: pop_perf_index: stack pop error");
176 }
177}
178
179static void
180state_must_be(const struct perf *p, const int wanted)
181{
182 if (p->state != wanted)
183 {
184 msg(M_FATAL, "PERF: bad state actual=%d wanted=%d",
185 p->state,
186 wanted);
187 }
188}
189
190static void
191update_sofar(struct perf *p)
192{
193 struct timeval current;
194 ASSERT(!gettimeofday(&current, NULL));
195 p->sofar += (double) tv_subtract(&current, &p->start, 600) / 1000000.0;
196 tv_clear(&p->start);
197}
198
199static void
200perf_start(struct perf *p)
201{
202 state_must_be(p, PS_INITIAL);
203 ASSERT(!gettimeofday(&p->start, NULL));
204 p->sofar = 0.0;
205 p->state = PS_METER_RUNNING;
206}
207
208static void
209perf_stop(struct perf *p)
210{
211 state_must_be(p, PS_METER_RUNNING);
212 update_sofar(p);
213 p->sum += p->sofar;
214 if (p->sofar > p->max)
215 {
216 p->max = p->sofar;
217 }
218 p->count += 1.0;
219 p->sofar = 0.0;
220 p->state = PS_INITIAL;
221}
222
223static void
224perf_interrupt(struct perf *p)
225{
226 state_must_be(p, PS_METER_RUNNING);
227 update_sofar(p);
228 p->state = PS_METER_INTERRUPTED;
229}
230
231static void
232perf_resume(struct perf *p)
233{
234 state_must_be(p, PS_METER_INTERRUPTED);
235 ASSERT(!gettimeofday(&p->start, NULL));
236 p->state = PS_METER_RUNNING;
237}
238
239void
240perf_push(int type)
241{
242 struct perf *prev;
243 struct perf *cur;
244
245 ASSERT(SIZE(metric_names) == PERF_N);
246 push_perf_index(type);
247
248 prev = get_perf(-2);
249 cur = get_perf(-1);
250
251 ASSERT(cur);
252
253 if (prev)
254 {
255 perf_interrupt(prev);
256 }
257 perf_start(cur);
258}
259
260void
261perf_pop(void)
262{
263 struct perf *prev;
264 struct perf *cur;
265
266 prev = get_perf(-2);
267 cur = get_perf(-1);
268
269 ASSERT(cur);
270 perf_stop(cur);
271
272 if (prev)
273 {
274 perf_resume(prev);
275 }
276
277 pop_perf_index();
278}
279
280void
282{
283 int i;
284 msg(M_INFO, "LATENCY PROFILE (mean and max are in milliseconds)");
285 for (i = 0; i < PERF_N; ++i)
286 {
287 struct perf *p = &perf_set.perf[i];
288 if (p->count > 0.0)
289 {
290 const double mean = p->sum / p->count;
291 msg(M_INFO, "%s n=%.0f mean=%.3f max=%.3f", metric_names[i], p->count, mean*1000.0, p->max*1000.0);
292 }
293 }
294}
295
296static void
297perf_print_state(int lev)
298{
299 struct gc_arena gc = gc_new();
300 int i;
301 msg(lev, "PERF STATE");
302 msg(lev, "Stack:");
303 for (i = 0; i < perf_set.stack_len; ++i)
304 {
305 const int j = perf_set.stack[i];
306 const struct perf *p = &perf_set.perf[j];
307 msg(lev, "[%d] %s state=%d start=%s sofar=%f sum=%f max=%f count=%f",
308 i,
309 metric_names[j],
310 p->state,
311 tv_string(&p->start, &gc),
312 p->sofar,
313 p->sum,
314 p->max,
315 p->count);
316 }
317 gc_free(&gc);
318}
319#endif /* ifdef ENABLE_PERFORMANCE_METRICS */
static void gc_free(struct gc_arena *a)
Definition buffer.h:1033
static struct gc_arena gc_new(void)
Definition buffer.h:1025
#define M_INFO
Definition errlevel.h:55
#define SIZE(x)
Definition basic.h:30
#define M_FATAL
Definition error.h:89
#define msg(flags,...)
Definition error.h:144
#define ASSERT(x)
Definition error.h:195
const char * tv_string(const struct timeval *tv, struct gc_arena *gc)
Definition otime.c:82
static int tv_subtract(const struct timeval *tv1, const struct timeval *tv2, const unsigned int max_seconds)
Definition otime.h:115
static void tv_clear(struct timeval *tv)
Definition otime.h:101
static void perf_push(int type)
Definition perf.h:78
static void perf_output_results(void)
Definition perf.h:86
static void perf_pop(void)
Definition perf.h:82
#define PERF_N
Definition perf.h:58
Garbage collection arena used to keep track of dynamically allocated memory.
Definition buffer.h:117
struct gc_arena gc
Definition test_ssl.c:155