brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 1 | #include <stdarg.h> |
| 2 | #include <stdio.h> |
| 3 | #include <string.h> |
| 4 | #include <time.h> |
| 5 | #include <sys/types.h> |
| 6 | #include <errno.h> |
| 7 | #include <unistd.h> |
| 8 | #include <limits.h> |
| 9 | |
| 10 | #include <algorithm> |
| 11 | |
| 12 | #include "aos/aos_core.h" |
| 13 | #include "aos/common/die.h" |
| 14 | |
| 15 | #define DECL_LEVEL(name, value) const log_level name = value; |
| 16 | DECL_LEVELS |
| 17 | #undef DECL_LEVEL |
| 18 | |
| 19 | log_level log_min = 0; |
| 20 | |
| 21 | static const aos_type_sig message_sig = {sizeof(log_queue_message), 1234, 1500}; |
| 22 | static const char *name; |
| 23 | static size_t name_size; |
| 24 | static aos_queue *queue; |
| 25 | static log_message corked_message; |
| 26 | static int cork_line_min, cork_line_max; |
| 27 | bool log_initted = false; |
| 28 | |
| 29 | static inline void cork_init() { |
| 30 | corked_message.message[0] = '\0'; // make strlen of it 0 |
| 31 | cork_line_min = INT_MAX; |
| 32 | cork_line_max = -1; |
| 33 | } |
| 34 | int log_init(const char *name_in){ |
| 35 | if (log_initted) { |
| 36 | return 1; |
| 37 | } |
| 38 | |
| 39 | const size_t name_in_len = strlen(name_in); |
| 40 | const char *last_slash = static_cast<const char *>(memrchr(name_in, |
| 41 | '/', name_in_len)); |
| 42 | if (last_slash == NULL) { |
| 43 | name_size = name_in_len; |
| 44 | last_slash = name_in - 1; |
| 45 | } else { |
| 46 | name_size = name_in + name_in_len - last_slash; |
| 47 | } |
| 48 | if (name_size >= sizeof(log_message::name)) { |
| 49 | fprintf(stderr, "logging: error: name '%s' (going to use %zu bytes) is too long\n", |
| 50 | name_in, name_size); |
| 51 | return -1; |
| 52 | } |
| 53 | char *const tmp = static_cast<char *>(malloc(name_size + 1)); |
| 54 | if (tmp == NULL) { |
| 55 | fprintf(stderr, "logging: error: couldn't malloc(%zd)\n", name_size + 1); |
| 56 | return -1; |
| 57 | } |
| 58 | name = tmp; |
| 59 | memcpy(tmp, last_slash + 1, name_size); |
| 60 | tmp[name_size] = 0; |
| 61 | queue = aos_fetch_queue("LoggingQueue", &message_sig); |
| 62 | if (queue == NULL) { |
| 63 | fprintf(stderr, "logging: error: couldn't fetch queue\n"); |
| 64 | return -1; |
| 65 | } |
| 66 | |
| 67 | cork_init(); |
| 68 | |
| 69 | log_initted = true; |
| 70 | return 0; |
| 71 | } |
| 72 | void log_uninit() { |
| 73 | free(const_cast<char *>(name)); |
| 74 | name = NULL; |
| 75 | name_size = 0; |
| 76 | queue = NULL; |
| 77 | log_initted = false; |
| 78 | } |
| 79 | |
| 80 | static inline void check_init() { |
| 81 | if (!log_initted) { |
| 82 | fprintf(stderr, "logging: warning: not initialized in %jd." |
| 83 | " initializing using \"<null>\" as name\n", static_cast<intmax_t>(getpid())); |
| 84 | log_init("<null>"); |
| 85 | } |
| 86 | } |
| 87 | |
| 88 | const log_message *log_read_next2(int flags, int *index) { |
| 89 | check_init(); |
| 90 | return static_cast<const log_message *>(aos_queue_read_msg_index(queue, flags, index)); |
| 91 | } |
| 92 | const log_message *log_read_next1(int flags) { |
| 93 | check_init(); |
| 94 | const log_message *r = NULL; |
| 95 | do { |
| 96 | r = static_cast<const log_message *>(aos_queue_read_msg(queue, flags)); |
| 97 | } while ((flags & BLOCK) && r == NULL); // not blocking means return a NULL if that's what it gets |
| 98 | return r; |
| 99 | } |
| 100 | void log_free_message(const log_message *msg) { |
| 101 | check_init(); |
| 102 | aos_queue_free_msg(queue, msg); |
| 103 | } |
| 104 | |
| 105 | int log_crio_message_send(log_crio_message &to_send) { |
| 106 | check_init(); |
| 107 | |
| 108 | log_crio_message *msg = static_cast<log_crio_message *>(aos_queue_get_msg(queue)); |
| 109 | if (msg == NULL) { |
| 110 | fprintf(stderr, "logging: error: couldn't get a message to send '%s'\n", |
| 111 | to_send.message); |
| 112 | return -1; |
| 113 | } |
| 114 | //*msg = to_send; |
| 115 | static_assert(sizeof(to_send) == sizeof(*msg), "something is very wrong here"); |
| 116 | memcpy(msg, &to_send, sizeof(to_send)); |
| 117 | if (aos_queue_write_msg(queue, msg, OVERRIDE) < 0) { |
| 118 | fprintf(stderr, "logging: error: writing crio message '%s' failed\n", msg->message); |
| 119 | aos_queue_free_msg(queue, msg); |
| 120 | return -1; |
| 121 | } |
| 122 | |
| 123 | return 0; |
| 124 | } |
| 125 | |
| 126 | // Prints format (with ap) into output and correctly deals with the message |
| 127 | // being too long etc. |
| 128 | // Returns whether it succeeded or not. |
| 129 | static inline bool vsprintf_in(char *output, size_t output_size, |
| 130 | const char *format, va_list ap) { |
| 131 | static const char *continued = "...\n"; |
| 132 | const size_t size = output_size - strlen(continued); |
| 133 | const int ret = vsnprintf(output, size, format, ap); |
| 134 | if (ret < 0) { |
| 135 | fprintf(stderr, "logging: error: vsnprintf failed with %d (%s)\n", |
| 136 | errno, strerror(errno)); |
| 137 | return false; |
| 138 | } else if (static_cast<uintmax_t>(ret) >= static_cast<uintmax_t>(size)) { |
| 139 | // overwrite the NULL at the end of the existing one and |
| 140 | // copy in the one on the end of continued |
| 141 | memcpy(&output[size - 1], continued, strlen(continued) + 1); |
| 142 | } |
| 143 | return true; |
| 144 | } |
| 145 | static inline bool write_message(log_message *msg, log_level level) { |
| 146 | msg->level = level; |
| 147 | msg->source = getpid(); |
| 148 | memcpy(msg->name, name, name_size + 1); |
| 149 | if (clock_gettime(CLOCK_REALTIME, &msg->time) == -1) { |
| 150 | fprintf(stderr, "logging: warning: couldn't get the current time " |
| 151 | "because of %d (%s)\n", errno, strerror(errno)); |
| 152 | msg->time.tv_sec = 0; |
| 153 | msg->time.tv_nsec = 0; |
| 154 | } |
| 155 | |
| 156 | static uint8_t local_sequence = -1; |
| 157 | msg->sequence = ++local_sequence; |
| 158 | |
| 159 | if (aos_queue_write_msg(queue, msg, OVERRIDE) < 0) { |
| 160 | fprintf(stderr, "logging: error: writing message '%s' failed\n", msg->message); |
| 161 | aos_queue_free_msg(queue, msg); |
| 162 | return false; |
| 163 | } |
| 164 | return true; |
| 165 | } |
| 166 | static inline int vlog_do(log_level level, const char *format, va_list ap) { |
| 167 | log_message *msg = static_cast<log_message *>(aos_queue_get_msg(queue)); |
| 168 | if (msg == NULL) { |
| 169 | fprintf(stderr, "logging: error: couldn't get a message to send '%s'\n", format); |
| 170 | return -1; |
| 171 | } |
| 172 | |
| 173 | if (!vsprintf_in(msg->message, sizeof(msg->message), format, ap)) { |
| 174 | return -1; |
| 175 | } |
| 176 | |
| 177 | if (!write_message(msg, level)) { |
| 178 | return -1; |
| 179 | } |
| 180 | |
| 181 | if (level == FATAL) { |
| 182 | aos::Die("%s", msg->message); |
| 183 | } |
| 184 | |
| 185 | return 0; |
| 186 | } |
| 187 | int log_do(log_level level, const char *format, ...) { |
| 188 | check_init(); |
| 189 | va_list ap; |
| 190 | va_start(ap, format); |
| 191 | const int ret = vlog_do(level, format, ap); |
| 192 | va_end(ap); |
| 193 | return ret; |
| 194 | } |
| 195 | |
| 196 | static inline int vlog_cork(int line, const char *format, va_list ap) { |
| 197 | const size_t message_length = strlen(corked_message.message); |
| 198 | if (line > cork_line_max) cork_line_max = line; |
| 199 | if (line < cork_line_min) cork_line_min = line; |
| 200 | return vsprintf_in(corked_message.message + message_length, |
| 201 | sizeof(corked_message.message) - message_length, format, ap) ? 0 : -1; |
| 202 | } |
| 203 | int log_cork(int line, const char *format, ...) { |
| 204 | check_init(); |
| 205 | va_list ap; |
| 206 | va_start(ap, format); |
| 207 | const int ret = vlog_cork(line, format, ap); |
| 208 | va_end(ap); |
| 209 | return ret; |
| 210 | } |
| 211 | static inline bool log_uncork_helper(char *output, size_t output_size, |
| 212 | const char *format, ...) { |
| 213 | check_init(); |
| 214 | va_list ap; |
| 215 | va_start(ap, format); |
| 216 | const bool ret = vsprintf_in(output, output_size, format, ap); |
| 217 | va_end(ap); |
| 218 | return ret; |
| 219 | } |
| 220 | int log_uncork(int line, log_level level, const char *begin_format, |
| 221 | const char *format, ...) { |
| 222 | check_init(); |
| 223 | va_list ap; |
| 224 | va_start(ap, format); |
| 225 | const int ret = vlog_cork(line, format, ap); |
| 226 | va_end(ap); |
| 227 | if (ret != 0) { |
| 228 | return ret; |
| 229 | } |
| 230 | |
| 231 | log_message *msg = static_cast<log_message *>(aos_queue_get_msg(queue)); |
| 232 | if (msg == NULL) { |
| 233 | fprintf(stderr, "logging: error: couldn't get a message to send '%s'\n", format); |
| 234 | cork_init(); |
| 235 | return -1; |
| 236 | } |
| 237 | |
| 238 | static char new_format[LOG_MESSAGE_LEN]; |
| 239 | if (!log_uncork_helper(new_format, sizeof(new_format), begin_format, |
| 240 | cork_line_min, cork_line_max)) { |
| 241 | cork_init(); |
| 242 | return -1; |
| 243 | } |
| 244 | const size_t new_length = strlen(new_format); |
| 245 | memcpy(msg->message, new_format, new_length); |
| 246 | memcpy(msg->message + new_length, corked_message.message, |
| 247 | std::min(strlen(corked_message.message) + 1, |
| 248 | sizeof(msg->message) - new_length)); |
| 249 | // in case corked_message.message was too long, it'll still be NULL-terminated |
| 250 | msg->message[sizeof(msg->message) - 1] = '\0'; |
| 251 | cork_init(); |
| 252 | |
| 253 | if (!write_message(msg, level)) { |
| 254 | return -1; |
| 255 | } |
| 256 | |
| 257 | return 0; |
| 258 | } |
| 259 | |