You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
424 lines
12 KiB
424 lines
12 KiB
/* |
|
* Copyright (c) 2020 Nordic Semiconductor |
|
* |
|
* SPDX-License-Identifier: Apache-2.0 |
|
*/ |
|
#include <zephyr/kernel.h> |
|
#include <zephyr/internal/syscall_handler.h> |
|
#include <zephyr/logging/log_internal.h> |
|
#include <zephyr/logging/log_ctrl.h> |
|
#include <zephyr/logging/log_frontend.h> |
|
#include <zephyr/logging/log_backend.h> |
|
#include <zephyr/logging/log.h> |
|
#include <zephyr/llext/symbol.h> |
|
LOG_MODULE_DECLARE(log); |
|
|
|
BUILD_ASSERT(sizeof(struct log_msg_desc) == sizeof(uint32_t), |
|
"Descriptor must fit in 32 bits"); |
|
|
|
/* Returns true if any backend is in use. */ |
|
#define BACKENDS_IN_USE() \ |
|
!(IS_ENABLED(CONFIG_LOG_FRONTEND) && \ |
|
(IS_ENABLED(CONFIG_LOG_FRONTEND_ONLY) || log_backend_count_get() == 0)) |
|
|
|
#define CBPRINTF_DESC_SIZE32 (sizeof(struct cbprintf_package_desc) / sizeof(uint32_t)) |
|
|
|
/* For simplified message handling cprintf package must have only 1 word. */ |
|
BUILD_ASSERT(!IS_ENABLED(CONFIG_LOG_SIMPLE_MSG_OPTIMIZE) || |
|
(IS_ENABLED(CONFIG_LOG_SIMPLE_MSG_OPTIMIZE) && (CBPRINTF_DESC_SIZE32 == 1))); |
|
|
|
|
|
void z_log_msg_finalize(struct log_msg *msg, const void *source, |
|
const struct log_msg_desc desc, const void *data) |
|
{ |
|
if (!msg) { |
|
z_log_dropped(false); |
|
|
|
return; |
|
} |
|
|
|
if (data) { |
|
uint8_t *d = msg->data + desc.package_len; |
|
|
|
memcpy(d, data, desc.data_len); |
|
} |
|
|
|
msg->hdr.desc = desc; |
|
msg->hdr.source = source; |
|
#if CONFIG_LOG_THREAD_ID_PREFIX |
|
msg->hdr.tid = k_is_in_isr() ? NULL : k_current_get(); |
|
#endif |
|
z_log_msg_commit(msg); |
|
} |
|
|
|
static bool frontend_runtime_filtering(const void *source, uint32_t level) |
|
{ |
|
if (!IS_ENABLED(CONFIG_LOG_RUNTIME_FILTERING)) { |
|
return true; |
|
} |
|
|
|
/* If only frontend is used and log got here it means that it was accepted |
|
* unless userspace is enabled then runtime filtering is done here. |
|
*/ |
|
if (!IS_ENABLED(CONFIG_USERSPACE) && IS_ENABLED(CONFIG_LOG_FRONTEND_ONLY)) { |
|
return true; |
|
} |
|
|
|
if (level == LOG_LEVEL_NONE) { |
|
return true; |
|
} |
|
|
|
struct log_source_dynamic_data *dynamic = (struct log_source_dynamic_data *)source; |
|
uint32_t f_level = LOG_FILTER_SLOT_GET(&dynamic->filters, LOG_FRONTEND_SLOT_ID); |
|
|
|
return level <= f_level; |
|
} |
|
|
|
/** @brief Create a log message using simplified method. |
|
* |
|
* Simple log message has 0-2 32 bit word arguments so creating cbprintf package |
|
* is straightforward as there is no padding or alignment to concern about. |
|
* This function takes input data which is fmt pointer + 0-2 arguments, creates |
|
* package header which is very simple as it only contain non-zero length field. |
|
* Then space is allocated and message is committed. Such simple approach can |
|
* be applied because it is known that input string does not have any arguments |
|
* which complicate things (string pointers, floating numbers). Simple method is |
|
* also limited to 32 bit arch. |
|
* |
|
* @param source Source. |
|
* @param level Severity level. |
|
* @param data Package content (without header). |
|
* @param len Package content length in words. |
|
*/ |
|
static void z_log_msg_simple_create(const void *source, uint32_t level, uint32_t *data, size_t len) |
|
{ |
|
/* Package length (in words) is increased by the header. */ |
|
size_t plen32 = len + CBPRINTF_DESC_SIZE32; |
|
/* Package length in bytes. */ |
|
size_t plen8 = sizeof(uint32_t) * plen32 + |
|
(IS_ENABLED(CONFIG_LOG_MSG_APPEND_RO_STRING_LOC) ? 1 : 0); |
|
struct log_msg *msg = z_log_msg_alloc(Z_LOG_MSG_ALIGNED_WLEN(plen8, 0)); |
|
union cbprintf_package_hdr package_hdr = { |
|
.desc = { |
|
.len = plen32, |
|
.ro_str_cnt = IS_ENABLED(CONFIG_LOG_MSG_APPEND_RO_STRING_LOC) ? 1 : 0 |
|
} |
|
}; |
|
|
|
if (msg) { |
|
uint32_t *package = (uint32_t *)msg->data; |
|
|
|
*package++ = (uint32_t)(uintptr_t)package_hdr.raw; |
|
for (size_t i = 0; i < len; i++) { |
|
*package++ = data[i]; |
|
} |
|
if (IS_ENABLED(CONFIG_LOG_MSG_APPEND_RO_STRING_LOC)) { |
|
/* fmt string located at index 1 */ |
|
*(uint8_t *)package = 1; |
|
} |
|
} |
|
|
|
struct log_msg_desc desc = { |
|
.level = level, |
|
.package_len = plen8, |
|
.data_len = 0, |
|
}; |
|
|
|
z_log_msg_finalize(msg, source, desc, NULL); |
|
} |
|
|
|
void z_impl_z_log_msg_simple_create_0(const void *source, uint32_t level, const char *fmt) |
|
{ |
|
|
|
if (IS_ENABLED(CONFIG_LOG_FRONTEND) && frontend_runtime_filtering(source, level)) { |
|
if (IS_ENABLED(CONFIG_LOG_FRONTEND_OPT_API)) { |
|
log_frontend_simple_0(source, level, fmt); |
|
} else { |
|
/* If frontend does not support optimized API prepare data for |
|
* the generic call. |
|
*/ |
|
uint32_t plen32 = CBPRINTF_DESC_SIZE32 + 1; |
|
union cbprintf_package_hdr hdr = { |
|
.desc = { |
|
.len = plen32, |
|
.ro_str_cnt = |
|
IS_ENABLED(CONFIG_LOG_MSG_APPEND_RO_STRING_LOC) ? 1 : 0 |
|
} |
|
}; |
|
uint8_t package[sizeof(uint32_t) * (CBPRINTF_DESC_SIZE32 + 1) + |
|
(IS_ENABLED(CONFIG_LOG_MSG_APPEND_RO_STRING_LOC) ? 1 : 0)] |
|
__aligned(sizeof(uint32_t)); |
|
uint32_t *p32 = (uint32_t *)package; |
|
|
|
*p32++ = (uint32_t)(uintptr_t)hdr.raw; |
|
*p32++ = (uint32_t)(uintptr_t)fmt; |
|
if (IS_ENABLED(CONFIG_LOG_MSG_APPEND_RO_STRING_LOC)) { |
|
/* fmt string located at index 1 */ |
|
*(uint8_t *)p32 = 1; |
|
} |
|
|
|
struct log_msg_desc desc = { |
|
.level = level, |
|
.package_len = sizeof(package), |
|
.data_len = 0, |
|
}; |
|
|
|
log_frontend_msg(source, desc, package, NULL); |
|
} |
|
} |
|
|
|
if (!BACKENDS_IN_USE()) { |
|
return; |
|
} |
|
|
|
uint32_t data[] = {(uint32_t)(uintptr_t)fmt}; |
|
|
|
z_log_msg_simple_create(source, level, data, ARRAY_SIZE(data)); |
|
} |
|
|
|
void z_impl_z_log_msg_simple_create_1(const void *source, uint32_t level, |
|
const char *fmt, uint32_t arg) |
|
{ |
|
if (IS_ENABLED(CONFIG_LOG_FRONTEND) && frontend_runtime_filtering(source, level)) { |
|
if (IS_ENABLED(CONFIG_LOG_FRONTEND_OPT_API)) { |
|
log_frontend_simple_1(source, level, fmt, arg); |
|
} else { |
|
/* If frontend does not support optimized API prepare data for |
|
* the generic call. |
|
*/ |
|
uint32_t plen32 = CBPRINTF_DESC_SIZE32 + 2; |
|
union cbprintf_package_hdr hdr = { |
|
.desc = { |
|
.len = plen32, |
|
.ro_str_cnt = |
|
IS_ENABLED(CONFIG_LOG_MSG_APPEND_RO_STRING_LOC) ? 1 : 0 |
|
} |
|
}; |
|
uint8_t package[sizeof(uint32_t) * (CBPRINTF_DESC_SIZE32 + 2) + |
|
(IS_ENABLED(CONFIG_LOG_MSG_APPEND_RO_STRING_LOC) ? 1 : 0)] |
|
__aligned(sizeof(uint32_t)); |
|
uint32_t *p32 = (uint32_t *)package; |
|
|
|
*p32++ = (uint32_t)(uintptr_t)hdr.raw; |
|
*p32++ = (uint32_t)(uintptr_t)fmt; |
|
*p32++ = arg; |
|
if (IS_ENABLED(CONFIG_LOG_MSG_APPEND_RO_STRING_LOC)) { |
|
/* fmt string located at index 1 */ |
|
*(uint8_t *)p32 = 1; |
|
} |
|
|
|
struct log_msg_desc desc = { |
|
.level = level, |
|
.package_len = sizeof(package), |
|
.data_len = 0, |
|
}; |
|
|
|
log_frontend_msg(source, desc, package, NULL); |
|
} |
|
} |
|
|
|
if (!BACKENDS_IN_USE()) { |
|
return; |
|
} |
|
|
|
uint32_t data[] = {(uint32_t)(uintptr_t)fmt, arg}; |
|
|
|
z_log_msg_simple_create(source, level, data, ARRAY_SIZE(data)); |
|
} |
|
|
|
void z_impl_z_log_msg_simple_create_2(const void *source, uint32_t level, |
|
const char *fmt, uint32_t arg0, uint32_t arg1) |
|
{ |
|
if (IS_ENABLED(CONFIG_LOG_FRONTEND) && frontend_runtime_filtering(source, level)) { |
|
if (IS_ENABLED(CONFIG_LOG_FRONTEND_OPT_API)) { |
|
log_frontend_simple_2(source, level, fmt, arg0, arg1); |
|
} else { |
|
/* If frontend does not support optimized API prepare data for |
|
* the generic call. |
|
*/ |
|
uint32_t plen32 = CBPRINTF_DESC_SIZE32 + 3; |
|
union cbprintf_package_hdr hdr = { |
|
.desc = { |
|
.len = plen32, |
|
.ro_str_cnt = |
|
IS_ENABLED(CONFIG_LOG_MSG_APPEND_RO_STRING_LOC) ? 1 : 0 |
|
} |
|
}; |
|
uint8_t package[sizeof(uint32_t) * (CBPRINTF_DESC_SIZE32 + 3) + |
|
(IS_ENABLED(CONFIG_LOG_MSG_APPEND_RO_STRING_LOC) ? 1 : 0)] |
|
__aligned(sizeof(uint32_t)); |
|
uint32_t *p32 = (uint32_t *)package; |
|
|
|
*p32++ = (uint32_t)(uintptr_t)hdr.raw; |
|
*p32++ = (uint32_t)(uintptr_t)fmt; |
|
*p32++ = arg0; |
|
*p32++ = arg1; |
|
if (IS_ENABLED(CONFIG_LOG_MSG_APPEND_RO_STRING_LOC)) { |
|
/* fmt string located at index 1 */ |
|
*(uint8_t *)p32 = 1; |
|
} |
|
|
|
struct log_msg_desc desc = { |
|
.level = level, |
|
.package_len = sizeof(package), |
|
.data_len = 0, |
|
}; |
|
|
|
log_frontend_msg(source, desc, package, NULL); |
|
} |
|
} |
|
|
|
if (!BACKENDS_IN_USE()) { |
|
return; |
|
} |
|
|
|
uint32_t data[] = {(uint32_t)(uintptr_t)fmt, arg0, arg1}; |
|
|
|
z_log_msg_simple_create(source, level, data, ARRAY_SIZE(data)); |
|
} |
|
|
|
void z_impl_z_log_msg_static_create(const void *source, |
|
const struct log_msg_desc desc, |
|
uint8_t *package, const void *data) |
|
{ |
|
if (IS_ENABLED(CONFIG_LOG_FRONTEND) && frontend_runtime_filtering(source, desc.level)) { |
|
log_frontend_msg(source, desc, package, data); |
|
} |
|
|
|
if (!BACKENDS_IN_USE()) { |
|
return; |
|
} |
|
|
|
struct log_msg_desc out_desc = desc; |
|
int inlen = desc.package_len; |
|
struct log_msg *msg; |
|
|
|
if (inlen > 0) { |
|
uint32_t flags = CBPRINTF_PACKAGE_CONVERT_RW_STR | |
|
(IS_ENABLED(CONFIG_LOG_MSG_APPEND_RO_STRING_LOC) ? |
|
CBPRINTF_PACKAGE_CONVERT_KEEP_RO_STR : 0) | |
|
(IS_ENABLED(CONFIG_LOG_FMT_SECTION_STRIP) ? |
|
0 : CBPRINTF_PACKAGE_CONVERT_PTR_CHECK); |
|
uint16_t strl[4]; |
|
int len; |
|
|
|
len = cbprintf_package_copy(package, inlen, |
|
NULL, 0, flags, |
|
strl, ARRAY_SIZE(strl)); |
|
|
|
if (len > Z_LOG_MSG_MAX_PACKAGE) { |
|
struct cbprintf_package_hdr_ext *pkg = |
|
(struct cbprintf_package_hdr_ext *)package; |
|
|
|
LOG_WRN("Message (\"%s\") dropped because it exceeds size limitation (%u)", |
|
pkg->fmt, (uint32_t)Z_LOG_MSG_MAX_PACKAGE); |
|
return; |
|
} |
|
/* Update package length with calculated value (which may be extended |
|
* when strings are copied into the package. |
|
*/ |
|
out_desc.package_len = len; |
|
msg = z_log_msg_alloc(log_msg_get_total_wlen(out_desc)); |
|
if (msg) { |
|
len = cbprintf_package_copy(package, inlen, |
|
msg->data, out_desc.package_len, |
|
flags, strl, ARRAY_SIZE(strl)); |
|
__ASSERT_NO_MSG(len >= 0); |
|
} |
|
} else { |
|
msg = z_log_msg_alloc(log_msg_get_total_wlen(out_desc)); |
|
} |
|
|
|
z_log_msg_finalize(msg, source, out_desc, data); |
|
} |
|
|
|
#ifdef CONFIG_USERSPACE |
|
static inline void z_vrfy_z_log_msg_static_create(const void *source, |
|
const struct log_msg_desc desc, |
|
uint8_t *package, const void *data) |
|
{ |
|
z_impl_z_log_msg_static_create(source, desc, package, data); |
|
} |
|
#include <zephyr/syscalls/z_log_msg_static_create_mrsh.c> |
|
#endif |
|
|
|
void z_log_msg_runtime_vcreate(uint8_t domain_id, const void *source, |
|
uint8_t level, const void *data, size_t dlen, |
|
uint32_t package_flags, const char *fmt, va_list ap) |
|
{ |
|
int plen; |
|
|
|
if (fmt) { |
|
va_list ap2; |
|
|
|
va_copy(ap2, ap); |
|
plen = cbvprintf_package(NULL, Z_LOG_MSG_ALIGN_OFFSET, |
|
package_flags, fmt, ap2); |
|
__ASSERT_NO_MSG(plen >= 0); |
|
va_end(ap2); |
|
} else { |
|
plen = 0; |
|
} |
|
|
|
if (plen > Z_LOG_MSG_MAX_PACKAGE) { |
|
LOG_WRN("Message dropped because it exceeds size limitation (%u)", |
|
(uint32_t)Z_LOG_MSG_MAX_PACKAGE); |
|
return; |
|
} |
|
|
|
size_t msg_wlen = Z_LOG_MSG_ALIGNED_WLEN(plen, dlen); |
|
struct log_msg *msg; |
|
uint8_t *pkg; |
|
struct log_msg_desc desc = |
|
Z_LOG_MSG_DESC_INITIALIZER(domain_id, level, plen, dlen); |
|
|
|
if (IS_ENABLED(CONFIG_USERSPACE) && k_is_user_context()) { |
|
pkg = alloca(plen); |
|
msg = NULL; |
|
} else if (IS_ENABLED(CONFIG_LOG_MODE_DEFERRED) && BACKENDS_IN_USE()) { |
|
msg = z_log_msg_alloc(msg_wlen); |
|
if (IS_ENABLED(CONFIG_LOG_FRONTEND) && msg == NULL) { |
|
pkg = alloca(plen); |
|
} else { |
|
pkg = msg ? msg->data : NULL; |
|
} |
|
} else { |
|
msg = alloca(msg_wlen * sizeof(int)); |
|
pkg = msg->data; |
|
} |
|
|
|
if (pkg && fmt) { |
|
plen = cbvprintf_package(pkg, (size_t)plen, package_flags, fmt, ap); |
|
__ASSERT_NO_MSG(plen >= 0); |
|
} |
|
|
|
if (IS_ENABLED(CONFIG_USERSPACE) && k_is_user_context()) { |
|
z_log_msg_static_create(source, desc, pkg, data); |
|
} else { |
|
if (IS_ENABLED(CONFIG_LOG_FRONTEND) && |
|
frontend_runtime_filtering(source, desc.level)) { |
|
log_frontend_msg(source, desc, pkg, data); |
|
} |
|
|
|
if (BACKENDS_IN_USE()) { |
|
z_log_msg_finalize(msg, source, desc, data); |
|
} |
|
} |
|
} |
|
EXPORT_SYMBOL(z_log_msg_runtime_vcreate); |
|
|
|
int16_t log_msg_get_source_id(struct log_msg *msg) |
|
{ |
|
if (!z_log_is_local_domain(log_msg_get_domain(msg))) { |
|
/* Remote domain is converting source pointer to ID */ |
|
return (int16_t)(uintptr_t)log_msg_get_source(msg); |
|
} |
|
|
|
void *source = (void *)log_msg_get_source(msg); |
|
|
|
if (source != NULL) { |
|
return log_source_id(source); |
|
} |
|
|
|
return -1; |
|
}
|
|
|