6fbf66fa |
/*
* OpenVPN -- An application to securely tunnel IP networks
* over a single TCP/UDP port, with support for SSL/TLS-based
* session authentication and key exchange,
* packet encryption, packet authentication, and
* packet compression.
* |
49979459 |
* Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net> |
6fbf66fa |
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* |
caa54ac3 |
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
6fbf66fa |
*/
|
c110b289 |
#ifdef HAVE_CONFIG_H
#include "config.h"
#elif defined(_MSC_VER)
#include "config-msvc.h"
#endif
|
6fbf66fa |
#include "syshead.h"
#include "perf.h"
#ifdef ENABLE_PERFORMANCE_METRICS
#include "error.h"
#include "otime.h"
#include "memdbg.h"
static const char *metric_names[] = { |
81d882d5 |
"PERF_BIO_READ_PLAINTEXT",
"PERF_BIO_WRITE_PLAINTEXT",
"PERF_BIO_READ_CIPHERTEXT",
"PERF_BIO_WRITE_CIPHERTEXT",
"PERF_TLS_MULTI_PROCESS",
"PERF_IO_WAIT",
"PERF_EVENT_LOOP",
"PERF_MULTI_CREATE_INSTANCE",
"PERF_MULTI_CLOSE_INSTANCE",
"PERF_MULTI_SHOW_STATS",
"PERF_MULTI_BCAST",
"PERF_MULTI_MCAST",
"PERF_SCRIPT",
"PERF_READ_IN_LINK",
"PERF_PROC_IN_LINK",
"PERF_READ_IN_TUN",
"PERF_PROC_IN_TUN",
"PERF_PROC_OUT_LINK",
"PERF_PROC_OUT_TUN",
"PERF_PROC_OUT_TUN_MTCP" |
6fbf66fa |
};
struct perf
{ |
81d882d5 |
#define PS_INITIAL 0
#define PS_METER_RUNNING 1
#define PS_METER_INTERRUPTED 2
int state;
struct timeval start;
double sofar;
double sum;
double max;
double count; |
6fbf66fa |
};
struct perf_set
{ |
81d882d5 |
int stack_len;
int stack[STACK_N];
struct perf perf[PERF_N]; |
6fbf66fa |
};
static struct perf_set perf_set;
|
81d882d5 |
static void perf_print_state(int lev); |
6fbf66fa |
static inline int |
81d882d5 |
get_stack_index(int sdelta) |
6fbf66fa |
{ |
81d882d5 |
const int sindex = perf_set.stack_len + sdelta;
if (sindex >= 0 && sindex < STACK_N)
{
return sindex;
}
else
{
return -1;
} |
6fbf66fa |
}
static int |
81d882d5 |
get_perf_index(int sdelta) |
6fbf66fa |
{ |
81d882d5 |
const int sindex = get_stack_index(sdelta);
if (sindex >= 0) |
6fbf66fa |
{ |
81d882d5 |
const int pindex = perf_set.stack[sindex];
if (pindex >= 0 && pindex < PERF_N)
{
return pindex;
}
else
{
return -1;
}
}
else
{
return -1; |
6fbf66fa |
}
}
static struct perf * |
81d882d5 |
get_perf(int sdelta) |
6fbf66fa |
{ |
81d882d5 |
const int pindex = get_perf_index(sdelta);
if (pindex >= 0)
{
return &perf_set.perf[pindex];
}
else
{
return NULL;
} |
6fbf66fa |
}
static void |
81d882d5 |
push_perf_index(int pindex) |
6fbf66fa |
{ |
81d882d5 |
const int sindex = get_stack_index(0);
const int newlen = get_stack_index(1);
if (sindex >= 0 && newlen >= 0
&& pindex >= 0 && pindex < PERF_N) |
6fbf66fa |
{ |
81d882d5 |
int i;
for (i = 0; i < sindex; ++i) |
4cd4899e |
{ |
81d882d5 |
if (perf_set.stack[i] == pindex)
{
perf_print_state(M_INFO);
msg(M_FATAL, "PERF: push_perf_index %s failed",
metric_names [pindex]);
} |
4cd4899e |
} |
81d882d5 |
perf_set.stack[sindex] = pindex;
perf_set.stack_len = newlen;
}
else
{
msg(M_FATAL, "PERF: push_perf_index: stack push error"); |
6fbf66fa |
}
}
static void |
81d882d5 |
pop_perf_index(void) |
6fbf66fa |
{ |
81d882d5 |
const int newlen = get_stack_index(-1);
if (newlen >= 0) |
6fbf66fa |
{ |
81d882d5 |
perf_set.stack_len = newlen;
}
else
{
msg(M_FATAL, "PERF: pop_perf_index: stack pop error"); |
6fbf66fa |
}
}
static void |
81d882d5 |
state_must_be(const struct perf *p, const int wanted) |
6fbf66fa |
{ |
81d882d5 |
if (p->state != wanted)
{
msg(M_FATAL, "PERF: bad state actual=%d wanted=%d",
p->state,
wanted);
} |
6fbf66fa |
}
static void |
81d882d5 |
update_sofar(struct perf *p) |
6fbf66fa |
{ |
81d882d5 |
struct timeval current;
ASSERT(!gettimeofday(¤t, NULL));
p->sofar += (double) tv_subtract(¤t, &p->start, 600) / 1000000.0;
tv_clear(&p->start); |
6fbf66fa |
}
static void |
81d882d5 |
perf_start(struct perf *p) |
6fbf66fa |
{ |
81d882d5 |
state_must_be(p, PS_INITIAL);
ASSERT(!gettimeofday(&p->start, NULL));
p->sofar = 0.0;
p->state = PS_METER_RUNNING; |
6fbf66fa |
}
static void |
81d882d5 |
perf_stop(struct perf *p) |
6fbf66fa |
{ |
81d882d5 |
state_must_be(p, PS_METER_RUNNING);
update_sofar(p);
p->sum += p->sofar;
if (p->sofar > p->max)
{
p->max = p->sofar;
}
p->count += 1.0;
p->sofar = 0.0;
p->state = PS_INITIAL; |
6fbf66fa |
}
static void |
81d882d5 |
perf_interrupt(struct perf *p) |
6fbf66fa |
{ |
81d882d5 |
state_must_be(p, PS_METER_RUNNING);
update_sofar(p);
p->state = PS_METER_INTERRUPTED; |
6fbf66fa |
}
static void |
81d882d5 |
perf_resume(struct perf *p) |
6fbf66fa |
{ |
81d882d5 |
state_must_be(p, PS_METER_INTERRUPTED);
ASSERT(!gettimeofday(&p->start, NULL));
p->state = PS_METER_RUNNING; |
6fbf66fa |
}
void |
81d882d5 |
perf_push(int type) |
6fbf66fa |
{ |
81d882d5 |
struct perf *prev;
struct perf *cur; |
6fbf66fa |
|
81d882d5 |
ASSERT(SIZE(metric_names) == PERF_N);
push_perf_index(type); |
6fbf66fa |
|
81d882d5 |
prev = get_perf(-2);
cur = get_perf(-1); |
6fbf66fa |
|
81d882d5 |
ASSERT(cur); |
6fbf66fa |
|
81d882d5 |
if (prev)
{
perf_interrupt(prev);
}
perf_start(cur); |
6fbf66fa |
}
void |
81d882d5 |
perf_pop(void) |
6fbf66fa |
{ |
81d882d5 |
struct perf *prev;
struct perf *cur; |
6fbf66fa |
|
81d882d5 |
prev = get_perf(-2);
cur = get_perf(-1); |
6fbf66fa |
|
81d882d5 |
ASSERT(cur);
perf_stop(cur); |
6fbf66fa |
|
81d882d5 |
if (prev)
{
perf_resume(prev);
} |
6fbf66fa |
|
81d882d5 |
pop_perf_index(); |
6fbf66fa |
}
void |
81d882d5 |
perf_output_results(void) |
6fbf66fa |
{ |
81d882d5 |
int i;
msg(M_INFO, "LATENCY PROFILE (mean and max are in milliseconds)");
for (i = 0; i < PERF_N; ++i) |
6fbf66fa |
{ |
81d882d5 |
struct perf *p = &perf_set.perf[i];
if (p->count > 0.0)
{
const double mean = p->sum / p->count;
msg(M_INFO, "%s n=%.0f mean=%.3f max=%.3f", metric_names[i], p->count, mean*1000.0, p->max*1000.0);
} |
6fbf66fa |
}
}
static void |
81d882d5 |
perf_print_state(int lev) |
6fbf66fa |
{ |
81d882d5 |
struct gc_arena gc = gc_new();
int i;
msg(lev, "PERF STATE");
msg(lev, "Stack:");
for (i = 0; i < perf_set.stack_len; ++i) |
6fbf66fa |
{ |
81d882d5 |
const int j = perf_set.stack[i];
const struct perf *p = &perf_set.perf[j];
msg(lev, "[%d] %s state=%d start=%s sofar=%f sum=%f max=%f count=%f",
i,
metric_names[j],
p->state,
tv_string(&p->start, &gc),
p->sofar,
p->sum,
p->max,
p->count); |
6fbf66fa |
} |
81d882d5 |
gc_free(&gc); |
6fbf66fa |
}
|
81d882d5 |
#else /* ifdef ENABLE_PERFORMANCE_METRICS */ |
eabb8eed |
#ifdef _MSC_VER /* Dummy function needed to avoid empty file compiler warning in Microsoft VC */ |
81d882d5 |
static void |
4cd4899e |
dummy(void)
{ |
81d882d5 |
} |
eabb8eed |
#endif |
81d882d5 |
#endif /* ifdef ENABLE_PERFORMANCE_METRICS */ |