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-2025 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, see <https://www.gnu.org/licenses/>.
21 */
22
23#ifdef HAVE_CONFIG_H
24#include "config.h"
25#endif
26
27#include "syshead.h"
28
29#include "perf.h"
30
31#ifdef ENABLE_PERFORMANCE_METRICS
32
33#include "error.h"
34#include "otime.h"
35
36#include "memdbg.h"
37
38static const char *metric_names[] = { "PERF_BIO_READ_PLAINTEXT",
39 "PERF_BIO_WRITE_PLAINTEXT",
40 "PERF_BIO_READ_CIPHERTEXT",
41 "PERF_BIO_WRITE_CIPHERTEXT",
42 "PERF_TLS_MULTI_PROCESS",
43 "PERF_IO_WAIT",
44 "PERF_EVENT_LOOP",
45 "PERF_MULTI_CREATE_INSTANCE",
46 "PERF_MULTI_CLOSE_INSTANCE",
47 "PERF_MULTI_SHOW_STATS",
48 "PERF_MULTI_BCAST",
49 "PERF_MULTI_MCAST",
50 "PERF_SCRIPT",
51 "PERF_READ_IN_LINK",
52 "PERF_PROC_IN_LINK",
53 "PERF_READ_IN_TUN",
54 "PERF_PROC_IN_TUN",
55 "PERF_PROC_OUT_LINK",
56 "PERF_PROC_OUT_TUN",
57 "PERF_PROC_OUT_TUN_MTCP" };
58
59struct perf
60{
61#define PS_INITIAL 0
62#define PS_METER_RUNNING 1
63#define PS_METER_INTERRUPTED 2
64 int state;
65
66 struct timeval start;
67 double sofar;
68 double sum;
69 double max;
70 double count;
71};
72
73struct perf_set
74{
75 int stack_len;
76 int stack[STACK_N];
77 struct perf perf[PERF_N];
78};
79
80static struct perf_set perf_set;
81
82static void perf_print_state(int lev);
83
84static inline int
85get_stack_index(int sdelta)
86{
87 const int sindex = perf_set.stack_len + sdelta;
88 if (sindex >= 0 && sindex < STACK_N)
89 {
90 return sindex;
91 }
92 else
93 {
94 return -1;
95 }
96}
97
98static int
99get_perf_index(int sdelta)
100{
101 const int sindex = get_stack_index(sdelta);
102 if (sindex >= 0)
103 {
104 const int pindex = perf_set.stack[sindex];
105 if (pindex >= 0 && pindex < PERF_N)
106 {
107 return pindex;
108 }
109 else
110 {
111 return -1;
112 }
113 }
114 else
115 {
116 return -1;
117 }
118}
119
120static struct perf *
121get_perf(int sdelta)
122{
123 const int pindex = get_perf_index(sdelta);
124 if (pindex >= 0)
125 {
126 return &perf_set.perf[pindex];
127 }
128 else
129 {
130 return NULL;
131 }
132}
133
134static void
135push_perf_index(int pindex)
136{
137 const int sindex = get_stack_index(0);
138 const int newlen = get_stack_index(1);
139 if (sindex >= 0 && newlen >= 0 && pindex >= 0 && pindex < PERF_N)
140 {
141 int i;
142 for (i = 0; i < sindex; ++i)
143 {
144 if (perf_set.stack[i] == pindex)
145 {
146 perf_print_state(M_INFO);
147 msg(M_FATAL, "PERF: push_perf_index %s failed", metric_names[pindex]);
148 }
149 }
150
151 perf_set.stack[sindex] = pindex;
152 perf_set.stack_len = newlen;
153 }
154 else
155 {
156 msg(M_FATAL, "PERF: push_perf_index: stack push error");
157 }
158}
159
160static void
161pop_perf_index(void)
162{
163 const int newlen = get_stack_index(-1);
164 if (newlen >= 0)
165 {
166 perf_set.stack_len = newlen;
167 }
168 else
169 {
170 msg(M_FATAL, "PERF: pop_perf_index: stack pop error");
171 }
172}
173
174static void
175state_must_be(const struct perf *p, const int wanted)
176{
177 if (p->state != wanted)
178 {
179 msg(M_FATAL, "PERF: bad state actual=%d wanted=%d", p->state, wanted);
180 }
181}
182
183static void
184update_sofar(struct perf *p)
185{
186 struct timeval current;
187 ASSERT(!gettimeofday(&current, NULL));
188 p->sofar += (double)tv_subtract(&current, &p->start, 600) / 1000000.0;
189 tv_clear(&p->start);
190}
191
192static void
193perf_start(struct perf *p)
194{
195 state_must_be(p, PS_INITIAL);
196 ASSERT(!gettimeofday(&p->start, NULL));
197 p->sofar = 0.0;
198 p->state = PS_METER_RUNNING;
199}
200
201static void
202perf_stop(struct perf *p)
203{
204 state_must_be(p, PS_METER_RUNNING);
205 update_sofar(p);
206 p->sum += p->sofar;
207 if (p->sofar > p->max)
208 {
209 p->max = p->sofar;
210 }
211 p->count += 1.0;
212 p->sofar = 0.0;
213 p->state = PS_INITIAL;
214}
215
216static void
217perf_interrupt(struct perf *p)
218{
219 state_must_be(p, PS_METER_RUNNING);
220 update_sofar(p);
221 p->state = PS_METER_INTERRUPTED;
222}
223
224static void
225perf_resume(struct perf *p)
226{
227 state_must_be(p, PS_METER_INTERRUPTED);
228 ASSERT(!gettimeofday(&p->start, NULL));
229 p->state = PS_METER_RUNNING;
230}
231
232void
233perf_push(int type)
234{
235 struct perf *prev;
236 struct perf *cur;
237
238 ASSERT(SIZE(metric_names) == PERF_N);
239 push_perf_index(type);
240
241 prev = get_perf(-2);
242 cur = get_perf(-1);
243
244 ASSERT(cur);
245
246 if (prev)
247 {
248 perf_interrupt(prev);
249 }
250 perf_start(cur);
251}
252
253void
254perf_pop(void)
255{
256 struct perf *prev;
257 struct perf *cur;
258
259 prev = get_perf(-2);
260 cur = get_perf(-1);
261
262 ASSERT(cur);
263 perf_stop(cur);
264
265 if (prev)
266 {
267 perf_resume(prev);
268 }
269
270 pop_perf_index();
271}
272
273void
275{
276 int i;
277 msg(M_INFO, "LATENCY PROFILE (mean and max are in milliseconds)");
278 for (i = 0; i < PERF_N; ++i)
279 {
280 struct perf *p = &perf_set.perf[i];
281 if (p->count > 0.0)
282 {
283 const double mean = p->sum / p->count;
284 msg(M_INFO, "%s n=%.0f mean=%.3f max=%.3f", metric_names[i], p->count, mean * 1000.0,
285 p->max * 1000.0);
286 }
287 }
288}
289
290static void
291perf_print_state(int lev)
292{
293 struct gc_arena gc = gc_new();
294 int i;
295 msg(lev, "PERF STATE");
296 msg(lev, "Stack:");
297 for (i = 0; i < perf_set.stack_len; ++i)
298 {
299 const int j = perf_set.stack[i];
300 const struct perf *p = &perf_set.perf[j];
301 msg(lev, "[%d] %s state=%d start=%s sofar=%f sum=%f max=%f count=%f", i, metric_names[j],
302 p->state, tv_string(&p->start, &gc), p->sofar, p->sum, p->max, p->count);
303 }
304 gc_free(&gc);
305}
306#endif /* ifdef ENABLE_PERFORMANCE_METRICS */
static void gc_free(struct gc_arena *a)
Definition buffer.h:1015
static struct gc_arena gc_new(void)
Definition buffer.h:1007
#define M_INFO
Definition errlevel.h:54
#define SIZE(x)
Definition basic.h:29
#define M_FATAL
Definition error.h:88
#define msg(flags,...)
Definition error.h:150
#define ASSERT(x)
Definition error.h:217
const char * tv_string(const struct timeval *tv, struct gc_arena *gc)
Definition otime.c:83
static int tv_subtract(const struct timeval *tv1, const struct timeval *tv2, const unsigned int max_seconds)
Definition otime.h:114
static void tv_clear(struct timeval *tv)
Definition otime.h:100
static void perf_push(int type)
Definition perf.h:77
static void perf_output_results(void)
Definition perf.h:85
static void perf_pop(void)
Definition perf.h:81
#define PERF_N
Definition perf.h:57
Garbage collection arena used to keep track of dynamically allocated memory.
Definition buffer.h:116
struct gc_arena gc
Definition test_ssl.c:154