diff --git a/CMakeLists.txt b/CMakeLists.txt index cd90442ea..9f57a5fbd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,6 +39,7 @@ add_library(iio context.c device.c events.c + library.c mask.c scan.c sort.c @@ -169,16 +170,26 @@ option(WITH_EXAMPLES "Build examples" OFF) option(WITH_UTILS "Build the Libiio utility programs" ON) if (NOT LOG_LEVEL) - set(LOG_LEVEL Info CACHE STRING "Log level" FORCE) + set(LOG_LEVEL Info CACHE STRING "Default log level" FORCE) set_property(CACHE LOG_LEVEL PROPERTY STRINGS NoLog Error Warning Info Debug) endif() +if (NOT MAX_LOG_LEVEL) + set(MAX_LOG_LEVEL Debug CACHE STRING "Maximum log level supported" FORCE) + set_property(CACHE MAX_LOG_LEVEL PROPERTY STRINGS NoLog Error Warning Info Debug) +endif() + set(LEVEL_NoLog 1) set(LEVEL_Error 2) set(LEVEL_Warning 3) set(LEVEL_Info 4) set(LEVEL_Debug 5) set(DEFAULT_LOG_LEVEL ${LEVEL_${LOG_LEVEL}}) +set(MAX_LOG_LEVEL_VALUE ${LEVEL_${MAX_LOG_LEVEL}}) + +if (DEFAULT_LOG_LEVEL GREATER MAX_LOG_LEVEL_VALUE) + message(SEND_ERROR "Default log level cannot be more than the maximum log level.") +endif() if (MSVC) target_compile_options(iio PRIVATE /Zi /W4 /wd4200 /wd4127 /wd4100) diff --git a/attr.c b/attr.c index 103517bd5..15614d455 100644 --- a/attr.c +++ b/attr.c @@ -233,8 +233,7 @@ int iio_device_add_attr(struct iio_device *dev, if (ret < 0) return ret; - dev_dbg(dev, "Added%s attr \'%s\' to device \'%s\'\n", - attr_type_string[type], name, dev->id); + dev_dbg(dev, "Added%s attr \'%s\'\n", attr_type_string[type], name); return 0; } @@ -249,8 +248,7 @@ int iio_channel_add_attr(struct iio_channel *chn, if (ret < 0) return ret; - chn_dbg(chn, "Added attr \'%s\' (\'%s\') to channel \'%s\'\n", - name, filename, chn->id); + chn_dbg(chn, "Added attr \'%s\' (\'%s\')\n", name, filename); return 0; } diff --git a/context.c b/context.c index bcf6cf96d..7326c5af3 100644 --- a/context.c +++ b/context.c @@ -44,6 +44,7 @@ static const struct iio_context_params default_params = { .err = NULL, /* stderr */ .log_level = (enum iio_log_level)DEFAULT_LOG_LEVEL, .stderr_level = LEVEL_WARNING, + .timestamp_level = LEVEL_DEBUG, }; const struct iio_context_params *get_default_params(void) @@ -459,6 +460,8 @@ struct iio_context * iio_create_context(const struct iio_context_params *params, params2.log_level = default_params.log_level; if (!params2.stderr_level) params2.stderr_level = default_params.stderr_level; + if (!params2.timestamp_level) + params2.timestamp_level = default_params.timestamp_level; if (!uri) { uri_dup = iio_getenv("IIOD_REMOTE"); diff --git a/iio-config.h.cmakein b/iio-config.h.cmakein index 6f2f84d3d..9b617105b 100644 --- a/iio-config.h.cmakein +++ b/iio-config.h.cmakein @@ -7,6 +7,7 @@ #define LOG_LEVEL @LOG_LEVEL@_L #define DEFAULT_LOG_LEVEL @DEFAULT_LOG_LEVEL@ +#define MAX_LOG_LEVEL @MAX_LOG_LEVEL_VALUE@ #define IIO_MODULES_DIR "@IIO_MODULES_DIR@" #define IIO_LIBRARY_SUFFIX "@CMAKE_SHARED_LIBRARY_SUFFIX@" diff --git a/iio-private.h b/iio-private.h index 3a5330f8b..5d6ec2951 100644 --- a/iio-private.h +++ b/iio-private.h @@ -237,6 +237,8 @@ extern const struct iio_backend iio_xml_backend; extern const struct iio_backend * const iio_backends[]; extern const unsigned int iio_backends_size; +extern uint64_t library_startup_time_us; + ssize_t iio_xml_print_and_sanitized_param(char *ptr, ssize_t len, const char *before, const char *param, @@ -255,5 +257,6 @@ static inline void iio_update_xml_indexes(ssize_t ret, char **ptr, ssize_t *len, bool iio_channel_is_hwmon(const char *id); int iio_block_io(struct iio_block *block); +void libiio_cleanup_xml_backend(void); #endif /* __IIO_PRIVATE_H__ */ diff --git a/iiod/debug.h b/iiod/debug.h index cfe2339c3..c5d877406 100644 --- a/iiod/debug.h +++ b/iiod/debug.h @@ -9,47 +9,23 @@ #ifndef __IIOD_DEBUG_H__ #define __IIOD_DEBUG_H__ -#include "../iio-config.h" +#include -#include +extern struct iio_context_params iiod_params; -#define NoLog_L 0 -#define Error_L 1 -#define Warning_L 2 -#define Info_L 3 -#define Debug_L 4 - -/* -------------------- */ - -/* Many of these debug printf include a Flawfinder: ignore, this is because, - * according to https://cwe.mitre.org/data/definitions/134.html which describes - * functions that accepts a format string as an argument, but the format - * string originates from an external source. All the IIO_DEBUG, IIO_INFO, - * IIO_WARNING, and IIO_ERRRO functions are called internally from the - * library, have fixed format strings and can not be modified externally. - */ #define IIO_DEBUG(...) \ - do { \ - if (LOG_LEVEL >= Debug_L) \ - fprintf(stdout, "DEBUG: " __VA_ARGS__); /* Flawfinder: ignore */ \ - } while (0) + prm_dbg(&iiod_params, __VA_ARGS__) #define IIO_INFO(...) \ - do { \ - if (LOG_LEVEL >= Info_L) \ - fprintf(stdout, __VA_ARGS__); /* Flawfinder: ignore */ \ - } while (0) + prm_info(&iiod_params, __VA_ARGS__) #define IIO_WARNING(...) \ - do { \ - if (LOG_LEVEL >= Warning_L) \ - fprintf(stderr, "WARNING: " __VA_ARGS__); /* Flawfinder: ignore */ \ - } while (0) + prm_warn(&iiod_params, __VA_ARGS__) #define IIO_ERROR(...) \ - do { \ - if (LOG_LEVEL >= Error_L) \ - fprintf(stderr, "ERROR: " __VA_ARGS__); /* Flawfinder: ignore */ \ - } while (0) + prm_err(&iiod_params, __VA_ARGS__) + +#define IIO_PERROR(err, ...) \ + prm_perror(&iiod_params, err, __VA_ARGS__) #endif /* __IIOD_DEBUG_H__ */ diff --git a/iiod/iiod.c b/iiod/iiod.c index 79fdf2002..5b905d5c5 100644 --- a/iiod/iiod.c +++ b/iiod/iiod.c @@ -61,6 +61,9 @@ bool server_demux; struct thread_pool *main_thread_pool; +struct iio_context_params iiod_params = { + .log_level = LEVEL_INFO, +}; static struct sockaddr_in sockaddr = { .sin_family = AF_INET, @@ -160,22 +163,14 @@ static int main_interactive(struct iio_context *ctx, bool verbose, bool use_aio, flags = fcntl(STDIN_FILENO, F_GETFL); if (flags >= 0) flags = fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK); - if (flags < 0) { - char err_str[1024]; - iio_strerror(errno, err_str, sizeof(err_str)); - IIO_ERROR("Could not get/set O_NONBLOCK on STDIN_FILENO" - " %s\n", err_str); - } + if (flags < 0) + IIO_PERROR(errno, "Could not get/set O_NONBLOCK on stdin"); flags = fcntl(STDOUT_FILENO, F_GETFL); if (flags >= 0) flags = fcntl(STDOUT_FILENO, F_SETFL, flags | O_NONBLOCK); - if (flags < 0) { - char err_str[1024]; - iio_strerror(errno, err_str, sizeof(err_str)); - IIO_ERROR("Could not get/set O_NONBLOCK on STDOUT_FILENO" - " %s\n", err_str); - } + if (flags < 0) + IIO_PERROR(errno, "Could not get/set O_NONBLOCK on stdout"); } interpreter(ctx, STDIN_FILENO, STDOUT_FILENO, verbose, @@ -193,12 +188,11 @@ static int main_server(struct iio_context *ctx, bool debug, keepalive_intvl = 10, keepalive_probes = 6; struct pollfd pfd[2]; - char err_str[1024]; bool ipv6; IIO_INFO("Starting IIO Daemon version %u.%u.%s\n", - LIBIIO_VERSION_MAJOR, LIBIIO_VERSION_MINOR, - LIBIIO_VERSION_GIT); + LIBIIO_VERSION_MAJOR, LIBIIO_VERSION_MINOR, + LIBIIO_VERSION_GIT); sockaddr.sin_port = htons(port); sockaddr.sin_addr.s_addr = htonl(INADDR_ANY); @@ -212,16 +206,13 @@ static int main_server(struct iio_context *ctx, bool debug, if (!ipv6) fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); if (fd < 0) { - iio_strerror(errno, err_str, sizeof(err_str)); - IIO_ERROR("Unable to create socket: %s\n", err_str); + IIO_PERROR(errno, "Unable to create socket"); return EXIT_FAILURE; } ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)); - if (ret < 0) { - iio_strerror(errno, err_str, sizeof(err_str)); - IIO_WARNING("setsockopt SO_REUSEADDR : %s\n", err_str); - } + if (ret < 0) + IIO_PERROR(errno, "Failed to set SO_REUSEADDR"); #ifdef HAVE_IPV6 if (ipv6) @@ -231,8 +222,7 @@ static int main_server(struct iio_context *ctx, bool debug, if (!ipv6) ret = bind(fd, (struct sockaddr *) &sockaddr, sizeof(sockaddr)); if (ret < 0) { - iio_strerror(errno, err_str, sizeof(err_str)); - IIO_ERROR("Bind failed: %s\n", err_str); + IIO_PERROR(errno, "Bind failed"); goto err_close_socket; } @@ -243,8 +233,7 @@ static int main_server(struct iio_context *ctx, bool debug, struct sockaddr_in sin; socklen_t len = sizeof(sin); if (getsockname(fd, (struct sockaddr *)&sin, &len) == -1) { - iio_strerror(errno, err_str, sizeof(err_str)); - IIO_ERROR("getsockname failed : %s\n", err_str); + IIO_PERROR(errno, "getsockname failed"); goto err_close_socket; } port = ntohs(sin.sin_port); @@ -255,8 +244,7 @@ static int main_server(struct iio_context *ctx, bool debug, IIO_INFO("IPv6 support enabled\n"); if (listen(fd, 16) < 0) { - iio_strerror(errno, err_str, sizeof(err_str)); - IIO_ERROR("Unable to mark as passive socket: %s\n", err_str); + IIO_PERROR(errno, "Unable to mark as passive socket"); goto err_close_socket; } @@ -291,9 +279,8 @@ static int main_server(struct iio_context *ctx, bool debug, if (new == -1) { if (errno == EAGAIN || errno == EINTR) continue; - iio_strerror(errno, err_str, sizeof(err_str)); - IIO_ERROR("Failed to create connection socket: %s\n", - err_str); + + IIO_PERROR(errno, "Failed to create connection socket"); continue; } @@ -308,33 +295,27 @@ static int main_server(struct iio_context *ctx, bool debug, * and disconnect the client if no reply was received for one * minute. */ ret = setsockopt(new, SOL_SOCKET, SO_KEEPALIVE, &yes, sizeof(yes)); - if (ret < 0) { - iio_strerror(errno, err_str, sizeof(err_str)); - IIO_WARNING("setsockopt SO_KEEPALIVE : %s", err_str); - } + if (ret < 0) + IIO_PERROR(errno, "setsockopt SO_KEEPALIVE"); + ret = setsockopt(new, IPPROTO_TCP, TCP_KEEPCNT, &keepalive_probes, sizeof(keepalive_probes)); - if (ret < 0) { - iio_strerror(errno, err_str, sizeof(err_str)); - IIO_WARNING("setsockopt TCP_KEEPCNT : %s", err_str); - } + if (ret < 0) + IIO_PERROR(errno, "setsockopt TCP_KEEPCNT"); + ret = setsockopt(new, IPPROTO_TCP, TCP_KEEPIDLE, &keepalive_time, sizeof(keepalive_time)); - if (ret < 0) { - iio_strerror(errno, err_str, sizeof(err_str)); - IIO_WARNING("setsockopt TCP_KEEPIDLE : %s", err_str); - } + if (ret < 0) + IIO_PERROR(errno, "setsockopt TCP_KEEPIDLE"); + ret = setsockopt(new, IPPROTO_TCP, TCP_KEEPINTVL, &keepalive_intvl, sizeof(keepalive_intvl)); - if (ret < 0) { - iio_strerror(errno, err_str, sizeof(err_str)); - IIO_WARNING("setsockopt TCP_KEEPINTVL : %s", err_str); - } + if (ret < 0) + IIO_PERROR(errno, "setsockopt TCP_KEEPINTVL"); + ret = setsockopt(new, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)); - if (ret < 0) { - iio_strerror(errno, err_str, sizeof(err_str)); - IIO_WARNING("setsockopt TCP_NODELAY : %s", err_str); - } + if (ret < 0) + IIO_PERROR(errno, "setsockopt TCP_NODELAY"); cdata->fd = new; cdata->ctx = ctx; @@ -342,7 +323,7 @@ static int main_server(struct iio_context *ctx, bool debug, cdata->xml_zstd = xml_zstd; cdata->xml_zstd_len = xml_zstd_len; - if (LOG_LEVEL >= Info_L) { + if (iiod_params.log_level >= LEVEL_INFO) { struct sockaddr_in *caddr4 = (struct sockaddr_in *)&caddr; char ipaddr[IP_ADDR_LEN]; int zone = 0; @@ -359,8 +340,7 @@ static int main_server(struct iio_context *ctx, bool debug, } if (!inet_ntop(caddr4->sin_family, addr, ipaddr, sizeof(ipaddr) - 1)) { - iio_strerror(errno, err_str, sizeof(err_str)); - IIO_ERROR("Error during inet_ntop: %s\n", err_str); + IIO_PERROR(errno, "Error during inet_ntop"); } else { ipaddr[IP_ADDR_LEN - 1] = '\0'; @@ -382,9 +362,7 @@ static int main_server(struct iio_context *ctx, bool debug, ret = thread_pool_add_thread(main_thread_pool, client_thd, cdata, "net_client_thd"); if (ret) { - iio_strerror(ret, err_str, sizeof(err_str)); - IIO_ERROR("Failed to create new client thread: %s\n", - err_str); + IIO_PERROR(ret, "Failed to create new client thread"); close(new); free(cdata); } @@ -556,16 +534,14 @@ int main(int argc, char **argv) main_thread_pool = thread_pool_new(); if (!main_thread_pool) { - iio_strerror(errno, err_str, sizeof(err_str)); - IIO_ERROR("Unable to create thread pool: %s\n", err_str); + IIO_PERROR(errno, "Unable to create thread pool"); return EXIT_FAILURE; } if (WITH_IIOD_USBD && ffs_mountpoint) { ret = init_usb_daemon(ffs_mountpoint, nb_pipes); if (ret < 0) { - iio_strerror(errno, err_str, sizeof(err_str)); - IIO_ERROR("Unable to init USB: %s\n", err_str); + IIO_PERROR(ret, "Unable to init USB"); thread_pool_destroy(main_thread_pool); return EXIT_FAILURE; @@ -602,15 +578,13 @@ static int start_iiod(const char *uri, const char *ffs_mountpoint, int ep0_fd) { struct iio_context *ctx; - char err_str[1024]; void *xml_zstd; size_t xml_zstd_len = 0; int ret; - ctx = iio_create_context(NULL, uri); + ctx = iio_create_context(&iiod_params, uri); if (iio_err(ctx)) { - iio_strerror(-iio_err(ctx), err_str, sizeof(err_str)); - IIO_ERROR("Unable to create local context: %s\n", err_str); + IIO_PERROR(iio_err(ctx), "Unable to create local context"); return EXIT_FAILURE; } @@ -629,8 +603,7 @@ static int start_iiod(const char *uri, const char *ffs_mountpoint, debug, true, (unsigned int) nb_pipes, ep0_fd, main_thread_pool, xml_zstd, xml_zstd_len); if (ret) { - iio_strerror(-ret, err_str, sizeof(err_str)); - IIO_ERROR("Unable to start USB daemon: %s\n", err_str); + IIO_PERROR(ret, "Unable to start USB daemon"); ret = EXIT_FAILURE; goto out_free_xml_data; } @@ -641,8 +614,7 @@ static int start_iiod(const char *uri, const char *ffs_mountpoint, debug, main_thread_pool, xml_zstd, xml_zstd_len); if (ret) { - iio_strerror(-ret, err_str, sizeof(err_str)); - IIO_ERROR("Unable to start serial daemon: %s\n", err_str); + IIO_PERROR(ret, "Unable to start serial daemon"); ret = EXIT_FAILURE; goto out_thread_pool_stop; } diff --git a/iiod/responder.c b/iiod/responder.c index 276c23998..d175237d9 100644 --- a/iiod/responder.c +++ b/iiod/responder.c @@ -725,14 +725,14 @@ static void handle_transfer_block(struct parser_pdata *pdata, buf = get_iio_buffer(pdata, cmd, &entry); ret = iio_err(buf); if (ret) { - IIO_ERROR("handle_transfer_block: Could not find IIO buffer\n"); + IIO_PERROR(ret, "handle_transfer_block: Could not find IIO buffer"); return; } block = get_iio_block(pdata, entry, cmd, &block_entry); ret = iio_err(block); if (ret) { - IIO_ERROR("handle_transfer_block: Could not find IIO block\n"); + IIO_PERROR(ret, "handle_transfer_block: Could not find IIO block"); return; } diff --git a/iiod/serial.c b/iiod/serial.c index 23770b403..d1b4c109c 100644 --- a/iiod/serial.c +++ b/iiod/serial.c @@ -77,8 +77,9 @@ static int serial_configure(int fd, unsigned int uart_bps, err = tcgetattr(fd, &tty_attrs); if (err == -1) { - IIO_ERROR("tcgetattr failed\n"); - return -errno; + err = -errno; + IIO_PERROR(err, "tcgetattr failed"); + return err; } tty_attrs.c_lflag &= ~(ISIG | ICANON | ECHO | IEXTEN); @@ -221,8 +222,9 @@ static int serial_configure(int fd, unsigned int uart_bps, err = tcsetattr(fd, TCSANOW, &tty_attrs); if (err == -1) { - IIO_ERROR("Unable to apply serial settings\n"); - return -errno; + err = -errno; + IIO_PERROR(err, "Unable to apply serial settings"); + return err; } return 0; diff --git a/iiod/usbd.c b/iiod/usbd.c index 0479ed386..75362e692 100644 --- a/iiod/usbd.c +++ b/iiod/usbd.c @@ -204,7 +204,7 @@ static void usbd_main(struct thread_pool *pool, void *d) ret = handle_event(pdata, &event); if (ret) { - IIO_ERROR("Unable to handle event: %i\n", ret); + IIO_PERROR(ret, "Unable to handle event"); break; } diff --git a/include/iio/iio-debug.h b/include/iio/iio-debug.h index 3f99d1868..c1218fe8d 100644 --- a/include/iio/iio-debug.h +++ b/include/iio/iio-debug.h @@ -2,7 +2,7 @@ /* * libiio - Library for interfacing industrial I/O (IIO) devices * - * Copyright (C) 2021 Analog Devices, Inc. + * Copyright (C) 2021-2023 Analog Devices, Inc. * Author: Paul Cercueil */ @@ -21,9 +21,17 @@ # define __iio_printf #endif +#ifdef LIBIIO_EXPORTS +#include "iio-config.h" +#define LIBIIO_MAX_LOG_LEVEL MAX_LOG_LEVEL +#else +#define LIBIIO_MAX_LOG_LEVEL LEVEL_DEBUG +#endif + #define __FIRST(a, ...) a #define ___OTHERS(a, ...) a, __VA_ARGS__ #define __OTHERS(a, b, ...) ___OTHERS(a, __VA_ARGS__) +#define __SKIPFIRST(a, ...) __VA_ARGS__ /** @brief Print a message with the given priority @@ -40,36 +48,87 @@ iio_prm_printf(const struct iio_context_params *params, #define __dev_ctx_or_null(dev) ((dev) ? iio_device_get_context(dev) : NULL) #define __chn_dev_or_null(chn) ((chn) ? iio_channel_get_device(chn) : NULL) -#define prm_err(prm, ...) iio_prm_printf((prm), LEVEL_ERROR, "ERROR: " __VA_ARGS__) -#define prm_warn(prm, ...) iio_prm_printf((prm), LEVEL_WARNING, "WARNING: " __VA_ARGS__) -#define prm_info(prm, ...) iio_prm_printf((prm), LEVEL_INFO, __VA_ARGS__) -#define prm_dbg(prm, ...) iio_prm_printf((prm), LEVEL_DEBUG, "DEBUG: " __VA_ARGS__) +#define __dev_id_or_null(dev) ((dev) ? iio_device_get_id(dev) : NULL) +#define __chn_id_or_null(chn) ((chn) ? iio_channel_get_id(chn) : NULL) + +#define prm_err(prm, ...) \ + do { \ + if (LIBIIO_MAX_LOG_LEVEL >= LEVEL_ERROR) \ + iio_prm_printf((prm), LEVEL_ERROR, "ERROR: " __VA_ARGS__); \ + } while (0) +#define prm_warn(prm, ...) \ + do { \ + if (LIBIIO_MAX_LOG_LEVEL >= LEVEL_WARNING) \ + iio_prm_printf((prm), LEVEL_WARNING, "WARNING: " __VA_ARGS__); \ + } while (0) +#define prm_info(prm, ...) \ + do { \ + if (LIBIIO_MAX_LOG_LEVEL >= LEVEL_INFO) \ + iio_prm_printf((prm), LEVEL_INFO, __VA_ARGS__); \ + } while (0) +#define prm_dbg(prm, ...) \ + do { \ + if (LIBIIO_MAX_LOG_LEVEL >= LEVEL_DEBUG) \ + iio_prm_printf((prm), LEVEL_DEBUG, "DEBUG: "__VA_ARGS__); \ + } while (0) #define ctx_err(ctx, ...) prm_err(__ctx_params_or_null(ctx), __VA_ARGS__) #define ctx_warn(ctx, ...) prm_warn(__ctx_params_or_null(ctx), __VA_ARGS__) #define ctx_info(ctx, ...) prm_info(__ctx_params_or_null(ctx), __VA_ARGS__) #define ctx_dbg(ctx, ...) prm_dbg(__ctx_params_or_null(ctx), __VA_ARGS__) -#define dev_err(dev, ...) ctx_err(__dev_ctx_or_null(dev), __VA_ARGS__) -#define dev_warn(dev, ...) ctx_warn(__dev_ctx_or_null(dev), __VA_ARGS__) -#define dev_info(dev, ...) ctx_info(__dev_ctx_or_null(dev), __VA_ARGS__) -#define dev_dbg(dev, ...) ctx_dbg(__dev_ctx_or_null(dev), __VA_ARGS__) +#define dev_err(dev, ...) ctx_err(__dev_ctx_or_null(dev), \ + "%s: " __FIRST(__VA_ARGS__, 0) "%s", \ + __dev_id_or_null(dev), \ + __SKIPFIRST(__VA_ARGS__, "")) +#define dev_warn(dev, ...) ctx_warn(__dev_ctx_or_null(dev), \ + "%s: " __FIRST(__VA_ARGS__, 0) "%s", \ + __dev_id_or_null(dev), \ + __SKIPFIRST(__VA_ARGS__, "")) +#define dev_info(dev, ...) ctx_info(__dev_ctx_or_null(dev), \ + "%s: " __FIRST(__VA_ARGS__, 0) "%s", \ + __dev_id_or_null(dev), \ + __SKIPFIRST(__VA_ARGS__, "")) +#define dev_dbg(dev, ...) ctx_dbg(__dev_ctx_or_null(dev), \ + "%s: " __FIRST(__VA_ARGS__, 0) "%s", \ + __dev_id_or_null(dev), \ + __SKIPFIRST(__VA_ARGS__, "")) -#define chn_err(chn, ...) dev_err(__chn_dev_or_null(chn), __VA_ARGS__) -#define chn_warn(chn, ...) dev_warn(__chn_dev_or_null(chn), __VA_ARGS__) -#define chn_info(chn, ...) dev_info(__chn_dev_or_null(chn), __VA_ARGS__) -#define chn_dbg(chn, ...) dev_dbg(__chn_dev_or_null(chn), __VA_ARGS__) +#define chn_err(dev, ...) dev_err(__chn_dev_or_null(chn), \ + "%s: " __FIRST(__VA_ARGS__, 0) "%s", \ + __chn_id_or_null(chn), \ + __SKIPFIRST(__VA_ARGS__, "")) +#define chn_warn(dev, ...) dev_warn(__chn_dev_or_null(chn), \ + "%s: " __FIRST(__VA_ARGS__, 0) "%s", \ + __chn_id_or_null(chn), \ + __SKIPFIRST(__VA_ARGS__, "")) +#define chn_info(dev, ...) dev_info(__chn_dev_or_null(chn), \ + "%s: " __FIRST(__VA_ARGS__, 0) "%s", \ + __chn_id_or_null(chn), \ + __SKIPFIRST(__VA_ARGS__, "")) +#define chn_dbg(dev, ...) dev_dbg(__chn_dev_or_null(chn), \ + "%s: " __FIRST(__VA_ARGS__, 0) "%s", \ + __chn_id_or_null(chn), \ + __SKIPFIRST(__VA_ARGS__, "")) #define prm_perror(params, err, ...) do { \ - char _buf[1024]; \ - int _err = -(err); \ - iio_strerror(_err, _buf, sizeof(_buf)); \ - prm_err(params, __FIRST(__VA_ARGS__, 0) \ - __OTHERS(": %s\n",__VA_ARGS__, _buf)); \ + if (LIBIIO_MAX_LOG_LEVEL >= LEVEL_ERROR) { \ + char _buf[1024]; \ + int _err = -(err); \ + iio_strerror(_err, _buf, sizeof(_buf)); \ + prm_err(params, __FIRST(__VA_ARGS__, 0) \ + __OTHERS(": %s\n",__VA_ARGS__, _buf)); \ + } \ } while (0) #define ctx_perror(ctx, err, ...) prm_perror(__ctx_params_or_null(ctx), err, __VA_ARGS__) -#define dev_perror(dev, err, ...) ctx_perror(__dev_ctx_or_null(dev), err, __VA_ARGS__) -#define chn_perror(dev, err, ...) dev_perror(__chn_dev_or_null(chn), err, __VA_ARGS__) +#define dev_perror(dev, err, ...) ctx_perror(__dev_ctx_or_null(dev), err, \ + "%s: " __FIRST(__VA_ARGS__, 0) "%s", \ + __dev_id_or_null(dev), \ + __SKIPFIRST(__VA_ARGS__, "")) +#define chn_perror(dev, err, ...) dev_perror(__chn_dev_or_null(chn), err, \ + "%s: " __FIRST(__VA_ARGS__, 0) "%s", \ + __chn_id_or_null(chn), \ + __SKIPFIRST(__VA_ARGS__, "")) #undef __api diff --git a/include/iio/iio.h b/include/iio/iio.h index bd2b355a1..4f7d4f4ee 100644 --- a/include/iio/iio.h +++ b/include/iio/iio.h @@ -126,9 +126,6 @@ enum iio_log_level { * created. */ struct iio_context_params { - /** @brief Timeout for I/O operations. If zero, the default timeout is used. */ - unsigned int timeout_ms; - /** @brief Handle to the standard output. If NULL, defaults to stdout. */ FILE *out; @@ -144,6 +141,19 @@ struct iio_context_params { * sent to the standard output. * If zero, defaults to LEVEL_WARNING. */ enum iio_log_level stderr_level; + + /** @brief Under this log level (excluded), messages are sent without + * timestamp; above this log level (included), messages are sent with + * a timestamp. If set to LEVEL_NOLOG, messages at all log levels are + * sent without a timestamp. + * If zero, defaults to LEVEL_DEBUG. */ + enum iio_log_level timestamp_level; + + /** @brief Timeout for I/O operations. If zero, the default timeout is used. */ + unsigned int timeout_ms; + + /** @brief Reserved for future fields. */ + char __rsrv[32]; }; /* @@ -463,7 +473,7 @@ iio_scan_get_uri(const struct iio_scan *ctx, size_t idx); /** @brief Get a string description of an error code - * @param err The error code + * @param err The error code. Can be positive or negative. * @param dst A pointer to the memory area where the NULL-terminated string * corresponding to the error message will be stored * @param len The available length of the memory area, in bytes */ diff --git a/library.c b/library.c new file mode 100644 index 000000000..7fc4a578d --- /dev/null +++ b/library.c @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/* + * libiio - Library for interfacing industrial I/O (IIO) devices + * + * Copyright (C) 2023 Analog Devices, Inc. + * Author: Paul Cercueil + */ + +#include "iio-private.h" +#include + +uint64_t library_startup_time_us; + +static void libiio_init(void) +{ + library_startup_time_us = iio_read_counter_us(); +} + +static void libiio_exit(void) +{ + if (WITH_XML_BACKEND) + libiio_cleanup_xml_backend(); +} + +#if defined(_MSC_BUILD) +#pragma section(".CRT$XCU", read) +#define __CONSTRUCTOR(f, p) \ + static void f(void); \ + __declspec(allocate(".CRT$XCU")) void (*f##_)(void) = f; \ + __pragma(comment(linker,"/include:" p #f "_")) \ + static void f(void) +#ifdef _WIN64 +#define _CONSTRUCTOR(f) __CONSTRUCTOR(f, "") +#else +#define _CONSTRUCTOR(f) __CONSTRUCTOR(f, "_") +#endif +#elif defined(__GNUC__) +#define _CONSTRUCTOR(f) static void __attribute__((constructor)) f(void) +#else +#define _CONSTRUCTOR(f) static void f(void) +#endif + +_CONSTRUCTOR(initialize) +{ + libiio_init(); + + /* + * When the library loads, register our destructor. + * Do it here and not in the context creation function, + * as it could otherwise end up registering the destructor + * many times. + */ + atexit(libiio_exit); +} +#undef _CONSTRUCTOR diff --git a/scan.c b/scan.c index 044693cb2..ed4b888a8 100644 --- a/scan.c +++ b/scan.c @@ -40,6 +40,8 @@ struct iio_scan * iio_scan(const struct iio_context_params *params, params2.log_level = default_params->log_level; if (!params2.stderr_level) params2.stderr_level = default_params->stderr_level; + if (!params2.timestamp_level) + params2.timestamp_level = default_params->timestamp_level; ctx = calloc(1, sizeof(*ctx)); if (!ctx) diff --git a/utilities.c b/utilities.c index 0a07cd395..3ae7b76a0 100644 --- a/utilities.c +++ b/utilities.c @@ -192,13 +192,17 @@ int write_double(char *buf, size_t len, double val) void iio_strerror(int err, char *buf, size_t len) { + /* strerror only works with positive error codes. + * Support negative error codes by just negating the value. */ + int ret = err < 0 ? -err : err; + #if defined(_WIN32) - int ret = strerror_s(buf, len, err); + ret = strerror_s(buf, len, ret); #elif defined(HAS_STRERROR_R) - int ret = strerror_r(err, buf, len); + ret = strerror_r(ret, buf, len); #else /* no strerror_s, no strerror_r... just use the default message */ - int ret = 0xf7de; + ret = 0xf7de; #endif if (ret != 0) iio_snprintf(buf, len, "Unknown error %i", err); @@ -381,6 +385,7 @@ void iio_prm_printf(const struct iio_context_params *params, const char *fmt, ...) { FILE *out = NULL; + uint64_t now; va_list ap; va_start(ap, fmt); @@ -394,8 +399,17 @@ void iio_prm_printf(const struct iio_context_params *params, out = msg_level <= LEVEL_WARNING ? stderr : stdout; } - if (out) + if (out) { + if (params + && params->timestamp_level > LEVEL_NOLOG + && params->timestamp_level <= msg_level) { + now = iio_read_counter_us() - library_startup_time_us; + fprintf(out, "(%u.%06us) ", (unsigned int)(now / 1000000), + (unsigned int)now % 1000000); + } + vfprintf(out, fmt, ap); + } va_end(ap); } diff --git a/utils/iio_info.c b/utils/iio_info.c index 68dfe32e3..c5348ce52 100644 --- a/utils/iio_info.c +++ b/utils/iio_info.c @@ -14,14 +14,24 @@ #include #include +/* For isatty() */ +#ifdef _WIN32 +#include +#else +#include +#endif + #include "iio_common.h" #define MY_NAME "iio_info" #ifdef _WIN32 #define snprintf sprintf_s +#define isatty _isatty #endif +static struct iio_context *ctx; + static const struct option options[] = { {0, 0, 0, 0}, }; @@ -48,22 +58,125 @@ static int dev_is_buffer_capable(const struct iio_device *dev) #define MY_OPTS "" +static bool colors; + +#define FMT_ERR "\e[1;31mERROR: %s\e[0m" +#define FMT_DEV "\e[1;32m%s\e[0m" +#define FMT_CHN "\e[0;33m%s\e[0m" +#define FMT_ATTR "\e[1;34m%s\e[0m" + +/* Keeps Codacy happy */ +#define print_fmt(fmt, ...) printf(fmt, __VA_ARGS__) /* Flawfinder: ignore */ + +static void print_attr(const struct iio_attr *attr, + unsigned int level, unsigned int idx) +{ + char buf[BUF_SIZE]; + const char *name, *fn, *value; + ssize_t ret = 0; + + buf[BUF_SIZE - 1] = '\0'; + + value = iio_attr_get_static_value(attr); + if (!value) { + ret = iio_attr_read_raw(attr, buf, sizeof(buf) - 1); + if (ret < 0) + iio_strerror(ret, buf, sizeof(buf)); + value = buf; + } + + printf("%.*sattr %2u: ", level, "\t\t\t\t", idx); + + name = iio_attr_get_name(attr); + fn = iio_attr_get_filename(attr); + + if (colors) + print_fmt(FMT_ATTR, name); + else + printf("%s", name); + + if (strcmp(name, fn)) + printf(" (%s)", fn); + + if (ret >= 0) + printf(" value: %s\n", value); + else if (colors) + print_fmt(" value: " FMT_ERR "\n", value); + else + printf(" value: ERROR: %s\n", value); +} + +static void print_channel(const struct iio_channel *chn) +{ + const struct iio_data_format *format; + const char *type_name, *name; + char sign, repeat[12]; + + if (iio_channel_is_output(chn)) + type_name = "output"; + else + type_name = "input"; + + name = iio_channel_get_name(chn); + if (colors) { + print_fmt("\t\t\t" FMT_CHN ": " FMT_CHN " (" FMT_CHN, + iio_channel_get_id(chn), + name ? name : "", type_name); + } else { + printf("\t\t\t%s: %s (%s", + iio_channel_get_id(chn), + name ? name : "", type_name); + } + + if (iio_channel_get_type(chn) == IIO_CHAN_TYPE_UNKNOWN) { + if (colors) + print_fmt(", " FMT_ERR, "iio_channel_get_type() = UNKNOWN"); + else + printf(", ERROR: iio_channel_get_type() = UNKNOWN"); + } + + if (iio_channel_is_scan_element(chn)) { + format = iio_channel_get_data_format(chn); + sign = format->is_signed ? 's' : 'u'; + + repeat[0] = '\0'; + + if (format->is_fully_defined) + sign += 'A' - 'a'; + + if (format->repeat > 1) + snprintf(repeat, sizeof(repeat), "X%u", + format->repeat); + + printf(", index: %lu, format: %ce:%c%u/%u%s>>%u)\n", + iio_channel_get_index(chn), + format->is_be ? 'b' : 'l', + sign, format->bits, + format->length, repeat, + format->shift); + } else { + printf(")\n"); + } +} + int main(int argc, char **argv) { char **argw, *buf; - struct iio_context *ctx; const struct iio_device *dev, *trig; const struct iio_channel *ch; - const struct iio_data_format *format; - char sign, repeat[12], err_str[1024]; const char *key, *value, *name, *label, *type_name; unsigned int i, j, k, nb_devices, nb_channels, nb_ctx_attrs, nb_attrs; struct iio_channels_mask *mask; const struct iio_attr *attr; struct iio_buffer *buffer; + struct iio_event_stream *stream; struct option *opts; int c, ret = EXIT_FAILURE; +#ifndef _MSC_BUILD + colors = isatty(STDOUT_FILENO) == 1; +#endif + argw = dup_argv(MY_NAME, argc, argv); ctx = handle_common_opts(MY_NAME, argc, argw, MY_OPTS, @@ -122,30 +235,39 @@ int main(int argc, char **argv) for (i = 0; i < nb_ctx_attrs; i++) { attr = iio_context_get_attr(ctx, i); - key = iio_attr_get_name(attr); - value = iio_attr_get_static_value(attr); - - printf("\t%s: %s\n", key, value); + print_attr(attr, 1, i); } nb_devices = iio_context_get_devices_count(ctx); printf("IIO context has %u devices:\n", nb_devices); - buf = xmalloc(BUF_SIZE, MY_NAME); for (i = 0; i < nb_devices; i++) { dev = iio_context_get_device(ctx, i); name = iio_device_get_name(dev); label = iio_device_get_label(dev); - - printf("\t%s:", iio_device_get_id(dev)); - if (name) - printf(" %s", name); + stream = iio_device_create_event_stream(dev); + + if (colors) + print_fmt("\t" FMT_DEV ":", iio_device_get_id(dev)); + else + printf("\t%s:", iio_device_get_id(dev)); + if (name) { + if (colors) + print_fmt(" " FMT_DEV, name); + else + printf(" %s", name); + } if (label) printf(" (label: %s)", label); if (dev_is_buffer_capable(dev)) printf(" (buffer capable)"); + if (!iio_err(stream)) + printf(" (events supported)"); printf("\n"); + if (!iio_err(stream)) + iio_event_stream_destroy(stream); + nb_channels = iio_device_get_channels_count(dev); printf("\t\t%u channels found:\n", nb_channels); @@ -157,41 +279,7 @@ int main(int argc, char **argv) if (mask) iio_channel_enable(ch, mask); - if (iio_channel_is_output(ch)) - type_name = "output"; - else - type_name = "input"; - - name = iio_channel_get_name(ch); - printf("\t\t\t%s: %s (%s", - iio_channel_get_id(ch), - name ? name : "", type_name); - - if (iio_channel_get_type(ch) == IIO_CHAN_TYPE_UNKNOWN) - printf(", WARN:iio_channel_get_type()=UNKNOWN"); - - if (iio_channel_is_scan_element(ch)) { - format = iio_channel_get_data_format(ch); - sign = format->is_signed ? 's' : 'u'; - - repeat[0] = '\0'; - - if (format->is_fully_defined) - sign += 'A' - 'a'; - - if (format->repeat > 1) - snprintf(repeat, sizeof(repeat), "X%u", - format->repeat); - - printf(", index: %lu, format: %ce:%c%u/%u%s>>%u)\n", - iio_channel_get_index(ch), - format->is_be ? 'b' : 'l', - sign, format->bits, - format->length, repeat, - format->shift); - } else { - printf(")\n"); - } + print_channel(ch); nb_attrs = iio_channel_get_attrs_count(ch); if (!nb_attrs) @@ -202,15 +290,7 @@ int main(int argc, char **argv) for (k = 0; k < nb_attrs; k++) { attr = iio_channel_get_attr(ch, k); - ret = (int) iio_attr_read_raw(attr, buf, BUF_SIZE); - - printf("\t\t\t\tattr %2u: %s ", k, - iio_attr_get_name(attr)); - - if (ret > 0) - printf("value: %s\n", buf); - else - ctx_perror(ctx, ret, ""); + print_attr(attr, 4, k); } } @@ -220,14 +300,7 @@ int main(int argc, char **argv) nb_attrs); for (j = 0; j < nb_attrs; j++) { attr = iio_device_get_attr(dev, j); - ret = (int) iio_attr_read_raw(attr, buf, BUF_SIZE); - - printf("\t\t\t\tattr %2u: %s ", - j, iio_attr_get_name(attr)); - if (ret > 0) - printf("value: %s\n", buf); - else - ctx_perror(ctx, ret, ""); + print_attr(attr, 3, j); } } @@ -239,14 +312,7 @@ int main(int argc, char **argv) printf("\t\t%u buffer attributes found:\n", nb_attrs); for (j = 0; j < nb_attrs; j++) { attr = iio_buffer_get_attr(buffer, j); - ret = (int) iio_attr_read_raw(attr, buf, BUF_SIZE); - - printf("\t\t\tattr %2u: %s ", j, - iio_attr_get_name(attr)); - if (ret > 0) - printf("Value: %s\n", buf); - else - ctx_perror(ctx, ret, ""); + print_attr(attr, 3, j); } iio_buffer_destroy(buffer); @@ -257,14 +323,7 @@ int main(int argc, char **argv) printf("\t\t%u debug attributes found:\n", nb_attrs); for (j = 0; j < nb_attrs; j++) { attr = iio_device_get_debug_attr(dev, j); - - ret = (int) iio_attr_read_raw(attr, buf, BUF_SIZE); - printf("\t\t\t\tdebug attr %2u: %s ", - j, iio_attr_get_name(attr)); - if (ret > 0) - printf("value: %s\n", buf); - else - ctx_perror(ctx, ret, ""); + print_attr(attr, 3, j); } } @@ -288,7 +347,6 @@ int main(int argc, char **argv) } free_argw(argc, argw); - free(buf); iio_context_destroy(ctx); return EXIT_SUCCESS; } diff --git a/xml.c b/xml.c index 1e25290e0..89b0413f5 100644 --- a/xml.c +++ b/xml.c @@ -458,7 +458,7 @@ xml_create_context(const struct iio_context_params *params, const char *arg) return ctx; } -static void cleanup_libxml2_stuff(void) +void libiio_cleanup_xml_backend(void) { /* * This function will be called only when the libiio library is @@ -470,33 +470,3 @@ static void cleanup_libxml2_stuff(void) xmlCleanupParser(); xmlMemoryDump(); } - -#if defined(_MSC_BUILD) -#pragma section(".CRT$XCU", read) -#define __CONSTRUCTOR(f, p) \ - static void f(void); \ - __declspec(allocate(".CRT$XCU")) void (*f##_)(void) = f; \ - __pragma(comment(linker,"/include:" p #f "_")) \ - static void f(void) -#ifdef _WIN64 -#define _CONSTRUCTOR(f) __CONSTRUCTOR(f, "") -#else -#define _CONSTRUCTOR(f) __CONSTRUCTOR(f, "_") -#endif -#elif defined(__GNUC__) -#define _CONSTRUCTOR(f) static void __attribute__((constructor)) f(void) -#else -#define _CONSTRUCTOR(f) static void f(void) -#endif - -_CONSTRUCTOR(initialize) -{ - /* - * When the library loads, register our destructor. - * Do it here and not in the context creation function, - * as it could otherwise end up registering the destructor - * many times. - */ - atexit(cleanup_libxml2_stuff); -} -#undef _CONSTRUCTOR