revive qemu logging by implementing macros

This commit is contained in:
Christoph Hindermann 2024-07-17 07:57:23 +02:00
parent 7d8b06b704
commit 45a872108b

View File

@ -30,20 +30,207 @@
/* Logging functions: */
/* log only if a bit is set on the current loglevel mask:
* @mask: bit to check in the mask
* @fmt: printf-style format string
* @args: optional arguments for format string
*/
#define qemu_log_mask(MASK, FMT, ...)
/* To verbose logging, enable the next line. */
//#define LOGGING_ENABLED // to enable logging
/* log only if a bit is set on the current loglevel mask
* and we are in the address range we care about:
* @mask: bit to check in the mask
* @addr: address to check in dfilter
* @fmt: printf-style format string
* @args: optional arguments for format string
*/
#define qemu_log_mask_and_addr(MASK, ADDR, FMT, ...)
#ifdef LOGGING_ENABLED
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
/**
* Reads the @p env_name and tries to parse the value into an uint32_t.
* @param env_name The environment variable name to parse.
* @return The parsed value.
** 0 in case if the value can not be parsed (or is 0).
** ULONG_MAX if the value is bigger than an uint32_t.
*/
static inline uint32_t read_and_parse_env(const char* env_name)
{
uint32_t data = 0;
const char* env_data = getenv(env_name);
if (env_data != NULL) {
char buffer[11] = {0}; // 0xFFFF'FFFF\0
strncpy(buffer, env_data, sizeof(buffer) - 1);
data = (uint32_t)strtoul(buffer, NULL, 0);
}
return data;
}
/**
* Gets the log level by reading it once.
* @return The log level.
*/
static inline uint32_t get_log_level()
{
static uint64_t log_level = UINT64_MAX;
if (log_level == UINT64_MAX) {
log_level = read_and_parse_env("UNICORN_LOG_LEVEL");
}
return (uint32_t)log_level;
}
/**
* Gets the log detail level by reading it once.
* @return The detail log level.
*/
static inline uint32_t get_log_detail_level()
{
static uint64_t log_detail_level = UINT64_MAX;
if (log_detail_level == UINT64_MAX) {
log_detail_level = read_and_parse_env("UNICORN_LOG_DETAIL_LEVEL");
}
return (uint32_t)log_detail_level;
}
/**
* Checks if the @p log_level is active.
* @param log_level The log level to be checked.
* @return True if the log level is active.
*/
static inline bool is_log_level_active(uint32_t log_level)
{
const uint32_t active_log_level = get_log_level();
const bool is_active = (active_log_level & log_level) == log_level;
return is_active;
}
/**
* Checks if the logging is enabled.
* @return True if enabled, else false.
*/
static inline bool is_logging_enabled()
{
const bool log_level = get_log_level();
return log_level != 0;
}
/**
* Gets the filename of the caller on given @p detail_level.
* @param filename The filename to process on.
* @param detail_level The level of detail of the filename.
** 0: Returns an empty string.
** 1: Returns the full filename including it's path.
** 2: Returns just the filename (to shorten the log).
* @return always an valid null-terminated string. Do NOT free it.
*/
static inline const char* const get_detailed_filename(const char* filename,
int detail_level)
{
filename = (filename != NULL) ? filename : "";
const char* resulting_filename = filename;
#if (defined(WIN32) || defined(WIN64) || defined(_WIN32) || defined(_WIN64))
const char path_separator = '\\';
#else
const char path_separator = '/';
#endif
switch (detail_level) {
default:
case 0:
resulting_filename = "";
break;
case 1:
resulting_filename = filename;
break;
case 2:
resulting_filename = strrchr(filename, path_separator);
if (resulting_filename == NULL) {
resulting_filename = filename;
}
else {
++resulting_filename;
}
break;
}
return resulting_filename;
}
/**
* Prints the formatted log message with details if enabled.
* @param mask The log mask to log on.
* @param filename The filename of the caller.
* @param line The line number of the caller.
* @param fmt Printf-style format string.
* @param args optional arguments for the format string.
*/
static inline void print_log(uint32_t mask, const char* filename, uint32_t line, const char* fmt, ...)
{
if ((mask & get_log_level()) == 0) {
return;
}
const uint32_t log_detail_level = get_log_detail_level();
if (log_detail_level > 0) {
const char* detailed_filename = get_detailed_filename(filename, log_detail_level);
printf("[%s:%u] ", detailed_filename, line);
}
va_list argptr;
va_start(argptr, fmt);
vfprintf(stdout, fmt, argptr);
va_end(argptr);
}
/**
* Logs only if the right log level is set.
* @param mask The log mask to log on.
* @param fmt Printf-style format string.
* @param args optional arguments for the format string.
*/
#define LOG_MESSAGE(mask, fmt, ...) \
do { \
print_log(mask, __FILE__, __LINE__, fmt, ## __VA_ARGS__); \
} while (0)
#else
#define LOG_MESSAGE(mask, fmt, ...)
/**
* Dummy implementation which returns always false.
* @return Always false.
*/
static inline bool is_logging_enabled()
{
return false;
}
/**
* Dummy implementation which returns always false.
* @param level The log level to be checked.
* @return Always false.
*/
static inline bool is_log_level_active(uint32_t level)
{
(void)level;
return false;
}
#endif /* LOGGING_ENABLED */
/**
* Logs only if the right log level is set.
* @param mask The log mask to log on.
* @param fmt Printf-style format string.
* @param args Optional arguments for the format string.
*/
#define qemu_log_mask(mask, fmt, ...) \
LOG_MESSAGE(mask, fmt, ## __VA_ARGS__)
#endif /* QEMU_LOG_H */