]> rtime.felk.cvut.cz Git - canping.git/blobdiff - src/vca_canping.c
Portability enhancements.
[canping.git] / src / vca_canping.c
index 21239768bae5f50ab7295f984ca4e69f68de5c3a..348d641e4163862ae57a7866d5477801358c419d 100644 (file)
@@ -2,7 +2,7 @@
 /* File: vca_canping.c - utility to test CAN functionality and throughput */
 /*                                                                        */
 /* LibVCA - Versatile CAN/CANopen API library                             */
-/* Copyright (C) 2005-2006 Michal Sojka, DCE FEE CTU Prague               */
+/* Copyright (C) 2005-2009 Michal Sojka, DCE FEE CTU Prague               */
 /* Copyright (C) 2006-2009 Pavel Pisa <pisa@cmp.felk.cvut.cz>             */
 /*                                                                        */
 /* LibVCA is free software; you can redistribute it and/or modify it      */
 #include <signal.h>
 #include <sys/types.h>
 #include <sys/time.h>
+#include <sys/select.h>
 #include <ul_list.h>
 #include <errno.h>
 #include <semaphore.h>
+#include <stdbool.h>
+#include <error.h>
 
 /* TODO: Handle the case where there are more canping slaves running
  * on one CAN bus. */
@@ -43,6 +46,7 @@
 
 #ifdef WITH_RTPRIO
 #include <sched.h>
+#include <sys/mman.h>
 
 int sched_policy = SCHED_OTHER;
 int sched_rtprio;
@@ -85,7 +89,7 @@ sem_t ready_sem;              /* Thread is ready for execution */
 
 
 /* Command line options */
-char *option_device = "/dev/can0";
+char *option_device = VCA_DEV_NAME;
 int option_masters = 0;
 long int option_first_id = 1000;
 int option_slaves = 0;
@@ -98,12 +102,21 @@ int option_wait_ms = 1000;
 int option_timeout = 4;
 int option_open_once = 0;
 int option_synch_start = 0;
-
+bool option_background = false;
+char *option_histogram = NULL;
+int option_msg_to_ignore = 0;
+char *option_save_all_times = NULL;
 /* Lists */
 typedef struct threads {
        ul_list_head_t head;
 } threads_t;
 
+struct histogram {
+       unsigned *data;
+       unsigned allocated;
+       unsigned resolution;
+};
+
 typedef struct thread_data {
        pthread_t tid;
        long int canid;
@@ -115,6 +128,9 @@ typedef struct thread_data {
        int min, max;           /* min/max response times */
        int timeout;            /* number of timeouts */
 
+       struct histogram hist;
+       unsigned *times;        /* Array of all measured times */
+
        ul_list_node_t node;
 } thread_data_t;
 
@@ -123,6 +139,48 @@ UL_LIST_CUST_DEC(thread_list, threads_t, thread_data_t, head, node);
 threads_t master_threads;
 threads_t slave_threads;
 
+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];
+       }
+}
+
 /* Subtract the `struct timeval' values X and Y, storing the result in
    RESULT.  Return 1 if the difference is negative, otherwise 0.  */
      
@@ -191,8 +249,9 @@ void *master_thread(void *arg)
        struct canfilt_t canfilt; /* filter for received messages */
 
        if (!option_open_once) {
-               /* Open can driver */
-               if(vca_open_handle(&vcah, option_device, NULL, 0) < 0) {
+               /* Open the CAN driver and disable (D) reception of
+                * all messages until we setup a filter below. */
+               if(vca_open_handle(&vcah, option_device, "D", 0) < 0) {
                        perror("open");
                        fprintf(stderr, "Error opening %s (for id %d)\n", option_device, ping_id);
                        exit(EXIT_CANNOT_OPEN); 
@@ -215,6 +274,15 @@ void *master_thread(void *arg)
                perror("ioctl CANQUE_QUEUE_FLUSH");
                exit(EXIT_FLUSH_ERROR);
        }
+
+       if (option_histogram)
+               histogram_init(&td->hist, 1000*1000/*max [us]*/, 5/*resolution [us]*/);
+
+       if (option_save_all_times) {
+               td->times = malloc(sizeof(*td->times)*option_count);
+               if (!td->times)
+                       error(1, errno, "malloc times");
+       }
        
        /* Prepare data structures for the select syscall. */
        FD_ZERO (&rdset);
@@ -229,7 +297,7 @@ void *master_thread(void *arg)
                pthread_mutex_unlock(&mut_start);
        }
 
-       while (!IS_FINISH_FLAG() && (option_count == 0 || ping_count++ < option_count)) {
+       while (!IS_FINISH_FLAG() && (option_count == 0 || ping_count++ < option_count + option_msg_to_ignore)) {
                /* Send a ping message */
                pingmsg.flags=0;
                pingmsg.id=ping_id;
@@ -282,19 +350,25 @@ void *master_thread(void *arg)
                                        printf("%d:%ld\n", ping_id, time); 
                                        break;
                                case 3:
-                                       printf("Pong response for id %d received in %ld us\n", ping_id, time);
+                                       printf("Pong response %d for id %d received in %ld us\n", ping_count, ping_id, time);
                                        break;
                                }
                                /* Update statistics */
-                               td->count++;
-                               td->mean = 
-                                       td->mean * ((double)(td->count - 1) / td->count) + 
-                                       (double)time / td->count;
-                               td->moment2nd = 
-                                       td->moment2nd * ((double)(td->count - 1) / td->count) + 
-                                       (double)time*time  / td->count;
-                               if (time > td->max) td->max = time;
-                               if (time < td->min) td->min = time;
+                               if (ping_count > option_msg_to_ignore) {
+                                       td->count++;
+                                       td->mean = 
+                                               td->mean * ((double)(td->count - 1) / td->count) + 
+                                               (double)time / td->count;
+                                       td->moment2nd = 
+                                               td->moment2nd * ((double)(td->count - 1) / td->count) + 
+                                               (double)time*time  / td->count;
+                                       if (time > td->max) td->max = time;
+                                       if (time < td->min) td->min = time;
+                                       if (option_histogram)
+                                               histogram_add(&td->hist, time);
+                                       if (option_save_all_times)
+                                               td->times[ping_count - 1 - option_msg_to_ignore] = time;
+                               }
                                total_count++;
                        } /* read */
                } 
@@ -319,6 +393,30 @@ void *master_thread(void *arg)
                vca_close_handle(vcah);
        }
 
+       if (option_histogram) {
+               FILE *f;
+               char buf[100];
+               snprintf(buf, sizeof(buf), "%s-%d.dat", option_histogram, ping_id);
+               f = fopen(buf, "w");
+               histogram_fprint(&td->hist, f);
+               fclose(f);
+       }
+
+       if (option_save_all_times) {
+               FILE *f;
+               char buf[100];
+               int i;
+               snprintf(buf, sizeof(buf), "%s-%d.dat", option_save_all_times, ping_id);
+               f = fopen(buf, "w");
+               for (i=0; i<option_count; i++) {
+                       if (!getenv("CANPING_MS")) 
+                               fprintf(f, "%u\n", td->times[i]);
+                       else
+                               fprintf(f, "%g\n", 1e-3*td->times[i]);
+               }
+               fclose(f);
+       }
+
        sem_post(&finish_sem);
        return (void *)ret;
 }
@@ -370,8 +468,9 @@ void *slave_thread(void *arg)
        struct canfilt_t canfilt; /* filter for received messages */
 
        if (!option_open_once) {
-               /* Open the CAN driver */
-               if(vca_open_handle(&vcah, option_device, NULL, 0) < 0) {
+               /* Open the CAN driver and disable (D) reception of
+                * all messages until we setup a filter below. */
+               if(vca_open_handle(&vcah, option_device, "D", 0) < 0) {
                        perror("open");
                        printf("Error opening %s (for id %d)\n", option_device, ping_id);
                        exit(EXIT_CANNOT_OPEN); 
@@ -404,6 +503,9 @@ void *slave_thread(void *arg)
        pongmsg.length = option_length;
        for (i=0; i < option_length; i++) pongmsg.data[i] = i;
 
+       /* Signal that I'm ready */
+       sem_post(&ready_sem);
+
        while (!IS_FINISH_FLAG()) {
                /* Receive a ping message */
                pingmsg.flags=0;
@@ -472,6 +574,9 @@ void start_slaves(int slaves, int first_id)
                pthread_create(&td->tid, NULL, slave_thread, (void *)td);
                dbg(2, "Slave thread: %p\n", (void *)td->tid);
        }
+
+       /* Wait for all threads beeing ready */
+       for (i = 0; i < slaves; i++) sem_wait(&ready_sem);
 }
 
 #ifdef WITH_RTPRIO
@@ -501,11 +606,15 @@ void print_help(void)
        printf("Usage: canping -m <master threads> [other options]\n"
               "       canping -s <slave threads> [other options]\n\n"
               "Other options:\n"
+              "  -b            go to background (fork) after initialization, prints child PID\n"
               "  -c count      how many messages each master sends\n"
               "  -d dev        device (e.g. /dev/can1)\n"
+              "  -e prefix     save all measured times in file <prefix>-<id>.dat\n"
+              "  -g prefix     store cumulative histogram in file <prefix>-<id>.dat\n"
               "  -h            print this help\n"
               "  -i id         id of first master message\n"
               "  -l length     length of the messages (0..8)\n"
+              "  -n number     ignore first <number> messages\n"
               "  -o            open a device only once for all threads (doesn't work)\n" /* due to filters */
               "  -t timeout    timeout in seconds (default 4 s)\n"
               "  -v            be verbose (use more than once to increase verbosity)\n"
@@ -526,15 +635,24 @@ int parse_options(int argc, char *argv[])
        int c;
        
        opterr = 0;
-       while ((c = getopt (argc, argv, "c:d:hi:l:m:os:t:vw:yrR:")) != -1)
+       while ((c = getopt (argc, argv, "bc:d:e:g:hi:l:m:n:os:t:vw:yrR:")) != -1)
                switch (c)
                {
+               case 'b':
+                       option_background = true;
+                       break;
                case 'c':
                        option_count = atoi(optarg);
                        break;
                case 'd':
                        option_device = optarg;
                        break;
+               case 'e':
+                       option_save_all_times = optarg;
+                       break;
+               case 'g':
+                       option_histogram = optarg;
+                       break;
                case 'h':
                        print_help();
                        exit(EXIT_OK);
@@ -550,6 +668,9 @@ int parse_options(int argc, char *argv[])
                case 'm':
                        option_masters = atoi(optarg);
                        break;
+               case 'n':
+                       option_msg_to_ignore = atoi(optarg);
+                       break;
                case 'o':
                        option_open_once = 1;
                        break;
@@ -684,9 +805,11 @@ int main(int argc, char *argv[])
        parse_options(argc, argv);
 
 #ifdef WITH_RTPRIO
-       if(sched_policy != SCHED_OTHER)
+       if(sched_policy != SCHED_OTHER) {
                if(set_sched_policy_and_prio(sched_policy, sched_rtprio) <0)
                        exit(EXIT_BAD_PARAM);
+               mlockall(MCL_CURRENT | MCL_FUTURE);
+       }
 #endif
 
 
@@ -712,9 +835,33 @@ int main(int argc, char *argv[])
                }
        }
 
-       if (option_masters) start_masters(option_masters, option_first_id);
-       if (option_slaves) start_slaves(option_slaves, option_first_id);
+       sem_t *child_ready;
+       int fork_ret = 0;
+       if ((child_ready = sem_open("canping", O_CREAT)) == NULL)
+               error(1, errno, "sem_open");
+       if (option_background) {
+               /* Go to background when everything is ready */
+               fork_ret = fork();
+               if (fork_ret < 0)
+                       error(1, errno, "Cannot go to background");
+               if (fork_ret == 0) {
+                       int devnull = open("/dev/null", O_WRONLY);
+                       dup2(devnull, 1);
+               }
+       }
 
+       if (fork_ret == 0) {
+               /* Child */
+               if (option_masters) start_masters(option_masters, option_first_id);
+               if (option_slaves) start_slaves(option_slaves, option_first_id);
+               sem_post(child_ready);
+       } else {
+               /* Parent */
+               sem_wait(child_ready);
+               printf("%d\n", fork_ret);
+               exit(0);
+       }
+       
        wait_for_threads();
 
        if (option_open_once) {