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:
parent
78eb718396
commit
892ab4e356
7 changed files with 399 additions and 43 deletions
|
@ -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
|
||||||
|
|
|
@ -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>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @}
|
* @}
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
5
subsys/testsuite/ztest/include/syscalls/log_core.h
Normal file
5
subsys/testsuite/ztest/include/syscalls/log_core.h
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019 Nordic Semiconductor ASA
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
5
subsys/testsuite/ztest/include/syscalls/log_ctrl.h
Normal file
5
subsys/testsuite/ztest/include/syscalls/log_ctrl.h
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019 Nordic Semiconductor ASA
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
Loading…
Add table
Add a link
Reference in a new issue