Mercurial > libjeffpc
view error.c @ 819:616a36d39703
error: add EEOF errno
Signed-off-by: Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
author | Josef 'Jeff' Sipek <jeffpc@josefsipek.net> |
---|---|
date | Sun, 06 Sep 2020 23:28:12 -0400 |
parents | dfbcf2a05902 |
children | ceb71a7a96f6 |
line wrap: on
line source
/* * Copyright (c) 2013-2020 Josef 'Jeff' Sipek <jeffpc@josefsipek.net> * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include <inttypes.h> #include <syslog.h> #include <stdio.h> #include <stdbool.h> #include <assert.h> #include <pthread.h> #include <execinfo.h> #include <ucontext.h> #include <jeffpc/config.h> #include <jeffpc/error.h> #include "init.h" #ifdef JEFFPC_HAVE_ASSFAIL /* See comment at the end of this file about assfail type checking */ extern int assfail(const char *a, const char *f, int l); #endif static inline void assertion_failed(const char *assertion, const char *file, int line, const char *function) { #if defined(JEFFPC_HAVE_ASSFAIL) assfail(assertion, file, line); #elif defined(JEFFPC_HAVE___ASSERT_FREEBSD_STYLE) __assert(function, file, line, assertion); #elif defined(JEFFPC_HAVE___ASSERT_LINUX_STYLE) __assert(assertion, file, line); #else #error "Don't know how to kill for assertion failures" #endif } void default_print(enum errlevel level, const char *fmt, va_list ap) { FILE *out; switch (level) { case CE_DEBUG: case CE_INFO: out = stdout; break; case CE_WARN: case CE_ERROR: case CE_CRIT: case CE_PANIC: default: out = stderr; break; } vfprintf(out, fmt, ap); /* * TODO: improve this * * stderr doesn't need to be flushed since it is unbuffered by * default. stdout o the other hand tends to default to fully * buffered instead of what we'd prefer - line buffered. In theory, * the buffering strategy can be changed via setvbuf(), but the * description of it seems to indicate that this only works before * stdout is used the first time. Unfortunately, we support using * cmn_err() before the library is initialized. */ if (out != stderr) fflush(out); } void jeffpc_print(enum errlevel level, const char *fmt, ...) { va_list ap; va_start(ap, fmt); libops.print(level, fmt, ap); va_end(ap); } void default_log(int loglevel, const char *fmt, va_list ap) { /* * This function is a no-op but it exists to allow consumers of * libjeffpc to override it with their own log implementation. */ } void jeffpc_log(int loglevel, const char *fmt, ...) { va_list ap; va_start(ap, fmt); libops.log(loglevel, fmt, ap); va_end(ap); } void default_assfail(const char *a, const char *f, int l, const char *fxn) { jeffpc_log(LOG_ALERT, "assertion failed: %s, file: %s, line: %d", a, f, l); print_stacktrace(CE_CRIT, NULL); assertion_failed(a, f, l, fxn); } void jeffpc_assfail(const char *a, const char *f, int l, const char *fxn) { libops.assfail(a, f, l, fxn); /* this is a hack to shut up gcc */ abort(); } void default_assfail3(const char *a, uintmax_t lv, const char *op, uintmax_t rv, const char *f, int l, const char *fxn) { char msg[512]; snprintf(msg, sizeof(msg), "%s (0x%"PRIx64" %s 0x%"PRIx64")", a, lv, op, rv); jeffpc_log(LOG_ALERT, "assertion failed: %s, file: %s, line: %d", msg, f, l); print_stacktrace(CE_CRIT, NULL); assertion_failed(msg, f, l, fxn); } void jeffpc_assfail3(const char *a, uintmax_t lv, const char *op, uintmax_t rv, const char *f, int l, const char *fxn) { libops.assfail3(a, lv, op, rv, f, l, fxn); /* this is a hack to shut up gcc */ abort(); } static const char *get_session(void) { if (!libops.get_session) return ""; return libops.get_session(); } void cmn_verr(enum errlevel level, const char *fmt, va_list ap) { const char *levelstr; const char *session; unsigned long tid; int loglevel; bool panic; char buf[256]; tid = (unsigned long) pthread_self(); panic = false; switch (level) { case CE_DEBUG: levelstr = "DEBUG"; loglevel = LOG_DEBUG; break; case CE_INFO: levelstr = "INFO"; loglevel = LOG_INFO; break; case CE_WARN: levelstr = "WARN"; loglevel = LOG_WARNING; break; case CE_ERROR: levelstr = "ERROR"; loglevel = LOG_ERR; break; case CE_CRIT: levelstr = "CRIT"; loglevel = LOG_CRIT; break; case CE_PANIC: levelstr = "PANIC"; loglevel = LOG_ALERT; panic = true; break; default: levelstr = "?????"; loglevel = LOG_CRIT; panic = true; break; } vsnprintf(buf, sizeof(buf), fmt, ap); /* * Get the session string. */ session = get_session(); /* * We are printing the thread ID as a 4-digit number. This will * allow systems that use small integers (e.g., Illumos) to have * short IDs. Systems that use large integers (e.g., Linux) will * use more digits. Since on those systems the IDs will be * clustered around some big integer, they will very likely always * print as the same number of digits. */ jeffpc_log(loglevel, "[%04lx] %-5s%s %s\n", tid, levelstr, session, buf); jeffpc_print(level, "[%04lx] %-5s%s %s\n", tid, levelstr, session, buf); if (panic) { print_stacktrace(CE_CRIT, NULL); abort(); } } void cmn_err(enum errlevel level, const char *fmt, ...) { va_list ap; va_start(ap, fmt); cmn_verr(level, fmt, ap); va_end(ap); } void panic(const char *fmt, ...) { va_list ap; va_start(ap, fmt); cmn_verr(CE_PANIC, fmt, ap); va_end(ap); /* this is a hack to shut up gcc */ abort(); } /* * Note: We must not allocate any memory, etc. because we want this function * to be callable from any context. */ void save_stacktrace(struct stack *stack) { size_t nframes; nframes = backtrace(stack->frames, ERROR_STACK_FRAMES); stack->nframes = nframes; /* NULL-out any unused frames */ while (nframes < ERROR_STACK_FRAMES) stack->frames[nframes++] = NULL; } #ifndef JEFFPC_HAVE_ADDRTOSYMSTR static void addrtosymstr(void *pc, char *buf, size_t buflen) { snprintf(buf, buflen, "[%p]", pc); } #endif /* * Note: We must not allocate any memory, etc. because we want this function * to be callable from any context. */ void print_stacktrace(enum errlevel level, struct stack *stack) { struct stack here; size_t i; if (!stack) { save_stacktrace(&here); stack = &here; } for (i = 0; i < stack->nframes; i++) { char tmp[256]; addrtosymstr(stack->frames[i], tmp, sizeof(tmp)); cmn_err(level, " %s", tmp); } } const char *xstrerror(int e) { if (libops.strerror) { const char *ret; ret = libops.strerror(e); if (ret) return ret; } switch (e) { case 0: return "Success"; case -ECKSUM: return "Checksum mismatch"; case -ENOTUNIQ: return "Name, identifier, or object not unique"; case -EEOF: return "End of file"; } return strerror(-e); } /* * If we have sys/debug.h, let's include it so that the assfail function * signature gets checked to be what we expect. * * This include is at the end of this file because it polutes the namespace * big time. */ #ifdef JEFFPC_HAVE_SYS_DEBUG_H #include <sys/debug.h> #endif