-
Notifications
You must be signed in to change notification settings - Fork 349
Probe log autostart and prebuffering #10096
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
856629d
7984ed9
a750a05
5c09c5d
f479fce
57df568
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,6 +14,10 @@ | |
| #include <sof/lib/notifier.h> | ||
| #include <sof/probe/probe.h> | ||
|
|
||
| #ifdef PROBE_LOG_DEBUG | ||
| #include <stdio.h> | ||
| #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; | ||
|
|
||
jsarha marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| 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); | ||
jsarha marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| 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); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't quite understand the flow, but the buffer is only allocated here, and freed in the first
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. probe_char_out() is called when ever sometihng is logged. probe_hook is set when probes dma is up and special "log"-probepoint is added (the autoenable option is set its added automatically when probes dma is set up). So the logs are buffered if there is no place to push them and the buffer is drained when the probes dma is started. .... but hmm, it does not appear the logging hook is disabled any where. That is unfortunate as the buffering is not enabled again even if the probes DMA is dropped. I think there is still need for one more commit.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jsarha ah, thanks for explaining! Somehow didn't catch that yesterday |
||
| } else { | ||
| if (prebuf.wpos) | ||
| pre_buffer_drain(); | ||
|
|
||
| log_push(data, length); | ||
| } | ||
|
|
||
| return length; | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. in the commit message by "probes module is unloaded" you mean the Linux kernel module? And after this call probes would return to pre-buffering? Sounds good
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, and that indeed happens. I tested that. Here is how it looks like if PROBE_LOG_DEBUG is set:
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jsarha looks good! But is the module there really unloaded or is it just the capture stopped?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @lyakh , at least probe_free() and probe_mod_init() is called according to the logs, so AFAICT the module has been unloaded and loaded again. |
||
| #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; | ||
| } | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this also implicitly assumes that
sizeof(msg) < mlenat this point. This should be true with an 80-charactermsgbuffer, but...There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok I'll add "&& mlen < sizeof(msg)" to the if statement. It would be better if snprintf size argument would be signed so that negative size would be detected.