-
Tom Barbette a rédigéTom Barbette a rédigé
timestampdiff.cc 12,92 Kio
/*
* timestampdiff.{cc,hh} -- Compute the difference between the recorded
* timestamp of a packet using RecordTimestamp and a fresh timestamp
* Cyril Soldani, Tom Barbette
*
* Various latency percentiles by Georgios Katsikas and Tom Barbette
*
*
* Copyright (c) 2015-2016 University of Liège
* Copyright (c) 2017 RISE SICS
* Copyright (c) 2019 KTH Royal Institute of Technology
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, subject to the conditions
* listed in the Click LICENSE file. These conditions include: you must
* preserve this copyright notice, and you cannot mention the copyright
* holders in advertising related to the Software without their permission.
* The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
* notice is a summary of the Click LICENSE file; the license in that file is
* legally binding.
*/
#include <click/config.h>
#include "timestampdiff.hh"
#include <climits>
#include <cmath>
#include <algorithm>
#include <click/args.hh>
#include <click/error.hh>
#include <click/straccum.hh>
#include <click/timestamp.hh>
#include "numberpacket.hh"
#include "recordtimestamp.hh"
CLICK_DECLS
TimestampDiff::TimestampDiff() :
_delays(), _offset(40), _limit(0), _net_order(false), _max_delay_ms(1000), _verbose(true)
{
_nd = 0;
}
TimestampDiff::~TimestampDiff() {
}
int TimestampDiff::configure(Vector<String> &conf, ErrorHandler *errh)
{
Element* e;
if (Args(conf, this, errh)
.read_mp("RECORDER", e)
.read("OFFSET",_offset)
.read("N", _limit)
.read("MAXDELAY", _max_delay_ms)
.read("NANO", _nano)
.read_or_set("SAMPLE", _sample, 1)
.read_or_set("VERBOSE", _verbose, false)
.read_or_set("TC_OFFSET", _tc_offset, -1)
.read_or_set("TC_MASK", _tc_mask, 0xff)
.complete() < 0)
return -1;
if ((_rt = static_cast<RecordTimestamp*>(e->cast("RecordTimestamp"))) == 0)
return errh->error("RECORDER must be a valid RecordTimestamp element");
_net_order = _rt->has_net_order();
if (_limit) {
_delays.resize(_limit, {0,0});
}
return 0;
}
int TimestampDiff::initialize(ErrorHandler *errh)
{
if (get_passing_threads().weight() > 1 && !_limit) {
return errh->error("TimestampDiff is only thread safe if N is set");
}
return 0;
}
enum {
TSD_AVG_HANDLER,
TSD_AVG_TC_HANDLER,
TSD_MIN_HANDLER,
TSD_MAX_HANDLER,
TSD_STD_HANDLER,
TSD_PERC_00_HANDLER,
TSD_PERC_01_HANDLER,
TSD_PERC_05_HANDLER,
TSD_PERC_10_HANDLER,
TSD_PERC_25_HANDLER,
TSD_MED_HANDLER,
TSD_PERC_75_HANDLER,
TSD_PERC_90_HANDLER,
TSD_PERC_95_HANDLER,
TSD_PERC_99_HANDLER,
TSD_PERC_100_HANDLER,
TSD_PERC_HANDLER,
TSD_LAST_SEEN,
TSD_CURRENT_INDEX,
TSD_DUMP_HANDLER,
TSD_DUMP_LIST_HANDLER
};
int TimestampDiff::handler(int operation, String &data, Element *e,
const Handler *handler, ErrorHandler *errh)
{
TimestampDiff *tsd = static_cast<TimestampDiff *>(e);
unsigned min = UINT_MAX;
double mean = 0.0;
unsigned max = 0;
unsigned begin = 0;
double perc = 0;
int tc = -1;
int opt = reinterpret_cast<intptr_t>(handler->user_data(Handler::f_read));
if (data != "") {
if (opt == TSD_PERC_HANDLER) {
int pos = data.find_left(' ');
if (pos == -1) pos = data.length();
if (!DoubleArg().parse(data.substring(0, pos), perc)) {
data = "<error>";
return -1;
}
data = data.substring(pos);
} else if (opt == TSD_AVG_TC_HANDLER) {
int pos = data.find_left(' ');
if (pos == -1) pos = data.length();
if (!IntArg().parse(data.substring(0, pos), tc)) {
data = "<error>";
return -1;
}
data = data.substring(pos); }
begin = atoi(data.c_str());
const uint32_t current_vector_length = static_cast<const uint32_t>(tsd->_nd.value());
if (begin >= current_vector_length) {
data = 0;
return 1;
}
}
switch (opt) {
case TSD_MIN_HANDLER:
tsd->min_mean_max(min, mean, max, begin);
data = String(min); break;
case TSD_AVG_HANDLER:
tsd->min_mean_max(min, mean, max, begin);
data = String(mean); break;
case TSD_AVG_TC_HANDLER:
tsd->min_mean_max(min, mean, max, begin, tc);
data = String(mean); break;
case TSD_MAX_HANDLER:
tsd->min_mean_max(min, mean, max, begin);
data = String(max); break;
case TSD_STD_HANDLER:
data = String(tsd->standard_deviation(mean)); break;
case TSD_PERC_00_HANDLER:
tsd->min_mean_max(min, mean, max, begin);
data = String(min); break;
case TSD_PERC_01_HANDLER:
data = String(tsd->percentile(1, begin)); break;
case TSD_PERC_05_HANDLER:
data = String(tsd->percentile(5, begin)); break;
case TSD_PERC_10_HANDLER:
data = String(tsd->percentile(10, begin)); break;
case TSD_PERC_25_HANDLER:
data = String(tsd->percentile(25, begin)); break;
case TSD_MED_HANDLER:
data = String(tsd->percentile(50, begin)); break;
case TSD_PERC_75_HANDLER:
data = String(tsd->percentile(75, begin)); break;
case TSD_PERC_90_HANDLER:
data = String(tsd->percentile(90, begin)); break;
case TSD_PERC_95_HANDLER:
data = String(tsd->percentile(95, begin)); break;
case TSD_PERC_99_HANDLER:
data = String(tsd->percentile(99, begin)); break;
case TSD_PERC_100_HANDLER:
tsd->min_mean_max(min, mean, max, begin);
data = String(max); break;
case TSD_PERC_HANDLER:
data = String(tsd->percentile(perc, begin)); break;
case TSD_LAST_SEEN:
data = String(tsd->last_value_seen()); break;
case TSD_CURRENT_INDEX: {
const int32_t last_vector_index = static_cast<const int32_t>(tsd->_nd.value() - 1);
data = String(last_vector_index); break;
}
case TSD_DUMP_LIST_HANDLER:
case TSD_DUMP_HANDLER: {
StringAccum s;
for (size_t i = 0; i < tsd->_nd; ++i) {
if (opt == TSD_DUMP_HANDLER)
s << i << ": " << String(tsd->_delays[i].delay) << "\n";
else
s << String(tsd->_delays[i].delay) << "\n";
}
data = s.take_string(); break;
}
default:
data = String("Unknown read handler for TimestampDiff"); break; }
return 0;
}
void TimestampDiff::add_handlers()
{
set_handler("average", Handler::f_read | Handler::f_read_param, handler, TSD_AVG_HANDLER, 0);
set_handler("avg", Handler::f_read | Handler::f_read_param, handler, TSD_AVG_HANDLER, 0);
set_handler("avg_tc", Handler::f_read | Handler::f_read_param, handler, TSD_AVG_TC_HANDLER, 0);
set_handler("min", Handler::f_read | Handler::f_read_param, handler, TSD_MIN_HANDLER, 0);
set_handler("max", Handler::f_read | Handler::f_read_param, handler, TSD_MAX_HANDLER, 0);
set_handler("stddev", Handler::f_read | Handler::f_read_param, handler, TSD_STD_HANDLER, 0);
set_handler("perc00", Handler::f_read | Handler::f_read_param, handler, TSD_PERC_00_HANDLER, 0);
set_handler("perc01", Handler::f_read | Handler::f_read_param, handler, TSD_PERC_01_HANDLER, 0);
set_handler("perc05", Handler::f_read | Handler::f_read_param, handler, TSD_PERC_05_HANDLER, 0);
set_handler("perc10", Handler::f_read | Handler::f_read_param, handler, TSD_PERC_10_HANDLER, 0);
set_handler("perc25", Handler::f_read | Handler::f_read_param, handler, TSD_PERC_25_HANDLER, 0);
set_handler("median", Handler::f_read | Handler::f_read_param, handler, TSD_MED_HANDLER, 0);
set_handler("perc75", Handler::f_read | Handler::f_read_param, handler, TSD_PERC_75_HANDLER, 0);
set_handler("perc90", Handler::f_read | Handler::f_read_param, handler, TSD_PERC_90_HANDLER, 0);
set_handler("perc95", Handler::f_read | Handler::f_read_param, handler, TSD_PERC_95_HANDLER, 0);
set_handler("perc99", Handler::f_read | Handler::f_read_param, handler, TSD_PERC_99_HANDLER, 0);
set_handler("perc100", Handler::f_read | Handler::f_read_param, handler, TSD_PERC_100_HANDLER, 0);
set_handler("perc", Handler::f_read | Handler::f_read_param, handler, TSD_PERC_HANDLER, 0);
set_handler("index", Handler::f_read, handler, TSD_CURRENT_INDEX, 0);
set_handler("last", Handler::f_read, handler, TSD_LAST_SEEN, 0);
set_handler("dump", Handler::f_read, handler, TSD_DUMP_HANDLER, 0);
set_handler("dump_list", Handler::f_read, handler, TSD_DUMP_LIST_HANDLER, 0);
}
inline int TimestampDiff::smaction(Packet *p)
{
TimestampT now = TimestampT::now_steady();
uint64_t i = NumberPacket::read_number_of_packet(p, _offset, _net_order);
TimestampT old = get_recordtimestamp_instance()->get(i);
if (old == TimestampT::uninitialized_t()) {
return 1;
}
if (_sample != 1) {
if ((uint32_t)i % _sample != 0) {
return 0;
}
}
TimestampT diff = now - old;
uint32_t usec = _nano? diff.nsecval() : diff.usecval();
if ((usec > _max_delay_ms * (_nano?1000000:1000))) {
if (_verbose) {
click_chatter(
"Packet %" PRIu64 " experienced delay %u ms > %u ms",
i, (usec)/ (_nano?1000000:1000), _max_delay_ms
);
}
}
else {
uint32_t next_index = _nd.fetch_and_add(1);
unsigned char tc = 0;
if (_tc_offset >= 0) {
tc = p->data()[_tc_offset] & _tc_mask;
}
if (_limit) {
_delays[next_index] = {usec,tc};
} else {
_delays.push_back({usec,tc});
}
} return 0;
}
void TimestampDiff::push(int, Packet *p)
{
int o = smaction(p);
checked_output_push(o, p);
}
#if HAVE_BATCH
void
TimestampDiff::push_batch(int, PacketBatch *batch)
{
CLASSIFY_EACH_PACKET(2, smaction, batch, checked_output_push_batch);
}
#endif
RecordTimestamp* TimestampDiff::get_recordtimestamp_instance()
{
return _rt;
}
void
TimestampDiff::min_mean_max(unsigned &min, double &mean, unsigned &max, uint32_t begin, int tc)
{
const uint32_t current_vector_length = static_cast<const uint32_t>(_nd.value());
double sum = 0.0;
uint32_t n = 0;
for (uint32_t i=begin; i<current_vector_length; i++) {
if (tc > -1 && _delays[i].tc != tc) continue;
unsigned delay = _delays[i].delay;
sum += static_cast<double>(delay);
if (delay < min) {
min = delay;
}
if (delay > max) {
max = delay;
}
n++;
}
// Set minimum properly if not updated above
if (min == UINT_MAX) {
min = 0;
}
if (current_vector_length == 0) {
mean = 0.0;
return;
}
mean = sum / static_cast<double>(n);
}
double
TimestampDiff::standard_deviation(const double mean, uint32_t begin)
{
const uint32_t current_vector_length = static_cast<const uint32_t>(_nd.value());
double var = 0.0;
for (uint32_t i=begin; i<current_vector_length; i++) {
var += pow(_delays[i].delay - mean, 2);
}
// Prevent square root of zero
if (var == 0) {
return static_cast<double>(0);
}
return sqrt(var / current_vector_length);
}
double
TimestampDiff::percentile(const double percent, uint32_t begin)
{
double perc = 0;
const uint32_t current_vector_length = static_cast<const uint32_t>(_nd.value());
// Implies empty vector, no percentile.
if (current_vector_length == 0 || begin >= current_vector_length) {
return 0;
}
// The desired percentile
size_t idx = (percent * (current_vector_length - begin)) / 100 + begin;
// Implies that user asked for the 0 percetile (i.e., min).
if (idx <= begin) {
return (double)(*std::min_element(_delays.begin() + begin, _delays.begin() + current_vector_length)).delay;
// Implies that user asked for the 100 percetile (i.e., max).
} else if (idx >= current_vector_length) {
return (double)(*std::max_element(_delays.begin() + begin, _delays.begin() + current_vector_length)).delay;
}
//else no need to sort, we use nth_element
auto nth = _delays.begin() + idx;
std::nth_element(_delays.begin() + begin, nth, _delays.begin() + current_vector_length);
perc = (double)(*nth).delay;
return perc;
}
unsigned
TimestampDiff::last_value_seen()
{
const int32_t last_vector_index = static_cast<const int32_t>(_nd.value() - 1);
if (last_vector_index < 0) {
return 0;
}
return _delays[last_vector_index].delay;
}
CLICK_ENDDECLS
ELEMENT_REQUIRES(userlevel)
EXPORT_ELEMENT(TimestampDiff)