/* * 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-2005 OpenVPN Solutions LLC * * 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 */ #ifdef WIN32 #include "config-win32.h" #else #include "config.h" #endif #include "syshead.h" #include "perf.h" #ifdef ENABLE_PERFORMANCE_METRICS #include "error.h" #include "otime.h" #include "memdbg.h" #ifdef USE_PTHREAD #error ENABLE_PERFORMANCE_METRICS is incompatible with USE_PTHREAD #endif 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 static void dummy(void) {} #endif