X-Git-Url: http://rtime.felk.cvut.cz/gitweb/can-benchmark.git/blobdiff_plain/07f3838cc70208c1b269ff21637af051fc7defb2..1323549a8a6d9b15f4415f7fe3b5baaa91292630:/latester/latester.c diff --git a/latester/latester.c b/latester/latester.c index 774e9d9..8d38540 100644 --- a/latester/latester.c +++ b/latester/latester.c @@ -29,6 +29,8 @@ #include #include #include +#include +#include #include "histogram.h" @@ -46,13 +48,24 @@ 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]; +struct options { + char **interface; + canid_t id; + unsigned period_us; + unsigned timeout_ms; + unsigned count; + unsigned oneattime; + char *file; + char *histogram_fn; +} opt = { + .id = 10, + .period_us = 0, + .timeout_ms = 1000, + +}; + int num_interfaces = 0; -canid_t option_id; -unsigned option_period_us = 100000; struct msg_info { canid_t id; @@ -66,6 +79,8 @@ struct msg_info { struct msg_info msg_infos[MAX_INFOS]; uint16_t curr_msg = 0; +struct histogram histogram; + static inline struct msg_info *frame2info(struct can_frame *frame) { uint16_t idx; @@ -78,23 +93,37 @@ static inline struct msg_info *frame2info(struct can_frame *frame) return &msg_infos[idx]; } -static inline char *tstamp_str(char *buf, struct timespec *tstamp) +static inline char *tstamp_str(const void *ctx, struct timespec *tstamp) { - sprintf(buf, "%ld.%06ld", tstamp->tv_sec, tstamp->tv_nsec/1000); + return talloc_asprintf(ctx, "%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); + struct timespec diff; + void *local = talloc_new (NULL); + +#define S(ts) tstamp_str(local, &ts) +#define DIFF(a, b) (timespec_subtract(&diff, &b, &a), S(diff)) + + if (num_interfaces == 2) + printf("%s -> %s (%s) = %s (%s)\n", + S(mi->ts_sent), S(mi->ts_rx_final_kern), S(mi->ts_rx_final), + DIFF(mi->ts_sent, mi->ts_rx_onwire_kern), + DIFF(mi->ts_sent, mi->ts_rx_onwire)); + else + printf("%s -> %s (%s) -> %s (%s) = %s (%s), %s (%s)\n", + S(mi->ts_sent), + S(mi->ts_rx_onwire_kern), S(mi->ts_rx_onwire), + S(mi->ts_rx_final_kern), S(mi->ts_rx_final), + DIFF(mi->ts_sent, mi->ts_rx_onwire_kern), + DIFF(mi->ts_sent, mi->ts_rx_onwire), + DIFF(mi->ts_rx_onwire_kern, mi->ts_rx_final_kern), + DIFF(mi->ts_rx_onwire, mi->ts_rx_final)); +#undef S +#undef DIFF + talloc_free (local); } @@ -131,6 +160,13 @@ void dbg_print_timespec(char *msg, struct timespec *tv) printf("%s sec=%ld nsec=%ld\n", msg, tv->tv_sec, tv->tv_nsec); } +static inline unsigned get_msg_latency_us(struct msg_info *mi) +{ + struct timespec diff; + timespec_subtract(&diff, &mi->ts_rx_final_kern, &mi->ts_rx_onwire_kern); + return diff.tv_sec * 1000000 + diff.tv_nsec/1000; +} + void set_sched_policy_and_prio(int policy, int rtprio) { struct sched_param scheduling_parameters; @@ -159,7 +195,7 @@ static inline int sock_get_if_index(int s, const char *if_name) strcpy(ifr.ifr_name, if_name); if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) - error(1, errno, "SIOCGIFINDEX"); + error(1, errno, "SIOCGIFINDEX '%s'", if_name); return ifr.ifr_ifindex; } @@ -174,7 +210,7 @@ int send_frame(int socket) struct msg_info *mi; int ret; - frame.can_id = option_id; + frame.can_id = opt.id; frame.can_dlc = 2; memcpy(frame.data, &curr_msg, sizeof(curr_msg)); mi = frame2info(&frame); @@ -193,13 +229,19 @@ static inline void get_next_timeout(struct timespec *timeout) 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); + if (opt.period_us != 0) { + last.tv_sec += opt.period_us/1000000; + last.tv_nsec += (opt.period_us%1000000)*1000; + while (last.tv_nsec >= 1000000000) { + last.tv_nsec -= 1000000000; + last.tv_sec++; + } + timespec_subtract(timeout, &last, &now); + } else if (opt.timeout_ms != 0) { + timeout->tv_sec = opt.timeout_ms/1000; + timeout->tv_nsec = (opt.timeout_ms%1000)*1000000; + } else + error(1, 0, "Timeout and period cannot be both zero"); } void receive(int s, struct can_frame *frame, struct timespec *ts_kern, struct timespec *ts_user) @@ -251,7 +293,13 @@ void process_tx(int s) void process_on_wire_rx(int s) { - error(1, 0, "%s: not implemented", __FUNCTION__); + 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_onwire_kern = ts_kern; + mi->ts_rx_onwire = ts_user; } @@ -265,16 +313,22 @@ void process_final_rx(int s) mi->ts_rx_final_kern = ts_kern; mi->ts_rx_final = ts_user; - print_msg_info(mi); + if (opt.histogram_fn) + histogram_add(&histogram, get_msg_latency_us(mi)); + + if (!opt.file) + print_msg_info(mi); } void *measure_thread(void *arg) { int s, i, ret; - struct pollfd pfd[MAX_INTERFACES]; + struct pollfd pfd[3]; struct timespec timeout; struct sockaddr_can addr; sigset_t set; + int count = 0; + unsigned msg_in_progress = 0; MEMSET_ZERO(pfd); @@ -283,7 +337,7 @@ void *measure_thread(void *arg) error(1, errno, "socket"); addr.can_family = AF_CAN; - addr.can_ifindex = sock_get_if_index(s, option_interface[i]); + addr.can_ifindex = sock_get_if_index(s, opt.interface[i]); if (i == 0) { /* TX socket */ /* disable default receive filter on this RAW socket */ @@ -302,42 +356,80 @@ void *measure_thread(void *arg) ×tamp_on, sizeof(timestamp_on)) < 0) error(1, errno, "setsockopt SO_TIMESTAMP"); - const int dropmonitor_on = 1; - if (setsockopt(s, SOL_SOCKET, SO_RXQ_OVFL, - &dropmonitor_on, sizeof(dropmonitor_on)) < 0) - error(1, errno, "setsockopt SO_RXQ_OVFL not supported by your Linux Kernel"); +// const int dropmonitor_on = 1; +// if (setsockopt(s, SOL_SOCKET, SO_RXQ_OVFL, +// &dropmonitor_on, sizeof(dropmonitor_on)) < 0) +// error(1, errno, "setsockopt SO_RXQ_OVFL not supported by your Linux Kernel"); pfd[i].fd = s; - pfd[i].events = (i == 0) ? POLLERR : POLLIN; + if (i == 0) + pfd[i].events = POLLIN | POLLERR | ((opt.period_us == 0 && !opt.oneattime) ? POLLOUT : 0); + else + pfd[i].events = POLLIN; } set_sched_policy_and_prio(SCHED_FIFO, 99); - while (!finish_flag) { +#define SEND() \ + do { printf("send\n"); \ + ret = send_frame(pfd[0].fd); \ + if (ret != sizeof(struct can_frame)) \ + error(1, errno, "send_frame %d", __LINE__); \ + count++; \ + msg_in_progress++; \ + } while (0) + + if (opt.oneattime) { + SEND(); + count = 1; + } + + while (!finish_flag && + (opt.count == 0 || count < opt.count || msg_in_progress != 0)) { + get_next_timeout(&timeout); + printf("ppoll"); + fflush(stdout); ret = ppoll(pfd, num_interfaces, &timeout, NULL); + printf("=%d\n", ret); switch (ret) { - case -1: + case -1: // Error if (!INTERRUPTED_SYSCALL(errno)) error(1, errno, "ppoll"); break; - case 0: - ret = send_frame(pfd[0].fd); - if (ret != sizeof(struct can_frame)) - error(1, errno, "send_frame"); + case 0: // Timeout + if (opt.period_us) { + if (opt.count == 0 || count < opt.count) { + SEND(); + } else { + error(1, 0, "poll timeout"); + } + } break; - default: - if (pfd[0].revents != 0) { + default: // Event + if (pfd[0].revents & (POLLIN|POLLERR)) { process_tx(pfd[0].fd); } - if (pfd[1].revents != 0) { - switch (num_interfaces) { - case 2: process_final_rx(pfd[1].fd); break; - case 3: process_on_wire_rx(pfd[1].fd); break; - } + if (pfd[0].revents & POLLOUT) { + if (opt.count == 0 || count < opt.count) + SEND(); } - if (pfd[2].revents != 0) { - process_final_rx(pfd[2].fd); + pfd[0].revents = 0; + + if (num_interfaces == 3 && pfd[1].revents != 0) { + process_on_wire_rx(pfd[1].fd); + pfd[1].revents = 0; + } + + i = (num_interfaces == 2) ? 1 : 2; + if (pfd[i].revents != 0) { + process_final_rx(pfd[i].fd); + msg_in_progress--; + pfd[i].revents = 0; + if ((opt.count == 0 || count < opt.count) && + opt.oneattime) { + SEND(); + } } } } @@ -348,67 +440,87 @@ void *measure_thread(void *arg) return NULL; } -void print_help(void) -{ - printf("Usage: latester -d -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[]) +struct poptOption optionsTable[] = { + { "device", 'd', POPT_ARG_ARGV, &opt.interface, 'd', "Interface to use. Must be given two times (tx, rx) or three times (tx, rx1, rx2)", "interface" }, + { "count", 'c', POPT_ARG_INT|POPT_ARGFLAG_SHOW_DEFAULT, &opt.count, 0, "The count of messages to send, zero corresponds to infinity", "num"}, + { "id", 'i', POPT_ARG_INT|POPT_ARGFLAG_SHOW_DEFAULT, &opt.id, 0, "CAN ID of sent messages", "id"}, + { "period", 'p', POPT_ARG_INT|POPT_ARGFLAG_SHOW_DEFAULT, &opt.period_us, 0, "Period for sending messages or zero (default) to send as fast as possible", "us"}, + { "timeout",'t', POPT_ARG_INT|POPT_ARGFLAG_SHOW_DEFAULT, &opt.timeout_ms,0, "Timeout when period is zero", "ms"}, + { "oneattime",'o', POPT_ARG_NONE, &opt.oneattime,0, "Send the next message only when the previous was finally received"}, + { "verbose",'v', POPT_ARG_NONE, NULL, 'v', "Send the next message only when the previous was finally received"}, + { "file", 'f', POPT_ARG_STRING, &opt.file, 0, "File where to store results", "filename"}, + { "histogram", 'h', POPT_ARG_STRING, &opt.histogram_fn, 0, "Store histogram in file", "filename"}, + POPT_AUTOHELP + { NULL, 0, 0, NULL, 0 } +}; + +int parse_options(int argc, const char *argv[]) { int c; + poptContext optCon; /* context for parsing command-line options */ - opterr = 0; - while ((c = getopt (argc, argv, "d:h")) != -1) - switch (c) - { + optCon = poptGetContext(NULL, argc, argv, optionsTable, 0); + //poptSetOtherOptionHelp(optCon, "[OPTIONS]* "); + + /* Now do options processing */ + while ((c = poptGetNextOpt(optCon)) >= 0) { + 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); + num_interfaces++; 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 (c < -1) + error(1, 0, "%s: %s\n", + poptBadOption(optCon, POPT_BADOPTION_NOALIAS), + poptStrerror(c)); + if (num_interfaces < 2 || num_interfaces > 3) error(1, 0, "-d option must be given exactly 2 or 3 times"); + + if (opt.oneattime && opt.period_us) + error(1, 0, "oneattime and period cannot be specified at the same time"); + + poptFreeContext(optCon); + return 0; } -int main(int argc, char *argv[]) + +int main(int argc, const char *argv[]) { pthread_t thread; sigset_t set; int ret; - + FILE *fhist; + parse_options(argc, argv); - + mlockall(MCL_CURRENT | MCL_FUTURE); signal(SIGINT, term_handler); signal(SIGTERM, term_handler); + if (opt.histogram_fn) { + histogram_init(&histogram, 1000000, 5); + fhist = fopen(opt.histogram_fn, "w"); + if (fhist == NULL) + error(1, errno, "open %s", opt.histogram_fn); + } + pthread_create(&thread, 0, measure_thread, NULL); - while (!finish_flag) { - sleep(1); - } +/* while (!finish_flag) { */ +/* sleep(1); */ +/* } */ pthread_join(thread, NULL); + if (opt.histogram_fn) { + histogram_fprint(&histogram, fhist); + fclose(fhist); + } + return 0; }