123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762 |
- /*
- * Copyright (c) 2018 Nordic Semiconductor ASA
- *
- * SPDX-License-Identifier: Apache-2.0
- */
- #include <logging/log_output.h>
- #include <logging/log_ctrl.h>
- #include <logging/log.h>
- #include <sys/__assert.h>
- #include <sys/cbprintf.h>
- #include <ctype.h>
- #include <time.h>
- #include <stdio.h>
- #include <stdbool.h>
- #define LOG_COLOR_CODE_DEFAULT "\x1B[0m"
- #define LOG_COLOR_CODE_RED "\x1B[1;31m"
- #define LOG_COLOR_CODE_YELLOW "\x1B[1;33m"
- #define HEXDUMP_BYTES_IN_LINE 16
- #define DROPPED_COLOR_PREFIX \
- Z_LOG_EVAL(CONFIG_LOG_BACKEND_SHOW_COLOR, (LOG_COLOR_CODE_RED), ())
- #define DROPPED_COLOR_POSTFIX \
- Z_LOG_EVAL(CONFIG_LOG_BACKEND_SHOW_COLOR, (LOG_COLOR_CODE_DEFAULT), ())
- static const char *const severity[] = {
- NULL,
- "err",
- "wrn",
- "inf",
- "dbg"
- };
- static const char *const colors[] = {
- NULL,
- LOG_COLOR_CODE_RED, /* err */
- LOG_COLOR_CODE_YELLOW, /* warn */
- NULL, /* info */
- NULL /* dbg */
- };
- static uint32_t freq;
- static uint32_t timestamp_div;
- extern void log_output_msg_syst_process(const struct log_output *output,
- struct log_msg *msg, uint32_t flag);
- extern void log_output_string_syst_process(const struct log_output *output,
- struct log_msg_ids src_level,
- const char *fmt, va_list ap, uint32_t flag);
- extern void log_output_hexdump_syst_process(const struct log_output *output,
- struct log_msg_ids src_level,
- const uint8_t *data, uint32_t length, uint32_t flag);
- /* The RFC 5424 allows very flexible mapping and suggest the value 0 being the
- * highest severity and 7 to be the lowest (debugging level) severity.
- *
- * 0 Emergency System is unusable
- * 1 Alert Action must be taken immediately
- * 2 Critical Critical conditions
- * 3 Error Error conditions
- * 4 Warning Warning conditions
- * 5 Notice Normal but significant condition
- * 6 Informational Informational messages
- * 7 Debug Debug-level messages
- */
- static int level_to_rfc5424_severity(uint32_t level)
- {
- uint8_t ret;
- switch (level) {
- case LOG_LEVEL_NONE:
- ret = 7U;
- break;
- case LOG_LEVEL_ERR:
- ret = 3U;
- break;
- case LOG_LEVEL_WRN:
- ret = 4U;
- break;
- case LOG_LEVEL_INF:
- ret = 6U;
- break;
- case LOG_LEVEL_DBG:
- ret = 7U;
- break;
- default:
- ret = 7U;
- break;
- }
- return ret;
- }
- static int out_func(int c, void *ctx)
- {
- const struct log_output *out_ctx = (const struct log_output *)ctx;
- int idx;
- if (IS_ENABLED(CONFIG_LOG_IMMEDIATE)) {
- /* Backend must be thread safe in synchronous operation. */
- out_ctx->func((uint8_t *)&c, 1, out_ctx->control_block->ctx);
- return 0;
- }
- if (out_ctx->control_block->offset == out_ctx->size) {
- log_output_flush(out_ctx);
- }
- idx = atomic_inc(&out_ctx->control_block->offset);
- out_ctx->buf[idx] = (uint8_t)c;
- __ASSERT_NO_MSG(out_ctx->control_block->offset <= out_ctx->size);
- return 0;
- }
- static int cr_out_func(int c, void *ctx)
- {
- out_func(c, ctx);
- if (c == '\n') {
- out_func((int)'\r', ctx);
- }
- return 0;
- }
- static int print_formatted(const struct log_output *output,
- const char *fmt, ...)
- {
- va_list args;
- int length = 0;
- va_start(args, fmt);
- length = cbvprintf(out_func, (void *)output, fmt, args);
- va_end(args);
- return length;
- }
- static void buffer_write(log_output_func_t outf, uint8_t *buf, size_t len,
- void *ctx)
- {
- int processed;
- do {
- processed = outf(buf, len, ctx);
- len -= processed;
- buf += processed;
- } while (len != 0);
- }
- void log_output_flush(const struct log_output *output)
- {
- buffer_write(output->func, output->buf,
- output->control_block->offset,
- output->control_block->ctx);
- output->control_block->offset = 0;
- }
- static int timestamp_print(const struct log_output *output,
- uint32_t flags, uint32_t timestamp)
- {
- int length;
- bool format =
- (flags & LOG_OUTPUT_FLAG_FORMAT_TIMESTAMP) |
- (flags & LOG_OUTPUT_FLAG_FORMAT_SYSLOG);
- if (!format) {
- length = print_formatted(output, "[%08lu] ", timestamp);
- } else if (freq != 0U) {
- uint32_t total_seconds;
- uint32_t remainder;
- uint32_t seconds;
- uint32_t hours;
- uint32_t mins;
- uint32_t ms;
- uint32_t us;
- timestamp /= timestamp_div;
- total_seconds = timestamp / freq;
- seconds = total_seconds;
- hours = seconds / 3600U;
- seconds -= hours * 3600U;
- mins = seconds / 60U;
- seconds -= mins * 60U;
- remainder = timestamp % freq;
- ms = (remainder * 1000U) / freq;
- us = (1000 * (remainder * 1000U - (ms * freq))) / freq;
- if (IS_ENABLED(CONFIG_LOG_BACKEND_NET) &&
- flags & LOG_OUTPUT_FLAG_FORMAT_SYSLOG) {
- #if defined(CONFIG_NEWLIB_LIBC)
- char time_str[sizeof("1970-01-01T00:00:00")];
- struct tm *tm;
- time_t time;
- time = total_seconds;
- tm = gmtime(&time);
- strftime(time_str, sizeof(time_str), "%FT%T", tm);
- length = print_formatted(output, "%s.%06uZ ",
- time_str, ms * 1000U + us);
- #else
- length = print_formatted(output,
- "1970-01-01T%02u:%02u:%02u.%06uZ ",
- hours, mins, seconds, ms * 1000U + us);
- #endif
- } else {
- length = print_formatted(output,
- "[%02u:%02u:%02u.%03u,%03u] ",
- hours, mins, seconds, ms, us);
- }
- } else {
- length = 0;
- }
- return length;
- }
- static void color_print(const struct log_output *output,
- bool color, bool start, uint32_t level)
- {
- if (color) {
- const char *log_color = start && (colors[level] != NULL) ?
- colors[level] : LOG_COLOR_CODE_DEFAULT;
- print_formatted(output, "%s", log_color);
- }
- }
- static void color_prefix(const struct log_output *output,
- bool color, uint32_t level)
- {
- color_print(output, color, true, level);
- }
- static void color_postfix(const struct log_output *output,
- bool color, uint32_t level)
- {
- color_print(output, color, false, level);
- }
- static int ids_print(const struct log_output *output, bool level_on,
- bool func_on, uint32_t domain_id, int16_t source_id,
- uint32_t level)
- {
- int total = 0;
- if (level_on) {
- total += print_formatted(output, "<%s> ", severity[level]);
- }
- if (source_id >= 0) {
- total += print_formatted(output,
- (func_on &&
- ((1 << level) & LOG_FUNCTION_PREFIX_MASK)) ?
- "%s." : "%s: ",
- log_source_name_get(domain_id, source_id));
- }
- return total;
- }
- static void newline_print(const struct log_output *ctx, uint32_t flags)
- {
- if (IS_ENABLED(CONFIG_LOG_BACKEND_NET) &&
- flags & LOG_OUTPUT_FLAG_FORMAT_SYSLOG) {
- return;
- }
- if ((flags & LOG_OUTPUT_FLAG_CRLF_NONE) != 0U) {
- return;
- }
- if ((flags & LOG_OUTPUT_FLAG_CRLF_LFONLY) != 0U) {
- print_formatted(ctx, "\n");
- } else {
- print_formatted(ctx, "\r\n");
- }
- }
- static void std_print(struct log_msg *msg,
- const struct log_output *output)
- {
- const char *str = log_msg_str_get(msg);
- uint32_t nargs = log_msg_nargs_get(msg);
- log_arg_t *args = alloca(sizeof(log_arg_t)*nargs);
- int i;
- for (i = 0; i < nargs; i++) {
- args[i] = log_msg_arg_get(msg, i);
- }
- switch (log_msg_nargs_get(msg)) {
- case 0:
- print_formatted(output, str);
- break;
- case 1:
- print_formatted(output, str, args[0]);
- break;
- case 2:
- print_formatted(output, str, args[0], args[1]);
- break;
- case 3:
- print_formatted(output, str, args[0], args[1], args[2]);
- break;
- case 4:
- print_formatted(output, str, args[0], args[1], args[2],
- args[3]);
- break;
- case 5:
- print_formatted(output, str, args[0], args[1], args[2],
- args[3], args[4]);
- break;
- case 6:
- print_formatted(output, str, args[0], args[1], args[2],
- args[3], args[4], args[5]);
- break;
- case 7:
- print_formatted(output, str, args[0], args[1], args[2],
- args[3], args[4], args[5], args[6]);
- break;
- case 8:
- print_formatted(output, str, args[0], args[1], args[2],
- args[3], args[4], args[5], args[6], args[7]);
- break;
- case 9:
- print_formatted(output, str, args[0], args[1], args[2],
- args[3], args[4], args[5], args[6], args[7],
- args[8]);
- break;
- case 10:
- print_formatted(output, str, args[0], args[1], args[2],
- args[3], args[4], args[5], args[6], args[7],
- args[8], args[9]);
- break;
- case 11:
- print_formatted(output, str, args[0], args[1], args[2],
- args[3], args[4], args[5], args[6], args[7],
- args[8], args[9], args[10]);
- break;
- case 12:
- print_formatted(output, str, args[0], args[1], args[2],
- args[3], args[4], args[5], args[6], args[7],
- args[8], args[9], args[10], args[11]);
- break;
- case 13:
- print_formatted(output, str, args[0], args[1], args[2],
- args[3], args[4], args[5], args[6], args[7],
- args[8], args[9], args[10], args[11], args[12]);
- break;
- case 14:
- print_formatted(output, str, args[0], args[1], args[2],
- args[3], args[4], args[5], args[6], args[7],
- args[8], args[9], args[10], args[11], args[12],
- args[13]);
- break;
- case 15:
- print_formatted(output, str, args[0], args[1], args[2],
- args[3], args[4], args[5], args[6], args[7],
- args[8], args[9], args[10], args[11], args[12],
- args[13], args[14]);
- break;
- default:
- /* Unsupported number of arguments. */
- __ASSERT_NO_MSG(true);
- break;
- }
- }
- static void hexdump_line_print(const struct log_output *output,
- const uint8_t *data, uint32_t length,
- int prefix_offset, uint32_t flags)
- {
- newline_print(output, flags);
- for (int i = 0; i < prefix_offset; i++) {
- print_formatted(output, " ");
- }
- for (int i = 0; i < HEXDUMP_BYTES_IN_LINE; i++) {
- if (i > 0 && !(i % 8)) {
- print_formatted(output, " ");
- }
- if (i < length) {
- print_formatted(output, "%02x ", data[i]);
- } else {
- print_formatted(output, " ");
- }
- }
- print_formatted(output, "|");
- for (int i = 0; i < HEXDUMP_BYTES_IN_LINE; i++) {
- if (i > 0 && !(i % 8)) {
- print_formatted(output, " ");
- }
- if (i < length) {
- char c = (char)data[i];
- print_formatted(output, "%c",
- isprint((int)c) ? c : '.');
- } else {
- print_formatted(output, " ");
- }
- }
- }
- static void hexdump_print(struct log_msg *msg,
- const struct log_output *output,
- int prefix_offset, uint32_t flags)
- {
- uint32_t offset = 0U;
- uint8_t buf[HEXDUMP_BYTES_IN_LINE];
- size_t length;
- print_formatted(output, "%s", log_msg_str_get(msg));
- do {
- length = sizeof(buf);
- log_msg_hexdump_data_get(msg, buf, &length, offset);
- if (length) {
- hexdump_line_print(output, buf, length,
- prefix_offset, flags);
- offset += length;
- } else {
- break;
- }
- } while (true);
- }
- static void log_msg2_hexdump(const struct log_output *output,
- uint8_t *data, uint32_t len,
- int prefix_offset, uint32_t flags)
- {
- size_t length;
- do {
- length = MIN(len, HEXDUMP_BYTES_IN_LINE);
- hexdump_line_print(output, data, length,
- prefix_offset, flags);
- data += length;
- len -= length;
- } while (len);
- }
- static void raw_string_print(struct log_msg *msg,
- const struct log_output *output)
- {
- __ASSERT_NO_MSG(output->size);
- size_t offset = 0;
- size_t length;
- bool eol = false;
- do {
- length = output->size;
- /* Sting is stored in a hexdump message. */
- log_msg_hexdump_data_get(msg, output->buf, &length, offset);
- output->control_block->offset = length;
- if (length != 0) {
- eol = (output->buf[length - 1] == '\n');
- }
- log_output_flush(output);
- offset += length;
- } while (length > 0);
- if (eol) {
- print_formatted(output, "\r");
- }
- }
- static uint32_t prefix_print(const struct log_output *output,
- uint32_t flags, bool func_on, uint32_t timestamp, uint8_t level,
- uint8_t domain_id, int16_t source_id)
- {
- uint32_t length = 0U;
- bool stamp = flags & LOG_OUTPUT_FLAG_TIMESTAMP;
- bool colors_on = flags & LOG_OUTPUT_FLAG_COLORS;
- bool level_on = flags & LOG_OUTPUT_FLAG_LEVEL;
- if (IS_ENABLED(CONFIG_LOG_BACKEND_NET) &&
- flags & LOG_OUTPUT_FLAG_FORMAT_SYSLOG) {
- /* TODO: As there is no way to figure out the
- * facility at this point, use a pre-defined value.
- * Change this to use the real facility of the
- * logging call when that info is available.
- */
- static const int facility = 16; /* local0 */
- length += print_formatted(
- output,
- "<%d>1 ",
- facility * 8 +
- level_to_rfc5424_severity(level));
- }
- if (stamp) {
- length += timestamp_print(output, flags, timestamp);
- }
- if (IS_ENABLED(CONFIG_LOG_BACKEND_NET) &&
- flags & LOG_OUTPUT_FLAG_FORMAT_SYSLOG) {
- length += print_formatted(
- output, "%s - - - - ",
- output->control_block->hostname ?
- output->control_block->hostname :
- "zephyr");
- } else {
- color_prefix(output, colors_on, level);
- }
- length += ids_print(output, level_on, func_on,
- domain_id, source_id, level);
- return length;
- }
- static void postfix_print(const struct log_output *output,
- uint32_t flags, uint8_t level)
- {
- color_postfix(output, (flags & LOG_OUTPUT_FLAG_COLORS),
- level);
- newline_print(output, flags);
- }
- void log_output_msg_process(const struct log_output *output,
- struct log_msg *msg,
- uint32_t flags)
- {
- bool std_msg = log_msg_is_std(msg);
- uint32_t timestamp = log_msg_timestamp_get(msg);
- uint8_t level = (uint8_t)log_msg_level_get(msg);
- uint8_t domain_id = (uint8_t)log_msg_domain_id_get(msg);
- int16_t source_id = (int16_t)log_msg_source_id_get(msg);
- bool raw_string = (level == LOG_LEVEL_INTERNAL_RAW_STRING);
- int prefix_offset;
- if (IS_ENABLED(CONFIG_LOG_MIPI_SYST_ENABLE) &&
- flags & LOG_OUTPUT_FLAG_FORMAT_SYST) {
- log_output_msg_syst_process(output, msg, flags);
- return;
- }
- prefix_offset = raw_string ?
- 0 : prefix_print(output, flags, std_msg, timestamp,
- level, domain_id, source_id);
- if (log_msg_is_std(msg)) {
- std_print(msg, output);
- } else if (raw_string) {
- raw_string_print(msg, output);
- } else {
- hexdump_print(msg, output, prefix_offset, flags);
- }
- if (!raw_string) {
- postfix_print(output, flags, level);
- }
- log_output_flush(output);
- }
- void log_output_msg2_process(const struct log_output *output,
- struct log_msg2 *msg, uint32_t flags)
- {
- log_timestamp_t timestamp = log_msg2_get_timestamp(msg);
- uint8_t level = log_msg2_get_level(msg);
- bool raw_string = (level == LOG_LEVEL_INTERNAL_RAW_STRING);
- uint32_t prefix_offset;
- if (IS_ENABLED(CONFIG_LOG_MIPI_SYST_ENABLE) &&
- flags & LOG_OUTPUT_FLAG_FORMAT_SYST) {
- __ASSERT_NO_MSG(0);
- /* todo not supported
- * log_output_msg_syst_process(output, msg, flags);
- */
- return;
- }
- if (!raw_string) {
- void *source = (void *)log_msg2_get_source(msg);
- uint8_t domain_id = log_msg2_get_domain(msg);
- int16_t source_id = source ?
- (IS_ENABLED(CONFIG_LOG_RUNTIME_FILTERING) ?
- log_dynamic_source_id(source) :
- log_const_source_id(source)) :
- -1;
- prefix_offset = prefix_print(output, flags, 0, timestamp,
- level, domain_id, source_id);
- } else {
- prefix_offset = 0;
- }
- size_t len;
- uint8_t *data = log_msg2_get_package(msg, &len);
- if (len) {
- int err = cbpprintf(raw_string ? cr_out_func : out_func,
- (void *)output, data);
- (void)err;
- __ASSERT_NO_MSG(err >= 0);
- }
- data = log_msg2_get_data(msg, &len);
- if (len) {
- log_msg2_hexdump(output, data, len, prefix_offset, flags);
- }
- if (!raw_string) {
- postfix_print(output, flags, level);
- }
- log_output_flush(output);
- }
- static bool ends_with_newline(const char *fmt)
- {
- char c = '\0';
- while (*fmt != '\0') {
- c = *fmt;
- fmt++;
- }
- return (c == '\n');
- }
- void log_output_string(const struct log_output *output,
- struct log_msg_ids src_level, uint32_t timestamp,
- const char *fmt, va_list ap, uint32_t flags)
- {
- int length;
- uint8_t level = (uint8_t)src_level.level;
- uint8_t domain_id = (uint8_t)src_level.domain_id;
- int16_t source_id = (int16_t)src_level.source_id;
- bool raw_string = (level == LOG_LEVEL_INTERNAL_RAW_STRING);
- if (IS_ENABLED(CONFIG_LOG_MIPI_SYST_ENABLE) &&
- flags & LOG_OUTPUT_FLAG_FORMAT_SYST) {
- log_output_string_syst_process(output,
- src_level, fmt, ap, flags);
- return;
- }
- if (!raw_string) {
- prefix_print(output, flags, true, timestamp,
- level, domain_id, source_id);
- }
- length = cbvprintf(out_func, (void *)output, fmt, ap);
- (void)length;
- if (raw_string) {
- /* add \r if string ends with newline. */
- if (ends_with_newline(fmt)) {
- print_formatted(output, "\r");
- }
- } else {
- postfix_print(output, flags, level);
- }
- log_output_flush(output);
- }
- void log_output_hexdump(const struct log_output *output,
- struct log_msg_ids src_level, uint32_t timestamp,
- const char *metadata, const uint8_t *data,
- uint32_t length, uint32_t flags)
- {
- uint32_t prefix_offset;
- uint8_t level = (uint8_t)src_level.level;
- uint8_t domain_id = (uint8_t)src_level.domain_id;
- int16_t source_id = (int16_t)src_level.source_id;
- if (IS_ENABLED(CONFIG_LOG_MIPI_SYST_ENABLE) &&
- flags & LOG_OUTPUT_FLAG_FORMAT_SYST) {
- log_output_hexdump_syst_process(output,
- src_level, data, length, flags);
- return;
- }
- prefix_offset = prefix_print(output, flags, true, timestamp,
- level, domain_id, source_id);
- /* Print metadata */
- print_formatted(output, "%s", metadata);
- while (length != 0U) {
- uint32_t part_len = length > HEXDUMP_BYTES_IN_LINE ?
- HEXDUMP_BYTES_IN_LINE : length;
- hexdump_line_print(output, data, part_len,
- prefix_offset, flags);
- data += part_len;
- length -= part_len;
- }
- postfix_print(output, flags, level);
- log_output_flush(output);
- }
- void log_output_dropped_process(const struct log_output *output, uint32_t cnt)
- {
- char buf[5];
- int len;
- static const char prefix[] = DROPPED_COLOR_PREFIX "--- ";
- static const char postfix[] =
- " messages dropped ---\r\n" DROPPED_COLOR_POSTFIX;
- log_output_func_t outf = output->func;
- cnt = MIN(cnt, 9999);
- len = snprintk(buf, sizeof(buf), "%d", cnt);
- buffer_write(outf, (uint8_t *)prefix, sizeof(prefix) - 1,
- output->control_block->ctx);
- buffer_write(outf, buf, len, output->control_block->ctx);
- buffer_write(outf, (uint8_t *)postfix, sizeof(postfix) - 1,
- output->control_block->ctx);
- }
- void log_output_timestamp_freq_set(uint32_t frequency)
- {
- timestamp_div = 1U;
- /* 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 /= 2U;
- timestamp_div *= 2U;
- }
- freq = frequency;
- }
- uint64_t log_output_timestamp_to_us(uint32_t timestamp)
- {
- timestamp /= timestamp_div;
- return ((uint64_t) timestamp * 1000000U) / freq;
- }
|