logging: Add basic userspace support

This commit adds basic userspace support to the logging subsystem.
With this change, the following API could be called from user mode:
- LOG_*()
- LOG_INST_*(),
- LOG_HEXDUMP_*(),
- LOG_HEXDUMP_INST_*(),
- LOG_PANIC(), LOG_PROCESS(),
- log_printk(), log_generic(), log_buffrered_cnt(),
- log_filter_set(NULL, ...)

With userspace disabled, the logger behavior and performance
is not affected. With userspace enabled, the calls from kernel
space have an additional overhead introduced by _is_user_context().

The logger behavior changes when it is called from the user context.
All strings logged using LOG_*() and LOG_INST_*() API from userspace
are rendered in place for security reasons and then placed in
log_strdup() memory pool, which should be large enough to hold bursts
of log messages.

Signed-off-by: Andrew Boie <andrew.p.boie@intel.com>
Signed-off-by: Piotr Zięcik <piotr.ziecik@nordicsemi.no>
This commit is contained in:
Piotr Zięcik 2019-08-22 11:13:22 +02:00 committed by Anas Nashif
commit 892ab4e356
7 changed files with 399 additions and 43 deletions

View file

@ -8,10 +8,11 @@
#include <logging/log_msg.h> #include <logging/log_msg.h>
#include <logging/log_instance.h> #include <logging/log_instance.h>
#include <sys/util.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <stdarg.h> #include <stdarg.h>
#include <syscall.h>
#include <sys/util.h>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@ -162,9 +163,11 @@ extern "C" {
#define Z_LOG_INTERNAL_X(N, ...) UTIL_CAT(_LOG_INTERNAL_, N)(__VA_ARGS__) #define Z_LOG_INTERNAL_X(N, ...) UTIL_CAT(_LOG_INTERNAL_, N)(__VA_ARGS__)
#define __LOG_INTERNAL(_src_level, ...) \ #define __LOG_INTERNAL(is_user_context, _src_level, ...) \
do { \ do { \
if (IS_ENABLED(CONFIG_LOG_IMMEDIATE)) { \ if (is_user_context) { \
log_from_user(_src_level, __VA_ARGS__); \
} else if (IS_ENABLED(CONFIG_LOG_IMMEDIATE)) { \
log_string_sync(_src_level, __VA_ARGS__); \ log_string_sync(_src_level, __VA_ARGS__); \
} else { \ } else { \
Z_LOG_INTERNAL_X(Z_LOG_NARGS_POSTFIX(__VA_ARGS__), \ Z_LOG_INTERNAL_X(Z_LOG_NARGS_POSTFIX(__VA_ARGS__), \
@ -212,8 +215,11 @@ extern "C" {
/******************************************************************************/ /******************************************************************************/
#define __LOG(_level, _id, _filter, ...) \ #define __LOG(_level, _id, _filter, ...) \
do { \ do { \
bool is_user_context = _is_user_context(); \
\
if (Z_LOG_CONST_LEVEL_CHECK(_level) && \ if (Z_LOG_CONST_LEVEL_CHECK(_level) && \
(_level <= LOG_RUNTIME_FILTER(_filter))) { \ (is_user_context || \
(_level <= LOG_RUNTIME_FILTER(_filter)))) { \
struct log_msg_ids src_level = { \ struct log_msg_ids src_level = { \
.level = _level, \ .level = _level, \
.domain_id = CONFIG_LOG_DOMAIN_ID, \ .domain_id = CONFIG_LOG_DOMAIN_ID, \
@ -221,10 +227,11 @@ extern "C" {
}; \ }; \
\ \
if ((BIT(_level) & LOG_FUNCTION_PREFIX_MASK) != 0U) {\ if ((BIT(_level) & LOG_FUNCTION_PREFIX_MASK) != 0U) {\
__LOG_INTERNAL(src_level, \ __LOG_INTERNAL(is_user_context, src_level, \
Z_LOG_STR(__VA_ARGS__)); \ Z_LOG_STR(__VA_ARGS__)); \
} else { \ } else { \
__LOG_INTERNAL(src_level, __VA_ARGS__); \ __LOG_INTERNAL(is_user_context, src_level, \
__VA_ARGS__); \
} \ } \
} else if (false) { \ } else if (false) { \
/* Arguments checker present but never evaluated.*/ \ /* Arguments checker present but never evaluated.*/ \
@ -254,15 +261,21 @@ extern "C" {
/******************************************************************************/ /******************************************************************************/
#define __LOG_HEXDUMP(_level, _id, _filter, _data, _length, _str) \ #define __LOG_HEXDUMP(_level, _id, _filter, _data, _length, _str) \
do { \ do { \
bool is_user_context = _is_user_context(); \
\
if (Z_LOG_CONST_LEVEL_CHECK(_level) && \ if (Z_LOG_CONST_LEVEL_CHECK(_level) && \
(_level <= LOG_RUNTIME_FILTER(_filter))) { \ (is_user_context || \
(_level <= LOG_RUNTIME_FILTER(_filter)))) { \
struct log_msg_ids src_level = { \ struct log_msg_ids src_level = { \
.level = _level, \ .level = _level, \
.source_id = _id, \ .source_id = _id, \
.domain_id = CONFIG_LOG_DOMAIN_ID \ .domain_id = CONFIG_LOG_DOMAIN_ID \
}; \ }; \
\ \
if (IS_ENABLED(CONFIG_LOG_IMMEDIATE)) { \ if (is_user_context) { \
log_hexdump_from_user(src_level, _str, \
_data, _length); \
} else if (IS_ENABLED(CONFIG_LOG_IMMEDIATE)) { \
log_hexdump_sync(src_level, _str, \ log_hexdump_sync(src_level, _str, \
_data, _length); \ _data, _length); \
} else { \ } else { \
@ -526,6 +539,15 @@ void log_hexdump_sync(struct log_msg_ids src_level, const char *metadata,
*/ */
void log_generic(struct log_msg_ids src_level, const char *fmt, va_list ap); void log_generic(struct log_msg_ids src_level, const char *fmt, va_list ap);
/**
* @brief Writes a generic log message to the log from user mode.
*
* @note This function is intended to be used internally
* by the logging subsystem.
*/
void log_generic_from_user(struct log_msg_ids src_level,
const char *fmt, va_list ap);
/** @brief Check if address belongs to the memory pool used for transient. /** @brief Check if address belongs to the memory pool used for transient.
* *
* @param buf Buffer. * @param buf Buffer.
@ -559,6 +581,42 @@ u32_t log_get_strdup_longest_string(void);
*/ */
void log_dropped(void); void log_dropped(void);
/** @brief Log a message from user mode context.
*
* @note This function is intended to be used internally
* by the logging subsystem.
*
* @param src_level Log identification.
* @param fmt String to format.
* @param ... Variable list of arguments.
*/
void __printf_like(2, 3) log_from_user(struct log_msg_ids src_level,
const char *fmt, ...);
/* Internal function used by log_from_user(). */
__syscall void z_log_string_from_user(u32_t src_level_val, const char *str);
/** @brief Log binary data (displayed as hexdump) from user mode conext.
*
* @note This function is intended to be used internally
* by the logging subsystem.
*
* @param src_level Log identification.
* @param metadata Raw string associated with the data.
* @param data Data.
* @param len Data length.
*/
void log_hexdump_from_user(struct log_msg_ids src_level, const char *metadata,
const u8_t *data, u32_t len);
/* Internal function used by log_hexdump_from_user(). */
__syscall void z_log_hexdump_from_user(u32_t src_level_val,
const char *metadata,
const u8_t *data, u32_t len);
#include <syscalls/log_core.h>
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View file

@ -75,7 +75,7 @@ int log_set_timestamp_func(timestamp_get_t timestamp_getter, u32_t freq);
* are flushed after switching to panic mode. In panic mode, all log * are flushed after switching to panic mode. In panic mode, all log
* messages must be processed in the context of the call. * messages must be processed in the context of the call.
*/ */
void log_panic(void); __syscall void log_panic(void);
/** /**
* @brief Process one pending log message. * @brief Process one pending log message.
@ -85,14 +85,14 @@ void log_panic(void);
* @retval true There is more messages pending to be processed. * @retval true There is more messages pending to be processed.
* @retval false No messages pending. * @retval false No messages pending.
*/ */
bool log_process(bool bypass); __syscall bool log_process(bool bypass);
/** /**
* @brief Return number of buffered log messages. * @brief Return number of buffered log messages.
* *
* @return Number of currently buffered log messages. * @return Number of currently buffered log messages.
*/ */
u32_t log_buffered_cnt(void); __syscall u32_t log_buffered_cnt(void);
/** @brief Get number of independent logger sources (modules and instances) /** @brief Get number of independent logger sources (modules and instances)
* *
@ -144,10 +144,10 @@ u32_t log_filter_get(struct log_backend const *const backend,
* @return Actual level set which may be limited by compiled level. If filter * @return Actual level set which may be limited by compiled level. If filter
* was set for all backends then maximal level that was set is returned. * was set for all backends then maximal level that was set is returned.
*/ */
u32_t log_filter_set(struct log_backend const *const backend, __syscall u32_t log_filter_set(struct log_backend const *const backend,
u32_t domain_id, u32_t domain_id,
u32_t src_id, u32_t src_id,
u32_t level); u32_t level);
/** /**
* *
@ -179,6 +179,8 @@ void log_backend_disable(struct log_backend const *const backend);
#define LOG_PROCESS() false #define LOG_PROCESS() false
#endif #endif
#include <syscalls/log_ctrl.h>
/** /**
* @} * @}
*/ */

View file

@ -6,6 +6,7 @@
menuconfig LOG menuconfig LOG
bool "Logging" bool "Logging"
select PRINTK if USERSPACE
help help
Global switch for the logger, when turned off log calls will not be Global switch for the logger, when turned off log calls will not be
compiled in. compiled in.
@ -94,7 +95,7 @@ config LOG_PRINTK
config LOG_PRINTK_MAX_STRING_LENGTH config LOG_PRINTK_MAX_STRING_LENGTH
int "Maximum string length supported by LOG_PRINTK" int "Maximum string length supported by LOG_PRINTK"
depends on LOG_PRINTK depends on LOG_PRINTK
depends on !LOG_IMMEDIATE depends on (!LOG_IMMEDIATE || USERSPACE)
default 128 default 128
help help
Array is allocated on the stack. Array is allocated on the stack.

View file

@ -15,11 +15,12 @@
#include <sys/atomic.h> #include <sys/atomic.h>
#include <ctype.h> #include <ctype.h>
#include <logging/log_frontend.h> #include <logging/log_frontend.h>
#include <syscall_handler.h>
LOG_MODULE_REGISTER(log); LOG_MODULE_REGISTER(log);
#ifndef CONFIG_LOG_PRINTK_MAX_STRING_LENGTH #ifndef CONFIG_LOG_PRINTK_MAX_STRING_LENGTH
#define CONFIG_LOG_PRINTK_MAX_STRING_LENGTH 1 #define CONFIG_LOG_PRINTK_MAX_STRING_LENGTH 0
#endif #endif
#ifndef CONFIG_LOG_PROCESS_THREAD_SLEEP_MS #ifndef CONFIG_LOG_PROCESS_THREAD_SLEEP_MS
@ -324,27 +325,37 @@ int log_printk(const char *fmt, va_list ap)
int length = 0; int length = 0;
if (IS_ENABLED(CONFIG_LOG_PRINTK)) { if (IS_ENABLED(CONFIG_LOG_PRINTK)) {
struct log_msg_ids src_level = { union {
.level = LOG_LEVEL_INTERNAL_RAW_STRING struct log_msg_ids structure;
u32_t value;
} src_level_union = {
{
.level = LOG_LEVEL_INTERNAL_RAW_STRING
}
}; };
if (IS_ENABLED(CONFIG_LOG_IMMEDIATE)) { if (_is_user_context()) {
log_generic(src_level, fmt, ap); u8_t str[CONFIG_LOG_PRINTK_MAX_STRING_LENGTH + 1];
length = vsnprintk(str, sizeof(str), fmt, ap);
length = MIN(length, sizeof(str));
z_log_string_from_user(src_level_union.value, str);
} else if (IS_ENABLED(CONFIG_LOG_IMMEDIATE)) {
log_generic(src_level_union.structure, fmt, ap);
} else { } else {
u8_t formatted_str[CONFIG_LOG_PRINTK_MAX_STRING_LENGTH]; u8_t str[CONFIG_LOG_PRINTK_MAX_STRING_LENGTH + 1];
struct log_msg *msg; struct log_msg *msg;
length = vsnprintk(formatted_str, length = vsnprintk(str, sizeof(str), fmt, ap);
sizeof(formatted_str), fmt, ap); length = MIN(length, sizeof(str));
length = MIN(length, sizeof(formatted_str));
msg = log_msg_hexdump_create(NULL, formatted_str, msg = log_msg_hexdump_create(NULL, str, length);
length);
if (msg == NULL) { if (msg == NULL) {
return 0; return 0;
} }
msg_finalize(msg, src_level); msg_finalize(msg, src_level_union.structure);
} }
} }
@ -375,7 +386,9 @@ static u32_t count_args(const char *fmt)
void log_generic(struct log_msg_ids src_level, const char *fmt, va_list ap) void log_generic(struct log_msg_ids src_level, const char *fmt, va_list ap)
{ {
if (IS_ENABLED(CONFIG_LOG_IMMEDIATE) && if (_is_user_context()) {
log_generic_from_user(src_level, fmt, ap);
} else if (IS_ENABLED(CONFIG_LOG_IMMEDIATE) &&
(!IS_ENABLED(CONFIG_LOG_FRONTEND))) { (!IS_ENABLED(CONFIG_LOG_FRONTEND))) {
struct log_backend const *backend; struct log_backend const *backend;
u32_t timestamp = timestamp_func(); u32_t timestamp = timestamp_func();
@ -550,7 +563,7 @@ int log_set_timestamp_func(timestamp_get_t timestamp_getter, u32_t freq)
return 0; return 0;
} }
void log_panic(void) void z_impl_log_panic(void)
{ {
struct log_backend const *backend; struct log_backend const *backend;
@ -580,6 +593,10 @@ void log_panic(void)
panic_mode = true; panic_mode = true;
} }
#ifdef CONFIG_USERSPACE
Z_SYSCALL_HANDLER0_SIMPLE_VOID(log_panic);
#endif
static bool msg_filter_check(struct log_backend const *backend, static bool msg_filter_check(struct log_backend const *backend,
struct log_msg *msg) struct log_msg *msg)
{ {
@ -635,7 +652,7 @@ void dropped_notify(void)
} }
} }
bool log_process(bool bypass) bool z_impl_log_process(bool bypass)
{ {
struct log_msg *msg; struct log_msg *msg;
@ -659,11 +676,22 @@ bool log_process(bool bypass)
return (log_list_head_peek(&list) != NULL); return (log_list_head_peek(&list) != NULL);
} }
u32_t log_buffered_cnt(void) #ifdef CONFIG_USERSPACE
Z_SYSCALL_HANDLER(log_process, bypass)
{
return (u32_t)log_process((bool)(bypass));
}
#endif
u32_t z_impl_log_buffered_cnt(void)
{ {
return buffered_cnt; return buffered_cnt;
} }
#ifdef CONFIG_USERSPACE
Z_SYSCALL_HANDLER0_SIMPLE(log_buffered_cnt);
#endif
void log_dropped(void) void log_dropped(void)
{ {
atomic_inc(&dropped_cnt); atomic_inc(&dropped_cnt);
@ -696,10 +724,10 @@ static u32_t max_filter_get(u32_t filters)
return max_filter; return max_filter;
} }
u32_t log_filter_set(struct log_backend const *const backend, u32_t z_impl_log_filter_set(struct log_backend const *const backend,
u32_t domain_id, u32_t domain_id,
u32_t src_id, u32_t src_id,
u32_t level) u32_t level)
{ {
assert(src_id < log_sources_count()); assert(src_id < log_sources_count());
@ -745,16 +773,29 @@ u32_t log_filter_set(struct log_backend const *const backend,
return level; return level;
} }
#ifdef CONFIG_USERSPACE
Z_SYSCALL_HANDLER(log_filter_set, backend, domain_id, src_id, level)
{
Z_OOPS(Z_SYSCALL_VERIFY_MSG(backend == 0,
"Setting per-backend filters from user mode is not supported"));
Z_OOPS(Z_SYSCALL_VERIFY_MSG(domain_id == CONFIG_LOG_DOMAIN_ID,
"Invalid log domain_id"));
Z_OOPS(Z_SYSCALL_VERIFY_MSG(src_id < log_sources_count(),
"Invalid log source id"));
Z_OOPS(Z_SYSCALL_VERIFY_MSG(
(level <= LOG_LEVEL_DBG) && (level >= LOG_LEVEL_NONE),
"Invalid log level"));
return z_impl_log_filter_set(NULL, domain_id, src_id, level);
}
#endif
static void backend_filter_set(struct log_backend const *const backend, static void backend_filter_set(struct log_backend const *const backend,
u32_t level) u32_t level)
{ {
if (IS_ENABLED(CONFIG_LOG_RUNTIME_FILTERING)) { if (IS_ENABLED(CONFIG_LOG_RUNTIME_FILTERING)) {
for (int i = 0; i < log_sources_count(); i++) { for (int i = 0; i < log_sources_count(); i++) {
log_filter_set(backend, log_filter_set(backend, CONFIG_LOG_DOMAIN_ID, i, level);
CONFIG_LOG_DOMAIN_ID,
i,
level);
} }
} }
} }
@ -802,7 +843,8 @@ char *log_strdup(const char *str)
struct log_strdup_buf *dup; struct log_strdup_buf *dup;
int err; int err;
if (IS_ENABLED(CONFIG_LOG_IMMEDIATE) || is_rodata(str)) { if (IS_ENABLED(CONFIG_LOG_IMMEDIATE) ||
is_rodata(str) || _is_user_context()) {
return (char *)str; return (char *)str;
} }
@ -865,6 +907,237 @@ void log_free(void *str)
} }
} }
#if defined(CONFIG_USERSPACE)
void z_impl_z_log_string_from_user(u32_t src_level_val, const char *str)
{
ARG_UNUSED(src_level_val);
ARG_UNUSED(str);
__ASSERT(false, "This function can be called from user mode only.");
}
Z_SYSCALL_HANDLER(z_log_string_from_user, src_level_val, user_string_ptr)
{
const char *str = (const char *)(user_string_ptr);
u8_t level, domain_id, source_id;
union {
struct log_msg_ids structure;
u32_t value;
} src_level_union;
size_t len;
int err;
src_level_union.value = src_level_val;
level = src_level_union.structure.level;
domain_id = src_level_union.structure.domain_id;
source_id = src_level_union.structure.source_id;
Z_OOPS(Z_SYSCALL_VERIFY_MSG(
(IS_ENABLED(CONFIG_LOG_PRINTK) || (level >= LOG_LEVEL_ERR)) &&
(level <= LOG_LEVEL_DBG),
"Invalid log level"));
Z_OOPS(Z_SYSCALL_VERIFY_MSG(domain_id == CONFIG_LOG_DOMAIN_ID,
"Invalid log domain_id"));
Z_OOPS(Z_SYSCALL_VERIFY_MSG(source_id < log_sources_count(),
"Invalid log source id"));
if (IS_ENABLED(CONFIG_LOG_RUNTIME_FILTERING) &&
(level != LOG_LEVEL_INTERNAL_RAW_STRING) &&
(level > LOG_FILTER_SLOT_GET(log_dynamic_filters_get(source_id),
LOG_FILTER_AGGR_SLOT_IDX))) {
/* Skip filtered out messages. */
return 0;
}
/*
* Validate and make a copy of the source string. Because we need
* the log subsystem to eventually free it, we're going to use
* log_strdup().
*/
len = z_user_string_nlen(str, (level == LOG_LEVEL_INTERNAL_RAW_STRING) ?
CONFIG_LOG_PRINTK_MAX_STRING_LENGTH :
CONFIG_LOG_STRDUP_MAX_STRING, &err);
Z_OOPS(Z_SYSCALL_VERIFY_MSG(err == 0, "invalid string passed in"));
Z_OOPS(Z_SYSCALL_MEMORY_READ(str, len));
if (IS_ENABLED(CONFIG_LOG_IMMEDIATE)) {
log_string_sync(src_level_union.structure, "%s", str);
} else if (IS_ENABLED(CONFIG_LOG_PRINTK) &&
(level == LOG_LEVEL_INTERNAL_RAW_STRING)) {
struct log_msg *msg;
msg = log_msg_hexdump_create(NULL, str, len);
if (msg != NULL) {
msg_finalize(msg, src_level_union.structure);
}
} else {
str = log_strdup(str);
log_1("%s", (log_arg_t)str, src_level_union.structure);
}
return 0;
}
void log_generic_from_user(struct log_msg_ids src_level,
const char *fmt, va_list ap)
{
char buffer[CONFIG_LOG_STRDUP_MAX_STRING + 1];
union {
struct log_msg_ids structure;
u32_t value;
} src_level_union;
vsnprintk(buffer, sizeof(buffer), fmt, ap);
__ASSERT_NO_MSG(sizeof(src_level) <= sizeof(u32_t));
src_level_union.structure = src_level;
z_log_string_from_user(src_level_union.value, buffer);
}
void log_from_user(struct log_msg_ids src_level, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
log_generic_from_user(src_level, fmt, ap);
va_end(ap);
}
void z_impl_z_log_hexdump_from_user(u32_t src_level_val, const char *metadata,
const u8_t *data, u32_t len)
{
ARG_UNUSED(src_level_val);
ARG_UNUSED(metadata);
ARG_UNUSED(data);
ARG_UNUSED(len);
__ASSERT(false, "This function can be called from user mode only.");
}
Z_SYSCALL_HANDLER(z_log_hexdump_from_user, src_level_val,
user_metadata_ptr, user_data_ptr, len)
{
const char *metadata = (const char *)(user_metadata_ptr);
const void *data = (const void *)(user_data_ptr);
union {
struct log_msg_ids structure;
u32_t value;
} src_level_union;
size_t mlen;
int err;
src_level_union.value = src_level_val;
Z_OOPS(Z_SYSCALL_VERIFY_MSG(
(src_level_union.structure.level <= LOG_LEVEL_DBG) &&
(src_level_union.structure.level >= LOG_LEVEL_ERR),
"Invalid log level"));
Z_OOPS(Z_SYSCALL_VERIFY_MSG(
src_level_union.structure.domain_id == CONFIG_LOG_DOMAIN_ID,
"Invalid log domain_id"));
Z_OOPS(Z_SYSCALL_VERIFY_MSG(
src_level_union.structure.source_id < log_sources_count(),
"Invalid log source id"));
if (IS_ENABLED(CONFIG_LOG_RUNTIME_FILTERING) &&
(src_level_union.structure.level > LOG_FILTER_SLOT_GET(
log_dynamic_filters_get(src_level_union.structure.source_id),
LOG_FILTER_AGGR_SLOT_IDX))) {
/* Skip filtered out messages. */
return 0;
}
/*
* Validate and make a copy of the metadata string. Because we
* need the log subsystem to eventually free it, we're going
* to use log_strdup().
*/
mlen = z_user_string_nlen(metadata, CONFIG_LOG_STRDUP_MAX_STRING, &err);
Z_OOPS(Z_SYSCALL_VERIFY_MSG(err == 0, "invalid string passed in"));
Z_OOPS(Z_SYSCALL_MEMORY_READ(metadata, mlen));
Z_OOPS(Z_SYSCALL_MEMORY_READ(data, len));
if (IS_ENABLED(CONFIG_LOG_IMMEDIATE)) {
log_hexdump_sync(src_level_union.structure,
metadata, data, len);
} else {
metadata = log_strdup(metadata);
log_hexdump(metadata, data, len, src_level_union.structure);
}
return 0;
}
void log_hexdump_from_user(struct log_msg_ids src_level, const char *metadata,
const u8_t *data, u32_t len)
{
union {
struct log_msg_ids structure;
u32_t value;
} src_level_union;
__ASSERT_NO_MSG(sizeof(src_level) <= sizeof(u32_t));
src_level_union.structure = src_level;
z_log_hexdump_from_user(src_level_union.value, metadata, data, len);
}
#elif defined(CONFIG_NO_OPTIMIZATIONS)
/*
* With the optimizations disabled, the dead code is not removed and
* references to functions used only by the usermode remains in the
* object files, causing linker error. To avoid this error, here we
* are providing stubs for affected API.
*/
void z_impl_z_log_string_from_user(u32_t src_level_val, const char *str)
{
ARG_UNUSED(src_level_val);
ARG_UNUSED(str);
__ASSERT_NO_MSG(false);
}
void z_impl_z_log_hexdump_from_user(u32_t src_level_val, const char *metadata,
const u8_t *data, u32_t len)
{
ARG_UNUSED(src_level_val);
ARG_UNUSED(metadata);
ARG_UNUSED(data);
ARG_UNUSED(len);
__ASSERT_NO_MSG(false);
}
void log_from_user(struct log_msg_ids src_level, const char *fmt, ...)
{
ARG_UNUSED(src_level);
ARG_UNUSED(fmt);
__ASSERT_NO_MSG(false);
}
void log_generic_from_user(struct log_msg_ids src_level,
const char *fmt, va_list ap)
{
ARG_UNUSED(src_level);
ARG_UNUSED(fmt);
ARG_UNUSED(ap);
__ASSERT_NO_MSG(false);
}
void log_hexdump_from_user(struct log_msg_ids src_level, const char *metadata,
const u8_t *data, u32_t len)
{
ARG_UNUSED(src_level);
ARG_UNUSED(metadata);
ARG_UNUSED(data);
ARG_UNUSED(len);
__ASSERT_NO_MSG(false);
}
#endif /* defined(CONFIG_NO_OPTIMIZATIONS) */
static void log_process_thread_func(void *dummy1, void *dummy2, void *dummy3) static void log_process_thread_func(void *dummy1, void *dummy2, void *dummy3)
{ {
__ASSERT_NO_MSG(log_backend_count_get() > 0); __ASSERT_NO_MSG(log_backend_count_get() > 0);

View file

@ -4,6 +4,7 @@
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
#include <kernel.h> #include <kernel.h>
#include <logging/log.h>
#include <logging/log_msg.h> #include <logging/log_msg.h>
#include <logging/log_ctrl.h> #include <logging/log_ctrl.h>
#include <logging/log_core.h> #include <logging/log_core.h>
@ -85,6 +86,17 @@ static void msg_free(struct log_msg *msg)
log_free(buf); log_free(buf);
} }
} }
} else if (IS_ENABLED(CONFIG_USERSPACE) &&
(log_msg_level_get(msg) != LOG_LEVEL_INTERNAL_RAW_STRING)) {
/*
* When userspace support is enabled, the hex message metadata
* might be located in log_strdup() memory pool.
*/
const char *str = log_msg_str_get(msg);
if (log_is_strdup(str)) {
log_free((void *)(str));
}
} }
if (msg->hdr.params.generic.ext == 1) { if (msg->hdr.params.generic.ext == 1) {

View file

@ -0,0 +1,5 @@
/*
* Copyright (c) 2019 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/

View file

@ -0,0 +1,5 @@
/*
* Copyright (c) 2019 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/