/* * The Rin Library – diagnostics module * * Copyright (C) 2015 Gediminas Jakutis * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; version 2.1 * of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #include "diagnostic_private.h" #include #include #include #include "rin/diagnostic.h" #include "rin/time.h" #include "rin/definitions.h" static const char default_format[] = "C:F:mn"; static const char valid_format[] = "CFtTn:m"; static struct iostate { char pidconv[8]; struct timespec start; FILE *err; FILE *warn; FILE *fixme; FILE *info; char *err_format; char *warn_format; char *fixme_format; char *info_format; } state = {"%08x:", {0, 0}, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; void rin_diag_cleanup(void) { char *tmp; tmp = state.err_format; state.err_format = NULL; free(tmp); tmp = state.warn_format; state.warn_format = NULL; free(tmp); tmp = state.fixme_format; state.fixme_format = NULL; free(tmp); tmp = state.info_format; state.info_format = NULL; free(tmp); } void rin_diag_init(void) { static const char convstr[8] = "%04hx:"; clock_gettime(RIN_CLOCK_WALL_COUNTER, &state.start); if (sizeof(pid_t) == 4) { memcpy(state.pidconv, convstr, sizeof(convstr)); } atexit(rin_diag_cleanup); } int rin_diag_format(enum rin_diag_outstream channel, const char *format) { char *tmp; size_t i, j; if (!format) { tmp = NULL; } else { for (i = 0; format[i]; ++i) { int valid = 0; for (j = 0; valid_format[j]; ++j) { if (valid_format[j] == format [i]) { valid = 1; break; } } if (!valid) { return EINVAL; } } tmp = strdup(format); } switch (channel) { case rin_diag_err: free(state.err_format); state.err_format = tmp; break; case rin_diag_warn: free(state.warn_format); state.warn_format = tmp; break; case rin_diag_fixme: free(state.fixme_format); state.fixme_format = tmp; break; case rin_diag_info: free(state.info_format); state.info_format = tmp; break; default: return EINVAL; } return 0; } int rin_diag_set_outstream(enum rin_diag_outstream channel, FILE *stream) { switch (channel) { case rin_diag_err: state.err = stream; break; case rin_diag_warn: state.warn = stream; break; case rin_diag_fixme: state.fixme = stream; break; case rin_diag_info: state.info = stream; break; default: return EINVAL; } return 0; } void __rin_err(const char *func_name, const char *format, ...) { va_list args; va_start(args, format); __rin_msg(state.err ? state.err : stderr, "error", func_name, state.err_format ? state.err_format : default_format, format, args); } void __rin_warn(const char *func_name, const char *format, ...) { va_list args; va_start(args, format); __rin_msg(state.warn ? state.warn : stderr, "warning", func_name, state.warn_format ? state.warn_format : default_format, format, args); } void __rin_fixme(const char *func_name, const char *format, ...) { va_list args; va_start(args, format); __rin_msg(state.fixme ? state.fixme : stderr, "fixme", func_name, state.fixme_format ? state.fixme_format : default_format, format, args); } void __rin_info(const char *func_name, const char *format, ...) { va_list args; va_start(args, format); __rin_msg(state.info ? state.info : stdout, "info", func_name, state.info_format ? state.info_format : default_format, format, args); } static void __rin_msg(FILE *stream, const char *prefix, const char *func_name, const char *rin_format, const char *format, va_list args) { struct timespec t; size_t i; int gottime = 0; for (i = 0; rin_format[i]; ++i) { switch (rin_format[i]) { case 'C': fprintf(stream, "%s", prefix); break; case 'F': fprintf(stream, "%s", func_name); break; case 't': fprintf(stream, state.pidconv, gettid()); break; case 'T': if (!gottime) { clock_gettime(RIN_CLOCK_WALL_COUNTER, &t); t = rin_time_sub(&t, &state.start); gottime = 1; } fprintf(stream, "%lus%06lu.%03luµs", t.tv_sec, t.tv_nsec / 1000, t.tv_nsec % 1000); break; case 'n': fprintf(stream, "\n"); break; case ':': fprintf(stream, ":"); break; case 'm': vfprintf(stream, format, args); break; } } }