src/openvpn/perf.c
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(&current, NULL));
     p->sofar += (double) tv_subtract(&current, &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 */