mirror of
https://github.com/OpenVPN/openvpn.git
synced 2024-11-27 11:43:51 +08:00
d02a86d37b
git-svn-id: http://svn.openvpn.net/projects/openvpn/branches/BETA21/openvpn@7051 e7ae566f-a301-0410-adde-c780ea21d3b5
294 lines
6.0 KiB
C
294 lines
6.0 KiB
C
/*
|
|
* 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.
|
|
*
|
|
* Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net>
|
|
*
|
|
* 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.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program (see the file COPYING included with this
|
|
* distribution); if not, write to the Free Software Foundation, Inc.,
|
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include "syshead.h"
|
|
|
|
#include "perf.h"
|
|
|
|
#ifdef ENABLE_PERFORMANCE_METRICS
|
|
|
|
#include "error.h"
|
|
#include "otime.h"
|
|
|
|
#include "memdbg.h"
|
|
|
|
static const char *metric_names[] = {
|
|
"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"
|
|
};
|
|
|
|
struct perf
|
|
{
|
|
# 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;
|
|
};
|
|
|
|
struct perf_set
|
|
{
|
|
int stack_len;
|
|
int stack[STACK_N];
|
|
struct perf perf[PERF_N];
|
|
};
|
|
|
|
static struct perf_set perf_set;
|
|
|
|
static void perf_print_state (int lev);
|
|
|
|
static inline int
|
|
get_stack_index (int sdelta)
|
|
{
|
|
const int sindex = perf_set.stack_len + sdelta;
|
|
if (sindex >= 0 && sindex < STACK_N)
|
|
return sindex;
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
static int
|
|
get_perf_index (int sdelta)
|
|
{
|
|
const int sindex = get_stack_index (sdelta);
|
|
if (sindex >= 0)
|
|
{
|
|
const int pindex = perf_set.stack[sindex];
|
|
if (pindex >= 0 && pindex < PERF_N)
|
|
return pindex;
|
|
else
|
|
return -1;
|
|
}
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
static struct perf *
|
|
get_perf (int sdelta)
|
|
{
|
|
const int pindex = get_perf_index (sdelta);
|
|
if (pindex >= 0)
|
|
return &perf_set.perf[pindex];
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
push_perf_index (int pindex)
|
|
{
|
|
const int sindex = get_stack_index (0);
|
|
const int newlen = get_stack_index (1);
|
|
if (sindex >= 0 && newlen >= 0
|
|
&& pindex >= 0 && pindex < PERF_N)
|
|
{
|
|
int i;
|
|
for (i = 0; i < sindex; ++i)
|
|
if (perf_set.stack[i] == pindex)
|
|
{
|
|
perf_print_state (M_INFO);
|
|
msg (M_FATAL, "PERF: push_perf_index %s failed",
|
|
metric_names [pindex]);
|
|
}
|
|
|
|
perf_set.stack[sindex] = pindex;
|
|
perf_set.stack_len = newlen;
|
|
}
|
|
else
|
|
msg (M_FATAL, "PERF: push_perf_index: stack push error");
|
|
}
|
|
|
|
static void
|
|
pop_perf_index (void)
|
|
{
|
|
const int newlen = get_stack_index (-1);
|
|
if (newlen >= 0)
|
|
{
|
|
perf_set.stack_len = newlen;
|
|
}
|
|
else
|
|
msg (M_FATAL, "PERF: pop_perf_index: stack pop error");
|
|
}
|
|
|
|
static void
|
|
state_must_be (const struct perf *p, const int wanted)
|
|
{
|
|
if (p->state != wanted)
|
|
msg (M_FATAL, "PERF: bad state actual=%d wanted=%d",
|
|
p->state,
|
|
wanted);
|
|
}
|
|
|
|
static void
|
|
update_sofar (struct perf *p)
|
|
{
|
|
struct timeval current;
|
|
ASSERT (!gettimeofday (¤t, NULL));
|
|
p->sofar += (double) tv_subtract (¤t, &p->start, 600) / 1000000.0;
|
|
tv_clear (&p->start);
|
|
}
|
|
|
|
static void
|
|
perf_start (struct perf *p)
|
|
{
|
|
state_must_be (p, PS_INITIAL);
|
|
ASSERT (!gettimeofday (&p->start, NULL));
|
|
p->sofar = 0.0;
|
|
p->state = PS_METER_RUNNING;
|
|
}
|
|
|
|
static void
|
|
perf_stop (struct perf *p)
|
|
{
|
|
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;
|
|
}
|
|
|
|
static void
|
|
perf_interrupt (struct perf *p)
|
|
{
|
|
state_must_be (p, PS_METER_RUNNING);
|
|
update_sofar (p);
|
|
p->state = PS_METER_INTERRUPTED;
|
|
}
|
|
|
|
static void
|
|
perf_resume (struct perf *p)
|
|
{
|
|
state_must_be (p, PS_METER_INTERRUPTED);
|
|
ASSERT (!gettimeofday (&p->start, NULL));
|
|
p->state = PS_METER_RUNNING;
|
|
}
|
|
|
|
void
|
|
perf_push (int type)
|
|
{
|
|
struct perf *prev;
|
|
struct perf *cur;
|
|
|
|
ASSERT (SIZE(metric_names) == PERF_N);
|
|
push_perf_index (type);
|
|
|
|
prev = get_perf (-2);
|
|
cur = get_perf (-1);
|
|
|
|
ASSERT (cur);
|
|
|
|
if (prev)
|
|
perf_interrupt (prev);
|
|
perf_start (cur);
|
|
}
|
|
|
|
void
|
|
perf_pop (void)
|
|
{
|
|
struct perf *prev;
|
|
struct perf *cur;
|
|
|
|
prev = get_perf (-2);
|
|
cur = get_perf (-1);
|
|
|
|
ASSERT (cur);
|
|
perf_stop (cur);
|
|
|
|
if (prev)
|
|
perf_resume (prev);
|
|
|
|
pop_perf_index ();
|
|
}
|
|
|
|
void
|
|
perf_output_results (void)
|
|
{
|
|
int i;
|
|
msg (M_INFO, "LATENCY PROFILE (mean and max are in milliseconds)");
|
|
for (i = 0; i < PERF_N; ++i)
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
perf_print_state (int lev)
|
|
{
|
|
struct gc_arena gc = gc_new ();
|
|
int i;
|
|
msg (lev, "PERF STATE");
|
|
msg (lev, "Stack:");
|
|
for (i = 0; i < perf_set.stack_len; ++i)
|
|
{
|
|
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);
|
|
}
|
|
gc_free (&gc);
|
|
}
|
|
|
|
#else
|
|
#ifdef _MSC_VER /* Dummy function needed to avoid empty file compiler warning in Microsoft VC */
|
|
static void dummy(void) {}
|
|
#endif
|
|
#endif
|