diff --git a/src/include/sof/probe/probe.h b/src/include/sof/probe/probe.h index f3f01435902b..de8873ad0a67 100644 --- a/src/include/sof/probe/probe.h +++ b/src/include/sof/probe/probe.h @@ -9,11 +9,12 @@ #define __SOF_PROBE_PROBE_H__ #include +#include /** * A buffer of logging data is available for processing. */ -typedef void(*probe_logging_hook_t)(uint8_t *buffer, size_t length); +typedef ssize_t(*probe_logging_hook_t)(uint8_t *buffer, size_t length); #if CONFIG_LOG_BACKEND_SOF_PROBE const struct log_backend *log_backend_probe_get(void); diff --git a/src/logging/Kconfig b/src/logging/Kconfig deleted file mode 100644 index 9d854f68aae4..000000000000 --- a/src/logging/Kconfig +++ /dev/null @@ -1,36 +0,0 @@ -# SPDX-License-Identifier: BSD-3-Clause - -# Trace configs - -menu "Logging" - -config LOG_BACKEND_SOF_PROBE - bool "Logging backend with SOF probes" - depends on LOG - select LOG_OUTPUT - help - Enable backend for log output via SOF probe interface. - Probe interface allows to transmit logs and PCM data, muxed over - a shared DMA channel. - Logging is enabled by setting up a probe point with - probe purpose value of PROBE_PURPOSE_LOGGING. - -config LOG_BACKEND_SOF_PROBE_OUTPUT_DICTIONARY - bool "Dictionary" - select LOG_DICTIONARY_SUPPORT - help - Set output format of the SOF probe logging backend to - Zephyr log dictionary. - The resulting log file can be parsed with tools in - zephyr/scripts/logging/dictionary - A dictionary for the binary is created during build. - -config LOG_BACKEND_SOF_PROBE_OUTPUT - int "Set logging output format" - default 1 if LOG_MIPI_SYST_ENABLE - default 2 if LOG_BACKEND_SOF_PROBE_OUTPUT_DICTIONARY - default 0 # Text encoding is default - help - Set the logging format for SOF probe output. - -endmenu diff --git a/src/logging/log_backend_probe.c b/src/logging/log_backend_probe.c index b701fb88756b..0b852d780455 100644 --- a/src/logging/log_backend_probe.c +++ b/src/logging/log_backend_probe.c @@ -14,6 +14,10 @@ #include #include +#ifdef PROBE_LOG_DEBUG +#include +#endif + /* * A lock is needed as log_process() and log_panic() have no internal locks * to prevent concurrency. Meaning if log_process is called after @@ -33,10 +37,81 @@ static uint8_t log_buf[LOG_BUF_SIZE]; static probe_logging_hook_t probe_hook; +static void log_push(uint8_t *data, size_t length) +{ + size_t pos = 0; + + do { + int ret = probe_hook(data + pos, length - pos); + + if (ret < 0) + break; + pos += ret; + } while (pos < length); +} + +#define PRE_BUFFER_SIZE 4096 +static struct probe_pre_buffer { + uint8_t *buf; + size_t wpos; + size_t len; +} prebuf; + +static void pre_buffer_drain(void) +{ +#ifdef PROBE_LOG_DEBUG + /* NOTE: The debug code only works with ascii/text log output */ + uint64_t stamp = sof_cycle_get_64(); + char msg[80]; + int mlen; + + mlen = snprintf(msg, sizeof(msg), "[Drain %zu bytes of pre buffered logs]\n", prebuf.wpos); + if (prebuf.len > prebuf.wpos && mlen < sizeof(msg)) + mlen += snprintf(msg + mlen, sizeof(msg) - mlen, "[%zu bytes dropped]\n", + prebuf.len - prebuf.wpos); + log_push(msg, MIN(mlen, sizeof(msg))); +#endif + + log_push(prebuf.buf, prebuf.wpos); + rfree(prebuf.buf); + prebuf.buf = NULL; + prebuf.len = 0; + prebuf.wpos = 0; + +#ifdef PROBE_LOG_DEBUG + mlen = snprintf(msg, sizeof(msg), "[Buffer drained in %llu us]\n", + k_cyc_to_us_near64(sof_cycle_get_64() - stamp)); + + log_push(msg, MIN(mlen, sizeof(msg))); +#endif +} + +static void pre_buffer(uint8_t *data, size_t length) +{ + int ret; + + prebuf.len += length; + if (!prebuf.buf) { + prebuf.buf = rzalloc(SOF_MEM_FLAG_USER, PRE_BUFFER_SIZE); + if (!prebuf.buf) + return; + } + /* Protection against buffer over flow relies on memcpy_s() */ + ret = memcpy_s(&prebuf.buf[prebuf.wpos], PRE_BUFFER_SIZE - prebuf.wpos, data, length); + if (!ret) + prebuf.wpos += length; +} + static int probe_char_out(uint8_t *data, size_t length, void *ctx) { - if (probe_hook) - probe_hook(data, length); + if (!probe_hook) { + pre_buffer(data, length); + } else { + if (prebuf.wpos) + pre_buffer_drain(); + + log_push(data, length); + } return length; } diff --git a/src/probe/probe.c b/src/probe/probe.c index 6421d4b473c3..7a9cb4121573 100644 --- a/src/probe/probe.c +++ b/src/probe/probe.c @@ -338,6 +338,29 @@ static enum task_state probe_task(void *data) return SOF_TASK_STATE_RESCHEDULE; } +#if CONFIG_LOG_BACKEND_SOF_PROBE_OUTPUT_AUTO_ENABLE +static void probe_auto_enable_logs(uint32_t stream_tag) +{ + struct probe_point log_point = { +#if CONFIG_IPC_MAJOR_4 + .buffer_id = { + .full_id = 0, + }, +#else + .buffer_id = 0, +#endif + .purpose = PROBE_PURPOSE_EXTRACTION, + .stream_tag = stream_tag, + }; + int ret; + + ret = probe_point_add(1, &log_point); + + if (ret) + tr_err(&pr_tr, "probe_auto_enable_logs() failed"); +} +#endif + int probe_init(const struct probe_dma *probe_dma) { struct probe_pdata *_probe = probe_get(); @@ -365,6 +388,14 @@ int probe_init(const struct probe_dma *probe_dma) return -ENOMEM; } + /* initialize injection DMAs as invalid */ + for (i = 0; i < CONFIG_PROBE_DMA_MAX; i++) + _probe->inject_dma[i].stream_tag = PROBE_DMA_INVALID; + + /* initialize probe points as invalid */ + for (i = 0; i < CONFIG_PROBE_POINTS_MAX; i++) + _probe->probe_points[i].stream_tag = PROBE_POINT_INVALID; + /* setup extraction dma if requested */ if (probe_dma) { tr_dbg(&pr_tr, "\tstream_tag = %u, dma_buffer_size = %u", @@ -394,20 +425,16 @@ int probe_init(const struct probe_dma *probe_dma) SOF_UUID(probe_task_uuid), SOF_SCHEDULE_LL_TIMER, SOF_TASK_PRI_LOW, probe_task, _probe, 0, 0); + +#if CONFIG_LOG_BACKEND_SOF_PROBE_OUTPUT_AUTO_ENABLE + probe_auto_enable_logs(probe_dma->stream_tag); +#endif } else { tr_dbg(&pr_tr, "\tno extraction DMA setup"); _probe->ext_dma.stream_tag = PROBE_DMA_INVALID; } - /* initialize injection DMAs as invalid */ - for (i = 0; i < CONFIG_PROBE_DMA_MAX; i++) - _probe->inject_dma[i].stream_tag = PROBE_DMA_INVALID; - - /* initialize probe points as invalid */ - for (i = 0; i < CONFIG_PROBE_POINTS_MAX; i++) - _probe->probe_points[i].stream_tag = PROBE_POINT_INVALID; - return 0; } @@ -840,27 +867,32 @@ static void kick_probe_task(struct probe_pdata *_probe) } #if CONFIG_LOG_BACKEND_SOF_PROBE -static void probe_logging_hook(uint8_t *buffer, size_t length) +static ssize_t probe_logging_hook(uint8_t *buffer, size_t length) { struct probe_pdata *_probe = probe_get(); uint64_t checksum; + size_t max_len; int ret; + max_len = _probe->ext_dma.dmapb.avail - sizeof(struct probe_data_packet) - sizeof(checksum); + length = MIN(max_len, length); + ret = probe_gen_header(PROBE_LOGGING_BUFFER_ID, length, 0, &checksum); if (ret < 0) - return; + return ret; ret = copy_to_pbuffer(&_probe->ext_dma.dmapb, buffer, length); if (ret < 0) - return; + return ret; ret = copy_to_pbuffer(&_probe->ext_dma.dmapb, &checksum, sizeof(checksum)); if (ret < 0) - return; + return ret; kick_probe_task(_probe); + return length; } #endif @@ -1401,6 +1433,10 @@ int probe_point_remove(uint32_t count, const uint32_t *buffer_id) if (_probe->probe_points[j].stream_tag != PROBE_POINT_INVALID && buf_id->full_id == buffer_id[i]) { +#if CONFIG_LOG_BACKEND_SOF_PROBE + if (enable_logs(&_probe->probe_points[j])) + probe_logging_init(NULL); +#endif #if CONFIG_IPC_MAJOR_4 dev = ipc_get_comp_by_id(ipc_get(), IPC4_COMP_ID(buf_id->fields.module_id, @@ -1466,6 +1502,9 @@ static int probe_free(struct processing_module *mod) probe_deinit(); +#if CONFIG_LOG_BACKEND_SOF_PROBE + probe_logging_init(NULL); +#endif return 0; } diff --git a/src/trace/Kconfig b/src/trace/Kconfig index 5ff6b2001130..92ccb461ed18 100644 --- a/src/trace/Kconfig +++ b/src/trace/Kconfig @@ -108,6 +108,16 @@ config LOG_BACKEND_SOF_PROBE Logging is enabled by setting up a probe point with probe purpose value of PROBE_PURPOSE_LOGGING. +config LOG_BACKEND_SOF_PROBE_OUTPUT_AUTO_ENABLE + bool "Automatically enable probes logging when probe dma is started" + depends on LOG_BACKEND_SOF_PROBE + help + Automatically enable logging as soon as probes DMA + starts. This option simplifies probes logging back + end usage as there is no need to enable logs after + every boot. The probes DMA is still needed before the + log output can be enabled. + config LOG_BACKEND_SOF_PROBE_OUTPUT_DICTIONARY bool "Dictionary" select LOG_DICTIONARY_SUPPORT