2018-05-16 08:50:33 +02:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2018 Nordic Semiconductor ASA
|
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <logging/log_output.h>
|
|
|
|
#include <logging/log_ctrl.h>
|
2018-07-09 16:12:53 +02:00
|
|
|
#include <assert.h>
|
2018-05-16 08:50:33 +02:00
|
|
|
#include <ctype.h>
|
|
|
|
|
|
|
|
#define HEXDUMP_BYTES_IN_LINE 8
|
|
|
|
|
|
|
|
#define LOG_COLOR_CODE_DEFAULT "\x1B[0m"
|
|
|
|
#define LOG_COLOR_CODE_BLACK "\x1B[1;30m"
|
|
|
|
#define LOG_COLOR_CODE_RED "\x1B[1;31m"
|
|
|
|
#define LOG_COLOR_CODE_GREEN "\x1B[1;32m"
|
|
|
|
#define LOG_COLOR_CODE_YELLOW "\x1B[1;33m"
|
|
|
|
#define LOG_COLOR_CODE_BLUE "\x1B[1;34m"
|
|
|
|
#define LOG_COLOR_CODE_MAGENTA "\x1B[1;35m"
|
|
|
|
#define LOG_COLOR_CODE_CYAN "\x1B[1;36m"
|
|
|
|
#define LOG_COLOR_CODE_WHITE "\x1B[1;37m"
|
|
|
|
|
|
|
|
static const char *const severity[] = {
|
|
|
|
NULL,
|
|
|
|
"err",
|
2018-07-19 21:58:26 +03:00
|
|
|
"wrn",
|
|
|
|
"inf",
|
2018-05-16 08:50:33 +02:00
|
|
|
"dbg"
|
|
|
|
};
|
|
|
|
|
|
|
|
static const char *const colors[] = {
|
|
|
|
NULL,
|
|
|
|
LOG_COLOR_CODE_RED, /* err */
|
|
|
|
LOG_COLOR_CODE_YELLOW, /* warn */
|
|
|
|
NULL, /* info */
|
|
|
|
NULL /* dbg */
|
|
|
|
};
|
|
|
|
|
|
|
|
static u32_t freq;
|
|
|
|
static u32_t timestamp_div;
|
|
|
|
|
|
|
|
typedef int (*out_func_t)(int c, void *ctx);
|
|
|
|
|
|
|
|
extern int _prf(int (*func)(), void *dest, char *format, va_list vargs);
|
2018-08-01 14:57:41 +02:00
|
|
|
extern void _vprintk(out_func_t out, void *log_output, const char *fmt, va_list ap);
|
2018-05-16 08:50:33 +02:00
|
|
|
|
|
|
|
static int out_func(int c, void *ctx)
|
|
|
|
{
|
2018-08-01 14:57:41 +02:00
|
|
|
const struct log_output *out_ctx =
|
|
|
|
(const struct log_output *)ctx;
|
2018-05-16 08:50:33 +02:00
|
|
|
|
2018-08-01 14:57:41 +02:00
|
|
|
out_ctx->buf[out_ctx->control_block->offset] = (u8_t)c;
|
|
|
|
out_ctx->control_block->offset++;
|
2018-05-16 08:50:33 +02:00
|
|
|
|
2018-08-01 14:57:41 +02:00
|
|
|
assert(out_ctx->control_block->offset <= out_ctx->size);
|
|
|
|
|
|
|
|
if (out_ctx->control_block->offset == out_ctx->size) {
|
|
|
|
log_output_flush(out_ctx);
|
2018-05-16 08:50:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-08-01 14:57:41 +02:00
|
|
|
static int print_formatted(const struct log_output *log_output,
|
|
|
|
const char *fmt, ...)
|
2018-05-16 08:50:33 +02:00
|
|
|
{
|
|
|
|
va_list args;
|
|
|
|
int length = 0;
|
|
|
|
|
|
|
|
va_start(args, fmt);
|
2018-09-13 12:52:32 +03:00
|
|
|
#if !defined(CONFIG_NEWLIB_LIBC) && !defined(CONFIG_ARCH_POSIX)
|
2018-08-01 14:57:41 +02:00
|
|
|
length = _prf(out_func, (void *)log_output, (char *)fmt, args);
|
2018-05-16 08:50:33 +02:00
|
|
|
#else
|
2018-08-01 14:57:41 +02:00
|
|
|
_vprintk(out_func, (void *)log_output, fmt, args);
|
2018-05-16 08:50:33 +02:00
|
|
|
#endif
|
|
|
|
va_end(args);
|
|
|
|
|
|
|
|
return length;
|
|
|
|
}
|
|
|
|
|
2018-08-01 14:57:41 +02:00
|
|
|
void log_output_flush(const struct log_output *log_output)
|
2018-05-16 08:50:33 +02:00
|
|
|
{
|
2018-08-01 14:57:41 +02:00
|
|
|
int offset = 0;
|
|
|
|
int len = log_output->control_block->offset;
|
|
|
|
int processed;
|
|
|
|
|
|
|
|
do {
|
|
|
|
processed = log_output->func(&log_output->buf[offset], len,
|
|
|
|
log_output->control_block->ctx);
|
|
|
|
len -= processed;
|
|
|
|
offset += processed;
|
|
|
|
} while (len);
|
|
|
|
|
|
|
|
log_output->control_block->offset = 0;
|
2018-05-16 08:50:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static int timestamp_print(struct log_msg *msg,
|
2018-08-01 14:57:41 +02:00
|
|
|
const struct log_output *log_output,
|
2018-05-16 08:50:33 +02:00
|
|
|
bool format)
|
|
|
|
{
|
|
|
|
int length;
|
|
|
|
u32_t timestamp = log_msg_timestamp_get(msg);
|
|
|
|
|
|
|
|
if (!format) {
|
2018-08-01 14:57:41 +02:00
|
|
|
length = print_formatted(log_output, "[%08lu] ", timestamp);
|
2018-07-11 13:25:58 +02:00
|
|
|
} else if (freq) {
|
2018-08-01 14:57:41 +02:00
|
|
|
u32_t remainder;
|
2018-05-16 08:50:33 +02:00
|
|
|
u32_t seconds;
|
|
|
|
u32_t hours;
|
|
|
|
u32_t mins;
|
|
|
|
u32_t ms;
|
|
|
|
u32_t us;
|
|
|
|
|
|
|
|
timestamp /= timestamp_div;
|
|
|
|
seconds = timestamp / freq;
|
|
|
|
hours = seconds / 3600;
|
|
|
|
seconds -= hours * 3600;
|
|
|
|
mins = seconds / 60;
|
|
|
|
seconds -= mins * 60;
|
|
|
|
|
2018-08-01 14:57:41 +02:00
|
|
|
remainder = timestamp % freq;
|
|
|
|
ms = (remainder * 1000) / freq;
|
|
|
|
us = (1000 * (1000 * remainder - (ms * freq))) / freq;
|
2018-05-16 08:50:33 +02:00
|
|
|
|
2018-08-01 14:57:41 +02:00
|
|
|
length = print_formatted(log_output,
|
|
|
|
"[%02d:%02d:%02d.%03d,%03d] ",
|
|
|
|
hours, mins, seconds, ms, us);
|
2018-07-11 13:25:58 +02:00
|
|
|
} else {
|
|
|
|
length = 0;
|
2018-05-16 08:50:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return length;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void color_print(struct log_msg *msg,
|
2018-08-01 14:57:41 +02:00
|
|
|
const struct log_output *log_output,
|
2018-05-16 08:50:33 +02:00
|
|
|
bool color,
|
|
|
|
bool start)
|
|
|
|
{
|
|
|
|
if (color) {
|
|
|
|
u32_t level = log_msg_level_get(msg);
|
|
|
|
|
|
|
|
if (colors[level] != NULL) {
|
|
|
|
const char *color = start ?
|
|
|
|
colors[level] : LOG_COLOR_CODE_DEFAULT;
|
|
|
|
|
2018-08-01 14:57:41 +02:00
|
|
|
print_formatted(log_output, "%s", color);
|
2018-05-16 08:50:33 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void color_prefix(struct log_msg *msg,
|
2018-08-01 14:57:41 +02:00
|
|
|
const struct log_output *log_output,
|
2018-05-16 08:50:33 +02:00
|
|
|
bool color)
|
|
|
|
{
|
2018-08-01 14:57:41 +02:00
|
|
|
color_print(msg, log_output, color, true);
|
2018-05-16 08:50:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void color_postfix(struct log_msg *msg,
|
2018-08-01 14:57:41 +02:00
|
|
|
const struct log_output *log_output,
|
2018-05-16 08:50:33 +02:00
|
|
|
bool color)
|
|
|
|
{
|
2018-08-01 14:57:41 +02:00
|
|
|
color_print(msg, log_output, color, false);
|
2018-05-16 08:50:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int ids_print(struct log_msg *msg,
|
2018-08-01 14:57:41 +02:00
|
|
|
const struct log_output *log_output)
|
2018-05-16 08:50:33 +02:00
|
|
|
{
|
|
|
|
u32_t domain_id = log_msg_domain_id_get(msg);
|
|
|
|
u32_t source_id = log_msg_source_id_get(msg);
|
|
|
|
u32_t level = log_msg_level_get(msg);
|
|
|
|
|
2018-08-01 14:57:41 +02:00
|
|
|
return print_formatted(log_output, "<%s> %s: ", severity[level],
|
2018-05-16 08:50:33 +02:00
|
|
|
log_source_name_get(domain_id, source_id));
|
|
|
|
}
|
|
|
|
|
2018-08-01 14:57:41 +02:00
|
|
|
static void newline_print(const struct log_output *ctx)
|
2018-05-16 08:50:33 +02:00
|
|
|
{
|
2018-08-01 14:57:41 +02:00
|
|
|
print_formatted(ctx, "\r\n");
|
2018-05-16 08:50:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void std_print(struct log_msg *msg,
|
2018-08-01 14:57:41 +02:00
|
|
|
const struct log_output *log_output)
|
2018-05-16 08:50:33 +02:00
|
|
|
{
|
|
|
|
const char *str = log_msg_str_get(msg);
|
|
|
|
|
|
|
|
switch (log_msg_nargs_get(msg)) {
|
|
|
|
case 0:
|
2018-08-01 14:57:41 +02:00
|
|
|
print_formatted(log_output, str);
|
2018-05-16 08:50:33 +02:00
|
|
|
break;
|
|
|
|
case 1:
|
2018-08-01 14:57:41 +02:00
|
|
|
print_formatted(log_output, str, log_msg_arg_get(msg, 0));
|
2018-05-16 08:50:33 +02:00
|
|
|
break;
|
|
|
|
case 2:
|
2018-08-01 14:57:41 +02:00
|
|
|
print_formatted(log_output, str,
|
2018-05-16 08:50:33 +02:00
|
|
|
log_msg_arg_get(msg, 0),
|
|
|
|
log_msg_arg_get(msg, 1));
|
|
|
|
break;
|
|
|
|
case 3:
|
2018-08-01 14:57:41 +02:00
|
|
|
print_formatted(log_output, str,
|
2018-05-16 08:50:33 +02:00
|
|
|
log_msg_arg_get(msg, 0),
|
|
|
|
log_msg_arg_get(msg, 1),
|
|
|
|
log_msg_arg_get(msg, 2));
|
|
|
|
break;
|
|
|
|
case 4:
|
2018-08-01 14:57:41 +02:00
|
|
|
print_formatted(log_output, str,
|
2018-05-16 08:50:33 +02:00
|
|
|
log_msg_arg_get(msg, 0),
|
|
|
|
log_msg_arg_get(msg, 1),
|
|
|
|
log_msg_arg_get(msg, 2),
|
|
|
|
log_msg_arg_get(msg, 3));
|
|
|
|
break;
|
|
|
|
case 5:
|
2018-08-01 14:57:41 +02:00
|
|
|
print_formatted(log_output, str,
|
2018-05-16 08:50:33 +02:00
|
|
|
log_msg_arg_get(msg, 0),
|
|
|
|
log_msg_arg_get(msg, 1),
|
|
|
|
log_msg_arg_get(msg, 2),
|
|
|
|
log_msg_arg_get(msg, 3),
|
|
|
|
log_msg_arg_get(msg, 4));
|
|
|
|
break;
|
|
|
|
case 6:
|
2018-08-01 14:57:41 +02:00
|
|
|
print_formatted(log_output, str,
|
2018-05-16 08:50:33 +02:00
|
|
|
log_msg_arg_get(msg, 0),
|
|
|
|
log_msg_arg_get(msg, 1),
|
|
|
|
log_msg_arg_get(msg, 2),
|
|
|
|
log_msg_arg_get(msg, 3),
|
|
|
|
log_msg_arg_get(msg, 4),
|
|
|
|
log_msg_arg_get(msg, 5));
|
|
|
|
break;
|
2018-07-09 16:52:35 +03:00
|
|
|
case 7:
|
2018-08-01 14:57:41 +02:00
|
|
|
print_formatted(log_output, str,
|
2018-07-09 16:52:35 +03:00
|
|
|
log_msg_arg_get(msg, 0),
|
|
|
|
log_msg_arg_get(msg, 1),
|
|
|
|
log_msg_arg_get(msg, 2),
|
|
|
|
log_msg_arg_get(msg, 3),
|
|
|
|
log_msg_arg_get(msg, 4),
|
|
|
|
log_msg_arg_get(msg, 5),
|
|
|
|
log_msg_arg_get(msg, 6));
|
|
|
|
break;
|
|
|
|
case 8:
|
2018-08-01 14:57:41 +02:00
|
|
|
print_formatted(log_output, str,
|
2018-07-09 16:52:35 +03:00
|
|
|
log_msg_arg_get(msg, 0),
|
|
|
|
log_msg_arg_get(msg, 1),
|
|
|
|
log_msg_arg_get(msg, 2),
|
|
|
|
log_msg_arg_get(msg, 3),
|
|
|
|
log_msg_arg_get(msg, 4),
|
|
|
|
log_msg_arg_get(msg, 5),
|
|
|
|
log_msg_arg_get(msg, 6),
|
|
|
|
log_msg_arg_get(msg, 7));
|
|
|
|
break;
|
|
|
|
case 9:
|
2018-08-01 14:57:41 +02:00
|
|
|
print_formatted(log_output, str,
|
2018-07-09 16:52:35 +03:00
|
|
|
log_msg_arg_get(msg, 0),
|
|
|
|
log_msg_arg_get(msg, 1),
|
|
|
|
log_msg_arg_get(msg, 2),
|
|
|
|
log_msg_arg_get(msg, 3),
|
|
|
|
log_msg_arg_get(msg, 4),
|
|
|
|
log_msg_arg_get(msg, 5),
|
|
|
|
log_msg_arg_get(msg, 6),
|
|
|
|
log_msg_arg_get(msg, 7),
|
|
|
|
log_msg_arg_get(msg, 8));
|
|
|
|
break;
|
2018-08-01 14:57:41 +02:00
|
|
|
default:
|
|
|
|
/* Unsupported number of arguments. */
|
|
|
|
assert(true);
|
2018-05-16 08:50:33 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static u32_t hexdump_line_print(struct log_msg *msg,
|
2018-08-01 14:57:41 +02:00
|
|
|
const struct log_output *log_output,
|
2018-05-16 08:50:33 +02:00
|
|
|
int prefix_offset,
|
|
|
|
u32_t offset)
|
|
|
|
{
|
|
|
|
u8_t buf[HEXDUMP_BYTES_IN_LINE];
|
|
|
|
size_t length = sizeof(buf);
|
|
|
|
|
|
|
|
log_msg_hexdump_data_get(msg, buf, &length, offset);
|
|
|
|
|
|
|
|
if (length > 0) {
|
2018-08-01 14:57:41 +02:00
|
|
|
newline_print(log_output);
|
2018-07-30 08:54:51 +02:00
|
|
|
|
|
|
|
for (int i = 0; i < prefix_offset; i++) {
|
2018-08-01 14:57:41 +02:00
|
|
|
print_formatted(log_output, " ");
|
2018-05-16 08:50:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 0; i < HEXDUMP_BYTES_IN_LINE; i++) {
|
|
|
|
if (i < length) {
|
2018-08-01 14:57:41 +02:00
|
|
|
print_formatted(log_output, "%02x ", buf[i]);
|
2018-05-16 08:50:33 +02:00
|
|
|
} else {
|
2018-08-01 14:57:41 +02:00
|
|
|
print_formatted(log_output, " ");
|
2018-05-16 08:50:33 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-01 14:57:41 +02:00
|
|
|
print_formatted(log_output, "|");
|
2018-05-16 08:50:33 +02:00
|
|
|
|
|
|
|
for (int i = 0; i < HEXDUMP_BYTES_IN_LINE; i++) {
|
|
|
|
if (i < length) {
|
|
|
|
char c = (char)buf[i];
|
|
|
|
|
2018-08-01 14:57:41 +02:00
|
|
|
print_formatted(log_output, "%c",
|
|
|
|
isprint((int)c) ? c : '.');
|
2018-05-16 08:50:33 +02:00
|
|
|
} else {
|
2018-08-01 14:57:41 +02:00
|
|
|
print_formatted(log_output, " ");
|
2018-05-16 08:50:33 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return length;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void hexdump_print(struct log_msg *msg,
|
2018-08-01 14:57:41 +02:00
|
|
|
const struct log_output *log_output,
|
2018-05-16 08:50:33 +02:00
|
|
|
int prefix_offset)
|
|
|
|
{
|
|
|
|
u32_t offset = 0;
|
|
|
|
u32_t length;
|
|
|
|
|
2018-08-01 14:57:41 +02:00
|
|
|
print_formatted(log_output, "%s", log_msg_str_get(msg));
|
2018-07-30 08:54:51 +02:00
|
|
|
|
2018-05-16 08:50:33 +02:00
|
|
|
do {
|
2018-08-01 14:57:41 +02:00
|
|
|
length = hexdump_line_print(msg, log_output, prefix_offset,
|
|
|
|
offset);
|
2018-05-16 08:50:33 +02:00
|
|
|
|
|
|
|
if (length < HEXDUMP_BYTES_IN_LINE) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
offset += length;
|
|
|
|
} while (1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void raw_string_print(struct log_msg *msg,
|
2018-08-01 14:57:41 +02:00
|
|
|
const struct log_output *log_output)
|
2018-05-16 08:50:33 +02:00
|
|
|
{
|
2018-08-01 14:57:41 +02:00
|
|
|
assert(log_output->size);
|
2018-07-09 16:12:53 +02:00
|
|
|
|
2018-05-16 08:50:33 +02:00
|
|
|
size_t offset = 0;
|
|
|
|
size_t length;
|
|
|
|
|
2018-07-09 16:12:53 +02:00
|
|
|
do {
|
2018-08-01 14:57:41 +02:00
|
|
|
length = log_output->size;
|
|
|
|
/* Sting is stored in a hexdump message. */
|
|
|
|
log_msg_hexdump_data_get(msg, log_output->buf, &length, offset);
|
|
|
|
log_output->control_block->offset = length;
|
|
|
|
log_output_flush(log_output);
|
2018-05-16 08:50:33 +02:00
|
|
|
offset += length;
|
2018-07-09 16:12:53 +02:00
|
|
|
} while (length > 0);
|
2018-05-16 08:50:33 +02:00
|
|
|
|
2018-08-01 14:57:41 +02:00
|
|
|
print_formatted(log_output, "\r");
|
2018-05-16 08:50:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static int prefix_print(struct log_msg *msg,
|
2018-08-01 14:57:41 +02:00
|
|
|
const struct log_output *log_output,
|
2018-05-16 08:50:33 +02:00
|
|
|
u32_t flags)
|
|
|
|
{
|
|
|
|
int length = 0;
|
|
|
|
|
|
|
|
if (!log_msg_is_raw_string(msg)) {
|
|
|
|
bool stamp_format = flags & LOG_OUTPUT_FLAG_FORMAT_TIMESTAMP;
|
|
|
|
bool colors_on = flags & LOG_OUTPUT_FLAG_COLORS;
|
|
|
|
|
2018-08-01 14:57:41 +02:00
|
|
|
length += timestamp_print(msg, log_output, stamp_format);
|
|
|
|
color_prefix(msg, log_output, colors_on);
|
|
|
|
length += ids_print(msg, log_output);
|
2018-05-16 08:50:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return length;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void postfix_print(struct log_msg *msg,
|
2018-08-01 14:57:41 +02:00
|
|
|
const struct log_output *log_output,
|
2018-05-16 08:50:33 +02:00
|
|
|
u32_t flags)
|
|
|
|
{
|
|
|
|
if (!log_msg_is_raw_string(msg)) {
|
2018-08-01 14:57:41 +02:00
|
|
|
color_postfix(msg, log_output,
|
|
|
|
(flags & LOG_OUTPUT_FLAG_COLORS));
|
|
|
|
newline_print(log_output);
|
2018-05-16 08:50:33 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-01 14:57:41 +02:00
|
|
|
void log_output_msg_process(const struct log_output *log_output,
|
|
|
|
struct log_msg *msg,
|
2018-05-16 08:50:33 +02:00
|
|
|
u32_t flags)
|
|
|
|
{
|
2018-08-01 14:57:41 +02:00
|
|
|
int prefix_offset = prefix_print(msg, log_output, flags);
|
2018-05-16 08:50:33 +02:00
|
|
|
|
|
|
|
if (log_msg_is_std(msg)) {
|
2018-08-01 14:57:41 +02:00
|
|
|
std_print(msg, log_output);
|
2018-05-16 08:50:33 +02:00
|
|
|
} else if (log_msg_is_raw_string(msg)) {
|
2018-08-01 14:57:41 +02:00
|
|
|
raw_string_print(msg, log_output);
|
2018-05-16 08:50:33 +02:00
|
|
|
} else {
|
2018-08-01 14:57:41 +02:00
|
|
|
hexdump_print(msg, log_output, prefix_offset);
|
2018-05-16 08:50:33 +02:00
|
|
|
}
|
|
|
|
|
2018-08-01 14:57:41 +02:00
|
|
|
postfix_print(msg, log_output, flags);
|
2018-05-16 08:50:33 +02:00
|
|
|
|
2018-08-01 14:57:41 +02:00
|
|
|
log_output_flush(log_output);
|
2018-05-16 08:50:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void log_output_timestamp_freq_set(u32_t frequency)
|
|
|
|
{
|
|
|
|
timestamp_div = 1;
|
|
|
|
/* There is no point to have frequency higher than 1MHz (ns are not
|
|
|
|
* printed) and too high frequency leads to overflows in calculations.
|
|
|
|
*/
|
|
|
|
while (frequency > 1000000) {
|
|
|
|
frequency /= 2;
|
|
|
|
timestamp_div *= 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
freq = frequency;
|
|
|
|
}
|