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-2023 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 
39 static 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 
62 struct 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 
76 struct perf_set
77 {
78  int stack_len;
79  int stack[STACK_N];
80  struct perf perf[PERF_N];
81 };
82 
83 static struct perf_set perf_set;
84 
85 static void perf_print_state(int lev);
86 
87 static inline int
88 get_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 
101 static int
102 get_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 
123 static struct perf *
124 get_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 
137 static void
138 push_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 
165 static void
166 pop_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 
179 static void
180 state_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 
190 static void
191 update_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 
199 static void
200 perf_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 
208 static void
209 perf_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 
223 static void
224 perf_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 
231 static void
232 perf_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 
239 void
240 perf_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 
260 void
261 perf_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 
280 void
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 
296 static void
297 perf_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 */
M_INFO
#define M_INFO
Definition: errlevel.h:55
gc_new
static struct gc_arena gc_new(void)
Definition: buffer.h:1031
M_FATAL
#define M_FATAL
Definition: error.h:95
tv_subtract
static int tv_subtract(const struct timeval *tv1, const struct timeval *tv2, const unsigned int max_seconds)
Definition: otime.h:115
perf_output_results
static void perf_output_results(void)
Definition: perf.h:86
ASSERT
#define ASSERT(x)
Definition: error.h:201
PERF_N
#define PERF_N
Definition: perf.h:58
perf_pop
static void perf_pop(void)
Definition: perf.h:82
SIZE
#define SIZE(x)
Definition: basic.h:30
syshead.h
gc_arena
Garbage collection arena used to keep track of dynamically allocated memory.
Definition: buffer.h:116
perf.h
otime.h
gc_free
static void gc_free(struct gc_arena *a)
Definition: buffer.h:1039
config.h
memdbg.h
msg
#define msg(flags,...)
Definition: error.h:150
tv_clear
static void tv_clear(struct timeval *tv)
Definition: otime.h:101
perf_push
static void perf_push(int type)
Definition: perf.h:78
tv_string
const char * tv_string(const struct timeval *tv, struct gc_arena *gc)
Definition: otime.c:82