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.
351 lines
7.2 KiB
351 lines
7.2 KiB
/* |
|
* Copyright (c) 2018 Nordic Semiconductor ASA |
|
* |
|
* SPDX-License-Identifier: Apache-2.0 |
|
*/ |
|
|
|
#include <logging/log_output.h> |
|
#include <logging/log_ctrl.h> |
|
#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", |
|
"warn", |
|
"info", |
|
"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); |
|
extern void _vprintk(out_func_t out, void *ctx, const char *fmt, va_list ap); |
|
|
|
static int out_func(int c, void *ctx) |
|
{ |
|
struct log_output_ctx *out_ctx = (struct log_output_ctx *)ctx; |
|
|
|
out_ctx->data[out_ctx->offset] = (u8_t)c; |
|
out_ctx->offset++; |
|
|
|
if (out_ctx->offset == out_ctx->length) { |
|
out_ctx->func(out_ctx->data, out_ctx->length, out_ctx->ctx); |
|
out_ctx->offset = 0; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
static int print(struct log_output_ctx *ctx, const char *fmt, ...) |
|
{ |
|
va_list args; |
|
int length = 0; |
|
|
|
va_start(args, fmt); |
|
#ifndef CONFIG_NEWLIB_LIBC |
|
length = _prf(out_func, ctx, (char *)fmt, args); |
|
#else |
|
_vprintk(out_func, ctx, fmt, args); |
|
#endif |
|
va_end(args); |
|
|
|
return length; |
|
} |
|
|
|
static void flush(struct log_output_ctx *ctx) |
|
{ |
|
ctx->func(ctx->data, ctx->offset, ctx->ctx); |
|
} |
|
|
|
static int timestamp_print(struct log_msg *msg, |
|
struct log_output_ctx *ctx, |
|
bool format) |
|
{ |
|
int length; |
|
u32_t timestamp = log_msg_timestamp_get(msg); |
|
|
|
if (!format) { |
|
length = print(ctx, "[%08lu] ", timestamp); |
|
} else { |
|
u32_t reminder; |
|
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; |
|
|
|
reminder = timestamp % freq; |
|
ms = (reminder * 1000) / freq; |
|
us = (1000 * (1000 * reminder - (ms * freq))) / freq; |
|
|
|
length = print(ctx, "[%02d:%02d:%02d.%03d,%03d] ", |
|
hours, mins, seconds, ms, us); |
|
} |
|
|
|
return length; |
|
} |
|
|
|
static void color_print(struct log_msg *msg, |
|
struct log_output_ctx *ctx, |
|
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; |
|
|
|
print(ctx, "%s", color); |
|
} |
|
} |
|
} |
|
|
|
static void color_prefix(struct log_msg *msg, |
|
struct log_output_ctx *ctx, |
|
bool color) |
|
{ |
|
color_print(msg, ctx, color, true); |
|
} |
|
|
|
static void color_postfix(struct log_msg *msg, |
|
struct log_output_ctx *ctx, |
|
bool color) |
|
{ |
|
color_print(msg, ctx, color, false); |
|
} |
|
|
|
|
|
static int ids_print(struct log_msg *msg, |
|
struct log_output_ctx *ctx) |
|
{ |
|
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); |
|
|
|
return print(ctx, "<%s> %s: ", severity[level], |
|
log_source_name_get(domain_id, source_id)); |
|
} |
|
|
|
static void newline_print(struct log_output_ctx *ctx) |
|
{ |
|
print(ctx, "\r\n"); |
|
} |
|
|
|
static void std_print(struct log_msg *msg, |
|
struct log_output_ctx *ctx) |
|
{ |
|
const char *str = log_msg_str_get(msg); |
|
|
|
switch (log_msg_nargs_get(msg)) { |
|
case 0: |
|
print(ctx, str); |
|
break; |
|
case 1: |
|
print(ctx, str, log_msg_arg_get(msg, 0)); |
|
break; |
|
case 2: |
|
print(ctx, str, |
|
log_msg_arg_get(msg, 0), |
|
log_msg_arg_get(msg, 1)); |
|
break; |
|
case 3: |
|
print(ctx, str, |
|
log_msg_arg_get(msg, 0), |
|
log_msg_arg_get(msg, 1), |
|
log_msg_arg_get(msg, 2)); |
|
break; |
|
case 4: |
|
print(ctx, str, |
|
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: |
|
print(ctx, str, |
|
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: |
|
print(ctx, str, |
|
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; |
|
} |
|
} |
|
|
|
static u32_t hexdump_line_print(struct log_msg *msg, |
|
struct log_output_ctx *ctx, |
|
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) { |
|
if (offset > 0) { |
|
newline_print(ctx); |
|
for (int i = 0; i < prefix_offset; i++) { |
|
print(ctx, " "); |
|
} |
|
} |
|
|
|
for (int i = 0; i < HEXDUMP_BYTES_IN_LINE; i++) { |
|
if (i < length) { |
|
print(ctx, " %02x", buf[i]); |
|
} else { |
|
print(ctx, " "); |
|
} |
|
} |
|
|
|
print(ctx, "|"); |
|
|
|
for (int i = 0; i < HEXDUMP_BYTES_IN_LINE; i++) { |
|
if (i < length) { |
|
char c = (char)buf[i]; |
|
|
|
print(ctx, "%c", isprint((int)c) ? c : '.'); |
|
} else { |
|
print(ctx, " "); |
|
} |
|
} |
|
} |
|
|
|
return length; |
|
} |
|
|
|
static void hexdump_print(struct log_msg *msg, |
|
struct log_output_ctx *ctx, |
|
int prefix_offset) |
|
{ |
|
u32_t offset = 0; |
|
u32_t length; |
|
|
|
do { |
|
length = hexdump_line_print(msg, ctx, prefix_offset, offset); |
|
|
|
if (length < HEXDUMP_BYTES_IN_LINE) { |
|
break; |
|
} |
|
|
|
offset += length; |
|
} while (1); |
|
} |
|
|
|
static void raw_string_print(struct log_msg *msg, |
|
struct log_output_ctx *ctx) |
|
{ |
|
size_t offset = 0; |
|
size_t length; |
|
|
|
while (length > 0) { |
|
length = ctx->length; |
|
log_msg_hexdump_data_get(msg, ctx->data, &length, offset); |
|
offset += length; |
|
ctx->func(ctx->data, length, ctx->ctx); |
|
} |
|
|
|
print(ctx, "\r"); |
|
} |
|
|
|
static int prefix_print(struct log_msg *msg, |
|
struct log_output_ctx *ctx, |
|
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; |
|
|
|
length += timestamp_print(msg, ctx, stamp_format); |
|
color_prefix(msg, ctx, colors_on); |
|
length += ids_print(msg, ctx); |
|
} |
|
|
|
return length; |
|
} |
|
|
|
static void postfix_print(struct log_msg *msg, |
|
struct log_output_ctx *ctx, |
|
u32_t flags) |
|
{ |
|
if (!log_msg_is_raw_string(msg)) { |
|
color_postfix(msg, ctx, (flags & LOG_OUTPUT_FLAG_COLORS)); |
|
newline_print(ctx); |
|
} |
|
} |
|
|
|
void log_output_msg_process(struct log_msg *msg, |
|
struct log_output_ctx *ctx, |
|
u32_t flags) |
|
{ |
|
int prefix_offset = prefix_print(msg, ctx, flags); |
|
|
|
if (log_msg_is_std(msg)) { |
|
std_print(msg, ctx); |
|
} else if (log_msg_is_raw_string(msg)) { |
|
raw_string_print(msg, ctx); |
|
} else { |
|
hexdump_print(msg, ctx, prefix_offset); |
|
} |
|
|
|
postfix_print(msg, ctx, flags); |
|
|
|
flush(ctx); |
|
} |
|
|
|
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; |
|
}
|
|
|