diff --git a/server/Makefile b/server/Makefile index b71f16836..ecd198e1a 100644 --- a/server/Makefile +++ b/server/Makefile @@ -1,5 +1,5 @@ TARGETS = server send random receive test -SRCS = server.c send.c receive.c random.c node.c path.c utils.c socket.c msg.c cfg.c if.c tc.c +SRCS = server.c send.c receive.c random.c node.c path.c utils.c socket.c msg.c cfg.c if.c tc.c hist.c # Default target: build everything all: $(TARGETS) @@ -7,11 +7,11 @@ all: $(TARGETS) COMMON = socket.o if.o utils.o msg.o node.o cfg.o tc.o hooks.o # Dependencies for individual binaries -server: $(COMMON) path.o +server: $(COMMON) path.o hist.o send: $(COMMON) receive: $(COMMON) random: utils.o msg.o -test: $(COMMON) +test: $(COMMON) hist.o VPATH = src diff --git a/server/include/config.h b/server/include/config.h index c6100ea30..c48558105 100644 --- a/server/include/config.h +++ b/server/include/config.h @@ -20,10 +20,6 @@ /** Socket priority */ #define SOCKET_PRIO 7 -/* Some parameters for histogram statistics */ -#define HIST_HEIGHT 50 -#define HIST_SEQ 17 - /* Protocol numbers */ #define IPPROTO_S2SS 137 #define ETH_P_S2SS 0xBABE diff --git a/server/include/hist.h b/server/include/hist.h new file mode 100644 index 000000000..7b8c1724e --- /dev/null +++ b/server/include/hist.h @@ -0,0 +1,36 @@ +/** Histogram functions. + * + * @author Steffen Vogel + * @copyright 2014, Institute for Automation of Complex Power Systems, EONERC + */ + +#ifndef _HIST_H_ +#define _HIST_H_ + +#define HIST_HEIGHT 50 +#define HIST_SEQ 17 + +/** Histogram structure */ +struct hist { + double start; + double end; + double resolution; + int length; + unsigned *data; +}; + +void hist_init(struct hist *h, double start, double end, double resolution); + +void hist_free(struct hist *h); + +void hist_reset(struct hist *h); + +void hist_put(struct hist *h, double value); + +/** Print ASCII style plot of histogram */ +void hist_plot(struct hist *h); + +/** Dump histogram data in Matlab format */ +void hist_dump(struct hist *h); + +#endif /* _HIST_H_ */ \ No newline at end of file diff --git a/server/include/path.h b/server/include/path.h index 7f71ce8f2..70f1533df 100644 --- a/server/include/path.h +++ b/server/include/path.h @@ -12,6 +12,7 @@ #include #include "config.h" +#include "hist.h" #include "node.h" #include "msg.h" #include "hooks.h" @@ -35,6 +36,9 @@ struct path /** A pointer to the last received message */ struct msg *last; + + /** Counter for received messages according to their sequence no displacement */ + struct hist histogram; /** Last known message number */ unsigned int sequence; @@ -49,8 +53,6 @@ struct path unsigned int skipped; /** Counter for dropped messages due to reordering */ unsigned int dropped; - /** Counter for received messages according to their sequence no displacement */ - unsigned int histogram[HIST_SEQ]; /** The thread id for this path */ pthread_t recv_tid; diff --git a/server/include/utils.h b/server/include/utils.h index 95458e9f1..72f292216 100644 --- a/server/include/utils.h +++ b/server/include/utils.h @@ -81,12 +81,6 @@ double timespec_delta(struct timespec *start, struct timespec *end); /** Get period as timespec from rate */ struct timespec timespec_rate(double rate); -/** Print ASCII style plot of histogram */ -void hist_plot(unsigned *hist, int length); - -/** Dump histogram data in Matlab format */ -void hist_dump(unsigned *hist, int length); - /** A system(2) emulator with popen/pclose(2) and proper output handling */ int system2(const char* cmd, ...); diff --git a/server/src/hist.c b/server/src/hist.c new file mode 100644 index 000000000..e425bc8ad --- /dev/null +++ b/server/src/hist.c @@ -0,0 +1,85 @@ +/** Histogram functions. + * + * @author Steffen Vogel + * @copyright 2014, Institute for Automation of Complex Power Systems, EONERC + */ + +#include +#include +#include +#include + +#include "utils.h" +#include "hist.h" + +void hist_init(struct hist *h, double start, double end, double resolution) +{ + h->start = start; + h->end = end; + h->resolution = resolution; + h->length = (end - start) / resolution; + h->data = malloc(h->length * sizeof(unsigned)); + + hist_reset(h); +} + +void hist_free(struct hist *h) +{ + free(h->data); +} + +void hist_put(struct hist *h, double value) +{ + int index = round((value - h->start) / h->resolution); + + if (index >= 0 && index < h->length) + h->data[index]++; +} + +void hist_reset(struct hist *h) +{ + memset(h->data, 0, h->length * sizeof(unsigned)); +} + +void hist_plot(struct hist *h) +{ + unsigned min = UINT_MAX; + unsigned max = 0; + + /* Get max, first & last */ + for (int i = 0; i < h->length; i++) { + if (h->data[i] > max) + max = h->data[i]; + if (h->data[i] < min) + min = h->data[i]; + } + + char buf[HIST_HEIGHT]; + memset(buf, '#', HIST_HEIGHT); + + /* Print plot */ + info("%5s | %5s | %s", "Value", "Occur", "Histogram Plot:"); + for (int i = 0; i < h->length; i++) { + double val = h->start + i * h->resolution; + int bar = HIST_HEIGHT * ((double) h->data[i] / max); + + if (h->data[i] == min) info("%5.2e | " GRN("%5u") " | %.*s", val, h->data[i], bar, buf); + else if (h->data[i] == max) info("%5.2e | " RED("%5u") " | %.*s", val, h->data[i], bar, buf); + else info("%5.2e | " "%5u" " | %.*s", val, h->data[i], bar, buf); + } +} + +void hist_dump(struct hist *h) +{ + char tok[8]; + char buf[h->length * sizeof(tok)]; + memset(buf, 0, sizeof(buf)); + + /* Print in Matlab vector format */ + for (int i = 0; i < h->length; i++) { + snprintf(tok, sizeof(tok), "%u ", h->data[i]); + strncat(buf, tok, sizeof(buf) - strlen(buf)); + } + + info("hist = [ %s]", buf); +} diff --git a/server/src/path.c b/server/src/path.c index 520b9b2c7..71e2e9846 100644 --- a/server/src/path.c +++ b/server/src/path.c @@ -93,9 +93,7 @@ static void * path_run(void *arg) if (dist > UINT16_MAX / 2) dist -= UINT16_MAX; - int idx = HIST_SEQ / 2 + dist; - if (idx < HIST_SEQ && idx >= 0) - p->histogram[idx]++; + hist_put(&p->histogram, dist); /* Handle simulation restart */ if (m->sequence == 0 && abs(dist) > 16) { @@ -112,8 +110,7 @@ static void * path_run(void *arg) p->skipped = 0; p->dropped = 0; - /* Reset sequence no tracking */ - memset(p->histogram, 0, sizeof(p->histogram)); + hist_reset(&p->histogram); } else if (dist <= 0 && p->received > 1) { p->dropped++; @@ -146,6 +143,8 @@ int path_start(struct path *p) { INDENT info("Starting path: %12s " GRN("=>") " %-12s", p->in->name, p->out->name); + hist_init(&p->histogram, -HIST_SEQ, +HIST_SEQ, 1); + /* At fixed rate mode, we start another thread for sending */ if (p->rate) pthread_create(&p->sent_tid, NULL, &path_send, (void *) p); @@ -165,11 +164,10 @@ int path_stop(struct path *p) pthread_join(p->sent_tid, NULL); } - if (p->received) { - path_stats(p); - hist_plot(p->histogram, HIST_SEQ); - hist_dump(p->histogram, HIST_SEQ); - } + path_stats(p); + hist_plot(&p->histogram); + hist_dump(&p->histogram); + hist_free(&p->histogram); return 0; } diff --git a/server/src/test.c b/server/src/test.c index 9fa826c89..192d576b6 100644 --- a/server/src/test.c +++ b/server/src/test.c @@ -19,6 +19,7 @@ #include "msg.h" #include "node.h" #include "utils.h" +#include "hist.h" static struct settings set; static struct node *node; @@ -28,11 +29,6 @@ int running = 1; #define CLOCK_ID CLOCK_MONOTONIC_RAW -#define RTT_MIN 20 -#define RTT_MAX 100 -#define RTT_RESOLUTION 2 -#define RTT_HIST (int) ((RTT_MAX - RTT_MIN) / RTT_RESOLUTION) - void quit(int sig, siginfo_t *si, void *ptr) { running = 0; @@ -82,11 +78,9 @@ int main(int argc, char *argv[]) double rtt_max = LLONG_MIN; double rtt_min = LLONG_MAX; double avg = 0; - int bar; - unsigned hist[RTT_HIST]; - - - memset(hist, 0, RTT_HIST * sizeof(unsigned)); + + struct hist histogram; + hist_init(&histogram, 0, 2e-4, 1e-5); #if 1 /* Print header */ fprintf(stdout, "%17s", "timestamp"); @@ -106,11 +100,8 @@ int main(int argc, char *argv[]) if (rtt < rtt_min) rtt_min = rtt; avg += rtt; - - /* Update histogram */ - bar = (rtt * 1000 / RTT_RESOLUTION) - (RTT_MIN / RTT_RESOLUTION); - if (bar < RTT_HIST) - hist[bar]++; + + hist_put(&histogram, rtt); #if 1 struct timespec ts; @@ -125,8 +116,9 @@ int main(int argc, char *argv[]) free(ts2); - hist_plot(hist, RTT_HIST); - hist_dump(hist, RTT_HIST); + hist_plot(&histogram); + hist_dump(&histogram); + hist_free(&histogram); } node_stop(node); diff --git a/server/src/utils.c b/server/src/utils.c index 6f0b6059c..7464ad8f1 100644 --- a/server/src/utils.c +++ b/server/src/utils.c @@ -103,49 +103,6 @@ struct timespec timespec_rate(double rate) return ts; } -void hist_plot(unsigned *hist, int length) -{ - char buf[HIST_HEIGHT + 32]; - int bar; - int max = 0; - - /* Get max, first & last */ - for (int i = 0; i < length; i++) { - if (hist[i] > hist[max]) - max = i; - } - - /* Print header */ - info("%2s | %5s | %s", "Id", "Value", "Histogram Plot:"); - - /* Print plot */ - memset(buf, '#', sizeof(buf)); - for (int i = 0; i < length; i++) { - bar = HIST_HEIGHT * (float) hist[i] / hist[max]; - if (hist[i] == 0) - info("%2u | " GRN("%5u") " | " , i, hist[i]); - else if (hist[i] == hist[max]) - info("%2u | " RED("%5u") " | " BLD("%.*s"), i, hist[i], bar, buf); - else - info("%2u | " "%5u" " | " "%.*s", i, hist[i], bar, buf); - } -} - -void hist_dump(unsigned *hist, int length) -{ - char tok[16]; - char buf[length * sizeof(tok)]; - memset(buf, 0, sizeof(buf)); - - /* Print in Matlab vector format */ - for (int i = 0; i < length; i++) { - snprintf(tok, sizeof(tok), "%u ", hist[i]); - strncat(buf, tok, sizeof(buf)-strlen(buf)); - } - - info("Matlab: hist = [ %s]", buf); -} - /** @todo: Proper way: create additional pipe for stderr in child process */ int system2(const char *cmd, ...) {