/* * Copyright (c) 2018 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ #ifndef ZEPHYR_INCLUDE_LOGGING_LOG_MSG_H_ #define ZEPHYR_INCLUDE_LOGGING_LOG_MSG_H_ #include #include #include #include #ifdef __cplusplus extern "C" { #endif /** * @brief Log message API * @defgroup log_msg Log message API * @ingroup logger * @{ */ /** @brief Maximum number of arguments in the standard log entry. */ #define LOG_MAX_NARGS 6 /** @brief Number of arguments in the log entry which fits in one chunk.*/ #define LOG_MSG_NARGS_SINGLE_CHUNK 3 /** @brief Number of arguments in the head of extended standard log message..*/ #define LOG_MSG_NARGS_HEAD_CHUNK (LOG_MSG_NARGS_SINGLE_CHUNK - 1) /** @brief Maximal amount of bytes in the hexdump entry which fits in one chunk. */ #define LOG_MSG_HEXDUMP_BYTES_SINGLE_CHUNK \ (LOG_MSG_NARGS_SINGLE_CHUNK * sizeof(u32_t)) /** @brief Number of bytes in the first chunk of hexdump message if message * consists of more than one chunk. */ #define LOG_MSG_HEXDUMP_BYTES_HEAD_CHUNK \ (LOG_MSG_HEXDUMP_BYTES_SINGLE_CHUNK - sizeof(void *)) /** @brief Number of bytes that can be stored in chunks following head chunk * in hexdump log message. */ #define HEXDUMP_BYTES_CONT_MSG \ (sizeof(struct log_msg) - sizeof(void *)) #define ARGS_CONT_MSG (HEXDUMP_BYTES_CONT_MSG / sizeof(u32_t)) /** @brief Flag indicating standard log message. */ #define LOG_MSG_TYPE_STD 0 /** @brief Flag indicating hexdump log message. */ #define LOG_MSG_TYPE_HEXDUMP 1 /** @brief Common part of log message header. */ #define COMMON_PARAM_HDR() \ u16_t type : 1; \ u16_t ext : 1 /** @brief Number of bits used for storing length of hexdump log message. */ #define LOG_MSG_HEXDUMP_LENGTH_BITS 13 /** @brief Maximum length of log hexdump message. */ #define LOG_MSG_HEXDUMP_MAX_LENGTH ((1 << LOG_MSG_HEXDUMP_LENGTH_BITS) - 1) /** @brief Part of log message header identifying source and level. */ struct log_msg_ids { u16_t level : 3; /*!< Severity. */ u16_t domain_id : 3; /*!< Originating domain. */ u16_t source_id : 10; /*!< Source ID. */ }; BUILD_ASSERT_MSG((sizeof(struct log_msg_ids) == sizeof(u16_t)), "Structure must fit in 2 bytes"); /** Part of log message header common to standard and hexdump log message. */ struct log_msg_generic_hdr { COMMON_PARAM_HDR(); u16_t reserved : 14; }; BUILD_ASSERT_MSG((sizeof(struct log_msg_generic_hdr) == sizeof(u16_t)), "Structure must fit in 2 bytes"); /** Part of log message header specific to standard log message. */ struct log_msg_std_hdr { COMMON_PARAM_HDR(); u16_t reserved : 10; u16_t nargs : 4; }; BUILD_ASSERT_MSG((sizeof(struct log_msg_std_hdr) == sizeof(u16_t)), "Structure must fit in 2 bytes"); /** Part of log message header specific to hexdump log message. */ struct log_msg_hexdump_hdr { COMMON_PARAM_HDR(); u16_t raw_string : 1; u16_t length : LOG_MSG_HEXDUMP_LENGTH_BITS; }; BUILD_ASSERT_MSG((sizeof(struct log_msg_hexdump_hdr) == sizeof(u16_t)), "Structure must fit in 2 bytes"); /** Log message header structure */ struct log_msg_hdr { atomic_t ref_cnt; /*!< Reference counter for tracking message users. */ union log_msg_hdr_params { struct log_msg_generic_hdr generic; struct log_msg_std_hdr std; struct log_msg_hexdump_hdr hexdump; u16_t raw; } params; struct log_msg_ids ids; /*!< Identification part of the message.*/ u32_t timestamp; /*!< Timestamp. */ }; /** @brief Data part of log message. */ union log_msg_head_data { u32_t args[LOG_MSG_NARGS_SINGLE_CHUNK]; u8_t bytes[LOG_MSG_HEXDUMP_BYTES_SINGLE_CHUNK]; }; /** @brief Data part of extended log message. */ struct log_msg_ext_head_data { struct log_msg_cont *next; union log_msg_ext_head_data_data { u32_t args[LOG_MSG_NARGS_HEAD_CHUNK]; u8_t bytes[LOG_MSG_HEXDUMP_BYTES_HEAD_CHUNK]; } data; }; /** @brief Log message structure. */ struct log_msg { struct log_msg *next; /*!< Used by logger core list.*/ struct log_msg_hdr hdr; /*!< Message header. */ const char *str; union log_msg_data { union log_msg_head_data single; struct log_msg_ext_head_data ext; } payload; /*!< Message data. */ }; BUILD_ASSERT_MSG((sizeof(union log_msg_head_data) == sizeof(struct log_msg_ext_head_data)), "Structure must be same size"); /** @brief Chunks following message head when message is extended. */ struct log_msg_cont { struct log_msg_cont *next; /*!< Pointer to the next chunk. */ union log_msg_cont_data { u32_t args[ARGS_CONT_MSG]; u8_t bytes[HEXDUMP_BYTES_CONT_MSG]; } payload; }; /** @brief Log message */ union log_msg_chunk { struct log_msg head; struct log_msg_cont cont; }; extern struct k_mem_slab log_msg_pool; /** @brief Function for initialization of the log message pool. */ void log_msg_pool_init(void); /** @brief Function for indicating that message is in use. * * @details Message can be used (read) by multiple users. Internal reference * counter is atomically increased. See @ref log_msg_put. * * @param msg Message. */ void log_msg_get(struct log_msg *msg); /** @brief Function for indicating that message is no longer in use. * * @details Internal reference counter is atomically decreased. If reference * counter equals 0 message is freed. * * @param msg Message. */ void log_msg_put(struct log_msg *msg); /** @brief Get domain ID of the message. * * @param msg Message * * @return Domain ID. */ static inline u32_t log_msg_domain_id_get(struct log_msg *msg) { return msg->hdr.ids.domain_id; } /** @brief Get source ID (module or instance) of the message. * * @param msg Message * * @return Source ID. */ static inline u32_t log_msg_source_id_get(struct log_msg *msg) { return msg->hdr.ids.source_id; } /** @brief Get severity level of the message. * * @param msg Message * * @return Severity message. */ static inline u32_t log_msg_level_get(struct log_msg *msg) { return msg->hdr.ids.level; } /** @brief Get timestamp of the message. * * @param msg Message * * @return Timestamp value. */ static inline u32_t log_msg_timestamp_get(struct log_msg *msg) { return msg->hdr.timestamp; } /** @brief Check if message is a raw string (see CONFIG_LOG_PRINTK). * * @param msg Message * * @retval true Message contains raw string. * @retval false Message does not contain raw string. */ static inline bool log_msg_is_raw_string(struct log_msg *msg) { return (msg->hdr.params.generic.type == LOG_MSG_TYPE_HEXDUMP) && (msg->hdr.params.hexdump.raw_string == 1); } /** @brief Check if message is of standard type. * * @param msg Message * * @retval true Standard message. * @retval false Hexdump message. */ static inline bool log_msg_is_std(struct log_msg *msg) { return (msg->hdr.params.generic.type == LOG_MSG_TYPE_STD); } /** @brief Returns number of arguments in standard log message. * * @param msg Standard log message. * * @return Number of arguments. */ u32_t log_msg_nargs_get(struct log_msg *msg); /** @brief Gets argument from standard log message. * * @param msg Standard log message. * @param arg_idx Argument index. * * @return Argument value. */ u32_t log_msg_arg_get(struct log_msg *msg, u32_t arg_idx); /** @brief Gets pointer to the unformatted string from standard log message. * * @param msg Standard log message. * * @return Pointer to the string. */ const char *log_msg_str_get(struct log_msg *msg); /** @brief Allocates chunks for hexdump message and copies the data. * * @details Function resets header and sets following fields: * - message type * - length * * @note Allocation and partial filling is combined for performance reasons. * * @param str String. * @param data Data. * @param length Data length. * * @return Pointer to allocated head of the message or NULL */ struct log_msg *log_msg_hexdump_create(const char *str, const u8_t *data, u32_t length); /** @brief Put data into hexdump log message. * * @param[in] msg Message. * @param[in] data Data to be copied. * @param[in, out] length Input: requested amount. Output: actual amount. * @param[in] offset Offset. */ void log_msg_hexdump_data_put(struct log_msg *msg, u8_t *data, size_t *length, size_t offset); /** @brief Get data from hexdump log message. * * @param[in] msg Message. * @param[in] data Buffer for data. * @param[in, out] length Input: requested amount. Output: actual amount. * @param[in] offset Offset. */ void log_msg_hexdump_data_get(struct log_msg *msg, u8_t *data, size_t *length, size_t offset); union log_msg_chunk *log_msg_no_space_handle(void); static inline union log_msg_chunk *log_msg_chunk_alloc(void) { union log_msg_chunk *msg = NULL; int err = k_mem_slab_alloc(&log_msg_pool, (void **)&msg, K_NO_WAIT); if (err != 0) { msg = log_msg_no_space_handle(); } return msg; } /** @brief Allocate chunk for standard log message. * * @return Allocated chunk of NULL. */ static inline struct log_msg *_log_msg_std_alloc(void) { struct log_msg *msg = (struct log_msg *)log_msg_chunk_alloc(); if (msg) { /* all fields reset to 0, reference counter to 1 */ msg->hdr.ref_cnt = 1; msg->hdr.params.raw = 0; msg->hdr.params.std.type = LOG_MSG_TYPE_STD; } return msg; } /** @brief Allocate chunk for extended standard log message. * * @details Extended standard log message is used when number of arguments * exceeds capacity of one chunk. Extended message consists of two * chunks. Such approach is taken to optimize memory usage and * performance assuming that log messages with more arguments * (@ref LOG_MSG_NARGS_SINGLE_CHUNK) are less common. * * @return Allocated chunk of NULL. */ static inline struct log_msg *_log_msg_ext_std_alloc(void) { struct log_msg_cont *cont; struct log_msg *msg = _log_msg_std_alloc(); if (msg != NULL) { cont = (struct log_msg_cont *)log_msg_chunk_alloc(); if (cont == NULL) { k_mem_slab_free(&log_msg_pool, (void **)&msg); return NULL; } msg->hdr.params.generic.ext = 1; msg->payload.ext.next = cont; cont->next = NULL; } return msg; } /** @brief Create standard log message with no arguments. * * @details Function resets header and sets following fields: * - message type * - string pointer * * @return Pointer to allocated head of the message or NULL. */ static inline struct log_msg *log_msg_create_0(const char *str) { struct log_msg *msg = _log_msg_std_alloc(); if (msg != NULL) { msg->str = str; } return msg; } /** @brief Create standard log message with one argument. * * @details Function resets header and sets following fields: * - message type * - string pointer * - number of arguments * - argument * * @param str String. * @param arg1 Argument. * * @return Pointer to allocated head of the message or NULL. */ static inline struct log_msg *log_msg_create_1(const char *str, u32_t arg1) { struct log_msg *msg = _log_msg_std_alloc(); if (msg != NULL) { msg->str = str; msg->hdr.params.std.nargs = 1; msg->payload.single.args[0] = arg1; } return msg; } /** @brief Create standard log message with two arguments. * * @details Function resets header and sets following fields: * - message type * - string pointer * - number of arguments * - arguments * * @param str String. * @param arg1 Argument 1. * @param arg2 Argument 2. * * @return Pointer to allocated head of the message or NULL. */ static inline struct log_msg *log_msg_create_2(const char *str, u32_t arg1, u32_t arg2) { struct log_msg *msg = _log_msg_std_alloc(); if (msg != NULL) { msg->str = str; msg->hdr.params.std.nargs = 2; msg->payload.single.args[0] = arg1; msg->payload.single.args[1] = arg2; } return msg; } /** @brief Create standard log message with three arguments. * * @details Function resets header and sets following fields: * - message type * - string pointer * - number of arguments * - arguments * * @param str String. * @param arg1 Argument 1. * @param arg2 Argument 2. * @param arg3 Argument 3. * * @return Pointer to allocated head of the message or NULL. */ static inline struct log_msg *log_msg_create_3(const char *str, u32_t arg1, u32_t arg2, u32_t arg3) { struct log_msg *msg = _log_msg_std_alloc(); if (msg != NULL) { msg->str = str; msg->hdr.params.std.nargs = 3; msg->payload.single.args[0] = arg1; msg->payload.single.args[1] = arg2; msg->payload.single.args[2] = arg3; } return msg; } /** @brief Create standard log message with variable number of arguments. * * @details Function resets header and sets following fields: * - message type * - string pointer * - number of arguments * - arguments * * @param str String. * @param args Array with arguments. * @param nargs Number of arguments. * * @return Pointer to allocated head of the message or NULL. */ struct log_msg *log_msg_create_n(const char *str, u32_t *args, u32_t nargs); /** * @} */ #ifdef __cplusplus } #endif #endif /* ZEPHYR_INCLUDE_LOGGING_LOG_MSG_H_ */