2018-03-28 16:35:05 -05:00
|
|
|
/* Copyright (c) 2017 Red Hat, Inc.
|
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at:
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
#include "stopwatch.h"
|
|
|
|
#include "openvswitch/shash.h"
|
|
|
|
#include "openvswitch/vlog.h"
|
|
|
|
#include "unixctl.h"
|
|
|
|
#include "openvswitch/dynamic-string.h"
|
|
|
|
#include "openvswitch/poll-loop.h"
|
|
|
|
#include "ovs-thread.h"
|
|
|
|
#include <unistd.h>
|
|
|
|
#include "socket-util.h"
|
2018-04-06 12:53:51 -07:00
|
|
|
#include "util.h"
|
2018-04-09 12:07:20 -05:00
|
|
|
#include "latch.h"
|
|
|
|
#include "guarded-list.h"
|
2018-03-28 16:35:05 -05:00
|
|
|
|
|
|
|
VLOG_DEFINE_THIS_MODULE(stopwatch);
|
|
|
|
|
|
|
|
struct average {
|
|
|
|
double average; /* Moving average */
|
|
|
|
double alpha; /* Weight given to new samples */
|
|
|
|
};
|
|
|
|
|
|
|
|
#define MARKERS 5
|
|
|
|
|
|
|
|
/* Number of samples to collect before reporting P-square calculated
|
|
|
|
* percentile
|
|
|
|
*/
|
|
|
|
#define P_SQUARE_MIN 50
|
|
|
|
|
|
|
|
/* The naming of these fields is based on the naming used in the
|
|
|
|
* P-square algorithm paper.
|
|
|
|
*/
|
|
|
|
struct percentile {
|
|
|
|
int n[MARKERS];
|
|
|
|
double n_prime[MARKERS];
|
|
|
|
double q[MARKERS];
|
|
|
|
double dn[MARKERS];
|
|
|
|
unsigned long long samples[P_SQUARE_MIN];
|
|
|
|
double percentile;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct stopwatch {
|
|
|
|
enum stopwatch_units units;
|
|
|
|
unsigned long long n_samples;
|
|
|
|
unsigned long long max;
|
|
|
|
unsigned long long min;
|
|
|
|
struct percentile pctl;
|
|
|
|
struct average short_term;
|
|
|
|
struct average long_term;
|
|
|
|
unsigned long long sample_start;
|
|
|
|
bool sample_in_progress;
|
|
|
|
};
|
|
|
|
|
|
|
|
enum stopwatch_op {
|
|
|
|
OP_START_SAMPLE,
|
|
|
|
OP_END_SAMPLE,
|
2018-03-28 16:35:09 -05:00
|
|
|
OP_SYNC,
|
2018-03-28 16:35:05 -05:00
|
|
|
OP_RESET,
|
|
|
|
OP_SHUTDOWN,
|
|
|
|
};
|
|
|
|
|
|
|
|
struct stopwatch_packet {
|
2018-04-09 12:07:20 -05:00
|
|
|
struct ovs_list list_node;
|
2018-03-28 16:35:05 -05:00
|
|
|
enum stopwatch_op op;
|
|
|
|
char name[32];
|
|
|
|
unsigned long long time;
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct shash stopwatches = SHASH_INITIALIZER(&stopwatches);
|
|
|
|
static struct ovs_mutex stopwatches_lock = OVS_MUTEX_INITIALIZER;
|
2018-03-28 16:35:09 -05:00
|
|
|
static pthread_cond_t stopwatches_sync = PTHREAD_COND_INITIALIZER;
|
2018-03-28 16:35:05 -05:00
|
|
|
|
2018-04-09 12:07:20 -05:00
|
|
|
static struct latch stopwatch_latch;
|
|
|
|
static struct guarded_list stopwatch_commands;
|
2018-03-28 16:35:05 -05:00
|
|
|
static pthread_t stopwatch_thread_id;
|
|
|
|
|
|
|
|
static const char *unit_name[] = {
|
|
|
|
[SW_MS] = "msec",
|
|
|
|
[SW_US] = "usec",
|
|
|
|
[SW_NS] = "nsec",
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Percentile value we are calculating */
|
|
|
|
#define P 0.95
|
|
|
|
|
|
|
|
static int
|
|
|
|
comp_samples(const void *left, const void *right)
|
|
|
|
{
|
|
|
|
const double *left_d = left;
|
|
|
|
const double *right_d = right;
|
|
|
|
|
2018-12-18 19:38:27 +03:00
|
|
|
return *right_d > *left_d ? -1 : *right_d < *left_d;
|
2018-03-28 16:35:05 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Calculate the percentile using the P-square algorithm. For more
|
|
|
|
* information, see https://www1.cse.wustl.edu/~jain/papers/ftp/psqr.pdf
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
calc_percentile(unsigned long long n_samples, struct percentile *pctl,
|
|
|
|
unsigned long long new_sample)
|
|
|
|
{
|
|
|
|
if (n_samples < P_SQUARE_MIN) {
|
|
|
|
pctl->samples[n_samples - 1] = new_sample;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* For the first MARKERS samples, we calculate the percentile
|
|
|
|
* in the traditional way in the pct->q array.
|
|
|
|
*/
|
|
|
|
if (n_samples <= MARKERS) {
|
|
|
|
pctl->q[n_samples - 1] = new_sample;
|
|
|
|
qsort(pctl->q, n_samples, sizeof *pctl->q, comp_samples);
|
|
|
|
if (n_samples == MARKERS) {
|
|
|
|
pctl->n[0] = 0;
|
|
|
|
pctl->n[1] = 1;
|
|
|
|
pctl->n[2] = 2;
|
|
|
|
pctl->n[3] = 3;
|
|
|
|
pctl->n[4] = 4;
|
|
|
|
|
|
|
|
pctl->n_prime[0] = 0;
|
|
|
|
pctl->n_prime[1] = 2 * P;
|
|
|
|
pctl->n_prime[2] = 4 * P;
|
|
|
|
pctl->n_prime[3] = 2 + 2 * P;
|
|
|
|
pctl->n_prime[4] = 4;
|
|
|
|
|
|
|
|
pctl->dn[0] = 0;
|
|
|
|
pctl->dn[1] = P / 2;
|
|
|
|
pctl->dn[2] = P;
|
|
|
|
pctl->dn[3] = (1 + P) / 2;
|
|
|
|
pctl->dn[4] = 1;
|
|
|
|
}
|
|
|
|
pctl->percentile = pctl->q[(int) P * n_samples];
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* From here on, update the markers using quadratic spline calculations */
|
|
|
|
int k;
|
|
|
|
if (new_sample < pctl->q[0]) {
|
|
|
|
k = 0;
|
|
|
|
pctl->q[0] = new_sample;
|
|
|
|
} else if (new_sample < pctl->q[1]) {
|
|
|
|
k = 0;
|
|
|
|
} else if (new_sample < pctl->q[2]) {
|
|
|
|
k = 1;
|
|
|
|
} else if (new_sample < pctl->q[3]) {
|
|
|
|
k = 2;
|
|
|
|
} else if (new_sample <= pctl->q[4]) {
|
|
|
|
k = 3;
|
|
|
|
} else {
|
|
|
|
k = 3;
|
|
|
|
pctl->q[4] = new_sample;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = k + 1; i < MARKERS; i++) {
|
|
|
|
pctl->n[i]++;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 0; i < MARKERS; i++) {
|
|
|
|
pctl->n_prime[i] += pctl->dn[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 1; i < MARKERS - 1; i++) {
|
|
|
|
double d = pctl->n_prime[i] - pctl->n[i];
|
|
|
|
|
|
|
|
if ((d >= 1 && pctl->n[i + 1] - pctl->n[i] > 1) ||
|
|
|
|
(d <= -1 && pctl->n[i - 1] - pctl->n[i] < -1)) {
|
|
|
|
d = d >= 0 ? 1 : -1;
|
|
|
|
|
|
|
|
double a = d / (pctl->n[i + 1] - pctl->n[i - 1]);
|
|
|
|
double b = (pctl->n[i] - pctl->n[i - 1] + d) *
|
|
|
|
(pctl->q[i + 1] - pctl->q[i]) / (pctl->n[i + 1] - pctl->n[i]);
|
|
|
|
double c = (pctl->n[i + 1] - pctl->n[i] - d) *
|
|
|
|
(pctl->q[i] - pctl->q[i - 1]) / (pctl->n[i] - pctl->n[i - 1]);
|
|
|
|
|
|
|
|
double candidate = pctl->q[i] + a * (b + c);
|
|
|
|
if (pctl->q[i - 1] < candidate && candidate < pctl->q[i + 1]) {
|
|
|
|
pctl->q[i] = candidate;
|
|
|
|
} else {
|
|
|
|
pctl->q[i] = pctl->q[i] +
|
|
|
|
(d * (pctl->q[i + (int)d] - pctl->q[i]) /
|
|
|
|
(pctl->n[i +(int)d] - pctl->n[i]));
|
|
|
|
}
|
|
|
|
|
|
|
|
pctl->n[i] += d;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Without enough samples, P-square is not very accurate. Until we reach
|
|
|
|
* P_SQUARE_MIN, use a traditional calculation for the percentile.
|
|
|
|
*/
|
|
|
|
if (n_samples < P_SQUARE_MIN) {
|
|
|
|
qsort(pctl->samples, n_samples, sizeof *pctl->samples, comp_samples);
|
|
|
|
pctl->percentile = pctl->samples[(int) (P * n_samples)];
|
|
|
|
} else {
|
|
|
|
pctl->percentile = pctl->q[2];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
calc_average(struct average *avg, double new_sample)
|
|
|
|
{
|
|
|
|
avg->average = new_sample * avg->alpha + (1 - avg->alpha) * avg->average;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
add_sample(struct stopwatch *sw, unsigned long long new_sample)
|
|
|
|
{
|
|
|
|
if (new_sample > sw->max) {
|
|
|
|
sw->max = new_sample;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (new_sample < sw->min || sw->n_samples == 0) {
|
|
|
|
sw->min = new_sample;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sw->n_samples++ == 0) {
|
|
|
|
sw->short_term.average = sw->long_term.average = new_sample;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-01-24 15:17:53 +01:00
|
|
|
calc_percentile(sw->n_samples, &sw->pctl, new_sample);
|
2018-03-28 16:35:05 -05:00
|
|
|
calc_average(&sw->short_term, new_sample);
|
|
|
|
calc_average(&sw->long_term, new_sample);
|
|
|
|
}
|
|
|
|
|
2018-03-28 16:35:08 -05:00
|
|
|
static bool
|
2018-03-28 16:35:09 -05:00
|
|
|
stopwatch_get_stats_protected(const char *name,
|
2018-04-06 10:08:39 -07:00
|
|
|
struct stopwatch_stats *stats)
|
2018-03-28 16:35:08 -05:00
|
|
|
{
|
2018-03-28 16:35:09 -05:00
|
|
|
struct stopwatch *perf;
|
2018-03-28 16:35:08 -05:00
|
|
|
|
2018-03-28 16:35:09 -05:00
|
|
|
perf = shash_find_data(&stopwatches, name);
|
2018-03-28 16:35:08 -05:00
|
|
|
if (!perf) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-03-28 16:35:10 -05:00
|
|
|
stats->count = perf->n_samples;
|
2018-03-28 16:35:08 -05:00
|
|
|
stats->unit = perf->units;
|
|
|
|
stats->max = perf->max;
|
|
|
|
stats->min = perf->min;
|
|
|
|
stats->pctl_95 = perf->pctl.percentile;
|
|
|
|
stats->ewma_50 = perf->short_term.average;
|
|
|
|
stats->ewma_1 = perf->long_term.average;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2018-03-28 16:35:09 -05:00
|
|
|
stopwatch_get_stats(const char *name, struct stopwatch_stats *stats)
|
2018-03-28 16:35:08 -05:00
|
|
|
{
|
|
|
|
bool found = false;
|
|
|
|
|
2018-03-28 16:35:09 -05:00
|
|
|
ovs_mutex_lock(&stopwatches_lock);
|
|
|
|
found = stopwatch_get_stats_protected(name, stats);
|
|
|
|
ovs_mutex_unlock(&stopwatches_lock);
|
2018-03-28 16:35:08 -05:00
|
|
|
|
|
|
|
return found;
|
|
|
|
}
|
|
|
|
|
2018-03-28 16:35:05 -05:00
|
|
|
static void
|
|
|
|
stopwatch_print(struct stopwatch *sw, const char *name,
|
|
|
|
struct ds *s)
|
|
|
|
{
|
|
|
|
ds_put_format(s, "Statistics for '%s'\n", name);
|
|
|
|
|
|
|
|
const char *units = unit_name[sw->units];
|
2018-05-25 16:58:25 -07:00
|
|
|
ds_put_format(s, " Total samples: %llu\n", sw->n_samples);
|
|
|
|
ds_put_format(s, " Maximum: %llu %s\n", sw->max, units);
|
|
|
|
ds_put_format(s, " Minimum: %llu %s\n", sw->min, units);
|
|
|
|
ds_put_format(s, " 95th percentile: %f %s\n",
|
2018-03-28 16:35:05 -05:00
|
|
|
sw->pctl.percentile, units);
|
2018-05-25 16:58:25 -07:00
|
|
|
ds_put_format(s, " Short term average: %f %s\n",
|
2018-03-28 16:35:05 -05:00
|
|
|
sw->short_term.average, units);
|
2018-05-25 16:58:25 -07:00
|
|
|
ds_put_format(s, " Long term average: %f %s\n",
|
2018-03-28 16:35:05 -05:00
|
|
|
sw->long_term.average, units);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
|
|
|
stopwatch_show_protected(int argc, const char *argv[], struct ds *s)
|
|
|
|
{
|
|
|
|
struct stopwatch *sw;
|
|
|
|
|
|
|
|
if (argc > 1) {
|
|
|
|
sw = shash_find_data(&stopwatches, argv[1]);
|
|
|
|
if (!sw) {
|
|
|
|
ds_put_cstr(s, "No such stopwatch");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
stopwatch_print(sw, argv[1], s);
|
|
|
|
} else {
|
|
|
|
struct shash_node *node;
|
|
|
|
SHASH_FOR_EACH (node, &stopwatches) {
|
|
|
|
sw = node->data;
|
|
|
|
stopwatch_print(sw, node->name, s);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
stopwatch_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
|
2018-04-06 10:08:39 -07:00
|
|
|
const char *argv[], void *aux OVS_UNUSED)
|
2018-03-28 16:35:05 -05:00
|
|
|
{
|
|
|
|
struct ds s = DS_EMPTY_INITIALIZER;
|
|
|
|
bool success;
|
|
|
|
|
|
|
|
ovs_mutex_lock(&stopwatches_lock);
|
|
|
|
success = stopwatch_show_protected(argc, argv, &s);
|
|
|
|
ovs_mutex_unlock(&stopwatches_lock);
|
|
|
|
|
|
|
|
if (success) {
|
|
|
|
unixctl_command_reply(conn, ds_cstr(&s));
|
|
|
|
} else {
|
|
|
|
unixctl_command_reply_error(conn, ds_cstr(&s));
|
|
|
|
}
|
|
|
|
ds_destroy(&s);
|
|
|
|
}
|
|
|
|
|
2018-04-09 12:07:20 -05:00
|
|
|
static struct stopwatch_packet *
|
|
|
|
stopwatch_packet_create(enum stopwatch_op op)
|
|
|
|
{
|
|
|
|
struct stopwatch_packet *pkt;
|
|
|
|
|
|
|
|
pkt = xzalloc(sizeof *pkt);
|
|
|
|
pkt->op = op;
|
|
|
|
|
|
|
|
return pkt;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
stopwatch_packet_write(struct stopwatch_packet *pkt)
|
|
|
|
{
|
|
|
|
guarded_list_push_back(&stopwatch_commands, &pkt->list_node, SIZE_MAX);
|
|
|
|
latch_set(&stopwatch_latch);
|
|
|
|
}
|
|
|
|
|
2018-03-28 16:35:05 -05:00
|
|
|
static void
|
|
|
|
stopwatch_reset(struct unixctl_conn *conn, int argc OVS_UNUSED,
|
2018-04-06 10:08:39 -07:00
|
|
|
const char *argv[], void *aux OVS_UNUSED)
|
2018-03-28 16:35:05 -05:00
|
|
|
{
|
2018-04-09 12:07:20 -05:00
|
|
|
struct stopwatch_packet *pkt = stopwatch_packet_create(OP_RESET);
|
2018-03-28 16:35:05 -05:00
|
|
|
if (argc > 1) {
|
2018-04-09 12:07:20 -05:00
|
|
|
ovs_strlcpy(pkt->name, argv[1], sizeof pkt->name);
|
2018-03-28 16:35:05 -05:00
|
|
|
}
|
2018-04-09 12:07:20 -05:00
|
|
|
stopwatch_packet_write(pkt);
|
2018-03-28 16:35:05 -05:00
|
|
|
unixctl_command_reply(conn, "");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
stopwatch_start_sample_protected(const struct stopwatch_packet *pkt)
|
|
|
|
{
|
|
|
|
struct stopwatch *sw = shash_find_data(&stopwatches, pkt->name);
|
|
|
|
if (!sw || sw->sample_in_progress) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
sw->sample_start = pkt->time;
|
|
|
|
sw->sample_in_progress = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
stopwatch_end_sample_protected(const struct stopwatch_packet *pkt)
|
|
|
|
{
|
|
|
|
struct stopwatch *sw = shash_find_data(&stopwatches, pkt->name);
|
|
|
|
if (!sw || !sw->sample_in_progress) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
add_sample(sw, pkt->time - sw->sample_start);
|
|
|
|
sw->sample_in_progress = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void reset_stopwatch(struct stopwatch *sw)
|
|
|
|
{
|
|
|
|
sw->short_term.average = 0;
|
|
|
|
sw->long_term.average = 0;
|
|
|
|
sw->pctl.percentile = 0;
|
|
|
|
sw->n_samples = 0;
|
|
|
|
sw->max = 0;
|
|
|
|
sw->min = 0;
|
|
|
|
/* Don't reset sw->sample_start or sw->sample_in_progress.
|
|
|
|
* This way, if a sample was currently in progress, it can be
|
|
|
|
* concluded properly after the reset.
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
stopwatch_reset_protected(const struct stopwatch_packet *pkt)
|
|
|
|
{
|
|
|
|
if (pkt->name[0]) {
|
|
|
|
struct stopwatch *sw = shash_find_data(&stopwatches, pkt->name);
|
|
|
|
if (!sw) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
reset_stopwatch(sw);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct shash_node *node;
|
|
|
|
SHASH_FOR_EACH (node, &stopwatches) {
|
|
|
|
struct stopwatch *sw = node->data;
|
|
|
|
reset_stopwatch(sw);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void *
|
|
|
|
stopwatch_thread(void *ign OVS_UNUSED)
|
|
|
|
{
|
|
|
|
bool should_exit = false;
|
|
|
|
|
|
|
|
while (!should_exit) {
|
2018-04-09 12:07:20 -05:00
|
|
|
struct ovs_list command_list;
|
|
|
|
struct stopwatch_packet *pkt;
|
|
|
|
|
2018-04-11 09:15:22 -05:00
|
|
|
latch_poll(&stopwatch_latch);
|
2018-04-09 12:07:20 -05:00
|
|
|
guarded_list_pop_all(&stopwatch_commands, &command_list);
|
|
|
|
ovs_mutex_lock(&stopwatches_lock);
|
|
|
|
LIST_FOR_EACH_POP (pkt, list_node, &command_list) {
|
|
|
|
switch (pkt->op) {
|
2018-03-28 16:35:05 -05:00
|
|
|
case OP_START_SAMPLE:
|
2018-04-09 12:07:20 -05:00
|
|
|
stopwatch_start_sample_protected(pkt);
|
2018-03-28 16:35:05 -05:00
|
|
|
break;
|
|
|
|
case OP_END_SAMPLE:
|
2018-04-09 12:07:20 -05:00
|
|
|
stopwatch_end_sample_protected(pkt);
|
2018-03-28 16:35:05 -05:00
|
|
|
break;
|
2018-03-28 16:35:09 -05:00
|
|
|
case OP_SYNC:
|
|
|
|
xpthread_cond_signal(&stopwatches_sync);
|
|
|
|
break;
|
2018-03-28 16:35:05 -05:00
|
|
|
case OP_RESET:
|
2018-04-09 12:07:20 -05:00
|
|
|
stopwatch_reset_protected(pkt);
|
2018-03-28 16:35:05 -05:00
|
|
|
break;
|
|
|
|
case OP_SHUTDOWN:
|
|
|
|
should_exit = true;
|
|
|
|
break;
|
|
|
|
}
|
2019-05-08 14:56:23 +02:00
|
|
|
free(pkt);
|
2018-03-28 16:35:05 -05:00
|
|
|
}
|
2018-04-09 12:07:20 -05:00
|
|
|
ovs_mutex_unlock(&stopwatches_lock);
|
2018-03-28 16:35:05 -05:00
|
|
|
|
|
|
|
if (!should_exit) {
|
2018-04-09 12:07:20 -05:00
|
|
|
latch_wait(&stopwatch_latch);
|
2018-03-28 16:35:05 -05:00
|
|
|
poll_block();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
stopwatch_exit(void)
|
|
|
|
{
|
2022-03-23 12:56:17 +01:00
|
|
|
struct shash_node *node;
|
2018-04-09 12:07:20 -05:00
|
|
|
struct stopwatch_packet *pkt = stopwatch_packet_create(OP_SHUTDOWN);
|
|
|
|
stopwatch_packet_write(pkt);
|
2018-03-28 16:35:05 -05:00
|
|
|
xpthread_join(stopwatch_thread_id, NULL);
|
|
|
|
|
|
|
|
/* Process is exiting and we have joined the only
|
|
|
|
* other competing thread. We are now the sole owners
|
|
|
|
* of all data in the file.
|
|
|
|
*/
|
2022-03-23 12:56:17 +01:00
|
|
|
SHASH_FOR_EACH_SAFE (node, &stopwatches) {
|
2018-03-28 16:35:05 -05:00
|
|
|
struct stopwatch *sw = node->data;
|
|
|
|
shash_delete(&stopwatches, node);
|
|
|
|
free(sw);
|
|
|
|
}
|
|
|
|
shash_destroy(&stopwatches);
|
|
|
|
ovs_mutex_destroy(&stopwatches_lock);
|
2018-04-09 12:07:20 -05:00
|
|
|
guarded_list_destroy(&stopwatch_commands);
|
|
|
|
latch_destroy(&stopwatch_latch);
|
2018-03-28 16:35:05 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
do_init_stopwatch(void)
|
|
|
|
{
|
|
|
|
unixctl_command_register("stopwatch/show", "[NAME]", 0, 1,
|
|
|
|
stopwatch_show, NULL);
|
|
|
|
unixctl_command_register("stopwatch/reset", "[NAME]", 0, 1,
|
|
|
|
stopwatch_reset, NULL);
|
2018-04-09 12:07:20 -05:00
|
|
|
guarded_list_init(&stopwatch_commands);
|
|
|
|
latch_init(&stopwatch_latch);
|
2018-03-28 16:35:05 -05:00
|
|
|
stopwatch_thread_id = ovs_thread_create(
|
|
|
|
"stopwatch", stopwatch_thread, NULL);
|
|
|
|
atexit(stopwatch_exit);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
stopwatch_init(void)
|
|
|
|
{
|
|
|
|
static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
|
|
|
|
if (ovsthread_once_start(&once)) {
|
|
|
|
do_init_stopwatch();
|
|
|
|
ovsthread_once_done(&once);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
stopwatch_create(const char *name, enum stopwatch_units units)
|
|
|
|
{
|
|
|
|
stopwatch_init();
|
|
|
|
|
|
|
|
struct stopwatch *sw = xzalloc(sizeof *sw);
|
|
|
|
sw->units = units;
|
|
|
|
sw->short_term.alpha = 0.50;
|
|
|
|
sw->long_term.alpha = 0.01;
|
|
|
|
|
|
|
|
ovs_mutex_lock(&stopwatches_lock);
|
|
|
|
shash_add(&stopwatches, name, sw);
|
|
|
|
ovs_mutex_unlock(&stopwatches_lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
stopwatch_start(const char *name, unsigned long long ts)
|
|
|
|
{
|
2018-04-09 12:07:20 -05:00
|
|
|
struct stopwatch_packet *pkt = stopwatch_packet_create(OP_START_SAMPLE);
|
|
|
|
ovs_strlcpy(pkt->name, name, sizeof pkt->name);
|
|
|
|
pkt->time = ts;
|
|
|
|
stopwatch_packet_write(pkt);
|
2018-03-28 16:35:05 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
stopwatch_stop(const char *name, unsigned long long ts)
|
|
|
|
{
|
2018-04-09 12:07:20 -05:00
|
|
|
struct stopwatch_packet *pkt = stopwatch_packet_create(OP_END_SAMPLE);
|
|
|
|
ovs_strlcpy(pkt->name, name, sizeof pkt->name);
|
|
|
|
pkt->time = ts;
|
|
|
|
stopwatch_packet_write(pkt);
|
2018-03-28 16:35:05 -05:00
|
|
|
}
|
2018-03-28 16:35:09 -05:00
|
|
|
|
|
|
|
void
|
|
|
|
stopwatch_sync(void)
|
|
|
|
{
|
2018-04-09 12:07:20 -05:00
|
|
|
struct stopwatch_packet *pkt = stopwatch_packet_create(OP_SYNC);
|
2018-03-28 16:35:09 -05:00
|
|
|
ovs_mutex_lock(&stopwatches_lock);
|
2018-04-09 12:07:20 -05:00
|
|
|
stopwatch_packet_write(pkt);
|
2018-03-28 16:35:09 -05:00
|
|
|
ovs_mutex_cond_wait(&stopwatches_sync, &stopwatches_lock);
|
|
|
|
ovs_mutex_unlock(&stopwatches_lock);
|
|
|
|
}
|