src/cc_debug.c (202 lines of code) (raw):

/* * ccommon - a cache common library. * Copyright (C) 2013 Twitter, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <cc_debug.h> #include <cc_log.h> #include <cc_mm.h> #include <cc_print.h> #include <ctype.h> #include <errno.h> #ifdef CC_BACKTRACE #include <execinfo.h> #endif /* CC_BACKTRACE */ #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include <pthread.h> #define BACKTRACE_DEPTH 64 #define DEBUG_MODULE_NAME "ccommon::debug" struct debug_logger default_logger; struct debug_logger *dlog = &default_logger; static bool debug_init = false; static char * level_str[] = { "ALWAYS", "CRIT", "ERROR", "WARN", "INFO", "DEBUG", "VERB", "VVERB" }; static char tname_buf[16]; static inline char * _thread_name(void) { /* we do not expect the following to fail ever, because buffer is sufficiently long */ pthread_getname_np(pthread_self(), tname_buf, 16); return tname_buf; } static void debug_stacktrace(int skip_count) { #ifdef CC_BACKTRACE void *stack[BACKTRACE_DEPTH]; char **symbols; int size, i, j; log_stderr("printing stracktrace (depth limit: %d)", BACKTRACE_DEPTH); size = backtrace(stack, BACKTRACE_DEPTH); symbols = backtrace_symbols(stack, size); if (symbols == NULL) { log_stderr("Could not print backtrace; no symbols"); return; } skip_count++; /* skip the current frame also */ for (i = skip_count, j = 0; i < size; i++, j++) { log_stderr("[%d] %s", j, symbols[i]); } free(symbols); /* when segfault happens, currently it will loop printing * stack trace forever, add abort to stop this */ abort(); #endif } void debug_assert(const char *cond, const char *file, int line, int panic) { log_stderr("[%s] assert '%s' failed @ (%s, %d)", _thread_name(), cond, file, line); if (panic) { debug_stacktrace(1); abort(); } } static void _stacktrace(int signo) { log_stderr("received signal %d", signo); debug_stacktrace(2); /* skipping functions inside signal module */ raise(signo); } /* this only works on the default handler given the sig_fn format */ static void _logrotate(int signo) { log_info("received signal %d, reopen log file", signo); log_reopen(dlog->logger, NULL); } void debug_log_flush(void *arg) { /* * arg is unused but necessary for debug_log_flush to be used in conjunction * with cc_timer and cc_wheel facilities, since to be inserted into a timing * wheel the function must have the type signature of timeout_cb_fn. */ (void)arg; log_flush(dlog->logger); } rstatus_i debug_setup(debug_options_st *options) { size_t log_nbuf = DEBUG_LOG_NBUF; char *filename = DEBUG_LOG_FILE; /* since logs are not setup yet, we have to log to stderr */ log_stderr("Set up the %s module", DEBUG_MODULE_NAME); if (debug_init) { log_stderr("%s has already been setup, overwrite", DEBUG_MODULE_NAME); if (dlog->logger != NULL) { log_destroy(&dlog->logger); } } dlog->level = DEBUG_LOG_LEVEL; if (options != NULL) { filename = option_str(&options->debug_log_file); log_nbuf = option_uint(&options->debug_log_nbuf); dlog->level = option_uint(&options->debug_log_level); } dlog->logger = log_create(filename, log_nbuf); if (dlog->logger == NULL) { log_stderr("Could not create logger"); goto error; } /* some adjustment on signal handling */ if (signal_override(SIGSEGV, "printing stacktrace when segfault", 0, 0, _stacktrace) < 0) { goto error; } /* to allow logrotate with nocopytruncate, use SIGHUP to reopen log */ if (signal_override(SIGHUP, "reopen log file", 0, 0, _logrotate) < 0) { goto error; } debug_init = true; return CC_OK; error: log_destroy(&dlog->logger); return CC_ERROR; } void debug_teardown(void) { log_stderr("Tear down the %s module", DEBUG_MODULE_NAME); if (!debug_init) { log_stderr("%s was never setup", DEBUG_MODULE_NAME); } if (dlog->logger != NULL) { log_destroy(&dlog->logger); } debug_init = false; } void _log(struct debug_logger *dl, const char *file, int line, int level, const char *fmt, ...) { int len, size, errno_save; char buf[LOG_MAX_LEN], *timestr; va_list args; struct tm *local; time_t t; if (dl == NULL || dl->logger == NULL || dl->level < level) { return; } errno_save = errno; len = 0; /* length of output buffer */ size = LOG_MAX_LEN; /* size of output buffer */ t = time(NULL); local = localtime(&t); timestr = asctime(local); len += cc_scnprintf(buf + len, size - len, "[%.*s][%s][%s] %s:%d ", strlen(timestr) - 1, timestr, _thread_name(), level_str[level], file, line); va_start(args, fmt); len += cc_vscnprintf(buf + len, size - len, fmt, args); va_end(args); buf[len++] = '\n'; log_write(dl->logger, buf, len); errno = errno_save; } /* * Hexadecimal dump in the canonical hex + ascii display * See -C option in man hexdump */ void _log_hexdump(struct debug_logger *dl, int level, char *data, int datalen) { char buf[8 * LOG_MAX_LEN]; int i, off, len, size, errno_save; if (dl->logger == NULL || dl->level < level) { return; } /* log hexdump */ errno_save = errno; off = 0; /* data offset */ len = 0; /* length of output buffer */ size = 8 * LOG_MAX_LEN; /* size of output buffer */ while (datalen != 0 && (len < size - 1)) { char *save; unsigned char c; int savelen; len += cc_scnprintf(buf + len, size - len, "%08x ", off); save = data; savelen = datalen; for (i = 0; datalen != 0 && i < 16; data++, datalen--, i++) { c = (unsigned char)(*data); len += cc_scnprintf(buf + len, size - len, "%02x%s", c, (i == 7) ? " " : " "); } for ( ; i < 16; i++) { len += cc_scnprintf(buf + len, size - len, " %s", (i == 7) ? " " : " "); } data = save; datalen = savelen; len += cc_scnprintf(buf + len, size - len, " |"); for (i = 0; datalen != 0 && i < 16; data++, datalen--, i++) { c = (unsigned char)(isprint(*data) ? *data : '.'); len += cc_scnprintf(buf + len, size - len, "%c", c); } len += cc_scnprintf(buf + len, size - len, "|\n"); off += 16; } log_write(dl->logger, buf, len); errno = errno_save; }