Commit 31441586 authored by Joanne Hugé's avatar Joanne Hugé

Add option to print histograms to stdout on exit

parent f696b55f
/* /*
* Real time packet sending thread * Real time packet sending thread
* *
* Bash options:
*
* -a Run the real time thread on CPU1
* -b Measure RTT
* -d BUF_LEN Set the length of tx buffer
* -e Set a txtime (to be used in an ETF qdisc)
* -f IF Set the network interface to be used
* -i USEC Wake up the real time thread every USEC microseconds (Default: 10ms)
* -l N Wake up the real time thread N times (Default: 0)
* -p PRIO Run the real time thread at priority PRIO
* -r USEC Refresh the non real time main thread every USEC microseconds (Default: 50ms)
* -t Enable timestamps
*
* Large portions taken from cyclictest * Large portions taken from cyclictest
* *
*/ */
...@@ -23,6 +10,7 @@ ...@@ -23,6 +10,7 @@
#include <error.h> #include <error.h>
#include <pthread.h> #include <pthread.h>
#include <sched.h> #include <sched.h>
#include <signal.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
...@@ -30,11 +18,14 @@ ...@@ -30,11 +18,14 @@
#include <time.h> #include <time.h>
#include <unistd.h> #include <unistd.h>
#include "send_packet.h"
#include "recv_packet.h" #include "recv_packet.h"
#include "send_packet.h"
#include "utilities.h" #include "utilities.h"
enum TSNTask {SEND_PACKET_TASK, RTT_TASK}; // Enum and structs
enum TSNTask { SEND_PACKET_TASK,
RTT_TASK };
typedef struct thread_stat { typedef struct thread_stat {
int nb_cycles; int nb_cycles;
...@@ -50,6 +41,7 @@ typedef struct thread_param { ...@@ -50,6 +41,7 @@ typedef struct thread_param {
int enable_affinity; int enable_affinity;
int enable_etf; int enable_etf;
int enable_timestamps; int enable_timestamps;
int enable_histograms;
enum TSNTask tsn_task; enum TSNTask tsn_task;
int sockfd; int sockfd;
...@@ -62,12 +54,41 @@ typedef struct thread_param { ...@@ -62,12 +54,41 @@ typedef struct thread_param {
typedef struct main_param { typedef struct main_param {
int refresh_rate; int refresh_rate;
int packet_priority; int packet_priority;
int verbose;
size_t tx_buffer_len; size_t tx_buffer_len;
} main_param_t; } main_param_t;
// Static functions
static void process_options(int argc, char *argv[], thread_param_t *param, static void process_options(int argc, char *argv[], thread_param_t *param,
main_param_t *main_param); main_param_t *main_param);
static void do_tsn_task(struct thread_param * param, uint64_t next_txtime); static void do_tsn_task(struct thread_param *param, uint64_t next_txtime);
static void print_histograms();
static void sigint_handler(int sig_num);
// Static variables
static int64_t histograms[NB_HISTOGRAMS][MAX_HIST_VAL];
static main_param_t main_param;
static thread_param_t *param;
static void help(char *argv[]) {
printf("Usage: %s [-abethgv] [-d BUF_LEN] [-f IF] [-i USEC] [-l N] [-p PRIO] [-r USEC]\n\n", argv[0]);
printf(" -a Run the real time thread on CPU1\n");
printf(" -b Measure RTT\n");
printf(" -d BUF_LEN Set the length of tx buffer\n");
printf(" -e Set a txtime (to be used in an ETF qdisc)\n");
printf(" -f IF Set the network interface to be used\n");
printf(" -g Print histograms to sdtout on exit\n");
printf(" -h Show help\n");
printf(" -i USEC Wake up the real time thread every USEC microseconds (Default: 10ms)\n");
printf(" -l N Wake up the real time thread N times (Default: 0)\n");
printf(" -p PRIO Run the real time thread at priority PRIO\n");
printf(" -r USEC Refresh the non real time main thread every USEC microseconds (Default: 50ms)\n");
printf(" -t Enable timestamps\n");
printf(" -v Verbose\n");
printf("\n");
}
// Real-time thread // Real-time thread
// Sends packets at a regular intervall // Sends packets at a regular intervall
...@@ -101,7 +122,8 @@ static void *packet_sending_thread(void *p) { ...@@ -101,7 +122,8 @@ static void *packet_sending_thread(void *p) {
// Packet sending loop // Packet sending loop
for (param->stats.nb_cycles = 0;; param->stats.nb_cycles++) { for (param->stats.nb_cycles = 0;; param->stats.nb_cycles++) {
if (param->max_cycles) if (param->max_cycles)
if (param->stats.nb_cycles >= param->max_cycles) break; if (param->stats.nb_cycles >= param->max_cycles)
break;
do_tsn_task(param, next_txtime); do_tsn_task(param, next_txtime);
...@@ -118,65 +140,86 @@ static void *packet_sending_thread(void *p) { ...@@ -118,65 +140,86 @@ static void *packet_sending_thread(void *p) {
// Handles the IO and creates real time threads // Handles the IO and creates real time threads
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
pthread_t thread; pthread_t thread;
thread_param_t param;
main_param_t main_param; signal(SIGINT, sigint_handler);
param = malloc(sizeof(thread_param_t));
// Default values // Default values
param.interval = 100000 * 1000; param->interval = 100000 * 1000;
param.max_cycles = 0; param->max_cycles = 0;
param.priority = 99; param->priority = 99;
param.enable_affinity = 0; param->enable_affinity = 0;
param.enable_etf = 0; param->enable_etf = 0;
param.enable_timestamps = 0; param->enable_timestamps = 0;
param.tsn_task = SEND_PACKET_TASK; param->enable_histograms = 0;
param->tsn_task = SEND_PACKET_TASK;
main_param.refresh_rate = 50000; main_param.refresh_rate = 50000;
main_param.packet_priority = 3; main_param.packet_priority = 3;
main_param.tx_buffer_len = 1024; main_param.tx_buffer_len = 1024;
main_param.verbose = 0;
// Process bash options // Process bash options
process_options(argc, argv, &param, &main_param); process_options(argc, argv, param, &main_param);
if(param->enable_histograms) {
// Init histograms
memset((int64_t *) histograms, 0, NB_HISTOGRAMS * MAX_HIST_VAL);
}
init_udp_send(param.enable_etf, param.enable_timestamps, main_param.packet_priority, param.network_if, main_param.tx_buffer_len); init_udp_send(param->enable_etf, param->enable_timestamps,
main_param.packet_priority, param->network_if,
main_param.tx_buffer_len);
if(param.tsn_task == RTT_TASK) if (param->tsn_task == RTT_TASK)
param.sockfd = init_udp_recv(); param->sockfd = init_udp_recv();
usleep(10000); usleep(10000);
if (pthread_create(&thread, NULL, packet_sending_thread, (void *)&param)) if (pthread_create(&thread, NULL, packet_sending_thread, (void *)param))
error(EXIT_FAILURE, errno, "Couldn't create thread"); error(EXIT_FAILURE, errno, "Couldn't create thread");
for (;;) { for (;;) {
usleep(main_param.refresh_rate); usleep(main_param.refresh_rate);
if(param.tsn_task == RTT_TASK) { if (main_param.verbose) {
printf("RTT: %" PRIu64 " (%d)\n", param.stats.rtt, param.stats.nb_cycles); if (param->tsn_task == RTT_TASK) {
printf("RTT: %" PRIu64 " (%d)\n", param->stats.rtt,
param->stats.nb_cycles);
} else if (param->enable_timestamps) {
printf("(%d)\n", param->stats.nb_cycles);
printf(" Enter send_udp_packet timestamp: %" PRIu64 "\n",
param->stats.packet_ts.user_enter_send);
printf(" Call sendmsg timestamp : %" PRIu64 "\n",
param->stats.packet_ts.user_call_sendmsg);
printf(" Leave kernel timestamp : %" PRIu64 "\n",
param->stats.packet_ts.kernel_leave);
} }
else if (param.enable_timestamps) {
printf("(%d)\n", param.stats.nb_cycles);
printf(" Enter send_udp_packet timestamp: %" PRIu64 "\n", param.stats.packet_ts.user_enter_send);
printf(" Call sendmsg timestamp : %" PRIu64 "\n", param.stats.packet_ts.user_call_sendmsg);
printf(" Leave kernel timestamp : %" PRIu64 "\n", param.stats.packet_ts.kernel_leave);
} }
if (param.max_cycles) if (param->max_cycles)
if (param.max_cycles == param.stats.nb_cycles) break; if (param->max_cycles == param->stats.nb_cycles)
break;
} }
if (param->enable_histograms)
print_histograms();
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
} }
static void do_tsn_task(struct thread_param * param, uint64_t next_txtime) { static void do_tsn_task(struct thread_param *param, uint64_t next_txtime) {
struct timespec t1, t2; struct timespec t1, t2;
if(param->tsn_task == SEND_PACKET_TASK) { if (param->tsn_task == SEND_PACKET_TASK) {
param->stats.packet_ts = send_udp_packet(param->enable_etf, param->enable_timestamps, next_txtime, param->ip_address); param->stats.packet_ts = send_udp_packet(
} param->enable_etf, param->enable_timestamps, next_txtime,
else if(param->tsn_task == RTT_TASK) { param->ip_address, histograms);
} else if (param->tsn_task == RTT_TASK) {
clock_gettime(CLOCK_MONOTONIC, &t1); clock_gettime(CLOCK_MONOTONIC, &t1);
send_udp_packet(param->enable_etf, param->enable_timestamps, next_txtime, param->ip_address); send_udp_packet(param->enable_etf, param->enable_timestamps, next_txtime,
param->ip_address, histograms);
recv_udp_packet(param->sockfd); recv_udp_packet(param->sockfd);
clock_gettime(CLOCK_MONOTONIC, &t2); clock_gettime(CLOCK_MONOTONIC, &t2);
...@@ -184,27 +227,52 @@ static void do_tsn_task(struct thread_param * param, uint64_t next_txtime) { ...@@ -184,27 +227,52 @@ static void do_tsn_task(struct thread_param * param, uint64_t next_txtime) {
} }
} }
static void help(char *argv[]) { static void print_histograms() {
printf("Usage: %s [-abet] [-d BUF_LEN] [-f IF] [-i USEC] [-l N] [-p PRIO] [-r USEC]\n\n", argv[0]); printf("{\"measure_type\": \"packet_timestamps\",\
printf(" -a Run the real time thread on CPU1\n"); \"props_names\": [\"user_space\", \"kernel_space\"],\
printf(" -b Measure RTT\n"); \"units\": [\"us\", \"us\"],\
printf(" -d BUF_LEN Set the length of tx buffer\n"); \"props\": [");
printf(" -e Set a txtime (to be used in an ETF qdisc)\n"); for (int i = 0; i < NB_HISTOGRAMS; i++) {
printf(" -f IF Set the network interface to be used\n"); int max_hist_val = MAX_HIST_VAL;
printf(" -i USEC Wake up the real time thread every USEC microseconds (Default: 10ms)\n"); for (int j = 0; j < MAX_HIST_VAL; j++)
printf(" -l N Wake up the real time thread N times (Default: 0)\n"); max_hist_val = histograms[i][j] ? j : max_hist_val;
printf(" -p PRIO Run the real time thread at priority PRIO\n");
printf(" -r USEC Refresh the non real time main thread every USEC microseconds (Default: 50ms)\n"); printf("[");
printf(" -t Enable timestamps\n"); for (int j = 0; j < max_hist_val; j++) {
printf("\n"); if (j + 1 < max_hist_val)
printf("%" PRIi64 ", ", histograms[i][j]);
else
printf("%" PRIi64, histograms[i][j]);
}
if (i + 1 < NB_HISTOGRAMS)
printf("], ");
else
printf("]");
}
int interval = param->interval / 1000;
uint64_t duration = interval * param->stats.nb_cycles;
int duration_hour = duration / (1000000l * 3600);
int duration_minutes = duration / (1000000l * 60) - (duration_hour * 60);
printf("], ");
printf("\"props_type\": \"histogram\", \"metadata\": {\"i\": \"%dus\", \"duration\": \"%dh%d\"}}\n", interval, duration_hour, duration_minutes);
}
static void sigint_handler(int sig_num) {
(void)sig_num;
if (param->enable_histograms)
print_histograms();
exit(EXIT_SUCCESS);
} }
static void process_options(int argc, char *argv[], thread_param_t *param, static void process_options(int argc, char *argv[], thread_param_t *param,
main_param_t *main_param) { main_param_t *main_param) {
for (;;) { for (;;) {
int c = getopt(argc, argv, "abd:ef:hi:l:p:q:r:t"); int c = getopt(argc, argv, "abd:ef:ghi:l:p:q:r:tv");
if (c == -1) break; if (c == -1)
break;
switch (c) { switch (c) {
case 'a': case 'a':
...@@ -215,7 +283,7 @@ static void process_options(int argc, char *argv[], thread_param_t *param, ...@@ -215,7 +283,7 @@ static void process_options(int argc, char *argv[], thread_param_t *param,
break; break;
case 'd': case 'd':
main_param->tx_buffer_len = atoi(optarg); main_param->tx_buffer_len = atoi(optarg);
if( main_param->tx_buffer_len < 1 ) { if (main_param->tx_buffer_len < 1) {
fprintf(stderr, "BUF_LEN should be greater than 1\n"); fprintf(stderr, "BUF_LEN should be greater than 1\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
...@@ -226,6 +294,9 @@ static void process_options(int argc, char *argv[], thread_param_t *param, ...@@ -226,6 +294,9 @@ static void process_options(int argc, char *argv[], thread_param_t *param,
case 'f': case 'f':
strcpy(param->network_if, optarg); strcpy(param->network_if, optarg);
break; break;
case 'g':
param->enable_histograms = 1;
break;
case 'h': case 'h':
help(argv); help(argv);
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
...@@ -248,6 +319,9 @@ static void process_options(int argc, char *argv[], thread_param_t *param, ...@@ -248,6 +319,9 @@ static void process_options(int argc, char *argv[], thread_param_t *param,
case 't': case 't':
param->enable_timestamps = 1; param->enable_timestamps = 1;
break; break;
case 'v':
main_param->verbose = 1;
break;
default: default:
help(argv); help(argv);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
......
...@@ -41,7 +41,7 @@ ...@@ -41,7 +41,7 @@
#define MESSAGE ((uint32_t)0x00FACADE) #define MESSAGE ((uint32_t)0x00FACADE)
static void process_timestamps(struct packet_timestamps *packet_ts); static void process_timestamps(struct packet_timestamps *packet_ts, int64_t histograms[NB_HISTOGRAMS][MAX_HIST_VAL]);
static void init_tx_buffer(size_t _tx_buffer_len); static void init_tx_buffer(size_t _tx_buffer_len);
static int so_priority = 3; static int so_priority = 3;
...@@ -148,7 +148,8 @@ uint64_t get_txtime() { ...@@ -148,7 +148,8 @@ uint64_t get_txtime() {
*/ */
struct packet_timestamps send_udp_packet(int use_etf, int use_timestamps, struct packet_timestamps send_udp_packet(int use_etf, int use_timestamps,
uint64_t txtime, uint64_t txtime,
const char *server_ip) { const char *server_ip,
int64_t histograms[NB_HISTOGRAMS][MAX_HIST_VAL]) {
char control[CMSG_SPACE(sizeof(txtime))] = {}; char control[CMSG_SPACE(sizeof(txtime))] = {};
struct sockaddr_in sin; struct sockaddr_in sin;
struct cmsghdr *cmsg; struct cmsghdr *cmsg;
...@@ -205,7 +206,7 @@ struct packet_timestamps send_udp_packet(int use_etf, int use_timestamps, ...@@ -205,7 +206,7 @@ struct packet_timestamps send_udp_packet(int use_etf, int use_timestamps,
if (use_timestamps) { if (use_timestamps) {
res = poll(&poll_fd, 1, 0); res = poll(&poll_fd, 1, 0);
if (res > 0) if (res > 0)
process_timestamps(&packet_ts); process_timestamps(&packet_ts, histograms);
else else
fprintf(stderr, "select failed\n"); fprintf(stderr, "select failed\n");
} }
...@@ -213,7 +214,27 @@ struct packet_timestamps send_udp_packet(int use_etf, int use_timestamps, ...@@ -213,7 +214,27 @@ struct packet_timestamps send_udp_packet(int use_etf, int use_timestamps,
return packet_ts; return packet_ts;
} }
static void process_timestamps(struct packet_timestamps *packet_ts) { static void fill_histograms(struct packet_timestamps *packet_ts, int64_t histograms[NB_HISTOGRAMS][MAX_HIST_VAL]) {
uint64_t user_space_time = packet_ts->user_call_sendmsg - packet_ts->user_enter_send;
uint64_t kernel_space_time = packet_ts->kernel_leave - packet_ts->user_call_sendmsg;
user_space_time /= 1000u;
kernel_space_time /= 1000u;
if(user_space_time > MAX_HIST_VAL) {
fprintf(stderr, "user_space_time value too high: %" PRIu64 "us\n", user_space_time);
exit(EXIT_FAILURE);
}
if(kernel_space_time > MAX_HIST_VAL) {
fprintf(stderr, "kernel_space_time value too high: %" PRIu64 "us\n", kernel_space_time);
exit(EXIT_FAILURE);
}
histograms[0][user_space_time]++;
histograms[1][kernel_space_time]++;
}
static void process_timestamps(struct packet_timestamps *packet_ts, int64_t histograms[NB_HISTOGRAMS][MAX_HIST_VAL]) {
char data[256]; char data[256];
struct msghdr msg; struct msghdr msg;
struct iovec entry; struct iovec entry;
...@@ -243,6 +264,7 @@ static void process_timestamps(struct packet_timestamps *packet_ts) { ...@@ -243,6 +264,7 @@ static void process_timestamps(struct packet_timestamps *packet_ts) {
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SO_TIMESTAMPING) { if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SO_TIMESTAMPING) {
struct timespec *stamp = (struct timespec *)CMSG_DATA(cmsg); struct timespec *stamp = (struct timespec *)CMSG_DATA(cmsg);
packet_ts->kernel_leave = ts_to_uint(*stamp); packet_ts->kernel_leave = ts_to_uint(*stamp);
fill_histograms(packet_ts, histograms);
} else { } else {
#ifdef DEBUG #ifdef DEBUG
fprintf(stderr, "process_timestamps: level %d type %d", cmsg->cmsg_level, fprintf(stderr, "process_timestamps: level %d type %d", cmsg->cmsg_level,
......
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include "utilities.h"
struct packet_timestamps { struct packet_timestamps {
uint64_t user_enter_send; uint64_t user_enter_send;
uint64_t user_call_sendmsg; uint64_t user_call_sendmsg;
...@@ -11,6 +13,6 @@ struct packet_timestamps { ...@@ -11,6 +13,6 @@ struct packet_timestamps {
}; };
void init_udp_send(int use_etf, int use_timestamps, int so_priority, char * network_if, size_t tx_buffer_len); void init_udp_send(int use_etf, int use_timestamps, int so_priority, char * network_if, size_t tx_buffer_len);
struct packet_timestamps send_udp_packet(int use_etf, int use_timestamps, uint64_t txtime, const char *server_ip); struct packet_timestamps send_udp_packet(int use_etf, int use_timestamps, uint64_t txtime, const char *server_ip, int64_t histograms[NB_HISTOGRAMS][MAX_HIST_VAL]);
#endif #endif
...@@ -98,7 +98,7 @@ static void *packet_receiving_thread(void *p) { ...@@ -98,7 +98,7 @@ static void *packet_receiving_thread(void *p) {
for (stats->packets_received = 0;; stats->packets_received++) { for (stats->packets_received = 0;; stats->packets_received++) {
if (param->tsn_task == RTT_TASK) { if (param->tsn_task == RTT_TASK) {
recv_udp_packet(param->sockfd); recv_udp_packet(param->sockfd);
send_udp_packet(0, 0, 0, param->ip_address); send_udp_packet(0, 0, 0, param->ip_address, NULL);
} else if (param->tsn_task == RECV_PACKET_TASK) { } else if (param->tsn_task == RECV_PACKET_TASK) {
recv_udp_packet(param->sockfd); recv_udp_packet(param->sockfd);
......
...@@ -10,6 +10,8 @@ ...@@ -10,6 +10,8 @@
#define NSEC_PER_SEC UINT64_C(1000000000) #define NSEC_PER_SEC UINT64_C(1000000000)
#define SERVER_PORT "50000" #define SERVER_PORT "50000"
#define SERVER_PORT_INT 50000 #define SERVER_PORT_INT 50000
#define MAX_HIST_VAL 1000
#define NB_HISTOGRAMS 3
uint64_t ts_to_uint(struct timespec t); uint64_t ts_to_uint(struct timespec t);
void add_ns(struct timespec *t, uint64_t ns); void add_ns(struct timespec *t, uint64_t ns);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment