diff --git a/server/Makefile b/server/Makefile index 44b79665d..88f35adbb 100644 --- a/server/Makefile +++ b/server/Makefile @@ -1,7 +1,7 @@ TARGETS = server send random receive test # Common dependencies for all binaries -OBJS = socket.o if.o utils.o msg.o node.o cfg.o tc.o hooks.o list.o path.o hist.o +OBJS = socket.o if.o utils.o msg.o node.o cfg.o tc.o hooks.o list.o path.o hist.o log.o VPATH = src diff --git a/server/include/log.h b/server/include/log.h new file mode 100644 index 000000000..5baf35b13 --- /dev/null +++ b/server/include/log.h @@ -0,0 +1,70 @@ +#ifndef _LOG_H_ +#define _LOG_H_ + +#ifdef __GNUC__ + #define INDENT int __attribute__ ((__cleanup__(log_outdent), unused)) _old_indent = log_indent(1); +#else + #define INDENT ; +#endif + +/** Global debug level used by the debug() macro. + * It defaults to V (defined by the Makefile) and can be + * overwritten by the 'debug' setting in the config file. + */ +extern int _debug; + +/** The log level which is passed as first argument to print() */ +enum log_level { + DEBUG, + INFO, + WARN, + ERROR +}; + +int log_indent(int levels); + +void log_outdent(int *); + +/** Reset the wallclock of debugging outputs */ +void log_reset(); + +/** Logs variadic messages to stdout. + * + * @param lvl The log level + * @param fmt The format string (printf alike) + */ +void log_print(enum log_level lvl, const char *fmt, ...) + __attribute__ ((format(printf, 2, 3))); + +/** Printf alike debug message with level. */ +#define debug(lvl, msg, ...) do if (lvl <= _debug) log_print(DEBUG, msg, ##__VA_ARGS__); while (0) + +/** Printf alike info message. */ +#define info(msg, ...) do log_print(INFO, msg, ##__VA_ARGS__); while (0) + +/** Printf alike warning message. */ +#define warn(msg, ...) do log_print(WARN, msg, ##__VA_ARGS__); while (0) + +/** Print error and exit. */ +#define error(msg, ...) do { \ + log_print(ERROR, msg, ##__VA_ARGS__); \ + die(); \ + } while (0) + +/** Print error and strerror(errno). */ +#define serror(msg, ...) do { \ + log_print(ERROR, msg ": %s", ##__VA_ARGS__, strerror(errno)); \ + die(); \ + } while (0) + +/** Print configuration error and exit. */ +#define cerror(c, msg, ...) do { \ + log_print(ERROR, msg " in %s:%u", ##__VA_ARGS__, \ + (config_setting_source_file(c)) ? \ + config_setting_source_file(c) : "(stdio)", \ + config_setting_source_line(c)); \ + die(); \ + } while (0) + +#endif /* _LOG_H_ */ + diff --git a/server/include/path.h b/server/include/path.h index e113bc69a..4195855c7 100644 --- a/server/include/path.h +++ b/server/include/path.h @@ -39,14 +39,13 @@ struct path double rate; /** A pointer to the last received message */ - struct msg *last; + struct msg *current; + /** A pointer to the previously received message */ + struct msg *previous; /** Counter for received messages according to their sequence no displacement */ struct hist histogram; - /** Last known message number */ - unsigned int sequence; - /** Counter for sent messages to all outgoing nodes */ unsigned int sent; /** Counter for received messages from all incoming nodes */ diff --git a/server/include/utils.h b/server/include/utils.h index dc4d2e8f3..fce60c599 100644 --- a/server/include/utils.h +++ b/server/include/utils.h @@ -13,6 +13,9 @@ #include #include #include +#include + +#include "log.h" #ifdef __GNUC__ #define EXPECT(x, v) __builtin_expect(x, v) @@ -69,34 +72,16 @@ b = tmp; \ } while(0) -/** The log level which is passed as first argument to print() */ -enum log_level { DEBUG, INFO, WARN, ERROR }; - /* Forward declarations */ struct settings; struct timespec; -/* These global variables allow changing the output style and verbosity */ -extern int _debug; -extern int _indent; - -void outdent(int *old); - -#ifdef __GNUC__ - #define INDENT int __attribute__ ((__cleanup__(outdent), unused)) _old_indent = _indent++; -#else - #define INDENT ; -#endif - -/** Reset the wallclock of debugging outputs */ -void epoch_reset(); - -/** Logs variadic messages to stdout. - * - * @param lvl The log level - * @param fmt The format string (printf alike) +/** The main thread id. + * This is used to notify the main thread about + * the program termination. + * See error() macros. */ -void print(enum log_level lvl, const char *fmt, ...); +extern pthread_t _mtid; /** Safely append a format string to an existing string. * @@ -123,9 +108,12 @@ double timespec_delta(struct timespec *start, struct timespec *end); /** Get period as timespec from rate */ struct timespec timespec_rate(double rate); -/** A system(2) emulator with popen/pclose(2) and proper output handling */ +/** A system(2) emulator with popen / pclose(2) and proper output handling */ int system2(const char* cmd, ...); +/** Call quit() in the main thread. */ +void die(); + /** Check assertion and exit if failed. */ #define assert(exp) do { \ if (EXPECT(!exp, 0)) { \ @@ -134,43 +122,5 @@ int system2(const char* cmd, ...); exit(EXIT_FAILURE); \ } } while (0) -/** Printf alike debug message with level. */ -#define debug(lvl, msg, ...) do { \ - if (lvl <= _debug) \ - print(DEBUG, msg, ##__VA_ARGS__); \ - } while (0) - -/** Printf alike info message. */ -#define info(msg, ...) do { \ - print(INFO, msg, ##__VA_ARGS__); \ - } while (0) - -/** Printf alike warning message. */ -#define warn(msg, ...) do { \ - print(WARN, msg, ##__VA_ARGS__); \ - } while (0) - -/** Print error and exit. */ -#define error(msg, ...) do { \ - print(ERROR, msg, ##__VA_ARGS__); \ - exit(EXIT_FAILURE); \ - } while (0) - -/** Print error and strerror(errno). */ -#define serror(msg, ...) do { \ - print(ERROR, msg ": %s", ##__VA_ARGS__, \ - strerror(errno)); \ - exit(EXIT_FAILURE); \ - } while (0) - -/** Print configuration error and exit. */ -#define cerror(c, msg, ...) do { \ - print(ERROR, msg " in %s:%u", ##__VA_ARGS__, \ - (config_setting_source_file(c)) ? \ - config_setting_source_file(c) : "(stdio)", \ - config_setting_source_line(c)); \ - exit(EXIT_FAILURE); \ - } while (0) - #endif /* _UTILS_H_ */ diff --git a/server/src/log.c b/server/src/log.c new file mode 100644 index 000000000..c41c858d3 --- /dev/null +++ b/server/src/log.c @@ -0,0 +1,69 @@ +#include +#include + +#include "log.h" +#include "utils.h" + +int _debug = V; + +static struct timespec epoch; + +#ifdef __GNUC__ +static __thread int indent = 0; + +/** Get thread-specific pointer to indent level */ +int log_indent(int levels) +{ + int old = indent; + indent += levels; + return old; +} + +void log_outdent(int *old) +{ + indent = *old; +} +#endif + +void log_reset() +{ + clock_gettime(CLOCK_REALTIME, &epoch); +} + +void log_print(enum log_level lvl, const char *fmt, ...) +{ + struct timespec ts; + char buf[512] = ""; + + va_list ap; + + /* Timestamp */ + clock_gettime(CLOCK_REALTIME, &ts); + strap(buf, sizeof(buf), "%8.3f ", timespec_delta(&epoch, &ts)); + + /* Severity */ + switch (lvl) { + case DEBUG: strap(buf, sizeof(buf), BLD("%-5s "), GRY("Debug")); break; + case INFO: strap(buf, sizeof(buf), BLD("%-5s "), " " ); break; + case WARN: strap(buf, sizeof(buf), BLD("%-5s "), YEL(" Warn")); break; + case ERROR: strap(buf, sizeof(buf), BLD("%-5s "), RED("Error")); break; + } + + /* Indention */ +#ifdef __GNUC__ + for (int i = 0; i < indent; i++) + strap(buf, sizeof(buf), GFX("\x78") " "); + strap(buf, sizeof(buf), GFX("\x74") " "); +#endif + + /* Format String */ + va_start(ap, fmt); + vstrap(buf, sizeof(buf), fmt, ap); + va_end(ap); + + /* Output */ +#ifdef ENABLE_OPAL_ASYNC + OpalPrint("S2SS: %s\n", buf); +#endif + fprintf(stderr, "\r%s\n", buf); +} diff --git a/server/src/opal.c b/server/src/opal.c index 11da0d17c..f97e06c0c 100644 --- a/server/src/opal.c +++ b/server/src/opal.c @@ -153,7 +153,7 @@ int opal_read(struct node *n, struct msg *m) state = OpalGetAsyncModelState(); if ((state == STATE_RESET) || (state == STATE_STOP)) { info("OpalGetAsyncModelState(): Model stopped or resetted!"); - exit(0); + die(); } return -1; // FIXME: correct return value @@ -195,7 +195,7 @@ int opal_read(struct node *n, struct msg *m) state = OpalGetAsyncModelState(); if ((state == STATE_RESET) || (state == STATE_STOP)) { info("OpalGetAsyncModelState(): Model stopped or resetted!"); - exit(0); + die(); } return 0; @@ -213,7 +213,7 @@ int opal_write(struct node *n, struct msg *m) state = OpalGetAsyncModelState(); if ((state == STATE_RESET) || (state == STATE_STOP)) { info("OpalGetAsyncModelState(): Model stopped or resetted!"); - exit(0); + die(); } OpalSetAsyncRecvIconStatus(m->sequence, o->recv_id); /* Set the Status to the message ID */ diff --git a/server/src/server.c b/server/src/server.c index ce0d7e3b2..f352f4b86 100644 --- a/server/src/server.c +++ b/server/src/server.c @@ -38,7 +38,7 @@ struct settings settings; config_t config; static void quit() -{ _indent = 0; +{ info("Stopping paths:"); FOREACH(&paths, it) path_stop(it->path); @@ -103,7 +103,6 @@ void signals_init() sigemptyset(&sa_quit.sa_mask); sigaction(SIGTERM, &sa_quit, NULL); sigaction(SIGINT, &sa_quit, NULL); - atexit(&quit); } void usage(const char *name) @@ -118,11 +117,13 @@ void usage(const char *name) printf("Simulator2Simulator Server %s (built on %s, %s)\n", BLU(VERSION), MAG(__DATE__), MAG(__TIME__)); - exit(EXIT_FAILURE); + die(); } int main(int argc, char *argv[]) { + _mtid = pthread_self(); + /* Check arguments */ #ifdef ENABLE_OPAL_ASYNC if (argc != 2 && argc != 4) @@ -130,8 +131,7 @@ int main(int argc, char *argv[]) if (argc != 2) #endif usage(argv[0]); - - epoch_reset(); + info("This is Simulator2Simulator Server (S2SS) %s (built on %s, %s, debug=%d)", BLD(YEL(VERSION)), BLD(MAG(__DATE__)), BLD(MAG(__TIME__)), _debug); @@ -196,7 +196,7 @@ int main(int argc, char *argv[]) else pause(); - /* Note: quit() is called by exit handler! */ + quit(); return 0; } diff --git a/server/src/utils.c b/server/src/utils.c index 4abf4d686..c0515cccf 100644 --- a/server/src/utils.c +++ b/server/src/utils.c @@ -12,8 +12,8 @@ #include #include #include -#include #include +#include #ifdef ENABLE_OPAL_ASYNC #define RTLAB @@ -24,20 +24,11 @@ #include "cfg.h" #include "utils.h" -/* This global variable contains the debug level for debug() and assert() macros */ -int _debug = V; -int _indent = 0; +pthread_t _mtid; -struct timespec epoch; - -void outdent(int *old) +void die() { - _indent = *old; -} - -void epoch_reset() -{ - clock_gettime(CLOCK_REALTIME, &epoch); + pthread_kill(_mtid, SIGTERM); } int strap(char *dest, size_t size, const char *fmt, ...) @@ -59,42 +50,6 @@ int vstrap(char *dest, size_t size, const char *fmt, va_list ap) return vsnprintf(dest + len, size - len, fmt, ap); } -void print(enum log_level lvl, const char *fmt, ...) -{ - struct timespec ts; - char buf[512] = ""; - - va_list ap; - - /* Timestamp */ - clock_gettime(CLOCK_REALTIME, &ts); - strap(buf, sizeof(buf), "%8.3f ", timespec_delta(&epoch, &ts)); - - /* Severity */ - switch (lvl) { - case DEBUG: strap(buf, sizeof(buf), BLD("%-5s "), GRY("Debug")); break; - case INFO: strap(buf, sizeof(buf), BLD("%-5s "), " " ); break; - case WARN: strap(buf, sizeof(buf), BLD("%-5s "), YEL(" Warn")); break; - case ERROR: strap(buf, sizeof(buf), BLD("%-5s "), RED("Error")); break; - } - - /* Indention */ - for (int i = 0; i < _indent; i++) - strap(buf, sizeof(buf), GFX("\x78") " "); - strap(buf, sizeof(buf), GFX("\x74") " "); - - /* Format String */ - va_start(ap, fmt); - vstrap(buf, sizeof(buf), fmt, ap); - va_end(ap); - - /* Output */ -#ifdef ENABLE_OPAL_ASYNC - OpalPrint("S2SS: %s\n", buf); -#endif - fprintf(stderr, "%s\n", buf); -} - cpu_set_t to_cpu_set(int set) { cpu_set_t cset;