forked from thesofproject/sof
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtrace.c
More file actions
579 lines (487 loc) · 16.7 KB
/
trace.c
File metadata and controls
579 lines (487 loc) · 16.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
// SPDX-License-Identifier: BSD-3-Clause
//
// Copyright(c) 2016 Intel Corporation. All rights reserved.
//
// Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
// Artur Kloniecki <arturx.kloniecki@linux.intel.com>
// Karol Trzcinski <karolx.trzcinski@linux.intel.com>
#include <rtos/panic.h>
#include <sof/ipc/msg.h>
#include <sof/ipc/topology.h>
#include <rtos/timer.h>
#include <rtos/alloc.h>
#include <rtos/cache.h>
#include <sof/lib/cpu.h>
#include <sof/lib/mailbox.h>
#include <sof/lib/memory.h>
#include <sof/platform.h>
#include <rtos/string.h>
#include <rtos/sof.h>
#include <rtos/spinlock.h>
#include <sof/trace/dma-trace.h>
#include <sof/trace/trace.h>
#include <ipc/topology.h>
#include <user/trace.h>
#include <stdarg.h>
#include <stdint.h>
/* every trace calls uses IPC context in this file */
LOG_MODULE_DECLARE(ipc, CONFIG_SOF_LOG_LEVEL);
extern struct tr_ctx dt_tr;
#if CONFIG_TRACE_FILTERING_ADAPTIVE
struct recent_log_entry {
uint32_t entry_id;
uint64_t message_ts;
uint64_t first_suppression_ts;
uint32_t trigger_count;
};
struct recent_trace_context {
struct recent_log_entry recent_entries[CONFIG_TRACE_RECENT_ENTRIES_COUNT];
};
#endif /* CONFIG_TRACE_FILTERING_ADAPTIVE */
/** MAILBOX_TRACE_BASE ring buffer */
struct trace {
uintptr_t pos ; /**< offset of the next byte to write */
uint32_t enable;
#if CONFIG_TRACE_FILTERING_ADAPTIVE
bool user_filter_override; /* whether filtering was overridden by user or not */
#endif /* CONFIG_TRACE_FILTERING_ADAPTIVE */
struct k_spinlock lock; /* locking mechanism */
#if CONFIG_TRACE_FILTERING_ADAPTIVE
struct recent_trace_context trace_core_context[CONFIG_CORE_COUNT];
#endif
};
/* calculates total message size, both header and payload in bytes */
#define MESSAGE_SIZE(args_num) \
(sizeof(struct log_entry_header) + args_num * sizeof(uint32_t))
/* calculated total message size in dwords */
#define MESSAGE_SIZE_DWORDS(args_num) \
(MESSAGE_SIZE(args_num) / sizeof(uint32_t))
/* calculates offset to param_idx of payload */
#define PAYLOAD_OFFSET(param_idx) \
(MESSAGE_SIZE_DWORDS(0) + param_idx)
#define TRACE_ID_MASK ((1 << TRACE_ID_LENGTH) - 1)
static void put_header(void *dst, const struct sof_uuid_entry *uid,
uint32_t id_1, uint32_t id_2,
uint32_t entry, uint64_t timestamp)
{
struct dma_trace_data *trace_data = dma_trace_data_get();
/* Support very early tracing */
uint64_t delta = dma_trace_initialized(trace_data) ? trace_data->time_delta : 0;
struct log_entry_header header;
int ret;
header.uid = (uintptr_t)uid;
header.id_0 = id_1 & TRACE_ID_MASK;
header.id_1 = id_2 & TRACE_ID_MASK;
header.core_id = cpu_get_id();
header.timestamp = timestamp + delta;
header.log_entry_address = entry;
ret = memcpy_s(dst, sizeof(header), &header, sizeof(header));
assert(!ret);
}
#ifndef __ZEPHYR__
/** Ring buffer for the mailbox trace */
void mtrace_event(const char *data, uint32_t length)
{
struct trace *trace = trace_get();
char *t = (char *)MAILBOX_TRACE_BASE;
const int32_t available = MAILBOX_TRACE_SIZE - trace->pos;
if (available < length) { /* wrap */
memset(t + trace->pos, 0xff, available);
dcache_writeback_region((__sparse_force char __sparse_cache *)t + trace->pos,
available);
trace->pos = 0;
}
memcpy_s(t + trace->pos, MAILBOX_TRACE_SIZE - trace->pos,
data, length);
dcache_writeback_region((__sparse_force char __sparse_cache *)t + trace->pos, length);
trace->pos += length;
}
#endif /* __ZEPHYR__ */
#if CONFIG_TRACE_FILTERING_VERBOSITY
/**
* \brief Runtime trace filtering based on verbosity level
* \param lvl log level (LOG_LEVEL_ ERROR, INFO, DEBUG ...)
* \param uuid uuid address
* \return false when trace is filtered out, otherwise true
*/
static inline bool trace_filter_verbosity(uint32_t lvl, const struct tr_ctx *ctx)
{
STATIC_ASSERT(LOG_LEVEL_CRITICAL < LOG_LEVEL_VERBOSE,
LOG_LEVEL_CRITICAL_MUST_HAVE_LOWEST_VALUE);
return lvl <= ctx->level;
}
#endif /* CONFIG_TRACE_FILTERING_VERBOSITY */
#if CONFIG_TRACE_FILTERING_ADAPTIVE
/** Report how many times an entry was suppressed and clear it. */
static void emit_suppressed_entry(struct recent_log_entry *entry)
{
_log_message(trace_log_unfiltered, false, LOG_LEVEL_INFO, _TRACE_INV_CLASS, &dt_tr,
_TRACE_INV_ID, _TRACE_INV_ID, "Suppressed %u similar messages: %pQ",
entry->trigger_count - CONFIG_TRACE_BURST_COUNT,
(void *)entry->entry_id);
memset(entry, 0, sizeof(*entry));
}
/** Flush entries that have not been seen again in the last
* CONFIG_TRACE_RECENT_TIME_THRESHOLD microseconds before current_ts.
*/
static void emit_recent_entries(uint64_t current_ts)
{
struct trace *trace = trace_get();
struct recent_log_entry *recent_entries =
trace->trace_core_context[cpu_get_id()].recent_entries;
int i;
/* Check if any tracked entries were dormant long enough to unsuppress them */
for (i = 0; i < CONFIG_TRACE_RECENT_ENTRIES_COUNT; i++) {
if (recent_entries[i].entry_id) {
if (current_ts - recent_entries[i].message_ts >
CONFIG_TRACE_RECENT_TIME_THRESHOLD) {
if (recent_entries[i].trigger_count > CONFIG_TRACE_BURST_COUNT)
emit_suppressed_entry(&recent_entries[i]);
else
memset(&recent_entries[i], 0, sizeof(recent_entries[i]));
}
}
}
}
/**
* \brief Runtime trace flood suppression
* \param log_level log verbosity level
* \param entry ID of log entry
* \param message_ts message timestamp
* \return true when the message must be printed by the caller because it was not filtered
*/
static bool trace_filter_flood(uint32_t log_level, uint32_t entry, uint64_t message_ts)
{
struct trace *trace = trace_get();
struct recent_log_entry *recent_entries =
trace->trace_core_context[cpu_get_id()].recent_entries;
struct recent_log_entry *oldest_entry = recent_entries;
int i;
/* don't attempt to suppress debug messages using this method, it would be uneffective */
if (log_level >= LOG_LEVEL_DEBUG)
return true;
/* check if same log entry was sent recently */
for (i = 0; i < CONFIG_TRACE_RECENT_ENTRIES_COUNT; i++) {
/* Identify the oldest (or empty) entry to evict if we have no match */
if (oldest_entry->first_suppression_ts > recent_entries[i].first_suppression_ts)
oldest_entry = &recent_entries[i];
if (recent_entries[i].entry_id == entry) {
/* We have a match but include this message in this burst only if the
* burst:
- 1. hasn't lasted for too long;
- 2. hasn't been quiet for too long.
*/
if (message_ts - recent_entries[i].first_suppression_ts <
CONFIG_TRACE_RECENT_MAX_TIME &&
message_ts - recent_entries[i].message_ts <
CONFIG_TRACE_RECENT_TIME_THRESHOLD) {
recent_entries[i].trigger_count++;
/* Refresh last seen time */
recent_entries[i].message_ts = message_ts;
/* Allow the start of a burst to be printed normally */
return recent_entries[i].trigger_count <= CONFIG_TRACE_BURST_COUNT;
}
/* Emit and clear this burst */
if (recent_entries[i].trigger_count > CONFIG_TRACE_BURST_COUNT)
emit_suppressed_entry(&recent_entries[i]);
else
memset(&recent_entries[i], 0, sizeof(recent_entries[i]));
return true;
}
}
/* Make room for tracking new entry, by emitting the oldest one in the filter */
if (oldest_entry->entry_id && oldest_entry->trigger_count > CONFIG_TRACE_BURST_COUNT)
emit_suppressed_entry(oldest_entry);
/* Start a new burst */
oldest_entry->entry_id = entry;
oldest_entry->message_ts = message_ts;
oldest_entry->first_suppression_ts = message_ts;
oldest_entry->trigger_count = 1;
return true;
}
#endif /* CONFIG_TRACE_FILTERING_ADAPTIVE */
/** Implementation shared and invoked by both adaptive filtering and
* not. Serializes events into trace messages and passes them to
* dtrace_event()
*/
static void dma_trace_log(bool send_atomic, uint32_t log_entry, const struct tr_ctx *ctx,
uint32_t lvl, uint32_t id_1, uint32_t id_2, int arg_count, va_list vargs)
{
uint32_t data[MESSAGE_SIZE_DWORDS(_TRACE_EVENT_MAX_ARGUMENT_COUNT)];
const int message_size = MESSAGE_SIZE(arg_count);
int i;
/* fill log content. arg_count is in the dictionary. */
put_header(data, ctx->uuid_p, id_1, id_2, log_entry, sof_cycle_get_64_safe());
for (i = 0; i < arg_count; ++i)
data[PAYLOAD_OFFSET(i)] = va_arg(vargs, uint32_t);
/* send event by */
if (send_atomic)
dtrace_event_atomic((const char *)data, message_size);
else
dtrace_event((const char *)data, message_size);
}
void trace_log_unfiltered(bool send_atomic, const void *log_entry, const struct tr_ctx *ctx,
uint32_t lvl, uint32_t id_1, uint32_t id_2, int arg_count, va_list vl)
{
struct trace *trace = trace_get();
if (!trace->enable) {
return;
}
dma_trace_log(send_atomic, (uint32_t)log_entry, ctx, lvl, id_1, id_2, arg_count, vl);
}
void trace_log_filtered(bool send_atomic, const void *log_entry, const struct tr_ctx *ctx,
uint32_t lvl, uint32_t id_1, uint32_t id_2, int arg_count, va_list vl)
{
struct trace *trace = trace_get();
if (!trace->enable) {
return;
}
#if CONFIG_TRACE_FILTERING_VERBOSITY
if (!trace_filter_verbosity(lvl, ctx))
return;
#endif /* CONFIG_TRACE_FILTERING_VERBOSITY */
#if CONFIG_TRACE_FILTERING_ADAPTIVE
if (!trace->user_filter_override) {
const uint64_t current_ts = sof_cycle_get_64_safe();
emit_recent_entries(current_ts);
if (!trace_filter_flood(lvl, (uint32_t)log_entry, current_ts))
return;
}
#endif /* CONFIG_TRACE_FILTERING_ADAPTIVE */
dma_trace_log(send_atomic, (uint32_t)log_entry, ctx, lvl, id_1, id_2, arg_count, vl);
}
struct sof_ipc_trace_filter_elem *trace_filter_fill(struct sof_ipc_trace_filter_elem *elem,
struct sof_ipc_trace_filter_elem *end,
struct trace_filter *filter)
{
filter->log_level = -1;
filter->uuid_id = 0;
filter->comp_id = -1;
filter->pipe_id = -1;
while (elem <= end) {
switch (elem->key & SOF_IPC_TRACE_FILTER_ELEM_TYPE_MASK) {
case SOF_IPC_TRACE_FILTER_ELEM_SET_LEVEL:
filter->log_level = elem->value;
break;
case SOF_IPC_TRACE_FILTER_ELEM_BY_UUID:
filter->uuid_id = elem->value;
break;
case SOF_IPC_TRACE_FILTER_ELEM_BY_COMP:
filter->comp_id = elem->value;
break;
case SOF_IPC_TRACE_FILTER_ELEM_BY_PIPE:
filter->pipe_id = elem->value;
break;
default:
tr_err(&ipc_tr, "Invalid SOF_IPC_TRACE_FILTER_ELEM 0x%x",
elem->key);
return NULL;
}
/* each filter set must be terminated with FIN flag and have new log level */
if (elem->key & SOF_IPC_TRACE_FILTER_ELEM_FIN) {
if (filter->log_level < 0) {
tr_err(&ipc_tr, "Each trace filter set must specify new log level");
return NULL;
} else {
return elem + 1;
}
}
++elem;
}
tr_err(&ipc_tr, "Trace filter elements set is not properly terminated");
return NULL;
}
/* update global components, which tr_ctx is stored inside special section */
static int trace_filter_update_global(int32_t log_level, uint32_t uuid_id)
{
int cnt = 0;
#if !defined(CONFIG_LIBRARY)
extern void *_trace_ctx_start;
extern void *_trace_ctx_end;
struct tr_ctx *ptr = (struct tr_ctx *)&_trace_ctx_start;
struct tr_ctx *end = (struct tr_ctx *)&_trace_ctx_end;
/* iterate over global `tr_ctx` entries located in their own section */
/* cppcheck-suppress comparePointers */
while (ptr < end) {
/*
* when looking for specific uuid element,
* then find, update and stop searching
*/
if ((uintptr_t)ptr->uuid_p == uuid_id) {
ptr->level = log_level;
return 1;
}
/* otherwise each element should be updated */
if (!ptr->uuid_p) {
ptr->level = log_level;
++cnt;
}
++ptr;
}
#endif
return cnt;
}
/* return trace context from any ipc component type */
static struct tr_ctx *trace_filter_ipc_comp_context(struct ipc_comp_dev *icd)
{
switch (icd->type) {
case COMP_TYPE_COMPONENT:
return &icd->cd->tctx;
case COMP_TYPE_BUFFER:
return &icd->cb->tctx;
case COMP_TYPE_PIPELINE:
return &icd->pipeline->tctx;
/* each COMP_TYPE must be specified */
default:
tr_err(&ipc_tr, "Unknown trace context for ipc component type 0x%X",
icd->type);
return NULL;
}
}
/* update ipc components, wchich tr_ctx may be read from ipc_comp_dev structure */
static int trace_filter_update_instances(int32_t log_level, uint32_t uuid_id,
int32_t pipe_id, int32_t comp_id)
{
struct ipc *ipc = ipc_get();
struct ipc_comp_dev *icd;
struct list_item *clist;
struct tr_ctx *ctx;
bool correct_comp;
int cnt = 0;
/* compare each ipc component with filter settings and update log level after pass */
list_for_item(clist, &ipc->comp_list) {
icd = container_of(clist, struct ipc_comp_dev, list);
ctx = trace_filter_ipc_comp_context(icd);
if (!ctx)
return -EINVAL;
correct_comp = comp_id == -1 || icd->id == comp_id; /* todo: icd->topo_id */
correct_comp &= uuid_id == 0 || (uintptr_t)ctx->uuid_p == uuid_id;
correct_comp &= pipe_id == -1 || ipc_comp_pipe_id(icd) == pipe_id;
if (correct_comp) {
ctx->level = log_level;
++cnt;
}
}
return cnt;
}
int trace_filter_update(const struct trace_filter *filter)
{
int ret = 0;
#if CONFIG_TRACE_FILTERING_ADAPTIVE
struct trace *trace = trace_get();
if (!trace->user_filter_override) {
trace->user_filter_override = true;
tr_info(&ipc_tr, "Adaptive filtering disabled by user");
}
#endif /* CONFIG_TRACE_FILTERING_ADAPTIVE */
/* validate log level, LOG_LEVEL_CRITICAL has low value, LOG_LEVEL_VERBOSE high */
if (filter->log_level < LOG_LEVEL_CRITICAL ||
filter->log_level > LOG_LEVEL_VERBOSE)
return -EINVAL;
/* update `*`, `name*` or global `name` */
if (filter->pipe_id == -1 && filter->comp_id == -1)
ret = trace_filter_update_global(filter->log_level, filter->uuid_id);
/* update `*`, `name*`, `nameX.*` or `nameX.Y`, `name` may be '*' */
ret += trace_filter_update_instances(filter->log_level, filter->uuid_id,
filter->pipe_id, filter->comp_id);
return ret > 0 ? ret : -EINVAL;
}
/** Sends all pending DMA messages to mailbox (for emergencies) */
void trace_flush_dma_to_mbox(void)
{
struct trace *trace = trace_get();
volatile uint64_t *t;
k_spinlock_key_t key;
key = k_spin_lock(&trace->lock);
/* get mailbox position */
t = (volatile uint64_t *)(MAILBOX_TRACE_BASE + trace->pos);
/* flush dma trace messages */
dma_trace_flush((void *)t);
k_spin_unlock(&trace->lock, key);
}
void trace_on(void)
{
struct trace *trace = trace_get();
k_spinlock_key_t key;
key = k_spin_lock(&trace->lock);
trace->enable = 1;
dma_trace_on();
k_spin_unlock(&trace->lock, key);
}
void trace_off(void)
{
struct trace *trace = trace_get();
k_spinlock_key_t key;
key = k_spin_lock(&trace->lock);
trace->enable = 0;
dma_trace_off();
k_spin_unlock(&trace->lock, key);
}
void trace_init(struct sof *sof)
{
sof->trace = rzalloc(SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT, sizeof(*sof->trace));
if (!sof->trace) {
mtrace_printf(LOG_LEVEL_ERROR, "trace_init(): allocation failed");
sof_panic(SOF_IPC_PANIC_IPC);
}
sof->trace->enable = 1;
sof->trace->pos = 0;
#if CONFIG_TRACE_FILTERING_ADAPTIVE
sof->trace->user_filter_override = false;
#endif /* CONFIG_TRACE_FILTERING_ADAPTIVE */
k_spinlock_init(&sof->trace->lock);
#ifndef __ZEPHYR__
/* Zephyr owns and has already initialized this buffer (and
* likely has already logged to it by the time we get here).
* Don't touch.
*/
bzero((void *)MAILBOX_TRACE_BASE, MAILBOX_TRACE_SIZE);
dcache_writeback_invalidate_region((__sparse_force void __sparse_cache *)MAILBOX_TRACE_BASE,
MAILBOX_TRACE_SIZE);
#endif
dma_trace_init_early(sof);
}
static void mtrace_dict_entry_vl(bool atomic_context, uint32_t dict_entry_address,
int n_args, va_list ap)
{
int i;
char packet[MESSAGE_SIZE(_TRACE_EVENT_MAX_ARGUMENT_COUNT)];
uint32_t *args = (uint32_t *)&packet[MESSAGE_SIZE(0)];
const uint64_t tstamp = sof_cycle_get_64_safe();
put_header(packet, dt_tr.uuid_p, _TRACE_INV_ID, _TRACE_INV_ID,
dict_entry_address, tstamp);
for (i = 0; i < MIN(n_args, _TRACE_EVENT_MAX_ARGUMENT_COUNT); i++)
args[i] = va_arg(ap, uint32_t);
if (atomic_context) {
mtrace_event(packet, MESSAGE_SIZE(n_args));
} else {
struct trace * const trace = trace_get();
k_spinlock_key_t key;
key = k_spin_lock(&trace->lock);
mtrace_event(packet, MESSAGE_SIZE(n_args));
k_spin_unlock(&trace->lock, key);
}
}
void mtrace_dict_entry(bool atomic_context, uint32_t dict_entry_address, int n_args, ...)
{
va_list ap;
va_start(ap, n_args);
mtrace_dict_entry_vl(atomic_context, dict_entry_address, n_args, ap);
va_end(ap);
}
void _log_sofdict(log_func_t sofdict_logf, bool atomic, const void *log_entry,
const struct tr_ctx *ctx, const uint32_t lvl,
uint32_t id_1, uint32_t id_2, int arg_count, ...)
{
va_list ap;
#ifndef __ZEPHYR__ /* for Zephyr see _log_nodict() in trace.h */
if (lvl <= MTRACE_DUPLICATION_LEVEL) {
va_start(ap, arg_count);
mtrace_dict_entry_vl(atomic, (uint32_t)log_entry, arg_count, ap);
va_end(ap);
}
#endif
va_start(ap, arg_count);
sofdict_logf(atomic, log_entry, ctx, lvl, id_1, id_2, arg_count, ap);
va_end(ap);
}