From: Michal Sojka Date: Sun, 28 Nov 2010 10:13:15 +0000 (+0100) Subject: Add first version of latency tester X-Git-Tag: fix-allnoconfig~330 X-Git-Url: http://rtime.felk.cvut.cz/gitweb/can-benchmark.git/commitdiff_plain/12ec364a504c247dd1ef41ab40687ad4aad6ccc4 Add first version of latency tester --- diff --git a/latester/Makefile b/latester/Makefile new file mode 100644 index 0000000..76b56fd --- /dev/null +++ b/latester/Makefile @@ -0,0 +1,14 @@ +# Generic directory or leaf node makefile for OCERA make framework + +ifndef MAKERULES_DIR +MAKERULES_DIR := $(shell ( old_pwd="" ; while [ ! -e Makefile.rules ] ; do if [ "$$old_pwd" = `pwd` ] ; then exit 1 ; else old_pwd=`pwd` ; cd -L .. 2>/dev/null ; fi ; done ; pwd ) ) +endif + +ifeq ($(MAKERULES_DIR),) +all : default +.DEFAULT:: + @echo -e "\nThe Makefile.rules has not been found in this or parent directory\n" +else +include $(MAKERULES_DIR)/Makefile.rules +endif + diff --git a/latester/Makefile.omk b/latester/Makefile.omk new file mode 100644 index 0000000..2a6c4b1 --- /dev/null +++ b/latester/Makefile.omk @@ -0,0 +1,5 @@ +# -*- makefile -*- + +bin_PROGRAMS += latester +latester_SOURCES = latester.c +latester_LIBS = rt pthread m ulut diff --git a/latester/histogram.h b/latester/histogram.h new file mode 100644 index 0000000..ad7591c --- /dev/null +++ b/latester/histogram.h @@ -0,0 +1,54 @@ +#ifndef HISTOGRAM_H +#define HISTOGRAM_H + +struct histogram { + unsigned *data; + unsigned allocated; + unsigned resolution; +}; + +int histogram_init(struct histogram *h, + unsigned max_value, + unsigned resolution) +{ + size_t mem; + h->allocated = max_value/resolution + 1; + h->resolution = resolution; + mem = h->allocated*sizeof(*h->data); + h->data = malloc(mem); + if (h->data) { + memset(h->data, 0, mem); + return 0; + } else + return -1; +} + +void histogram_add(struct histogram *h, unsigned value) +{ + unsigned index = value / h->resolution; + if (index >= h->allocated) + index = h->allocated - 1; + h->data[index]++; +} + +void histogram_fprint(struct histogram *h, FILE *f) +{ + unsigned long long sum = 0, cum; + unsigned i; + for (i = 0; i < h->allocated; i++) + sum += h->data[i]; + cum = sum; + for (i = 0; i < h->allocated; i++) { + if (h->data[i] != 0) { + if (!getenv("CANPING_MS")) + fprintf(f, "%d %lld\n", i*h->resolution, cum); + else + fprintf(f, "%g %lld\n", 1e-3*(i*h->resolution), cum); + } + cum -= h->data[i]; + } +} + + + +#endif diff --git a/latester/latester.c b/latester/latester.c new file mode 100644 index 0000000..0329f48 --- /dev/null +++ b/latester/latester.c @@ -0,0 +1,414 @@ +/**************************************************************************/ +/* CAN latency tester */ +/* Copyright (C) 2010 Michal Sojka, DCE FEE CTU Prague */ +/* License: GPLv2 */ +/**************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "histogram.h" + +#ifndef DEBUG +#define dbg(level, fmt, arg...) do {} while (0) +#else +#define dbg(level, fmt, arg...) do { if (level <= DEBUG) { printf("candping: " fmt, ## arg); } } while (0) +#endif + +#define INTERRUPTED_SYSCALL(errno) (errno == EINTR || errno == ERESTART) + +#define MEMSET_ZERO(obj) memset(&(obj), 0, sizeof(obj)) + +/* Global variables */ +volatile sig_atomic_t finish_flag = 0; /* Threads should terminate. */ +sem_t finish_sem; /* Thread signals a termination */ + +#define MAX_INTERFACES 4 + +/* Command line options */ +char *option_interface[MAX_INTERFACES]; +int num_interfaces = 0; +canid_t option_id; +unsigned option_period_us = 100000; + +struct msg_info { + canid_t id; + uint8_t length; + struct timespec ts_sent, ts_sent_kern; + struct timespec ts_rx_onwire, ts_rx_onwire_kern; + struct timespec ts_rx_final, ts_rx_final_kern; +}; + +#define MAX_INFOS 10000 +struct msg_info msg_infos[MAX_INFOS]; +uint16_t curr_msg = 0; + +static inline struct msg_info *frame2info(struct can_frame *frame) +{ + uint16_t idx; + if (frame->can_dlc == 2) { + memcpy(&idx, frame->data, sizeof(idx)); + if (idx >= MAX_INFOS) + error(1, 0, "%s idx too high", __FUNCTION__); + } else + error(1, 0, "%s error", __FUNCTION__); + return &msg_infos[idx]; +} + +static inline char *tstamp_str(char *buf, struct timespec *tstamp) +{ + sprintf(buf, "%ld.%06ld", tstamp->tv_sec, tstamp->tv_nsec/1000); +} + +void print_msg_info(struct msg_info *mi) +{ + struct timespec ts_diff1, ts_diff2; + char str_sent[32], str_kern[32], str_user[32], str_diff1[32], str_diff2[32]; + timespec_subtract(&ts_diff1, &mi->ts_rx_final_kern, &mi->ts_sent); + timespec_subtract(&ts_diff2, &mi->ts_rx_final, &mi->ts_sent); + tstamp_str(str_sent, &mi->ts_sent); + tstamp_str(str_kern, &mi->ts_rx_final_kern); + tstamp_str(str_user, &mi->ts_rx_final); + tstamp_str(str_diff1, &ts_diff1); + tstamp_str(str_diff2, &ts_diff2); + printf("%s -> %s (%s) = %s (%s)\n", str_sent, str_user, str_kern, str_diff1, str_diff2); +} + + +/* Subtract the `struct timespec' values X and Y, storing the result in + RESULT. Return 1 if the difference is negative, otherwise 0. */ + +int timespec_subtract (struct timespec *result, struct timespec *x, struct timespec *yy) +{ + struct timespec ylocal = *yy, *y = &ylocal; + /* Perform the carry for the later subtraction by updating Y. */ + if (x->tv_nsec < y->tv_nsec) { + int nsec = (y->tv_nsec - x->tv_nsec) / 1000000000 + 1; + y->tv_nsec -= 1000000000 * nsec; + y->tv_sec += nsec; + } + if (x->tv_nsec - y->tv_nsec > 1000000000) { + int nsec = (x->tv_nsec - y->tv_nsec) / 1000000000; + y->tv_nsec += 1000000000 * nsec; + y->tv_sec -= nsec; + } + + /* Compute the time remaining to wait. + `tv_nsec' is certainly positive. */ + result->tv_sec = x->tv_sec - y->tv_sec; + result->tv_nsec = x->tv_nsec - y->tv_nsec; + + /* Return 1 if result is negative. */ + return x->tv_sec < y->tv_sec; +} + +void dbg_print_timespec(char *msg, struct timespec *tv) +{ + + printf("%s sec=%ld nsec=%ld\n", msg, tv->tv_sec, tv->tv_nsec); +} + +void set_sched_policy_and_prio(int policy, int rtprio) +{ + struct sched_param scheduling_parameters; + int maxprio=sched_get_priority_max(policy); + int minprio=sched_get_priority_min(policy); + + if((rtprio < minprio) || (rtprio > maxprio)) + error(1, 0, "The priority for requested policy is out of <%d, %d> range\n", + minprio, maxprio); + + scheduling_parameters.sched_priority = rtprio; + + if (0 != pthread_setschedparam(pthread_self(), policy, &scheduling_parameters)) + error(1, errno, "pthread_setschedparam error"); +} + +void term_handler(int signum) +{ + finish_flag = 1; +} + +static inline int sock_get_if_index(int s, const char *if_name) +{ + struct ifreq ifr; + MEMSET_ZERO(ifr); + + strcpy(ifr.ifr_name, if_name); + if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) + error(1, errno, "SIOCGIFINDEX"); + return ifr.ifr_ifindex; +} + +static inline get_tstamp(struct timespec *ts) +{ + clock_gettime(CLOCK_REALTIME, ts); +} + +int send_frame(int socket) +{ + struct can_frame frame; + struct msg_info *mi; + int ret; + + frame.can_id = option_id; + frame.can_dlc = 2; + memcpy(frame.data, &curr_msg, sizeof(curr_msg)); + mi = frame2info(&frame); + + get_tstamp(&mi->ts_sent); + ret = write(socket, &frame, sizeof(frame)); + return ret; +} + +static inline void get_next_timeout(struct timespec *timeout) +{ + struct timespec now; + static struct timespec last = {-1, 0 }; + + clock_gettime(CLOCK_MONOTONIC, &now); + + if (last.tv_sec == -1) + last = now; + last.tv_sec += option_period_us/1000000; + last.tv_nsec += (option_period_us%1000000)*1000; + while (last.tv_nsec >= 1000000000) { + last.tv_nsec -= 1000000000; + last.tv_sec++; + } + timespec_subtract(timeout, &last, &now); +} + +void receive(int s, struct can_frame *frame, struct timespec *ts_kern, struct timespec *ts_user) +{ + char ctrlmsg[CMSG_SPACE(sizeof(struct timeval)) + CMSG_SPACE(sizeof(__u32))]; + struct iovec iov; + struct msghdr msg; + struct cmsghdr *cmsg; + struct sockaddr_can addr; + int nbytes; + static uint64_t dropcnt = 0; + + iov.iov_base = frame; + msg.msg_name = &addr; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = &ctrlmsg; + + /* these settings may be modified by recvmsg() */ + iov.iov_len = sizeof(*frame); + msg.msg_namelen = sizeof(addr); + msg.msg_controllen = sizeof(ctrlmsg); + msg.msg_flags = 0; + + nbytes = recvmsg(s, &msg, 0); + if (nbytes < 0) + error(1, errno, "recvmsg"); + + if (nbytes < sizeof(struct can_frame)) + error(1, 0, "recvmsg: incomplete CAN frame\n"); + + get_tstamp(ts_user); + MEMSET_ZERO(*ts_kern); + for (cmsg = CMSG_FIRSTHDR(&msg); + cmsg && (cmsg->cmsg_level == SOL_SOCKET); + cmsg = CMSG_NXTHDR(&msg,cmsg)) { + if (cmsg->cmsg_type == SO_TIMESTAMPNS) + *ts_kern = *(struct timespec *)CMSG_DATA(cmsg); + else if (cmsg->cmsg_type == SO_RXQ_OVFL) + dropcnt += *(__u32 *)CMSG_DATA(cmsg); + } + +} + +void process_tx(int s) +{ + error(1, 0, "%s: not implemented", __FUNCTION__); +} + +void process_on_wire_rx(int s) +{ + error(1, 0, "%s: not implemented", __FUNCTION__); +} + + +void process_final_rx(int s) +{ + struct timespec ts_kern, ts_user, ts_diff; + struct can_frame frame; + struct msg_info *mi; + receive(s, &frame, &ts_kern, &ts_user); + mi = frame2info(&frame); + mi->ts_rx_final_kern = ts_kern; + mi->ts_rx_final = ts_user; + + print_msg_info(mi); +} + +void *measure_thread(void *arg) +{ + int s, i, ret; + struct pollfd pfd[MAX_INTERFACES]; + struct timespec timeout; + struct sockaddr_can addr; + sigset_t set; + + MEMSET_ZERO(pfd); + + for (i=0; i -d ... [other options]\n" + "Other options:\n" + " -d Interface to use. Must be given two times (tx, rx) or three times (tx, rx1, rx2).\n" + ); +} + +int parse_options(int argc, char *argv[]) +{ + int c; + + opterr = 0; + while ((c = getopt (argc, argv, "d:h")) != -1) + switch (c) + { + case 'd': + if (num_interfaces < MAX_INTERFACES) { + option_interface[num_interfaces] = optarg; + num_interfaces++; + } else + error(1, 0, "error: Too many -d options"); + break; + case 'h': + print_help(); + exit(0); + break; + case '?': + if (isprint (optopt)) + error (1, 0, "Unknown option `-%c'.\n", optopt); + else + error (1, 0, "Unknown option character `\\x%x'.\n", optopt); + default: + error(1, 0, "Unhandled parameter"); + } + if (num_interfaces < 2 || num_interfaces > 3) + error(1, 0, "-d option must be given exactly 2 or 3 times"); + return 0; +} + +int main(int argc, char *argv[]) +{ + pthread_t thread; + sigset_t set; + int ret; + + parse_options(argc, argv); + + mlockall(MCL_CURRENT | MCL_FUTURE); + + signal(SIGINT, term_handler); + signal(SIGTERM, term_handler); + + + pthread_create(&thread, 0, measure_thread, NULL); + + while (!finish_flag) { + sleep(1); + } + + pthread_join(thread, NULL); + + return 0; +}