blob: 76ac2c0d0f91152801638566953175209f1ef9a7 [file] [log] [blame]
/*
* Copyright (c) 2018 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef LOG_MSG_H
#define LOG_MSG_H
#include <kernel.h>
#include <atomic.h>
#include <assert.h>
#include <string.h>
#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 /* LOG_MSG_H */