common: added runtime-controllable debug infra.
authorKrisztian Litkey <krisztian.litkey@intel.com>
Wed, 20 Jun 2012 15:40:01 +0000 (18:40 +0300)
committerKrisztian Litkey <krisztian.litkey@intel.com>
Wed, 20 Jun 2012 15:46:27 +0000 (18:46 +0300)
src/Makefile.am
src/common/debug.c [new file with mode: 0644]
src/common/debug.h [new file with mode: 0644]
src/common/log.c
src/common/log.h

index a9b5233..1cf7c7d 100644 (file)
@@ -33,6 +33,7 @@ libmurphy_common_la_HEADERS =         \
                common/macros.h         \
                common/list.h           \
                common/log.h            \
+               common/debug.h          \
                common/mm.h             \
                common/hashtbl.h        \
                common/mainloop.h       \
@@ -43,6 +44,7 @@ libmurphy_common_la_HEADERS =         \
 
 libmurphy_common_la_SOURCES =                  \
                common/log.c                    \
+               common/debug.c                  \
                common/mm.c                     \
                common/hashtbl.c                \
                common/mainloop.c               \
@@ -113,7 +115,7 @@ libmurphy_core_la_LDFLAGS =         \
 libmurphy_core_la_LIBADD  =            \
                libmurphy-common.la -ldl
 
-libmurphy_core_la_DEPENDENCIES = linker-script.core
+libmurphy_core_la_DEPENDENCIES = linker-script.core libmurphy-common.la
 
 # core linker script generation
 linker-script.core: $(libmurphy_core_la_HEADERS)
@@ -295,6 +297,7 @@ plugin_console_la_LIBADD  = $(CONSOLE_PLUGIN_LIBS)
 plugin_LTLIBRARIES    += plugin-console.la
 endif
 
+
 ###################################
 # murphy daemon
 #
diff --git a/src/common/debug.c b/src/common/debug.c
new file mode 100644 (file)
index 0000000..27fe1e4
--- /dev/null
@@ -0,0 +1,374 @@
+#include <stdarg.h>
+#include <limits.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/list.h>
+#include <murphy/common/log.h>
+#include <murphy/common/hashtbl.h>
+#include <murphy/common/utils.h>
+#include <murphy/common/debug.h>
+
+#define WILDCARD  "*"
+
+int mrp_debug_stamp = 0;     /* debug config stamp */
+
+static int         debug_enabled;           /* debug messages enabled */
+static mrp_htbl_t *rules_on;                /* enabling rules */
+static mrp_htbl_t *rules_off;               /* disabling rules */
+
+
+static void free_rule_cb(void *key, void *entry)
+{
+    MRP_UNUSED(key);
+
+    mrp_free(entry);
+}
+
+
+static int init_rules(void)
+{
+    mrp_htbl_config_t hcfg;
+    
+    mrp_clear(&hcfg);
+    hcfg.comp = mrp_string_comp;
+    hcfg.hash = mrp_string_hash;
+    hcfg.free = free_rule_cb;
+
+    rules_on  = mrp_htbl_create(&hcfg);
+    rules_off = mrp_htbl_create(&hcfg);
+
+    if (rules_on == NULL || rules_off == NULL)
+       return FALSE;
+    else
+       return TRUE;
+}
+
+
+static void reset_rules(void)
+{
+    if (rules_on != NULL)
+       mrp_htbl_destroy(rules_on , TRUE);
+    if (rules_off != NULL)
+       mrp_htbl_destroy(rules_off, TRUE);    
+}
+
+
+void mrp_debug_reset(void)
+{
+    debug_enabled = FALSE;
+    reset_rules();
+}
+
+
+int mrp_debug_enable(int enabled)
+{
+    int prev = debug_enabled;
+    
+    debug_enabled = !!enabled;
+    mrp_log_enable(MRP_LOG_MASK_DEBUG);
+
+    return prev;
+}
+
+
+static int add_rule(const char *func, const char *file, int line, int off)
+{
+    mrp_htbl_t  *ht;
+    char        *rule, *r, buf[PATH_MAX * 2];
+
+    if (rules_on == NULL)
+       if (!init_rules())
+           return FALSE;
+    
+    if (!off)
+       ht = rules_on;
+    else
+       ht = rules_off;
+
+    if (func != NULL && file == NULL && line == 0) {
+       r    = mrp_htbl_lookup(ht, (void *)func);
+       rule = (char *)func;
+    }
+    else if (func != NULL && file != NULL && line == 0) {
+       snprintf(buf, sizeof(buf), "%s@%s", func, file);
+       r    = mrp_htbl_lookup(ht, (void *)buf);
+       rule = buf;
+    }
+    else if (func == NULL && file != NULL && line == 0) {
+       snprintf(buf, sizeof(buf), "@%s", file);
+       r    = mrp_htbl_lookup(ht, (void *)buf);
+       rule = buf;
+    }
+    else if (func == NULL && file != NULL && line > 0) {
+       snprintf(buf, sizeof(buf), "%s:%d", file, line);
+       r    = mrp_htbl_lookup(ht, (void *)buf);
+       rule = buf;
+    }
+    
+    if (r != NULL)
+       return FALSE;
+
+    rule = mrp_strdup(rule);
+    if (rule == NULL)
+       return FALSE;
+
+    if (mrp_htbl_insert(ht, rule, rule)) {
+       mrp_debug_stamp++;
+
+       return TRUE;
+    }
+    else {
+       mrp_free(rule);
+
+       return FALSE;
+    }
+}
+
+
+static int del_rule(const char *func, const char *file, int line, int off)
+{
+    mrp_htbl_t  *ht;
+    char        *r, buf[PATH_MAX * 2];
+
+    if (rules_on == NULL)
+       if (!init_rules())
+           return FALSE;
+    
+    if (!off)
+       ht = rules_on;
+    else
+       ht = rules_off;
+
+    if (func != NULL && file == NULL && line == 0) {
+       r = mrp_htbl_remove(ht, (void *)func, TRUE);
+    }
+    else if (func != NULL && file != NULL && line == 0) {
+       snprintf(buf, sizeof(buf), "%s@%s", func, file);
+       r = mrp_htbl_remove(ht, (void *)buf, TRUE);
+    }
+    else if (func == NULL && file != NULL && line == 0) {
+       snprintf(buf, sizeof(buf), "@%s", file);
+       r = mrp_htbl_remove(ht, (void *)buf, TRUE);
+    }
+    else if (func == NULL && file != NULL && line > 0) {
+       snprintf(buf, sizeof(buf), "%s:%d", file, line);
+       r = mrp_htbl_remove(ht, (void *)buf, TRUE);
+    }
+    
+    if (r != NULL) {
+       mrp_debug_stamp++;
+
+       return TRUE;
+    }
+    else
+       return FALSE;
+}
+
+
+int mrp_debug_set_config(const char *cmd)
+{
+    char    buf[2 * PATH_MAX + 1], *colon, *at, *eq;
+    char   *func, *file, *end;
+    size_t  len;
+    int     del, off, line;
+
+    if (*cmd == '+' || *cmd == '-')
+       del = (*cmd++ == '-');
+    else
+       del = FALSE;
+    
+    eq = strchr(cmd, '=');
+    
+    if (eq == NULL) {
+       strncpy(buf, cmd, sizeof(buf) - 1);
+       buf[sizeof(buf) - 1] = '\0';
+       off = FALSE;
+    }
+    else {
+       if (!strcmp(eq + 1, "on"))
+           off = FALSE;
+       else if (!strcmp(eq + 1, "off"))
+           off = TRUE;
+       else
+           return FALSE;
+
+       len = eq - cmd;
+       if (len >= sizeof(buf))
+           len = sizeof(buf) - 1;
+       
+       strncpy(buf, cmd, len);
+       buf[len] = '\0';
+    }
+    
+    colon = strchr(buf, ':');
+    
+    if (colon != NULL) {
+       if (strchr(buf, '@') != NULL)
+           return FALSE;
+
+       *colon = '\0';
+       func   = NULL;
+       file   = buf;
+       line   = strtoul(colon + 1, &end, 10);
+       
+       if (end && *end)
+           return FALSE;
+       
+       mrp_log_info("%s file='%s', line=%d, %s", del ? "del" : "add",
+                    file, line, off ? "off" : "on");
+    }
+    else {
+       at = strchr(buf, '@');
+       
+       if (at != NULL) {
+           *at = '\0';
+           func = (at == buf ? NULL : buf);
+           file = at + 1;
+           line = 0;
+
+           mrp_log_info("%s func='%s', file='%s', %s", del ? "del" : "add",
+                        func ? func : "", file, off ? "off" : "on");
+       }
+       else {
+           func = buf;
+           file = NULL;
+           line = 0;
+
+           mrp_log_info("%s func='%s' %s", del ? "del" : "add",
+                        func, off ? "off" : "on");
+       }
+    }
+
+    if (!del)
+       return add_rule(func, file, line, off);
+    else
+       return del_rule(func, file, line, off);
+    
+    return TRUE;
+}
+
+
+typedef struct {
+    FILE *fp;
+    int   on;
+} dump_t;
+
+
+static int dump_rule_cb(void *key, void *object, void *user_data)
+{
+    dump_t     *d     = (dump_t *)user_data;
+    FILE       *fp    = d->fp;
+    const char *state = d->on ? "on" : "off";
+    
+    MRP_UNUSED(key);
+
+    fprintf(fp, "    %s %s\n", (char *)object, state);
+
+    return MRP_HTBL_ITER_MORE;
+}
+
+
+int mrp_debug_dump_config(FILE *fp)
+{
+    dump_t d;
+
+    fprintf(fp, "Debugging is %sabled\n", debug_enabled ? "en" : "dis");
+    
+    if (rules_on != NULL) {
+       fprintf(fp, "Debugging rules:\n");
+       
+       d.fp = fp;
+       d.on = TRUE;
+       mrp_htbl_foreach(rules_on , dump_rule_cb, &d);
+       d.on = FALSE;
+       mrp_htbl_foreach(rules_off, dump_rule_cb, &d);
+    }
+    else
+       fprintf(fp, "No debugging rules defined.\n");
+    
+    return TRUE;
+}
+
+
+void mrp_debug_msg(const char *site, const char *file, int line,
+                  const char *func, const char *format, ...)
+{
+    va_list ap;
+    
+    MRP_UNUSED(site);
+    
+    va_start(ap, format);
+    mrp_log_msgv(MRP_LOG_DEBUG, file, line, func, format, ap);
+    va_end(ap);
+}
+
+
+int mrp_debug_check(const char *func, const char *file, int line)
+{
+    char  buf[2 * PATH_MAX], *base;
+    void *key;
+
+    if (!debug_enabled || rules_on == NULL)
+       return FALSE;
+
+    key = (void *)func;
+    if (mrp_htbl_lookup(rules_on, key) != NULL)
+       goto check_suppress;
+    
+    base = strrchr(file, '/');
+    if (base != NULL) {
+       key = base + 1;
+       if (mrp_htbl_lookup(rules_on, key) != NULL)
+           goto check_suppress;
+    }
+
+    key = buf;
+
+    snprintf(buf, sizeof(buf), "@%s", file);
+    if (mrp_htbl_lookup(rules_on, key) != NULL)
+       goto check_suppress;
+    
+    snprintf(buf, sizeof(buf), "%s@%s", func, file);
+    if (mrp_htbl_lookup(rules_on, key) != NULL)
+       goto check_suppress;
+    
+    snprintf(buf, sizeof(buf), "%s:%d", file, line);
+    if (mrp_htbl_lookup(rules_on, key) != NULL)
+       goto check_suppress;
+    
+    if (mrp_htbl_lookup(rules_on, (void *)WILDCARD) == NULL)
+       return FALSE;
+
+    
+ check_suppress:
+    if (rules_off == NULL)
+       return TRUE;
+           
+    key = (void *)func;
+    if (mrp_htbl_lookup(rules_off, key) != NULL)
+       return FALSE;
+
+    base = strrchr(file, '/');
+    if (base != NULL) {
+       key = base + 1;
+       if (mrp_htbl_lookup(rules_on, key) != NULL)
+           return FALSE;
+    }
+
+    key = buf;
+
+    snprintf(buf, sizeof(buf), "@%s", file);
+    if (mrp_htbl_lookup(rules_off, key) != NULL)
+       return FALSE;
+    
+    snprintf(buf, sizeof(buf), "%s@%s", func, file);
+    if (mrp_htbl_lookup(rules_off, key) != NULL)
+       return FALSE;
+
+    snprintf(buf, sizeof(buf), "%s:%d", file, line);
+    if (mrp_htbl_lookup(rules_off, key) != NULL)
+       return FALSE;
+    
+    return TRUE;
+}
diff --git a/src/common/debug.h b/src/common/debug.h
new file mode 100644 (file)
index 0000000..9683f9a
--- /dev/null
@@ -0,0 +1,55 @@
+#ifndef __MURPHY_DEBUG_H__
+#define __MURPHY_DEBUG_H__
+
+#include <stdio.h>
+
+#include <murphy/common/macros.h>
+
+MRP_CDECL_BEGIN
+
+/** Macro to generate a debug site string. */
+#define MRP_DEBUG_SITE(file, line, func)       \
+    "__DEBUG_SITE_"file":"MRP_STRINGIFY(line)
+
+/** Log a debug message if the invoking debug site is enabled. */
+#define mrp_debug(fmt, args...)        do {                                    \
+       static const char *__site =                                     \
+           MRP_DEBUG_SITE(__FILE__, __LINE__, __FUNCTION__);           \
+       static int __site_stamp = -1;                                   \
+       static int __site_enabled;                                      \
+                                                                       \
+       if (MRP_UNLIKELY(__site_stamp != mrp_debug_stamp)) {            \
+           __site_enabled = mrp_debug_check(__FUNCTION__,              \
+                                            __FILE__, __LINE__);       \
+           __site_stamp   = mrp_debug_stamp;                           \
+       }                                                               \
+                                                                       \
+       if (MRP_UNLIKELY(__site_enabled))                               \
+           mrp_debug_msg(__site, __LOC__, fmt, ## args);               \
+    } while (0)
+
+/** Global debug configuration stamp, exported for minimum-overhead checking. */
+extern int mrp_debug_stamp;
+
+/** Enable/disable debug messages globally. */
+int mrp_debug_enable(int enabled);
+
+/** Reset all debug configuration to the defaults. */
+void mrp_debug_reset(void);
+
+/** Apply the debug configuration settings given in cmd. */
+int mrp_debug_set_config(const char *cmd);
+
+/** Dump the active debug configuration. */
+int mrp_debug_dump_config(FILE *fp);
+
+/** Low-level log wrapper for debug messages. */
+void mrp_debug_msg(const char *site, const char *file, int line,
+                  const char *func, const char *format, ...);
+
+/** Check if the given debug site is enabled. */
+int mrp_debug_check(const char *func, const char *file, int line);
+
+MRP_CDECL_END
+
+#endif /* __MURPHY_DEBUG_H__ */
index 8263319..f6f49ce 100644 (file)
@@ -137,10 +137,9 @@ int mrp_log_set_target(const char *target)
 }
 
 
-void mrp_log_msg(mrp_log_level_t level, const char *file,
-                int line, const char *func, const char *format, ...)
+void mrp_log_msgv(mrp_log_level_t level, const char *file,
+                 int line, const char *func, const char *format, va_list ap)
 {
-    va_list     ap;
     int         lvl;
     const char *prefix;
     char        prfx[2*1024];
@@ -164,8 +163,6 @@ void mrp_log_msg(mrp_log_level_t level, const char *file,
        return;
     }
     
-    va_start(ap, format);
-
     if (log_fp == NULL)
        vsyslog(lvl, format, ap);
     else {
@@ -173,7 +170,22 @@ void mrp_log_msg(mrp_log_level_t level, const char *file,
        vfprintf(log_fp, format, ap); fputs("\n", log_fp);
        fflush(log_fp);
     }
+}
+
 
+void mrp_log_msg(mrp_log_level_t level, const char *file,
+                int line, const char *func, const char *format, ...)
+{
+    va_list     ap;
+
+    (void)file;
+    (void)line;
+
+    if (!(log_mask & (1 << level)))
+       return;
+
+    va_start(ap, format);
+    mrp_log_msgv(level, file, line, func, format, ap);
     va_end(ap);
 }
 
index 5315e2b..86ad6c5 100644 (file)
@@ -6,8 +6,10 @@
  */
 
 #include <syslog.h>
+#include <stdarg.h>
 
 #include <murphy/common/macros.h>
+#include <murphy/common/debug.h>
 
 MRP_CDECL_BEGIN
 
@@ -68,6 +70,7 @@ mrp_log_mask_t mrp_log_disable(mrp_log_mask_t mask);
 #define MRP_LOG_TO_SYSLOG     ((const char *)3)
 #define MRP_LOG_TO_FILE(path) ((const char *)(path))
 
+
 /** Parse a log target name to MRP_LOG_TO_*. */
 const char *mrp_log_parse_target(const char *target);
 
@@ -86,15 +89,16 @@ int mrp_log_set_target(const char *target);
 #define mrp_log_info(fmt, args...) \
     mrp_log_msg(MRP_LOG_INFO, __LOC__, fmt , ## args)
 
-/** Log an (unclassified) debugging message. */
-#define mrp_debug(fmt, args...) \
-    mrp_log_msg(MRP_LOG_DEBUG, __LOC__, fmt , ## args)
-
 /** Generic logging function. */
 void mrp_log_msg(mrp_log_level_t level,
                 const char *file, int line, const char *func,
                 const char *format, ...) MRP_PRINTF_LIKE(5, 6);
 
+/** Generic logging function for easy wrapping. */
+void mrp_log_msgv(mrp_log_level_t level, const char *file,
+                 int line, const char *func, const char *format, va_list ap);
+
+
 MRP_CDECL_END
 
 #endif /* __MURPHY_LOG_H__ */