Skip to content

Trace throttling - #3362

Closed
lyakh wants to merge 2 commits into
thesofproject:masterfrom
lyakh:throttle
Closed

Trace throttling#3362
lyakh wants to merge 2 commits into
thesofproject:masterfrom
lyakh:throttle

Conversation

@lyakh

@lyakh lyakh commented Aug 28, 2020

Copy link
Copy Markdown
Collaborator

The first patch is mostly ready, constants can be tuned and made configurable, some other adjustments can be made, maybe the simple number-based throttling should be dropped completely. The second patch is just an example. From the first patch commit message:

trace: add trace throttling

Add two methods of trace message throttling: by time and by number. Throttling by time allows up to "burst" messages within a timeout, after that it blocks further messages from the same source. After the timeout expires, next time the same source sends a trace, the number of suppressed messages is printed and the throttling is reset.
Throttling by number is simpler: every time the trace count reaches the burst number, further messages from the same source are suppressed until the pre-configured number is reached. Then the same "suppressed" message is sent and throttling is reset.

Based in part on 995da5a

Comment thread src/include/sof/trace/trace.h Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

spelling

THR

Comment thread src/include/sof/trace/trace.h Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it 'trace throttling' what the kernel calls 'rate_limit' ?

If yes, any reason why we would use a different wording?

@lyakh lyakh Aug 28, 2020

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@plbossart yes, sorry, forgot to mention the original work by @lgirdwood, now in the PR description. He called his method throttling, but yes, I think it's quite similar to the kernel rate-limiting and I'd call it that way too

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah ok, must be british/US words. custom/bespoke, zucchini/courgette. tomayto/tomahto, etc. :-)

I think it's a really good addition when we get lots of errors for the same issue (e.g. buffer w/ zero space, etc).

@lgirdwood lgirdwood Aug 31, 2020

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep, my wording - lets align it with the kernel to keep is simple.

Comment thread src/audio/pipeline.c Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_rate_limited() ?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one should stay as comp_info as it's only emitted at topology loading.

@lyakh

lyakh commented Aug 31, 2020

Copy link
Copy Markdown
Collaborator Author

@lgirdwood something like this?

@lgirdwood lgirdwood left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is good. I would say we use rate_limt() and I would target the new API at agent, buffer under/overruns, anything in the pipeline copy() path. @akloniex any comments ?

Comment thread src/audio/pipeline.c Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one should stay as comp_info as it's only emitted at topology loading.

@lyakh

lyakh commented Aug 31, 2020

Copy link
Copy Markdown
Collaborator Author

This one should stay as comp_info as it's only emitted at topology loading.

@lgirdwood sure, the second patch is just a couple of use-examples, the final PR will only contain patch 1, we can add any users as needed

@lgirdwood lgirdwood left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good - just need the comments describing algo and passing CI.

Comment thread src/trace/trace.c Outdated

@lgirdwood lgirdwood left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we are good to go for the core code. Can you include the agent warning as initial user in non draft PR update and remove these example users.

@lyakh

lyakh commented Sep 1, 2020

Copy link
Copy Markdown
Collaborator Author

I think we are good to go for the core code. Can you include the agent warning as initial user in non draft PR update and remove these example users.

@lgirdwood sure, but let me first wait for the CI build to complete - there are many configuration options - with trace, without trace, as a library, for unit testing, which I'd rather have a CI test for

@lyakh

lyakh commented Sep 2, 2020

Copy link
Copy Markdown
Collaborator Author

I think we are good to go for the core code. Can you include the agent warning as initial user in non draft PR update and remove these example users.

@lgirdwood one more comment to the agent case: it currently uses tr_warn() for the trace, that you want to throttle. In my patch in this series I so far only added rate-limiting macros for info-severity level traces, with the idea, that warnings and errors you really want to see always, and debug you normally don't see anyway, and if you do want to debug and enable them, then you probably also want them all. So, I'll convert that agent warning to info for now, but we can other severity levels too.
In fact I don't even think blindly suppressing some (many or most, if using counter-based rate-limiting) of the agent messages is a good idea, it kind of defeats its purpose. Those messages are the sole reason for the agent to exist. I would instead make those messages less frequent by not printing every such event, but collecting some simple statistics instead and printing, maybe, once every second.

@lyakh

lyakh commented Sep 2, 2020

Copy link
Copy Markdown
Collaborator Author

@lgirdwood also, I did follow your original implementation in 995da5a here, but TBH I'm not sure the frequency-based rate-limiting is all that useful and meaningful. It suppresses trace messages purely based on the number of times they have been issued - without taking the time into consideration at all. So, if you use it for whatever message, even if it only appears once per second or once per streaming start or whatever - most of them will disappear. I wouldn't mind removing that counter-based mode and keeping only the time-based one.

@lyakh
lyakh marked this pull request as ready for review September 2, 2020 06:26
@lyakh
lyakh requested a review from lgirdwood September 2, 2020 12:06
@lyakh lyakh changed the title [RFC] Trace throttling Trace throttling Sep 2, 2020

@lgirdwood lgirdwood left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs a few more comments around the more complex conditional logic.

Comment thread src/trace/trace.c Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we have a comment here describing decision.

Comment thread src/trace/trace.c Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto here.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lgirdwood done. Interesting, CI isn't restarting automatically. I don't expect it to be smart enough to notice, that only comments changed from the previous version... We can kick it manually or rely on its apparent intelligence.

@paulstelian97

Copy link
Copy Markdown
Collaborator

Opt in throttling also saves on some overhead and foot print too, since we don't have to check for throttling on every trace.

That is true, checking every trace will cost us additional processing power that will be correlated with tracing rate. On the other hand the kernel approach will increase firmware memory footprint with each new trace marked for throttling.

The impact of the check is actually quite significant since traces are supposed to be very fast (heck, why do we just send 5 numbers [for 4-parameter trace messages] over some channel [like etrace or dtrace] for every trace message? Not because it is simply fast?)

@lyakh

lyakh commented Sep 8, 2020

Copy link
Copy Markdown
Collaborator Author

IMHO some of the advantages in principle (provided perfect both implementations) of the explicit kernel-style throttling are (1) no metering overhead for all messages, (2) no accidental suppression of important messages, whereas an advantage of the universal throttling is (1) automatic rate-limiting of "all" messages (I think a correct implementation wouldn't throttle warnings and errors). I don't count memory overhead - I don't think 16 bytes per throttled message are critical :-)
I think a "perfect" rate-limiting implementation should:

  1. as long as messages don't repeat too often, pass them through with minimum overhead
  2. if a certain message appears too often within a pre-set time interval, start blocking it
  3. once a certain message hasn't been printed for a further pre-defined time interval, print how many messages have been suppressed and unblock it

The only choice is whether to apply this to all messages or only to some. Note, that (3) above is a bit tricky and I don't think either of the two versions currently implement it well. Ideally you would want to print "5 messages from function X line Y have been dropped" after a time-out expires after the last of those messages. But it isn't very easy to achieve that. Ideally you would want to run a global periodic task, regularly checking for all such blocked messages, but that adds complexity and overhead. A less intrusive approach would be to perform those checks during the next trace call. This isn't perfect, because trace calls can also stop for undefined length of time. But at least there is a good chance that some traces will be printed again after some time, and you can even force some. Note, that my approach is currently even more limited in this respect: the "N blocked" message is only sent when that specific message is triggered again, and this might not happen for a very long time. It can be extended though by adding a global list of blocked messages and tracking them there.

@jajanusz

jajanusz commented Sep 8, 2020

Copy link
Copy Markdown
Contributor

@akloniex Can throttling be disabled, if for some reason I want to keep messages? I guess not, cos I don't see any kconfig here.

IRT which solution is better.
Static approach may be good for developers, but what about bug reporters? We sometimes get useless logs that have spam of 1 message. Also developer can manually disable logs that go in the way durning develoment.

Argument that it is done like this in kernel, is not good imho, as on the contrary I see usually loggers have dynamic throttling (like @akloniex solution), for example android logger.

I'd go against @akloniex solution only if it'd be really CPU intensive, but if I'm correct it has const cost.

@lyakh

lyakh commented Sep 8, 2020

Copy link
Copy Markdown
Collaborator Author

Let's think about a generic solution. Typically when some activity happens in the FW, that generates a sequence of trace messages they come with a less than 10us interval. And there can be about 10 of them, mostly different ones. What would be a typical "babbling" threshold? 1ms? I.e. if a message repeats every 1ms we should consider it flooding and throttle it, right? So, 10 messages, coming all at 10us intervals in the end of that chain are all still candidates for throttling and should be monitored, right? So, I'd say we need more than 5 entries that @akloniex had in his implementation. And how many are enough? 8? 16? So on each trace call we'd have to scan the list of up to 16 entries, search for a previous occurrence, if found - move to the top of the list as the newest monitor, and evict expired ones. So we'd rather need a linked list than an array. We want to keep them sorted them and add and drop them. So I think the current implementation would need a few changes. Then there are also some messages that do print in short bursts, e.g.

[    62853.958333] (      108.333336) c0 pipe         7.48      src/audio/pipeline.c:234  pipeline complete, clock freq 400000000Hz
[    62908.906250] (       54.947918) c0 pipe         6.40      src/audio/pipeline.c:234  pipeline complete, clock freq 400000000Hz
[    62962.604167] (       53.697918) c0 pipe         5.34      src/audio/pipeline.c:234  pipeline complete, clock freq 400000000Hz
[    63010.781250] (       48.177082) c0 pipe         4.27      src/audio/pipeline.c:234  pipeline complete, clock freq 400000000Hz
[    63058.541667] (       47.760418) c0 pipe         3.21      src/audio/pipeline.c:234  pipeline complete, clock freq 400000000Hz
[    63104.531250] (       45.989582) c0 pipe         2.15      src/audio/pipeline.c:234  pipeline complete, clock freq 400000000Hz
[    63150.989583] (       46.458332) c0 pipe         1.9       src/audio/pipeline.c:234  pipeline complete, clock freq 400000000Hz
[    63202.239583] (       51.250000) c0 eq-iir       8.51  ../audio/eq_iir/eq_iir.c:797  eq_iir_cmd()
[    63205.729167] (        3.489583) c0 eq-iir       8.51  ../audio/eq_iir/eq_iir.c:646  iir_cmd_get_data(), SOF_CTRL_CMD_BINARY
[    63208.697917] (        2.968750) c0 eq-iir       8.51  ../audio/eq_iir/eq_iir.c:680  iir_cmd_get_data(), chunk size 88 msg index 0 max size 260 offset 0
[    63281.458333] (       72.760414) c0 pga          8.50  ../audio/volume/volume.c:641  volume_ctrl_get_cmd(), channel = 0, value = 65536
[    63284.635417] (        3.177083) c0 pga          8.50  ../audio/volume/volume.c:641  volume_ctrl_get_cmd(), channel = 1, value = 65536
[    63343.802083] (       59.166668) c0 eq-iir       7.43  ../audio/eq_iir/eq_iir.c:797  eq_iir_cmd()
[    63346.822917] (        3.020833) c0 eq-iir       7.43  ../audio/eq_iir/eq_iir.c:646  iir_cmd_get_data(), SOF_CTRL_CMD_BINARY
[    63349.635417] (        2.812500) c0 eq-iir       7.43  ../audio/eq_iir/eq_iir.c:680  iir_cmd_get_data(), chunk size 88 msg index 0 max size 260 offset 0
[    63422.395833] (       72.760414) c0 pga          7.42  ../audio/volume/volume.c:641  volume_ctrl_get_cmd(), channel = 0, value = 65536
[    63425.520833] (        3.125000) c0 pga          7.42  ../audio/volume/volume.c:641  volume_ctrl_get_cmd(), channel = 1, value = 65536
[    63481.614583] (       56.093750) c0 pga          7.42  ../audio/volume/volume.c:641  volume_ctrl_get_cmd(), channel = 0, value = 65536
[    63484.739583] (        3.125000) c0 pga          7.42  ../audio/volume/volume.c:641  volume_ctrl_get_cmd(), channel = 1, value = 65536
[    63541.979167] (       57.239582) c0 pga          6.36  ../audio/volume/volume.c:641  volume_ctrl_get_cmd(), channel = 0, value = 65536
[    63545.260417] (        3.281250) c0 pga          6.36  ../audio/volume/volume.c:641  volume_ctrl_get_cmd(), channel = 1, value = 65536
[    63600.677083] (       55.416668) c0 pga          5.29  ../audio/volume/volume.c:641  volume_ctrl_get_cmd(), channel = 0, value = 65536
[    63603.958333] (        3.281250) c0 pga          5.29  ../audio/volume/volume.c:641  volume_ctrl_get_cmd(), channel = 1, value = 65536
[    63658.750000] (       54.791668) c0 pga          4.23  ../audio/volume/volume.c:641  volume_ctrl_get_cmd(), channel = 0, value = 65536
[    63662.031250] (        3.281250) c0 pga          4.23  ../audio/volume/volume.c:641  volume_ctrl_get_cmd(), channel = 1, value = 65536
[    63716.510417] (       54.479168) c0 pga          3.17  ../audio/volume/volume.c:641  volume_ctrl_get_cmd(), channel = 0, value = 65536
[    63719.635417] (        3.125000) c0 pga          3.17  ../audio/volume/volume.c:641  volume_ctrl_get_cmd(), channel = 1, value = 65536
[    63788.906250] (       69.270836) c0 pga          2.11  ../audio/volume/volume.c:641  volume_ctrl_get_cmd(), channel = 0, value = 65536
[    63792.031250] (        3.125000) c0 pga          2.11  ../audio/volume/volume.c:641  volume_ctrl_get_cmd(), channel = 1, value = 65536
[    63849.218750] (       57.187500) c0 pga          1.2   ../audio/volume/volume.c:641  volume_ctrl_get_cmd(), channel = 0, value = 65536
[    63852.239583] (        3.020833) c0 pga          1.2   ../audio/volume/volume.c:641  volume_ctrl_get_cmd(), channel = 0, value = 0

and they look harmless and don't really need to be throttled. I think one of drawbacks of the generic solution would be potentially losing bursting messages.

@lyakh

lyakh commented Sep 8, 2020

Copy link
Copy Markdown
Collaborator Author

all that being said, if we still don't have an agreement, I think we need to measure a performance hit from @akloniex 's automatic implementation (at least in its present form)

@mwasko

mwasko commented Sep 8, 2020

Copy link
Copy Markdown
Contributor

Let's think about a generic solution. Typically when some activity happens in the FW, that generates a sequence of trace messages they come with a less than 10us interval. And there can be about 10 of them, mostly different ones. What would be a typical "babbling" threshold? 1ms? I.e. if a message repeats every 1ms we should consider it flooding and throttle it, right? So, 10 messages, coming all at 10us intervals in the end of that chain are all still candidates for throttling and should be monitored, right? So, I'd say we need more than 5 entries that @akloniex had in his implementation. And how many are enough? 8? 16? So on each trace call we'd have to scan the list of up to 16 entries, search for a previous occurrence, if found - move to the top of the list as the newest monitor, and evict expired ones. So we'd rather need a linked list than an array. We want to keep them sorted them and add and drop them. So I think the current implementation would need a few changes.

These are very good points @lyakh and I agree that we can improve current @akloniex implementation. Partially that is why I favor generic solution - it gives us more space to tune it for our needs.

I have asked @akloniex to do latency and MCPS measurements, so as long as those values will be on acceptable level then I would recommend it. If we find results not satisfying then I don't have problem to go with kernel based implementation.

As for the warning & error messages imho we should suppress them as well (aggregate to single line not remove/hide). We do not need the entire wall of repeated warning/error messages, while the root cause of the problem is usually before the first error pop up.

@akloniex

akloniex commented Sep 8, 2020

Copy link
Copy Markdown
Member

I've put perf counters in logging function for 3 cases:

  1. Baseline - current master without any changes
  2. My implementation - measured from beginning to end of trace_log() function
  3. Guennadi's implementation - measured from beginning to end of trace_dev_info_ratelimit() macro expansion, with trace_dev_info_t_ratelimit variant being evaluated.

ad 1. Avg plat: 71.6 cpu: 1460.1
Peak plat: 98 cpu: 2090
ad 2. Avg plat: 105 cpu: 2191.2
Peak plat: 199 cpu: 4138
ad 3. Avg plat: 79.5 cpu: 1653.1
Peak plat: 146 cpu: 3070

All measurements taken on same APL platform, during the same test case being tested.

Load for my implementation is clearly most 'peaky' and overall most cpu-heavy, which was expected.
Guennadi's version features much smaller increase, and only for the message that was actively rate-limited, all other trace messages should follow the baseline.

@mwasko

mwasko commented Sep 9, 2020

Copy link
Copy Markdown
Contributor

Then there are also some messages that do print in short bursts

@akloniex the burst messages as @lyakh pointed out are something that we do not want to suppress. I am thinking that maybe we could somehow combine yours two approaches. For example instead of define throttling trace function create dedicated burst message function that will bypass generic suppress mechanism while all the other traces by default will be actively tracked & suppressed if necessary?

@akloniex

akloniex commented Sep 9, 2020

Copy link
Copy Markdown
Member

@mwasko We could do that. I don't see any other method of bypassing the generic filter, other than manually defining traces that are allowed to pass the filter.

@lgirdwood

Copy link
Copy Markdown
Member

@mwasko @lyakh @akloniex The trace mechanism must have negligible impact on MCPS and never filter the golden trace messages.

I think we all agree the annoyance here is that too much trace data decreases the trace SNR and means we risk loosing the important error entry conditions (due to the periodic nature of the pipelines quickly filling the trace log).

Something really simple and cheap is needed here. i.e.

/* only emit the trace after every X messages (where X is power of 2) - this example is 4k */
#define trace_error_permit_every4k(trace_context, message) \
    if ((trace_context->count & 0xfff) == 0) {   \
        trace_error(trace_context, message); /* really print the trace */ \
        trace_error(trace_context, "message was repeated 4096 times"); \
    } \
     trace_context->count++;

/* emit the first 16 trace and then after every X messages (where X is power of 2) - this example is 4k */
#define trace_error16_permit_every4k(trace_context, message) \
    if (trace_context->count < 16) { \
        trace_error(trace_context, message); \
    } else if ((trace_context->count & 0xfff) == 0) {   \
        trace_error(trace_context, message); /* really print the trace */ \
        trace_error(trace_context, "message was repeated 4096 times"); \
    } \
     trace_context->count++;

This is opt-in and ideal for recurrent pipeline errors (above top example prints every 4 seconds) preserving the log SNR. The second example prints the first 16 errors and then every 4k.

Both above are cheap with only 32bit addition and logical ANDs.

@akloniex can you do the perf check with the above macros in the same position as your earlier tests. Context->count is a uint32_t.

@lyakh

lyakh commented Sep 9, 2020

Copy link
Copy Markdown
Collaborator Author

Both above are cheap with only 32bit addition and logical ANDs.

@akloniex can you do the perf check with the above macros in the same position as your earlier tests. Context->count is a uint32_t.

@lrgirdwo that's a bit too simple. Firstly the context is per file / subsystem, so, you'll punish "innocent" traces if you place the counter into the context. Secondly this will drop random messages - even those emitting 1 message per connection or whatever. You really need to measure the frequency per message.

@lgirdwood

Copy link
Copy Markdown
Member

@lyakh I think we have to make simpler debugging the initial goal here (i.e. debugging critical pipeline errors) as I think you are getting into more performance and event measurement trace throttling (i.e. "info: my buffer was empty" was printed 17 times in the last second).

Secondly I want to avoid any DSP loading differences associated with trace i.e. we should never have "feature X worked when trace config Y is enabled". I'm not saying we should have a higher level trace intelligence as you and @akloniex have been working on, but it should be a config option used by developers.

@lrgirdwo that's a bit too simple. Firstly the context is per file / subsystem, so, you'll punish "innocent" traces if you place the counter into the context.

Yep, there will be 1 word added to each trace context. No big deal.

Secondly this will drop random messages - even those emitting 1 message per connection or whatever.

No random or important messages will be dropped. You will see the first X messages and a message every Y messages. The first X messages are critical for the debug of the issue as mostly everything thereafter is noise. This is intended for fixing errors.

You really need to measure the frequency per message.

Not for errors. For errors you just need the initial context as the rest is superfluous.

@lyakh

lyakh commented Sep 9, 2020

Copy link
Copy Markdown
Collaborator Author

@lrgirdwo that's a bit too simple. Firstly the context is per file / subsystem, so, you'll punish "innocent" traces if you place the counter into the context.

Yep, there will be 1 word added to each trace context. No big deal.

@lgirdwood sorry, that isn't what I meant. Look in dai.c, it has half a dozen tr_info(&dai_tr,...) there, so, they all will have the same counter.

Secondly this will drop random messages - even those emitting 1 message per connection or whatever.

No random or important messages will be dropped. You will see the first X messages and a message every Y messages. The first X messages are critical for the debug of the issue as mostly everything thereafter is noise. This is intended for fixing errors.

Ah, so your assumption is, that only error messages can flood the log? So we only need to throttle (some) error messages? So we would only use these rate-limiting traces for error and warning messages, that have a potential for flooding and that shouldn't be printed at all otherwise. Ok, understand, not sure whether that's really how it is - whether it's impossible, that a "normal," possibly "info" level message floods the trace?

@lgirdwood

Copy link
Copy Markdown
Member

@lrgirdwo that's a bit too simple. Firstly the context is per file / subsystem, so, you'll punish "innocent" traces if you place the counter into the context.

Yep, there will be 1 word added to each trace context. No big deal.

@lgirdwood sorry, that isn't what I meant. Look in dai.c, it has half a dozen tr_info(&dai_tr,...) there, so, they all will have the same counter.

Hmm, there should not be half a dozen different trace contexts in one file. I would expect most 1 or 2 trace contexts per feature (so could need fixing). Btw I can only see 2 in dai.c

lrg@sif:~/work/sof/sof$ grep -rn "tr_info(&dai" --include dai.c
src/lib/dai.c:163:		tr_info(&dai_tr, "dai_get %d.%d new sref %d",
src/lib/dai.c:188:	tr_info(&dai_tr, "dai_put %d.%d new sref %d",

Secondly this will drop random messages - even those emitting 1 message per connection or whatever.

No random or important messages will be dropped. You will see the first X messages and a message every Y messages. The first X messages are critical for the debug of the issue as mostly everything thereafter is noise. This is intended for fixing errors.

Ah, so your assumption is, that only error messages can flood the log? So we only need to throttle (some) error messages? So we would only use these rate-limiting traces for error and warning messages, that have a potential for flooding and that shouldn't be printed at all otherwise. Ok, understand, not sure whether that's really how it is - whether it's impossible, that a "normal," possibly "info" level message floods the trace?

The errors that flood the log today are all recurrent agent, scheduler, IRQ or pipeline runtime errors. So I would treat error and warning traces here as likely to flood. i.e. agent flooded log on zephyr (and could not be stopped ever after it started).

@lyakh

lyakh commented Sep 9, 2020

Copy link
Copy Markdown
Collaborator Author

@lgirdwood sorry, that isn't what I meant. Look in dai.c, it has half a dozen tr_info(&dai_tr,...) there, so, they all will have the same counter.

Hmm, there should not be half a dozen different trace contexts in one file. I would expect most 1 or 2 trace contexts per feature (so could need fixing). Btw I can only see 2 in dai.c

@lgirdwood sorry, that isn't what I was trying to say: there are 2 tr_info() and 4 tr_err() calls there and they all use the same context. So, if you add a counter there, all those 6 calls will increment and check the same counter.

@lgirdwood

Copy link
Copy Markdown
Member

@lgirdwood sorry, that isn't what I meant. Look in dai.c, it has half a dozen tr_info(&dai_tr,...) there, so, they all will have the same counter.

Hmm, there should not be half a dozen different trace contexts in one file. I would expect most 1 or 2 trace contexts per feature (so could need fixing). Btw I can only see 2 in dai.c

@lgirdwood sorry, that isn't what I was trying to say: there are 2 tr_info() and 4 tr_err() calls there and they all use the same context. So, if you add a counter there, all those 6 calls will increment and check the same counter.

Thanks got you now, it should be fine passing a local counter here (and these can be grouped if necessary). i..e

/* emit the first 16 trace and then after every X messages (where X is power of 2) - this example is 4k */
#define trace_error16_permit_every4k(trace_context, count, message) \
    if (count < 16) { \
        trace_error(trace_context, message); \
    } else if ((count & 0xfff) == 0) {   \
        trace_error(trace_context, message); /* really print the trace */ \
        trace_error(trace_context, "message was repeated 4096 times, total %d, count"); \
    } \
     count++;

I really do see this as 2 features now (which can be independently developed)

1. Trace throttling for development
A kconfig option(s) to enable a higher level system wide or opt in throttling based on time or other parameters to aid with code development (@akloniex and @lyakh examples). This mechanism can increase MCPS if necessary and is intended to help developers see end to end use cases. e.g. Pipeline state transitions and operations START -> COPY(n) -> STOP where we only care about seeing first n copies.

2. Trace throttling for debug
A standard option to enable local trace throttling where recurrent high frequency errors dilute the trace value. There should be no notable MCPS impact.

@ktrzcinx

ktrzcinx commented Sep 10, 2020

Copy link
Copy Markdown
Member

What about implementing this mechanism fully in sof-logger ? Then none 'golden message' will be permanently dropped, it will be easy to enable/disable/configure (without fw recompilation, so it will be feature for both development and debug), no extra DSP cost (and advanced tracing logic). In logger number of dropped messages can be easily calculated and counted independently for each trace message (not trace context). Single drawback which I can see is we don't gain trace memory window bandwidth (it will be kept as it is right now).

@paulstelian97

Copy link
Copy Markdown
Collaborator

What about implementing this mechanism fully in sof-logger ? Then none 'golden message' will be permanently dropped, it will be easy to enable/disable/configure (without fw recompilation, so it will be feature for both development and debug), no extra DSP cost (and advanced tracing logic). In logger number of dropped messages can be easily calculated and counted independently for each trace message (not trace context). Single drawback which I can see is we don't gain trace memory window bandwidth (it will be kept as it is right now).

That could be good if the kernel also holds a buffer with the entire trace (DMA trace output, not etrace)

@lgirdwood

Copy link
Copy Markdown
Member

No objection from me with throttling in sof-logger providing we are all happy with available trace bandwidth ? (which seems good today).

@lyakh

lyakh commented Sep 10, 2020

Copy link
Copy Markdown
Collaborator Author

No objection from me with throttling in sof-logger providing we are all happy with available trace bandwidth ? (which seems good today).

@lgirdwood @ktrzcinx sure, or just use grep. If you know that you want to filter when you start the logger and if no messages are dropped, you can as well just use grep.

@lgirdwood

Copy link
Copy Markdown
Member

@lyakh grep is nice for single items, but most users care about multiple events i.e. the error, events leading upto the error, the error handling/recovery.

@mwasko

mwasko commented Sep 10, 2020

Copy link
Copy Markdown
Contributor

No objection from me with throttling in sof-logger providing we are all happy with available trace bandwidth ? (which seems good today).

Implementing more sophisticated suppress mechanism in sof-logger is an option but probably would have to be enabled by 'default', otherwise we would still receive flooded traces from the field. Regardless to that I would still recommend at least some basic mechanism that will control & throttle FW trace message rates.

@lgirdwood @ktrzcinx sure, or just use grep. If you know that you want to filter when you start the logger and if no messages are dropped, you can as well just use grep.

@lyakh grep is nice to entirely filter out messages but will not suppress ten's repeated messages into one line trace with info about suppression.

@plbossart

Copy link
Copy Markdown
Member

Let's not conflate separate capabilities. Take the kernel example. you can
a) filter dmesg by error level (that would be the sof-logger suggested filter)
b) add/remove dynamic debug traces (which I understand as being added in a separate PR)
c) rate_limit traces. This is super useful during heavy duty debug cases.

All these three cases have their respective merits. More capabilities don't hurt here.

@akloniex

Copy link
Copy Markdown
Member

@lgirdwood @lyakh @mwasko
I'm fairly certain, that we could tweak parameters of my implementation to accommodate for some burst messages (eg. topology building) while still separating each-period-spam (eg. host no bytes to copy), by setting time threshold just above most common pipeline period, 1ms.
That should leave most burst traces intact, and if during development someone needed to spam trace something in more rapid fashion, time threshold could be changed locally to allow that.
I'm well aware that there are places to improve my implementation (eg. release message after reaching certain count, to sporadically emit suppressed message, if it's permanently being re-suppressed), but I believe that having an adaptive solution in FW could benefit us more, preventing log loss in case new spamming trace shows up in the wild, after new FW or HW release, which we didn't catch during testing.

@mwasko

mwasko commented Sep 18, 2020

Copy link
Copy Markdown
Contributor

Since the burst messages can be handled with threshold parameters tuning within @akloniex solution then I would like to recommend it as a way to go for message throttling/suppress.

I have also discussed the eventual performance impact with @mmaka1 and it is negligible for message tracing.

@akloniex please create a PR with your changes for review. We can also discuss the approach further on Tuesday call.

@lgirdwood

Copy link
Copy Markdown
Member

@lyakh shall we close this one ?

@lyakh

lyakh commented Oct 7, 2020

Copy link
Copy Markdown
Collaborator Author

@lgirdwood sure

@lyakh lyakh closed this Oct 7, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants