/* * Copyright (c) 2001-2020 Mellanox Technologies, Ltd. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU * General Public License (GPL) Version 2, available from the file * COPYING in the main directory of this source tree, or the * BSD license below: * * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * - Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. * * - Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * 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 "vlogger.h" #include #include #include #include #include #include #include #include #include "utils/bullseye.h" #include "vma/util/utils.h" #include "vma/util/sys_vars.h" #define VLOG_DEFAULT_MODULE_NAME "VMA" #define VMA_LOG_CB_ENV_VAR "VMA_LOG_CB_FUNC_PTR" char g_vlogger_module_name[VLOG_MODULE_MAX_LEN] = VLOG_DEFAULT_MODULE_NAME; int g_vlogger_fd = -1; FILE* g_vlogger_file = NULL; vlog_levels_t g_vlogger_level = VLOG_DEFAULT; vlog_levels_t* g_p_vlogger_level = NULL; uint8_t g_vlogger_details = 0; uint8_t* g_p_vlogger_details = NULL; uint32_t g_vlogger_usec_on_startup = 0; bool g_vlogger_log_in_colors = MCE_DEFAULT_LOG_COLORS; vma_log_cb_t g_vlogger_cb = NULL; namespace log_level { typedef struct { vlog_levels_t level; const char * output_name; const char * output_color; const char ** input_names; } level_names; static const char *log_names_none[] = {"none", NULL}; static const char *log_names_panic[] = {"panic", "0", NULL}; static const char *log_names_error[] = {"error", "1", NULL}; static const char *log_names_warn[] = {"warn", "warning", "2", NULL}; static const char *log_names_info[] = {"info", "information", "3", NULL}; static const char *log_names_details[] = {"details", NULL}; static const char *log_names_debug[] = {"debug", "4", NULL}; static const char *log_names_fine[] = {"fine", "func", "5", NULL}; static const char *log_names_finer[] = {"finer", "func+", "funcall", "func_all", "func-all", "6", NULL}; static const char *log_names_all[] = {"all", NULL}; // must be by order because "to_str" relies on that! static const level_names levels[] = { {VLOG_NONE, "NONE", "\e[0;31m" /*Red*/, (const char ** )log_names_none}, {VLOG_PANIC, "PANIC", "\e[0;31m" /*Red*/, (const char ** )log_names_panic}, {VLOG_ERROR, "ERROR", "\e[0;31m" /*Red*/, (const char ** )log_names_error}, {VLOG_WARNING, "WARNING", "\e[2;35m" /*Magenta*/, (const char ** )log_names_warn}, {VLOG_INFO, "INFO", "\e[0m" /*Default*/, (const char ** )log_names_info}, {VLOG_DETAILS, "DETAILS", "\e[0m" /*Default*/, (const char ** )log_names_details}, {VLOG_DEBUG, "DEBUG", "\e[0m" /*Default*/, (const char ** )log_names_debug}, {VLOG_FINE, "FINE", "\e[2m" /*Grey*/, (const char ** )log_names_fine}, {VLOG_FINER, "FINER", "\e[2m" /*Grey*/, (const char ** )log_names_finer}, {VLOG_ALL, "ALL", "\e[2m" /*Grey*/, (const char ** )log_names_all}, }; // convert str to vlog_levels_t; upon error - returns the given 'def_value' vlog_levels_t from_str(const char* str, vlog_levels_t def_value) { size_t num_levels = sizeof(levels) / sizeof(levels[0]); for (size_t i = 0; i < num_levels; ++i) { const char ** input_name = levels[i].input_names; while (*input_name) { if (strcasecmp(str, *input_name) == 0) { /* Set maximum accessible logging level in case * a user requests level that is reduced during compilation * or requested one if the level is in valid range */ if (levels[i].level <= VMA_MAX_DEFINED_LOG_LEVEL) { return levels[i].level; } def_value = (vlog_levels_t)(VMA_MAX_DEFINED_LOG_LEVEL); vlog_printf(VLOG_WARNING, "VMA trace level set to max level %s\n", to_str(def_value)); return def_value; } input_name++; } } return def_value; // not found. use given def_value } // convert int to vlog_levels_t; upon error - returns the given 'def_value' vlog_levels_t from_int(const int int_log, vlog_levels_t def_value) { if (int_log >= VLOG_NONE && int_log <= VLOG_ALL) { return static_cast(int_log); } return def_value; // not found. use given def_value } const char * to_str(vlog_levels_t level) { static int base = VLOG_NONE; return levels[level - base].output_name; } const char * get_color(vlog_levels_t level) { static int base = VLOG_NONE; return levels[level - base].output_color; } } pid_t gettid(void) { return syscall(__NR_gettid); } #if _BullseyeCoverage #pragma BullseyeCoverage off #endif // Credit for the C++ de-mangler go to: http://tombarta.wordpress.com/2008/08/01/c-stack-traces-with-gcc/ #include void printf_backtrace(void) { char **backtrace_strings; void* backtrace_addrs[10]; int backtrace_depth = backtrace(backtrace_addrs, 10); printf("[tid: %d] ------ printf_backtrace ------ \n", gettid()); backtrace_strings = backtrace_symbols(backtrace_addrs, backtrace_depth); for (int i = 1; i < backtrace_depth; i++) { #if 0 printf("[%d] %p: %s\n", i, backtrace_addrs[i], backtrace_strings[i]); #else size_t sz = 1024; // just a guess, template names will go much wider char *function = NULL; char *begin = 0, *end = 0; // find the parentheses and address offset surrounding the mangled name for (char *j = backtrace_strings[i]; *j; ++j) { if (*j == '(') { begin = j; } else if (*j == '+') { end = j; } } if (begin && end) { *begin++ = '\0'; *end = '\0'; // found our mangled name, now in [begin, end) int status; function = abi::__cxa_demangle(begin, NULL, &sz, &status); if (NULL == function) { // demangling failed, just pretend it's a C function with no args function = static_cast(malloc(sz)); if (function) { status = snprintf(function, sz - 1, "%s()", begin); if (status > 0) { function[status] = '\0'; } else { function[0] = '\0'; } } } // fprintf(out, " %s:%s\n", stack.backtrace_strings[i], function); printf("[%d] %p: %s:%s\n", i, backtrace_addrs[i], backtrace_strings[i], (function ? function : "n/a")); if (function) { free(function); } } else { // didn't find the mangled name, just print the whole line printf("[%d] %p: %s\n", i, backtrace_addrs[i], backtrace_strings[i]); } #endif } free(backtrace_strings); } #if _BullseyeCoverage #pragma BullseyeCoverage on #endif //////////////////////////////////////////////////////////////////////////////// // NOTE: this function matches 'bool vma_log_set_cb_func(vma_log_cb_t log_cb)' that // we gave customers; hence, you must not change our side without considering their side static vma_log_cb_t vma_log_get_cb_func() { vma_log_cb_t log_cb = NULL; const char* const CB_STR = getenv(VMA_LOG_CB_ENV_VAR); if (!CB_STR || !*CB_STR) return NULL; if (1 != sscanf(CB_STR, "%p", &log_cb)) return NULL; return log_cb; } void vlog_start(const char* log_module_name, vlog_levels_t log_level, const char* log_filename, int log_details, bool log_in_colors) { g_vlogger_file = stderr; g_vlogger_cb = vma_log_get_cb_func(); strncpy(g_vlogger_module_name, log_module_name, sizeof(g_vlogger_module_name) - 1); g_vlogger_module_name[sizeof(g_vlogger_module_name) - 1] = '\0'; vlog_get_usec_since_start(); char local_log_filename[255]; if (log_filename != NULL && strcmp(log_filename,"")) { sprintf(local_log_filename, "%s", log_filename); g_vlogger_fd = open(local_log_filename, O_WRONLY|O_CREAT|O_TRUNC, 0644); if (g_vlogger_fd < 0) { vlog_printf(VLOG_PANIC, "Failed to open logfile: %s\n",local_log_filename); exit(1); } g_vlogger_file = fdopen(g_vlogger_fd, "w"); BULLSEYE_EXCLUDE_BLOCK_START if (g_vlogger_file == NULL) { g_vlogger_file = stderr; vlog_printf(VLOG_PANIC, "Failed to open logfile: %s\n",local_log_filename); exit(1); } BULLSEYE_EXCLUDE_BLOCK_END } g_vlogger_level = log_level; g_p_vlogger_level = &g_vlogger_level; g_vlogger_details = log_details; g_p_vlogger_details = &g_vlogger_details; int file_fd = fileno(g_vlogger_file); if (file_fd >= 0 && isatty(file_fd) && log_in_colors) g_vlogger_log_in_colors = log_in_colors; } void vlog_stop(void) { // Closing logger // Allow only really extreme (PANIC) logs to go out g_vlogger_level = VLOG_PANIC; //set default module name strcpy(g_vlogger_module_name, VLOG_DEFAULT_MODULE_NAME); // Close output stream if(g_vlogger_file && g_vlogger_file != stderr) fclose(g_vlogger_file); //fix for using LD_PRELOAD with LBM. Unset the pointer given by the parent process, so a child could get his own pointer without issues. unsetenv(VMA_LOG_CB_ENV_VAR); } void vlog_output(vlog_levels_t log_level, const char* fmt , ... ) { int len = 0; char buf[VLOGGER_STR_SIZE]; // Format header // Set color scheme if (g_vlogger_log_in_colors) len += snprintf(buf+len, VLOGGER_STR_SIZE-len-1, "%s", log_level::get_color(log_level)); switch (g_vlogger_details) { case 3: // Time len += snprintf(buf+len, VLOGGER_STR_SIZE-len-1, " Time: %9.3f", ((float)vlog_get_usec_since_start())/1000); // fallthrough case 2: // Pid len += snprintf(buf+len, VLOGGER_STR_SIZE-len-1, " Pid: %5u", getpid()); // fallthrough case 1: // Tid len += snprintf(buf+len, VLOGGER_STR_SIZE-len-1, " Tid: %5u", gettid()); // fallthrough case 0: // Func default: len += snprintf(buf+len, VLOGGER_STR_SIZE-len-1, " %s %s: ", g_vlogger_module_name, log_level::to_str(log_level)); } if (len < 0) { return ; } buf[len+1] = '\0'; // Format body va_list ap; va_start(ap, fmt); if (fmt != NULL) len += vsnprintf(buf+len, VLOGGER_STR_SIZE-len, fmt, ap); va_end(ap); // Reset color scheme if (g_vlogger_log_in_colors) { // Save enough room for color code termination and EOL if (len > VLOGGER_STR_SIZE - VLOGGER_STR_TERMINATION_SIZE) len = VLOGGER_STR_SIZE - VLOGGER_STR_TERMINATION_SIZE - 1; len = snprintf(buf + len, VLOGGER_STR_TERMINATION_SIZE, VLOGGER_STR_COLOR_TERMINATION_STR); if (len < 0) { return ; } } if (g_vlogger_cb) { g_vlogger_cb(log_level, buf); } else if (g_vlogger_file) { // Print out fprintf(g_vlogger_file, "%s", buf); fflush(g_vlogger_file); } else { printf("%s", buf); } }