Log: Add a new logging infrastructure
Replace the existing 4 logging facilities by a signle one.
Support for log levels (compile and run time) and log modules.
See doc/logging.md for a writeup.
Signed-off-by: Andreas Fuchs <andreas.fuchs@sit.fraunhofer.de>
diff --git a/bootstrap b/bootstrap
index ded9463..23c8652 100755
--- a/bootstrap
+++ b/bootstrap
@@ -22,10 +22,6 @@
src_listvar "sysapi/sysapi_util" "*.c" "SYSAPIUTIL_C"
printf "SYSAPI_SRC = \$(SYSAPI_H) \$(SYSAPI_C) \$(SYSAPIUTIL_C)\n"
- src_listvar "common" "*.c" "COMMON_C"
- src_listvar "common" "*.h" "COMMON_H"
- printf "COMMON_SRC = \$(COMMON_C) \$(COMMON_H)\n"
-
src_listvar "marshal" "*.c" "MARSHAL_C"
src_listvar "marshal" "*.h" "MARSHAL_H"
printf "MARSHAL_SRC = \$(MARSHAL_C) \$(MARSHAL_H)"
diff --git a/configure.ac b/configure.ac
index 79ca595..3d12e27 100644
--- a/configure.ac
+++ b/configure.ac
@@ -54,6 +54,13 @@
AX_ADD_PREPROC_FLAG([-D_DEFAULT_SOURCE])
+AC_ARG_WITH([loglevel],
+ [AS_HELP_STRING([--with-loglevel={none,error,warning,info,debug,trace}],
+ [sets the maximum log level (default is trace)])],
+ [],
+ [with_loglevel=trace])
+AC_DEFINE_UNQUOTED([LOGLEVEL], [$with_loglevel])
+
AC_ARG_ENABLE([debug],
[AS_HELP_STRING([--enable-debug],
[build with debug info (default is no)])],
diff --git a/doc/logging.md b/doc/logging.md
new file mode 100644
index 0000000..0652265
--- /dev/null
+++ b/doc/logging.md
@@ -0,0 +1,32 @@
+# Compile time log levels
+
+At compile time the maximum log level can be specified. This allows distros to
+disable the overhead code and reduce overall code size.
+
+# Runtime log level
+
+At runtime, the log level is determined by an environment variable. The default
+log level is WARNING. The level can be changed by setting the TSS2_LOG
+environment variable.
+
+Possible levels are: NONE, ERROR, WARNING, INFO, DEBUG, TRACE
+
+The level can be set for all module using the `all` module name or individually
+per module. The environment variable is evaluated left to right.
+
+Example: `TSS2_LOG=all+ERROR,marshal+TRACE,tcti+DEBUG
+
+# Implementation
+
+Each source code file specifies its corresponding module before including log.h.
+```
+#define LOGMODULE tcti
+#include "log.h"
+```
+Optionally, the default log-level for this module can be set:
+```
+#define LOGMODULE tcti
+#define LOGDEFAULT ERROR
+#include "log.h"
+```
+
diff --git a/log/log.c b/log/log.c
index 40fc890..2ce1910 100644
--- a/log/log.c
+++ b/log/log.c
@@ -1,43 +1,112 @@
-#include "log.h"
+#include <string.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
-/*
- * This array of structures defines the mapping from the log_level
- * enumeration to their string representation.
- */
-static const struct log_string {
- log_level level;
- const char *string;
-} log_strings [] = {
- {
- DEBUG,
- "DEBUG",
- },
- {
- INFO,
- "INFO",
- },
- {
- WARNING,
- "WARNING",
- },
- {
- ERROR,
- "ERROR",
- }
-};
-/*
- * This function converts a member of the log_level enumeration to its
- * string representation. It's a simple lookup in the log_strings array
- * defined above.
- */
-const char*
-level_to_str (log_level level)
+#define LOGMODULE log
+#include "log/log.h"
+
+#define likely(x) __builtin_expect(!!(x), 1)
+#define unlikely(x) __builtin_expect(!!(x), 0)
+
+log_level
+getLogLevel(const char *module, log_level logdefault);
+
+void
+doLogBlob(log_level loglevel, const char *module, log_level logdefault,
+ log_level *status,
+ const char *file, const char *func, int line,
+ const uint8_t *blob, size_t size, const char *fmt, ...)
{
- unsigned int i;
- for (i = 0; i < sizeof (log_strings) / sizeof (log_strings[0]); ++i) {
- if (level == log_strings[i].level) {
- return log_strings[i].string;
+ if (unlikely(*status == LOGLEVEL_UNDEFINED))
+ *status = getLogLevel(module, logdefault);
+
+ if (loglevel > *status)
+ return;
+
+ size_t width = 8;
+ size_t buffer_size = (size * 2) + (size / width) * 2 + 1;
+ char buffer[buffer_size];
+ for (size_t i = 0, off = 0; i < size && off < buffer_size; i++, off+=2) {
+ if (width < buffer_size && i % width == 0) {
+ *(&buffer[0] + off) = '\n';
+ off += 1;
+ *(&buffer[0] + off) = '\t';
+ off += 1;
+ }
+ sprintf(&buffer[0] + off, "%02x", blob[i]);
+ }
+
+ va_list vaargs;
+ va_start(vaargs, fmt);
+ /* TODO: Unfortunately, vsnprintf(NULL, 0, ...) do not behave the same as
+ snprintf(NULL, 0, ...). Until there is an alternative, messages on
+ logblob are restricted to 255 characters
+ int msg_len = vsnprintf(NULL, 0, fmt, vaargs); */
+ int msg_len = 255;
+ char msg[msg_len+1];
+ vsnprintf(msg, sizeof(msg), fmt, vaargs);
+ va_end(vaargs);
+
+ doLog(loglevel, module, logdefault, status, file, func, line,
+ "%s (size=%zi): %s", msg, size, buffer);
+}
+
+void
+doLog(log_level loglevel, const char *module, log_level logdefault,
+ log_level *status,
+ const char *file, const char *func, int line,
+ const char *msg, ...)
+{
+ if (unlikely(*status == LOGLEVEL_UNDEFINED))
+ *status = getLogLevel(module, logdefault);
+
+ if (loglevel > *status)
+ return;
+
+ int size = snprintf(NULL, 0, "%s:%s:%s:%d:%s() %s \n",
+ log_strings[loglevel], module, file, line, func, msg);
+ char fmt[size+1];
+ snprintf(fmt, sizeof(fmt), "%s:%s:%s:%d:%s() %s \n",
+ log_strings[loglevel], module, file, line, func, msg);
+
+ va_list vaargs;
+ va_start(vaargs, msg);
+ vfprintf (stderr, fmt,
+ //log_strings[loglevel], module, file, func, line,
+ vaargs);
+ va_end(vaargs);
+}
+
+log_level
+log_stringlevel(const char *n)
+{
+ for(log_level i = 0; i < sizeof(log_strings)/sizeof(log_strings[0]); i++) {
+ if (strncasecmp(log_strings[i], n, strlen(log_strings[i])) == 0) {
+ return i;
}
}
- return "unknown";
+ return LOGLEVEL_UNDEFINED;
}
+
+log_level
+getLogLevel(const char *module, log_level logdefault)
+{
+ log_level loglevel = logdefault;
+ char *envlevel = getenv("TSS2_LOG");
+ char *i = envlevel;
+ if (envlevel == NULL)
+ return loglevel;
+ while ((i = index(i, '+')) != NULL) {
+ if ((envlevel <= i - strlen("all") && strncasecmp(i - 3, "all", 3) == 0) ||
+ (envlevel <= i - strlen(module) &&
+ strncasecmp(i - strlen(module), module, strlen(module)) == 0)) {
+ log_level tmp = log_stringlevel(i+1);
+ if (tmp != LOGLEVEL_UNDEFINED)
+ loglevel = tmp;
+ }
+ i = i + 1;
+ }
+ return loglevel;
+}
+
diff --git a/log/log.h b/log/log.h
index c3742a0..d1f0465 100644
--- a/log/log.h
+++ b/log/log.h
@@ -1,14 +1,148 @@
#ifndef LOG_H
#define LOG_H
+#include <stdint.h>
+#include <stddef.h>
+
+#ifndef LOGMODULE
+#error "LOGMODULE must be set before including log/log.h"
+#endif
+
+#ifndef LOGDEFAULT
+#define LOGDEFAULT LOGLEVEL_WARNING
+#endif
+
typedef enum {
- DEBUG,
- INFO,
- WARNING,
- ERROR,
- OFF
+ LOGLEVEL_NONE = 0,
+ LOGLEVEL_ERROR = 1,
+ LOGLEVEL_WARNING = 2,
+ LOGLEVEL_INFO = 3,
+ LOGLEVEL_DEBUG = 4,
+ LOGLEVEL_TRACE = 5,
+ LOGLEVEL_UNDEFINED = 0xff
} log_level;
-const char* level_to_str (log_level level);
+static const char *log_strings[] __attribute__((unused)) = {
+ "none",
+ "ERROR",
+ "WARNING",
+ "info",
+ "debug",
+ "trace"
+};
+
+#define xstr(s) str(s)
+#define str(s) #s
+
+static log_level LOGMODULE_status __attribute__((unused)) = LOGLEVEL_UNDEFINED;
+
+#if LOGLEVEL == LOGLEVEL_ERROR || \
+ LOGLEVEL == LOGLEVEL_WARNING || \
+ LOGLEVEL == LOGLEVEL_INFO || \
+ LOGLEVEL == LOGLEVEL_DEBUG || \
+ LOGLEVEL == LOGLEVEL_TRACE
+#define LOG_ERROR(FORMAT, ...) doLog(LOGLEVEL_ERROR, \
+ xstr(LOGMODULE), LOGDEFAULT, \
+ &LOGMODULE_status, \
+ __FILE__, __func__, __LINE__, \
+ FORMAT, ## __VA_ARGS__)
+#define LOGBLOB_ERROR(BUFFER, SIZE, FORMAT, ...) doLogBlob(LOGLEVEL_ERROR, \
+ xstr(LOGMODULE), LOGDEFAULT, \
+ &LOGMODULE_status, \
+ __FILE__, __func__, __LINE__, \
+ BUFFER, SIZE, \
+ FORMAT, ## __VA_ARGS__)
+#else /* LOGLEVEL >= LOGLEVEL_ERROR */
+#define LOG_ERROR(FORMAT, ...) {}
+#define LOGBLOB_ERROR(FORMAT, ...) {}
+#endif /* LOGLEVEL >= LOGLEVEL_ERROR */
+
+#if LOGLEVEL == LOGLEVEL_WARNING || \
+ LOGLEVEL == LOGLEVEL_INFO || \
+ LOGLEVEL == LOGLEVEL_DEBUG || \
+ LOGLEVEL == LOGLEVEL_TRACE
+#define LOG_WARNING(FORMAT, ...) doLog(LOGLEVEL_WARNING, \
+ xstr(LOGMODULE), LOGDEFAULT, \
+ &LOGMODULE_status, \
+ __FILE__, __func__, __LINE__, \
+ FORMAT, ## __VA_ARGS__)
+#define LOGBLOB_WARNING(BUFFER, SIZE, FORMAT, ...) doLogBlob(LOGLEVEL_WARNING, \
+ xstr(LOGMODULE), LOGDEFAULT, \
+ &LOGMODULE_status, \
+ __FILE__, __func__, __LINE__, \
+ BUFFER, SIZE, \
+ FORMAT, ## __VA_ARGS__)
+#else /* LOGLEVEL >= LOGLEVEL_WARNING */
+#define LOG_WARNING(FORMAT, ...) {}
+#define LOGBLOB_WARNING(FORMAT, ...) {}
+#endif /* LOGLEVEL >= LOGLEVEL_WARNING */
+
+#if LOGLEVEL == LOGLEVEL_INFO || \
+ LOGLEVEL == LOGLEVEL_DEBUG || \
+ LOGLEVEL == LOGLEVEL_TRACE
+#define LOG_INFO(FORMAT, ...) doLog(LOGLEVEL_INFO, \
+ xstr(LOGMODULE), LOGDEFAULT, \
+ &LOGMODULE_status, \
+ __FILE__, __func__, __LINE__, \
+ FORMAT, ## __VA_ARGS__)
+#define LOGBLOB_INFO(BUFFER, SIZE, FORMAT, ...) doLogBlob(LOGLEVEL_INFO, \
+ xstr(LOGMODULE), LOGDEFAULT, \
+ &LOGMODULE_status, \
+ __FILE__, __func__, __LINE__, \
+ BUFFER, SIZE, \
+ FORMAT, ## __VA_ARGS__)
+#else /* LOGLEVEL >= LOGLEVEL_INFO */
+#define LOG_INFO(FORMAT, ...) {}
+#define LOGBLOB_INFO(FORMAT, ...) {}
+#endif /* LOGLEVEL >= LOGLEVEL_INFO */
+
+#if LOGLEVEL == LOGLEVEL_DEBUG || \
+ LOGLEVEL == LOGLEVEL_TRACE
+#define LOG_DEBUG(FORMAT, ...) doLog(LOGLEVEL_DEBUG, \
+ xstr(LOGMODULE), LOGDEFAULT, \
+ &LOGMODULE_status, \
+ __FILE__, __func__, __LINE__, \
+ FORMAT, ## __VA_ARGS__)
+#define LOGBLOB_DEBUG(BUFFER, SIZE, FORMAT, ...) doLogBlob(LOGLEVEL_DEBUG, \
+ xstr(LOGMODULE), LOGDEFAULT, \
+ &LOGMODULE_status, \
+ __FILE__, __func__, __LINE__, \
+ BUFFER, SIZE, \
+ FORMAT, ## __VA_ARGS__)
+#else /* LOGLEVEL >= LOGLEVEL_DEBUG */
+#define LOG_DEBUG(FORMAT, ...) {}
+#define LOGBLOB_DEBUG(FORMAT, ...) {}
+#endif /* LOGLEVEL >= LOGLEVEL_DEBUG */
+
+#if LOGLEVEL == LOGLEVEL_TRACE
+#define LOG_TRACE(FORMAT, ...) doLog(LOGLEVEL_TRACE, \
+ xstr(LOGMODULE), LOGDEFAULT, \
+ &LOGMODULE_status, \
+ __FILE__, __func__, __LINE__, \
+ FORMAT, ## __VA_ARGS__)
+#define LOGBLOB_TRACE(BUFFER, SIZE, FORMAT, ...) doLogBlob(LOGLEVEL_TRACE, \
+ xstr(LOGMODULE), LOGDEFAULT, \
+ &LOGMODULE_status, \
+ __FILE__, __func__, __LINE__, \
+ BUFFER, SIZE, \
+ FORMAT, ## __VA_ARGS__)
+#else /* LOGLEVEL >= LOGLEVEL_TRACE */
+#define LOG_TRACE(FORMAT, ...) {}
+#define LOGBLOB_TRACE(FORMAT, ...) {}
+#endif /* LOGLEVEL >= LOGLEVEL_TRACE */
+
+void
+doLog(log_level loglevel, const char *module, log_level logdefault,
+ log_level *status,
+ const char *file, const char *func, int line,
+ const char *msg, ...)
+ __attribute__((unused, format (printf, 8, 9)));
+
+void
+doLogBlob(log_level loglevel, const char *module, log_level logdefault,
+ log_level *status,
+ const char *file, const char *func, int line,
+ const uint8_t *buffer, size_t size, const char *msg, ...)
+ __attribute__((unused, format (printf, 10, 11)));
#endif /* LOG_H */
diff --git a/marshal/log.h b/marshal/log.h
index 40d3b5d..747678c 100644
--- a/marshal/log.h
+++ b/marshal/log.h
@@ -1,5 +1,6 @@
#ifndef TSS2T_LOG_H
+#define LOGMODULE marshal
#include "log/log.h"
#include <stdio.h>
@@ -25,7 +26,7 @@
if (level >= MARSHAL_LOG_LEVEL) do { \
fprintf (stderr, \
"%s:marshal:%s:%d " fmt "\n", \
- level_to_str (level), \
+ log_strings[level], \
__FILE__, \
__LINE__, \
##__VA_ARGS__); \