diff --git a/CMakeLists.txt b/CMakeLists.txt index 4bcfce8..c5261a4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,7 +19,7 @@ project(tstest VERSION 1.0) # ---- BUILD ---- add_executable(tstest) -target_sources(tstest PRIVATE src/liblink.c src/timestamping.c src/ptp_message.c src/extts.c src/pkt.c src/tstest.c src/delay.c src/check.c src/tc.c) +target_sources(tstest PRIVATE src/liblink.c src/timestamping.c src/ptp_message.c src/extts.c src/pkt.c src/tstest.c src/delay.c src/check.c src/tc.c src/stats.c) target_include_directories(tstest PUBLIC ${PROJECT_INCLUDES}) target_link_libraries(tstest PUBLIC) diff --git a/include/stats.h b/include/stats.h new file mode 100644 index 0000000..29329b4 --- /dev/null +++ b/include/stats.h @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-2.0-only +// SPDX-FileCopyrightText: 2024 Casper Andersson + +#ifndef __STATS_H__ +#define __STATS_H__ + +#include + +typedef struct { + int64_t mean; + int64_t max; + int64_t min; +} Result; + +/* Ingress/egress latency of host should be accounted for in these timestamps */ +struct tsinfo { + uint16_t seqid; + int64_t tx_ts; + int64_t rx_ts; + int64_t correction; +}; + +typedef struct { + int count; + int size; + struct tsinfo *tsinfo; +} Stats; + +int init_stats(Stats *s, int size); +void free_stats(Stats *s); +int add_stats(Stats *s, int64_t tx_ts, int64_t rx_ts, int64_t correction, uint16_t seqid); +Result stats_get_time_error(Stats *s); +Result stats_get_latency(Stats *s); +Result stats_get_pdv(Stats *s); +void show_stats(Stats *s, char *p1, char *p2, int count_left); + +#endif /* __STATS_H__ */ diff --git a/src/stats.c b/src/stats.c new file mode 100644 index 0000000..a3176fd --- /dev/null +++ b/src/stats.c @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: GPL-2.0-only +// SPDX-FileCopyrightText: 2024 Casper Andersson + +#include +#include +#include +#include +#include + +#include "liblink.h" +#include "stats.h" + +/* TODO: + * - If performance becomes slow, aggregate all calculations into one + loop and return a struct with all results. + */ + +int init_stats(Stats *s, int size) +{ + if (size == 0) + s->size = 100; + else + s->size = size; + s->count = 0; + s->tsinfo = malloc(sizeof(struct tsinfo) * s->size); + if (!s->tsinfo) { + ERR("failed to allocate stats array"); + return ENOMEM; + } + return 0; +} + +void free_stats(Stats *s) +{ + free(s->tsinfo); + s->tsinfo = NULL; +} + +int add_stats(Stats *s, int64_t tx_ts, int64_t rx_ts, int64_t correction, uint16_t seqid) +{ + if (s->count == s->size) { + s->size = s->size * 2; + s->tsinfo = realloc(s->tsinfo, sizeof(struct tsinfo) * s->size); + if (!s->tsinfo) { + ERR("failed to reallocate stats array"); + return ENOMEM; + } + } + + s->tsinfo[s->count].seqid = seqid; + s->tsinfo[s->count].tx_ts = tx_ts; + s->tsinfo[s->count].rx_ts = rx_ts; + s->tsinfo[s->count].correction = correction; + s->count++; + return 0; +} + +static int64_t tsinfo_get_error(struct tsinfo tsinfo) +{ + return tsinfo.rx_ts - tsinfo.tx_ts - tsinfo.correction; +} + +static int64_t tsinfo_get_latency(struct tsinfo tsinfo) +{ + return tsinfo.rx_ts - tsinfo.tx_ts; +} + +Result stats_get_time_error(Stats *s) +{ + int64_t sum_err = 0; + int64_t timeerror; + Result r; + + timeerror = tsinfo_get_error(s->tsinfo[0]); + r.max = timeerror; + r.min = timeerror; + for (int i = 0; i < s->count; i++) { + timeerror = tsinfo_get_error(s->tsinfo[i]); + if (timeerror > r.max) + r.max = timeerror; + if (timeerror < r.min) + r.min = timeerror; + sum_err += timeerror; + } + r.mean = sum_err / s->count; + return r; +} + +Result stats_get_latency(Stats *s) +{ + int64_t sum_lat = 0; + int64_t latency; + Result r; + + latency = tsinfo_get_latency(s->tsinfo[0]); + r.max = latency; + r.min = latency; + for (int i = 0; i < s->count; i++) { + latency = tsinfo_get_latency(s->tsinfo[i]); + if (latency > r.max) + r.max = latency; + if (latency < r.min) + r.min = latency; + sum_lat += latency; + } + + r.mean = sum_lat / s->count; + return r; +} + +static int64_t pdv_get_lucky_packet(Stats *s) +{ + int64_t lucky_packet, tmp; + + lucky_packet = s->tsinfo[0].rx_ts - s->tsinfo[0].tx_ts; + for (int i = 1; i < s->count; i++) { + tmp = s->tsinfo[i].rx_ts - s->tsinfo[i].tx_ts; + if (tmp < lucky_packet) + lucky_packet = tmp; + } + return lucky_packet; +} + +Result stats_get_pdv(Stats *s) +{ + int64_t lucky_packet, tmp, sum = 0; + Result r = { 0 }; + + lucky_packet = pdv_get_lucky_packet(s); + r.max = 0; + r.min = 0; + for (int i = 0; i < s->count; i++) { + tmp = (s->tsinfo[i].rx_ts - s->tsinfo[i].tx_ts) - lucky_packet; + sum += tmp; + if (tmp > r.max) + r.max = tmp; + } + r.mean = sum / s->count; + return r; +} + +void show_stats(Stats *s, char *p1, char *p2, int count_left) +{ + if (s->count == 0) { + printf("No measurements\n"); + return; + } + + printf("===============\n"); + if (count_left) + printf("%d measurements (exited early, expected %d)\n", s->count, + s->count + count_left); + else + printf("%d measurements\n", s->count); + printf("%s -> %s\n", p1, p2); + + Result time_error = stats_get_time_error(s); + Result latency = stats_get_latency(s); + Result pdv = stats_get_pdv(s); + + printf("--- TIME ERROR ---\n"); + printf("Mean: %" PRId64 "\n", time_error.mean); + printf("Max : %" PRId64 "\n", time_error.max); + printf("Min : %" PRId64 "\n", time_error.min); + printf("--- LATENCY ---\n"); + printf("Mean: %" PRId64 "\n", latency.mean); + printf("Max : %" PRId64 "\n", latency.max); + printf("Min : %" PRId64 "\n", latency.min); + printf("--- PDV ---\n"); + printf("Mean: %" PRId64 "\n", pdv.mean); + printf("Max : %" PRId64 "\n", pdv.max); + printf("Min : %" PRId64 " (lucky packet)\n", pdv.min); + printf("===============\n"); +} diff --git a/src/tc.c b/src/tc.c index 189d040..6b858e3 100644 --- a/src/tc.c +++ b/src/tc.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include "net_tstamp_cpy.h" @@ -20,6 +19,7 @@ #include "liblink.h" #include "tstest.h" #include "pkt.h" +#include "stats.h" /* Measure the one-way inaccuracy through a TC. Runs on two ports that * are either synchronized or use the same PHC. The inaccuracies will @@ -138,122 +138,6 @@ static void send_pkt(struct pkt_cfg *cfg, int sock, int64_t *tx_ts) } } -/* Ingress/egress latency of host should be accounted for in these timestamps */ -struct tsinfo { - uint16_t seqid; - int64_t tx_ts; - int64_t rx_ts; - int64_t correction; -}; - -typedef struct { - int count; - int size; - struct tsinfo *tsinfo; -} Stats; - -static int init_stats(Stats *s, int size) -{ - if (size == 0) - s->size = 100; - else - s->size = size; - s->count = 0; - s->tsinfo = malloc(sizeof(struct tsinfo) * s->size); - if (!s->tsinfo) { - ERR("failed to allocate stats array"); - return ENOMEM; - } - return 0; -} - -static void free_stats(Stats *s) -{ - free(s->tsinfo); -} - -static int add_stats(Stats *s, int64_t tx_ts, int64_t rx_ts, int64_t correction, uint16_t seqid) -{ - if (s->count == s->size) { - s->size = s->size * 2; - s->tsinfo = realloc(s->tsinfo, sizeof(struct tsinfo) * s->size); - if (!s->tsinfo) { - ERR("failed to reallocate stats array"); - return ENOMEM; - } - } - - s->tsinfo[s->count].seqid = seqid; - s->tsinfo[s->count].tx_ts = tx_ts; - s->tsinfo[s->count].rx_ts = rx_ts; - s->tsinfo[s->count].correction = correction; - s->count++; - return 0; -} - -static int64_t tsinfo_get_error(struct tsinfo tsinfo) -{ - return tsinfo.rx_ts - tsinfo.tx_ts - tsinfo.correction; -} - -static int64_t tsinfo_get_latency(struct tsinfo tsinfo) -{ - return tsinfo.rx_ts - tsinfo.tx_ts; -} - -static void show_stats(Stats *s, char *p1, char *p2, int count_left) -{ - int64_t max_err, min_err, sum_err = 0; - int64_t max_lat, min_lat, sum_lat = 0; - int64_t timeerror, latency; - - if (s->count == 0) { - printf("No measurements\n"); - return; - } - - printf("===============\n"); - if (count_left) - printf("%d measurements (exited early, expected %d)\n", s->count, - s->count + count_left); - else - printf("%d measurements\n", s->count); - printf("%s -> %s\n", p1, p2); - - timeerror = tsinfo_get_error(s->tsinfo[0]); - latency = tsinfo_get_latency(s->tsinfo[0]); - max_err = timeerror; - min_err = timeerror; - max_lat = latency; - min_lat = latency; - - for (int i = 0; i < s->count; i++) { - timeerror = tsinfo_get_error(s->tsinfo[i]); - latency = tsinfo_get_latency(s->tsinfo[i]); - if (timeerror > max_err) - max_err = timeerror; - if (timeerror < min_err) - min_err = timeerror; - sum_err += timeerror; - - if (latency > max_lat) - max_lat = latency; - if (latency < min_lat) - min_lat = latency; - sum_lat += latency; - } - - printf("--- TIME ERROR ---\n"); - printf("Mean: %" PRId64 "\n", sum_err / s->count); - printf("Max : %" PRId64 "\n", max_err); - printf("Min : %" PRId64 "\n", min_err); - printf("--- LATENCY ---\n"); - printf("Mean: %" PRId64 "\n", sum_lat / s->count); - printf("Max : %" PRId64 "\n", max_lat); - printf("Min : %" PRId64 "\n", min_lat); - printf("===============\n"); -} - static void run(struct pkt_cfg *cfg, char *p1, char *p2, int p1_sock, int p2_sock) { int64_t rx_ts, tx_ts, correction, total_err;