1 // SPDX-License-Identifier: BSD-3-Clause
2 //
3 // Copyright(c) 2018 Intel Corporation. All rights reserved.
4 //
5 // Author: Bartosz Kokoszko <bartoszx.kokoszko@linux.intel.com>
6 // Artur Kloniecki <arturx.kloniecki@linux.intel.com>
7
8 #include <assert.h>
9 #include <endian.h>
10 #include <stdbool.h>
11 #include <stdlib.h>
12 #include <stdint.h>
13 #include <string.h>
14 #include <errno.h>
15 #include <unistd.h>
16 #include <math.h>
17 #include <sof/lib/uuid.h>
18 #include <time.h>
19 #include <user/abi_dbg.h>
20 #include <user/trace.h>
21 #include "convert.h"
22 #include "filter.h"
23 #include "misc.h"
24
25 #define CEIL(a, b) ((a+b-1)/b)
26
27 #define TRACE_MAX_PARAMS_COUNT 4
28 #define TRACE_MAX_TEXT_LEN 1024
29 #define TRACE_MAX_FILENAME_LEN 128
30 #define TRACE_MAX_IDS_STR 10
31 #define TRACE_IDS_MASK ((1 << TRACE_ID_LENGTH) - 1)
32 #define INVALID_TRACE_ID (-1 & TRACE_IDS_MASK)
33
34 /** Dictionary entry. This MUST match the start of the linker output
35 * defined by _DECLARE_LOG_ENTRY().
36 */
37 struct ldc_entry_header {
38 uint32_t level;
39 uint32_t component_class;
40 uint32_t params_num;
41 uint32_t line_idx;
42 uint32_t file_name_len;
43 uint32_t text_len;
44 };
45
46 /** Dictionary entry + unformatted parameters */
47 struct ldc_entry {
48 struct ldc_entry_header header;
49 char *file_name;
50 char *text;
51 uint32_t *params;
52 };
53
54 /** Dictionary entry + formatted parameters */
55 struct proc_ldc_entry {
56 int subst_mask;
57 struct ldc_entry_header header;
58 char *file_name;
59 char *text;
60 uintptr_t params[TRACE_MAX_PARAMS_COUNT];
61 };
62
63 static const char *BAD_PTR_STR = "<bad uid ptr 0x%.8x>";
64
65 #define UUID_LOWER "%s%s%s<%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x>%s%s%s"
66 #define UUID_UPPER "%s%s%s<%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X>%s%s%s"
67
68 /* pointer to config for global context */
69 struct convert_config *global_config;
70
71 static int read_entry_from_ldc_file(struct ldc_entry *entry, uint32_t log_entry_address);
72
format_uid_raw(const struct sof_uuid_entry * uid_entry,int use_colors,int name_first,bool be,bool upper)73 char *format_uid_raw(const struct sof_uuid_entry *uid_entry, int use_colors, int name_first,
74 bool be, bool upper)
75 {
76 const struct sof_uuid *uid_val = &uid_entry->id;
77 uint32_t a = be ? htobe32(uid_val->a) : uid_val->a;
78 uint16_t b = be ? htobe16(uid_val->b) : uid_val->b;
79 uint16_t c = be ? htobe16(uid_val->c) : uid_val->c;
80 char *str;
81
82 str = asprintf(upper ? UUID_UPPER : UUID_LOWER,
83 use_colors ? KBLU : "",
84 name_first ? uid_entry->name : "",
85 name_first ? " " : "",
86 a, b, c,
87 uid_val->d[0], uid_val->d[1], uid_val->d[2],
88 uid_val->d[3], uid_val->d[4], uid_val->d[5],
89 uid_val->d[6], uid_val->d[7],
90 name_first ? "" : " ",
91 name_first ? "" : uid_entry->name,
92 use_colors ? KNRM : "");
93 return str;
94 }
95
get_uuid_entry(uint32_t uid_ptr)96 static const struct sof_uuid_entry *get_uuid_entry(uint32_t uid_ptr)
97 {
98 const struct snd_sof_uids_header *uids_dict = global_config->uids_dict;
99
100 return (const struct sof_uuid_entry *)
101 ((uint8_t *)uids_dict + uids_dict->data_offset + uid_ptr -
102 uids_dict->base_address);
103 }
104
105 /*
106 * Use uids dictionary content, to convert address of uuid `entry` from logger
107 * memory space to corresponding uuid key address used in firmware trace system
108 * (with base UUID_ENTRY_ELF_BASE, 0x1FFFA000 as usual). Function get_uuid_entry
109 * works in oppopsite direction.
110 */
get_uuid_key(const struct sof_uuid_entry * entry)111 uint32_t get_uuid_key(const struct sof_uuid_entry *entry)
112 {
113 const struct snd_sof_uids_header *uids_dict = global_config->uids_dict;
114
115 /*
116 * uids_dict->data_offset and uids_dict->base_address are both constants,
117 * related with given ldc file.
118 * Uuid address used in firmware, points unusable memory region,
119 * so its treated as key value.
120 */
121 return (uintptr_t)entry - (uintptr_t)uids_dict -
122 uids_dict->data_offset + uids_dict->base_address;
123 }
124
format_uid(uint32_t uid_ptr,int use_colors,bool be,bool upper)125 static const char *format_uid(uint32_t uid_ptr, int use_colors, bool be, bool upper)
126 {
127 const struct snd_sof_uids_header *uids_dict = global_config->uids_dict;
128 const struct sof_uuid_entry *uid_entry;
129 char *str;
130
131 if (uid_ptr < uids_dict->base_address ||
132 uid_ptr >= uids_dict->base_address + uids_dict->data_length) {
133 str = calloc(1, strlen(BAD_PTR_STR) + 1 + 6);
134 sprintf(str, BAD_PTR_STR, uid_ptr);
135 } else {
136 uid_entry = get_uuid_entry(uid_ptr);
137 str = format_uid_raw(uid_entry, use_colors, 1, be, upper);
138 }
139
140 return str;
141 }
142
143 /* fmt should point '%pUx`, return pointer to UUID string or zero */
asprintf_uuid(const char * fmt,uint32_t uuid_key,bool use_colors,int * fmt_len)144 static const char *asprintf_uuid(const char *fmt, uint32_t uuid_key, bool use_colors, int *fmt_len)
145 {
146 const char *fmt_end = fmt + strlen(fmt);
147 int be, upper;
148 int len = 4; /* assure full formating, with x */
149
150 if (fmt + 2 >= fmt_end || fmt[1] != 'p' || fmt[2] != 'U') {
151 *fmt_len = 0;
152 return NULL;
153 }
154 /* check 'x' value */
155 switch (fmt + 3 < fmt_end ? fmt[3] : 0) {
156 case 'b':
157 be = 1;
158 upper = 0;
159 break;
160 case 'B':
161 be = 1;
162 upper = 1;
163 break;
164 case 'l':
165 be = 0;
166 upper = 0;
167 break;
168 case 'L':
169 be = 0;
170 upper = 1;
171 break;
172 default:
173 be = 0;
174 upper = 0;
175 --len;
176 break;
177 }
178 *fmt_len = len;
179 return format_uid(uuid_key, use_colors, be, upper);
180 }
181
asprintf_entry_text(uint32_t entry_address)182 static const char *asprintf_entry_text(uint32_t entry_address)
183 {
184 struct ldc_entry entry;
185 int ret;
186
187 ret = read_entry_from_ldc_file(&entry, entry_address);
188 if (ret)
189 return NULL;
190
191 free(entry.file_name);
192
193 return entry.text;
194 }
195
196 /** printf-like formatting from the binary ldc_entry input to the
197 * formatted proc_lpc_entry output. Also copies the unmodified
198 * ldc_entry_header from input to output.
199 *
200 * @param[out] pe copy of the header + formatted output
201 * @param[in] e copy of the dictionary entry where unformatted,
202 uint32_t params have been inserted.
203 @param[in] use_colors whether to use ANSI terminal codes
204 */
process_params(struct proc_ldc_entry * pe,const struct ldc_entry * e,int use_colors)205 static void process_params(struct proc_ldc_entry *pe,
206 const struct ldc_entry *e,
207 int use_colors)
208 {
209 char *p = e->text;
210 const char *t_end = p + strlen(e->text);
211 int uuid_fmt_len;
212 int i = 0;
213
214 pe->subst_mask = 0;
215 pe->header = e->header;
216 pe->file_name = e->file_name;
217 pe->text = e->text;
218
219 /*
220 * Scan the text for possible replacements. We follow the Linux kernel
221 * that uses %pUx formats for UUID / GUID printing, where 'x' is
222 * optional and can be one of 'b', 'B', 'l' (default), and 'L'.
223 * For decoding log entry text from pointer %pQ is used.
224 */
225 while ((p = strchr(p, '%'))) {
226 uint32_t raw_param;
227
228 if (i >= e->header.params_num) {
229 /* Don't read e->params[] out of bounds. */
230 log_err("Too many %% conversion specifiers in '%s'\n",
231 e->text);
232 break;
233 }
234 raw_param = e->params[i];
235
236 /* % can't be the last char */
237 if (p + 1 >= t_end) {
238 log_err("Invalid format string\n");
239 break;
240 }
241
242 /* scan format string */
243 if (p[1] == '%') {
244 /* Skip "%%" */
245 p += 2;
246 } else if (p[1] == 's') {
247 /* %s format specifier */
248 /* check for string printing, because it leads to logger crash */
249 log_err("String printing is not supported\n");
250 pe->params[i] = (uintptr_t)asprintf("<String @ 0x%08x>", raw_param);
251 pe->subst_mask |= 1 << i;
252 ++i;
253 p += 2;
254 } else if (p + 2 < t_end && p[1] == 'p' && p[2] == 'U') {
255 /* %pUx format specifier */
256 /* substitute UUID entry address with formatted string pointer from heap */
257 pe->params[i] = (uintptr_t)asprintf_uuid(p, raw_param, use_colors,
258 &uuid_fmt_len);
259 pe->subst_mask |= 1 << i;
260 ++i;
261 /* replace uuid formatter with %s */
262 p[1] = 's';
263 memmove(&p[2], &p[uuid_fmt_len], (int)(t_end - &p[uuid_fmt_len]) + 1);
264 p += uuid_fmt_len - 2;
265 t_end -= uuid_fmt_len - 2;
266 } else if (p + 2 < t_end && p[1] == 'p' && p[2] == 'Q') {
267 /* %pQ format specifier */
268 /* substitute log entry address with formatted entry text */
269 pe->params[i] = (uintptr_t)asprintf_entry_text(raw_param);
270 pe->subst_mask |= 1 << i;
271 ++i;
272
273 /* replace entry formatter with %s */
274 p[1] = 's';
275 memmove(&p[2], &p[3], t_end - &p[2]);
276 p++;
277 t_end--;
278 } else {
279 /* arguments different from %pU and %pQ should be passed without
280 * modification
281 */
282 pe->params[i] = raw_param;
283 ++i;
284 p += 2;
285 }
286 }
287 if (i < e->header.params_num)
288 log_err("Too few %% conversion specifiers in '%s'\n", e->text);
289 }
290
free_proc_ldc_entry(struct proc_ldc_entry * pe)291 static void free_proc_ldc_entry(struct proc_ldc_entry *pe)
292 {
293 int i;
294
295 for (i = 0; i < TRACE_MAX_PARAMS_COUNT; i++) {
296 if (pe->subst_mask & (1 << i))
297 free((void *)pe->params[i]);
298 pe->params[i] = 0;
299 }
300 }
301
to_usecs(uint64_t time)302 static double to_usecs(uint64_t time)
303 {
304 /* trace timestamp uses CPU system clock at default 25MHz ticks */
305 // TODO: support variable clock rates
306 return (double)time / global_config->clock;
307 }
308
309 /** Justified timestamp width for printf format string */
timestamp_width(unsigned int precision)310 static unsigned int timestamp_width(unsigned int precision)
311 {
312 /* 64bits yields less than 20 digits precision. As reported by
313 * gcc 9.3, this avoids a very long precision causing snprintf()
314 * to truncate time_fmt
315 */
316 assert(precision >= 0 && precision < 20);
317 /*
318 * 12 digits for units is enough for 1M seconds = 11 days which
319 * should be enough for most test runs.
320 *
321 * Add 1 for the comma when there is one.
322 */
323 return 12 + (precision > 0 ? 1 : 0) + precision;
324 }
325
print_table_header(void)326 static inline void print_table_header(void)
327 {
328 FILE *out_fd = global_config->out_fd;
329 int hide_location = global_config->hide_location;
330 char time_fmt[32];
331
332 char date_string[64];
333 const time_t epoc_secs = time(NULL);
334 /* See SOF_IPC_TRACE_DMA_PARAMS_EXT in the kernel sources */
335 struct timespec ktime;
336 const int gettime_ret = clock_gettime(CLOCK_MONOTONIC, &ktime);
337
338 if (gettime_ret) {
339 log_err("clock_gettime() failed: %s\n",
340 strerror(gettime_ret));
341 exit(1);
342 }
343
344 if (global_config->time_precision >= 0) {
345 const unsigned int ts_width =
346 timestamp_width(global_config->time_precision);
347 snprintf(time_fmt, sizeof(time_fmt), "%%-%ds(us)%%%ds ",
348 ts_width, ts_width);
349 fprintf(out_fd, time_fmt, " TIMESTAMP", "DELTA");
350 }
351
352 fprintf(out_fd, "%2s %-18s ", "C#", "COMPONENT");
353 if (!hide_location)
354 fprintf(out_fd, "%-29s ", "LOCATION");
355 fprintf(out_fd, "%s", "CONTENT");
356
357 if (global_config->time_precision >= 0) {
358 /* e.g.: ktime=4263.487s @ 2021-04-27 14:21:13 -0700 PDT */
359 fprintf(out_fd, "\tktime=%lu.%03lus",
360 ktime.tv_sec, ktime.tv_nsec / 1000000);
361 if (strftime(date_string, sizeof(date_string),
362 "%F %X %z %Z", localtime(&epoc_secs)))
363 fprintf(out_fd, " @ %s", date_string);
364 }
365
366 fprintf(out_fd, "\n");
367 fflush(out_fd);
368 }
369
get_level_color(uint32_t level)370 static const char *get_level_color(uint32_t level)
371 {
372 switch (level) {
373 case LOG_LEVEL_CRITICAL:
374 return KRED;
375 case LOG_LEVEL_WARNING:
376 return KYEL;
377 default:
378 return KNRM;
379 }
380 }
381
get_level_name(uint32_t level)382 static const char *get_level_name(uint32_t level)
383 {
384 switch (level) {
385 case LOG_LEVEL_CRITICAL:
386 return "ERROR ";
387 case LOG_LEVEL_WARNING:
388 return "WARN ";
389 case LOG_LEVEL_INFO:
390 return "INFO ";
391 case LOG_LEVEL_DEBUG:
392 return "DEBUG ";
393 default:
394 return "UNKNOWN ";
395 }
396 }
397
get_component_name(uint32_t trace_class,uint32_t uid_ptr)398 static const char *get_component_name(uint32_t trace_class, uint32_t uid_ptr)
399 {
400 const struct snd_sof_uids_header *uids_dict = global_config->uids_dict;
401 const struct sof_uuid_entry *uid_entry;
402
403 /* if uid_ptr is non-zero, find name in the ldc file */
404 if (uid_ptr) {
405 if (uid_ptr < uids_dict->base_address ||
406 uid_ptr >= uids_dict->base_address +
407 uids_dict->data_length)
408 return "<uid?>";
409 uid_entry = get_uuid_entry(uid_ptr);
410 return uid_entry->name;
411 }
412
413 /* do not resolve legacy (deprecated) trace class name */
414 return "unknown";
415 }
416
417 /* remove superfluous leading file path and shrink to last 20 chars */
format_file_name(char * file_name_raw,int full_name)418 static char *format_file_name(char *file_name_raw, int full_name)
419 {
420 char *name;
421 int len;
422
423 /* most/all string should have "src" */
424 name = strstr(file_name_raw, "src");
425 if (!name)
426 name = file_name_raw;
427
428 if (full_name)
429 return name;
430 /* keep the last 24 chars */
431 len = strlen(name);
432 if (len > 24) {
433 char *sep_pos = NULL;
434
435 name += (len - 24);
436 sep_pos = strchr(name, '/');
437 if (!sep_pos)
438 return name;
439 while (--sep_pos >= name)
440 *sep_pos = '.';
441 }
442 return name;
443 }
444
445 static int entry_number = 1;
446 /** Formats and outputs one entry from the trace + the corresponding
447 * ldc_entry from the dictionary passed as arguments. Expects the log
448 * variables to have already been copied into the ldc_entry.
449 */
print_entry_params(const struct log_entry_header * dma_log,const struct ldc_entry * entry,uint64_t last_timestamp)450 static void print_entry_params(const struct log_entry_header *dma_log,
451 const struct ldc_entry *entry, uint64_t last_timestamp)
452 {
453 static uint64_t timestamp_origin;
454
455 FILE *out_fd = global_config->out_fd;
456 int use_colors = global_config->use_colors;
457 int raw_output = global_config->raw_output;
458 int hide_location = global_config->hide_location;
459 int time_precision = global_config->time_precision;
460
461 char ids[TRACE_MAX_IDS_STR];
462 float dt = to_usecs(dma_log->timestamp - last_timestamp);
463 struct proc_ldc_entry proc_entry;
464 static char time_fmt[64];
465 int ret;
466
467 if (raw_output)
468 use_colors = 0;
469
470 /* Something somewhere went wrong */
471 if (dt > 1000.0 * 1000.0 * 1000.0)
472 dt = NAN;
473
474 if (dma_log->timestamp < last_timestamp) {
475 fprintf(out_fd,
476 "\n\t\t --- negative DELTA = %.3f us: wrap, IPC_TRACE, other? ---\n\n",
477 -to_usecs(last_timestamp - dma_log->timestamp));
478 entry_number = 1;
479 }
480
481 /* The first entry:
482 * - is never shown with a relative TIMESTAMP (to itself!?)
483 * - shows a zero DELTA
484 */
485 if (entry_number == 1) {
486 entry_number++;
487 /* Display absolute (and random) timestamps */
488 timestamp_origin = 0;
489 dt = 0;
490 } else if (entry_number == 2) {
491 entry_number++;
492 if (global_config->relative_timestamps == 1)
493 /* Switch to relative timestamps from now on. */
494 timestamp_origin = last_timestamp;
495 } /* We don't need the exact entry_number after 3 */
496
497 if (dma_log->id_0 != INVALID_TRACE_ID &&
498 dma_log->id_1 != INVALID_TRACE_ID)
499 sprintf(ids, "%d.%d", (dma_log->id_0 & TRACE_IDS_MASK),
500 (dma_log->id_1 & TRACE_IDS_MASK));
501 else
502 ids[0] = '\0';
503
504 if (raw_output) { /* "raw" means script-friendly (not all hex) */
505 const char *entry_fmt = "%s%u %u %s%s%s ";
506
507 if (time_precision >= 0)
508 snprintf(time_fmt, sizeof(time_fmt), "%%.%df %%.%df ",
509 time_precision, time_precision);
510
511 fprintf(out_fd, entry_fmt,
512 entry->header.level == use_colors ?
513 (LOG_LEVEL_CRITICAL ? KRED : KNRM) : "",
514 dma_log->core_id,
515 entry->header.level,
516 get_component_name(entry->header.component_class, dma_log->uid),
517 raw_output && strlen(ids) ? "-" : "",
518 ids);
519 if (time_precision >= 0)
520 fprintf(out_fd, time_fmt,
521 to_usecs(dma_log->timestamp - timestamp_origin), dt);
522 if (!hide_location)
523 fprintf(out_fd, "(%s:%u) ",
524 format_file_name(entry->file_name, raw_output),
525 entry->header.line_idx);
526 } else {
527 if (time_precision >= 0) {
528 const unsigned int ts_width = timestamp_width(time_precision);
529
530 snprintf(time_fmt, sizeof(time_fmt),
531 "%%s[%%%d.%df] (%%%d.%df)%%s ",
532 ts_width, time_precision, ts_width, time_precision);
533
534 fprintf(out_fd, time_fmt,
535 use_colors ? KGRN : "",
536 to_usecs(dma_log->timestamp - timestamp_origin), dt,
537 use_colors ? KNRM : "");
538 }
539
540 /* core id */
541 fprintf(out_fd, "c%d ", dma_log->core_id);
542
543 /* component name and id */
544 fprintf(out_fd, "%s%-12s %-5s%s ",
545 use_colors ? KYEL : "",
546 get_component_name(entry->header.component_class, dma_log->uid),
547 ids,
548 use_colors ? KNRM : "");
549
550 /* location */
551 if (!hide_location)
552 fprintf(out_fd, "%24s:%-4u ",
553 format_file_name(entry->file_name, raw_output),
554 entry->header.line_idx);
555
556 /* level name */
557 fprintf(out_fd, "%s%s",
558 use_colors ? get_level_color(entry->header.level) : "",
559 get_level_name(entry->header.level));
560 }
561
562 /* Minimal, printf-like formatting */
563 process_params(&proc_entry, entry, use_colors);
564
565 switch (proc_entry.header.params_num) {
566 case 0:
567 ret = fprintf(out_fd, "%s", proc_entry.text);
568 break;
569 case 1:
570 ret = fprintf(out_fd, proc_entry.text, proc_entry.params[0]);
571 break;
572 case 2:
573 ret = fprintf(out_fd, proc_entry.text, proc_entry.params[0], proc_entry.params[1]);
574 break;
575 case 3:
576 ret = fprintf(out_fd, proc_entry.text, proc_entry.params[0], proc_entry.params[1],
577 proc_entry.params[2]);
578 break;
579 case 4:
580 ret = fprintf(out_fd, proc_entry.text, proc_entry.params[0], proc_entry.params[1],
581 proc_entry.params[2], proc_entry.params[3]);
582 break;
583 default:
584 log_err("Unsupported number of arguments for '%s'", proc_entry.text);
585 ret = 0; /* don't log ferror */
586 break;
587 }
588 free_proc_ldc_entry(&proc_entry);
589 /* log format text comes from ldc file (may be invalid), so error check is needed here */
590 if (ret < 0)
591 log_err("trace fprintf failed for '%s', %d '%s'",
592 proc_entry.text, ferror(out_fd), strerror(ferror(out_fd)));
593 fprintf(out_fd, "%s\n", use_colors ? KNRM : "");
594 fflush(out_fd);
595 }
596
read_entry_from_ldc_file(struct ldc_entry * entry,uint32_t log_entry_address)597 static int read_entry_from_ldc_file(struct ldc_entry *entry, uint32_t log_entry_address)
598 {
599 uint32_t base_address = global_config->logs_header->base_address;
600 uint32_t data_offset = global_config->logs_header->data_offset;
601
602 int ret;
603
604 /* evaluate entry offset in input file */
605 uint32_t entry_offset = (log_entry_address - base_address) + data_offset;
606
607 entry->file_name = NULL;
608 entry->text = NULL;
609 entry->params = NULL;
610
611 /* set file position to beginning of processed entry */
612 fseek(global_config->ldc_fd, entry_offset, SEEK_SET);
613
614 /* fetching elf header params */
615 ret = fread(&entry->header, sizeof(entry->header), 1, global_config->ldc_fd);
616 if (ret != 1) {
617 log_err("Failed to read entry header for offset 0x%x in dictionary.\n",
618 entry_offset);
619 ret = -1;
620 goto out;
621 }
622 if (entry->header.file_name_len > TRACE_MAX_FILENAME_LEN) {
623 log_err("Invalid filename length %d or ldc file does not match firmware\n",
624 entry->header.file_name_len);
625 ret = -EINVAL;
626 goto out;
627 }
628 entry->file_name = (char *)malloc(entry->header.file_name_len);
629
630 if (!entry->file_name) {
631 log_err("can't allocate %d byte for entry.file_name\n",
632 entry->header.file_name_len);
633 ret = -ENOMEM;
634 goto out;
635 }
636
637 ret = fread(entry->file_name, sizeof(char), entry->header.file_name_len,
638 global_config->ldc_fd);
639 if (ret != entry->header.file_name_len) {
640 log_err("Failed to read source filename for offset 0x%x in dictionary.\n",
641 entry_offset);
642 ret = -1;
643 goto out;
644 }
645
646 /* fetching text */
647 if (entry->header.text_len > TRACE_MAX_TEXT_LEN) {
648 log_err("Invalid text length.\n");
649 ret = -EINVAL;
650 goto out;
651 }
652 entry->text = (char *)malloc(entry->header.text_len);
653 if (!entry->text) {
654 log_err("can't allocate %d byte for entry.text\n", entry->header.text_len);
655 ret = -ENOMEM;
656 goto out;
657 }
658 ret = fread(entry->text, sizeof(char), entry->header.text_len, global_config->ldc_fd);
659 if (ret != entry->header.text_len) {
660 log_err("Failed to read log message at offset 0x%x from dictionary.\n",
661 entry_offset);
662 ret = -1;
663 goto out;
664 }
665
666 return 0;
667
668 out:
669 free(entry->text);
670 entry->text = NULL;
671 free(entry->file_name);
672 entry->file_name = NULL;
673
674 return ret;
675 }
676
677 /** Gets the dictionary entry matching the log entry argument, reads
678 * from the log the variable number of arguments needed by this entry
679 * and passes everything to print_entry_params() to finish processing
680 * this log entry. So not just "fetch" but everything else after it too.
681 *
682 * @param[in] dma_log protocol header from any trace (not just from the
683 * "DMA" trace)
684 * @param[in,out] last_timestamp timestamp found for this entry
685 */
fetch_entry(const struct log_entry_header * dma_log,uint64_t * last_timestamp)686 static int fetch_entry(const struct log_entry_header *dma_log, uint64_t *last_timestamp)
687 {
688 struct ldc_entry entry;
689 int ret;
690
691 ret = read_entry_from_ldc_file(&entry, dma_log->log_entry_address);
692 if (ret < 0) {
693 log_err("read_entry_from_ldc_file(0x%x) returned %d\n",
694 dma_log->log_entry_address, ret);
695 goto out;
696 }
697
698 /* fetching entry params from dma dump */
699 if (entry.header.params_num > TRACE_MAX_PARAMS_COUNT) {
700 log_err("Invalid number of parameters.\n");
701 ret = -EINVAL;
702 goto out;
703 }
704 entry.params = (uint32_t *)malloc(sizeof(uint32_t) * entry.header.params_num);
705 if (!entry.params) {
706 log_err("can't allocate %d byte for entry.params\n",
707 (int)(sizeof(uint32_t) * entry.header.params_num));
708 ret = -ENOMEM;
709 goto out;
710 }
711
712 if (global_config->serial_fd < 0) {
713 ret = fread(entry.params, sizeof(uint32_t), entry.header.params_num,
714 global_config->in_fd);
715 if (ret != entry.header.params_num) {
716 fprintf(global_config->out_fd,
717 "warn: failed to fread() %d params from the log for %s:%d\n",
718 entry.header.params_num,
719 entry.file_name, entry.header.line_idx);
720
721 ret = ferror(global_config->in_fd) ? -1 : 0;
722
723 if (feof(global_config->in_fd))
724 fprintf(global_config->out_fd,
725 "warn: log's End Of File. Device suspend?\n");
726
727 goto out;
728 }
729 } else { /* serial */
730 size_t size = sizeof(uint32_t) * entry.header.params_num;
731 uint8_t *n;
732
733 /* Repeatedly read() how much we still miss until we got
734 * enough for the number of params needed by this
735 * particular statement.
736 */
737 for (n = (uint8_t *)entry.params; size; n += ret, size -= ret) {
738 ret = read(global_config->serial_fd, n, size);
739 if (ret < 0) {
740 ret = -errno;
741 log_err("Failed to fread %d params from serial: %s\n",
742 entry.header.params_num, strerror(errno));
743 goto out;
744 }
745 if (ret != size)
746 log_err("Partial read of %u bytes of %lu, reading more\n",
747 ret, size);
748 }
749 } /* serial */
750
751 /* printing entry content */
752 print_entry_params(dma_log, &entry, *last_timestamp);
753 *last_timestamp = dma_log->timestamp;
754
755 /* set f_ldc file position to the beginning */
756 rewind(global_config->ldc_fd);
757
758 ret = 0;
759 out:
760 /* free alocated memory */
761 free(entry.params);
762 free(entry.text);
763 free(entry.file_name);
764
765 return ret;
766 }
767
serial_read(uint64_t * last_timestamp)768 static int serial_read(uint64_t *last_timestamp)
769 {
770 struct log_entry_header dma_log;
771 size_t len;
772 uint8_t *n;
773 int ret;
774
775 for (len = 0, n = (uint8_t *)&dma_log; len < sizeof(dma_log); n += sizeof(uint32_t)) {
776 ret = read(global_config->serial_fd, n, sizeof(*n) * sizeof(uint32_t));
777 if (ret < 0)
778 return -errno;
779
780 /* In the beginning we read 1 spurious byte */
781 if (ret < sizeof(*n) * sizeof(uint32_t))
782 n -= sizeof(uint32_t);
783 else
784 len += ret;
785 }
786
787 /* Skip all trace_point() values, although this test isn't 100% reliable */
788 while ((dma_log.log_entry_address < global_config->logs_header->base_address) ||
789 dma_log.log_entry_address > global_config->logs_header->base_address +
790 global_config->logs_header->data_length) {
791 /*
792 * 8 characters and a '\n' come from the serial port, append a
793 * '\0'
794 */
795 char s[10];
796 uint8_t *c;
797 size_t len;
798
799 c = (uint8_t *)&dma_log;
800
801 memcpy(s, c, sizeof(s) - 1);
802 s[sizeof(s) - 1] = '\0';
803 fprintf(global_config->out_fd, "Trace point %s", s);
804
805 memmove(&dma_log, c + 9, sizeof(dma_log) - 9);
806
807 c = (uint8_t *)(&dma_log + 1) - 9;
808 for (len = 9; len; len -= ret, c += ret) {
809 ret = read(global_config->serial_fd, c, len);
810 if (ret < 0)
811 return ret;
812 }
813 }
814
815 /* fetching entry from elf dump and complete processing this log
816 * line
817 */
818 return fetch_entry(&dma_log, last_timestamp);
819 }
820
821 /** Main logger loop */
logger_read(void)822 static int logger_read(void)
823 {
824 struct log_entry_header dma_log;
825 int ret = 0;
826 uint64_t last_timestamp = 0;
827
828 bool ldc_address_OK = false;
829 unsigned int skipped_dwords = 0;
830
831 if (!global_config->raw_output)
832 print_table_header();
833
834 if (global_config->serial_fd >= 0)
835 /* Wait for CTRL-C */
836 for (;;) {
837 ret = serial_read(&last_timestamp);
838 if (ret < 0)
839 return ret;
840 }
841
842 /* One iteration per log statement */
843 while (!ferror(global_config->in_fd)) {
844 /* getting entry parameters from dma dump */
845 ret = fread(&dma_log, sizeof(dma_log), 1, global_config->in_fd);
846 if (ret != 1) {
847 /*
848 * use ferror (not errno) to check fread fail -
849 * see https://www.gnu.org/software/gnulib/manual/html_node/fread.html
850 */
851 ret = -ferror(global_config->in_fd);
852 if (ret) {
853 log_err("in %s(), fread(..., %s) failed: %s(%d)\n",
854 __func__, global_config->in_file,
855 strerror(-ret), ret);
856 break;
857 }
858 /* for trace mode, try to reopen */
859 if (global_config->trace) {
860 fprintf(global_config->out_fd,
861 "\n ---- %s; %s -----\n\n",
862 "Re-opening trace input file",
863 "device suspend?");
864 if (freopen(NULL, "rb", global_config->in_fd)) {
865 entry_number = 1;
866 continue;
867 } else {
868 log_err("in %s(), freopen(..., %s) failed: %s(%d)\n",
869 __func__, global_config->in_file,
870 strerror(errno), errno);
871 ret = -errno;
872 break;
873 }
874 } else {
875 /* EOF */
876 if (!feof(global_config->in_fd))
877 log_err("file '%s' is unaligned with trace entry size (%ld)\n",
878 global_config->in_file, sizeof(dma_log));
879 break;
880 }
881 }
882
883 /* checking if received trace address is located in
884 * entry section in elf file.
885 */
886 if (dma_log.log_entry_address < global_config->logs_header->base_address ||
887 dma_log.log_entry_address > global_config->logs_header->base_address +
888 global_config->logs_header->data_length) {
889 /* Finding uninitialized and incomplete log statements in the
890 * mailbox ring buffer is routine. Take note in both cases but
891 * report errors only for the DMA trace.
892 */
893 if (global_config->trace && ldc_address_OK) {
894 /* FIXME: make this a log_err() */
895 fprintf(global_config->out_fd,
896 "warn: log_entry_address %#8x is not in dictionary range!\n",
897 dma_log.log_entry_address);
898 fprintf(global_config->out_fd,
899 "warn: Seeking forward 4 bytes at a time until re-synchronize.\n");
900 }
901 ldc_address_OK = false;
902 /* When the address is not correct, move forward by one DWORD (not
903 * entire struct dma_log)
904 */
905 fseek(global_config->in_fd, -(sizeof(dma_log) - sizeof(uint32_t)),
906 SEEK_CUR);
907 skipped_dwords++;
908 continue;
909
910 } else if (!ldc_address_OK) {
911 /* Just found a valid address (again) */
912
913 /* At this point, skipped_dwords can be == 0
914 * only when we just started to run.
915 */
916 if (skipped_dwords != 0) {
917 fprintf(global_config->out_fd,
918 "\nFound valid LDC address after skipping %zu bytes (one line uses %zu + 0 to 16 bytes)\n",
919 sizeof(uint32_t) * skipped_dwords, sizeof(dma_log));
920 }
921
922 ldc_address_OK = true;
923 skipped_dwords = 0;
924 }
925
926 /* fetching entry from dictionary, read the number of
927 * arguments needed and finish the entire processing of
928 * this log line.
929 */
930 ret = fetch_entry(&dma_log, &last_timestamp);
931 if (ret) {
932 log_err("fetch_entry() failed with: %d, aborting\n", ret);
933 break;
934 }
935 } /* next log entry */
936
937 /* End of (etrace) file */
938 fprintf(global_config->out_fd,
939 "Skipped %zu bytes after the last statement",
940 sizeof(uint32_t) * skipped_dwords);
941
942 if (!global_config->trace &&
943 /* maximum 4 arguments supported */
944 skipped_dwords < sizeof(dma_log) + 4 * sizeof(uint32_t))
945 fprintf(global_config->out_fd,
946 ". Potential mailbox wrap, check the start of the output for later logs");
947
948 fprintf(global_config->out_fd, ".\n");
949
950 return ret;
951 }
952
953 /* fw verification */
verify_fw_ver(void)954 static int verify_fw_ver(void)
955 {
956 struct sof_ipc_fw_version ver;
957 int count;
958
959 if (!global_config->version_fd)
960 return 0;
961
962 /* here fw verification should be exploited */
963 count = fread(&ver, sizeof(ver), 1, global_config->version_fd);
964 if (!count) {
965 log_err("Error while reading %s.\n", global_config->version_file);
966 return -ferror(global_config->version_fd);
967 }
968
969 /* compare source hash value from version file and ldc file */
970 if (ver.src_hash != global_config->logs_header->version.src_hash) {
971 log_err("src hash value from version file (0x%x) differ from src hash version saved in dictionary (0x%x).\n",
972 ver.src_hash, global_config->logs_header->version.src_hash);
973 return -EINVAL;
974 }
975 return 0;
976 }
977
dump_ldc_info(void)978 static int dump_ldc_info(void)
979 {
980 struct snd_sof_uids_header *uids_dict = global_config->uids_dict;
981 ssize_t remaining = uids_dict->data_length;
982 const struct sof_uuid_entry *uid_ptr;
983 FILE *out_fd = global_config->out_fd;
984 uintptr_t uid_addr;
985 int cnt = 0;
986 char *name;
987
988 fprintf(out_fd, "logger ABI Version is\t%d:%d:%d\n",
989 SOF_ABI_VERSION_MAJOR(SOF_ABI_DBG_VERSION),
990 SOF_ABI_VERSION_MINOR(SOF_ABI_DBG_VERSION),
991 SOF_ABI_VERSION_PATCH(SOF_ABI_DBG_VERSION));
992 fprintf(out_fd, "ldc_file ABI Version is\t%d:%d:%d\n",
993 SOF_ABI_VERSION_MAJOR(global_config->logs_header->version.abi_version),
994 SOF_ABI_VERSION_MINOR(global_config->logs_header->version.abi_version),
995 SOF_ABI_VERSION_PATCH(global_config->logs_header->version.abi_version));
996 fprintf(out_fd, "\n");
997 fprintf(out_fd, "Components uuid dictionary size:\t%ld bytes\n",
998 remaining);
999 fprintf(out_fd, "Components uuid base address: \t0x%x\n",
1000 uids_dict->base_address);
1001 fprintf(out_fd, "Components uuid entries:\n");
1002 fprintf(out_fd, "\t%10s %38s %s\n", "ADDRESS", "UUID", "NAME");
1003
1004 uid_ptr = (const struct sof_uuid_entry *)
1005 ((uintptr_t)uids_dict + uids_dict->data_offset);
1006
1007 while (remaining > 0) {
1008 name = format_uid_raw(&uid_ptr[cnt], 0, 0, false, false);
1009 uid_addr = get_uuid_key(&uid_ptr[cnt]);
1010 fprintf(out_fd, "\t0x%lX %s\n", uid_addr, name);
1011
1012 if (name) {
1013 free(name);
1014 name = NULL;
1015 }
1016 remaining -= sizeof(struct sof_uuid_entry);
1017 ++cnt;
1018 }
1019
1020 fprintf(out_fd, "\t-------------------------------------------------- cnt: %d\n",
1021 cnt);
1022
1023 return 0;
1024 }
1025
convert(struct convert_config * config)1026 int convert(struct convert_config *config)
1027 {
1028 struct snd_sof_logs_header snd;
1029 struct snd_sof_uids_header uids_hdr;
1030 int count, ret = 0;
1031
1032 config->logs_header = &snd;
1033 global_config = config;
1034
1035 count = fread(&snd, sizeof(snd), 1, config->ldc_fd);
1036 if (!count) {
1037 log_err("Error while reading %s.\n", config->ldc_file);
1038 return -ferror(config->ldc_fd);
1039 }
1040
1041 if (strncmp((char *) snd.sig, SND_SOF_LOGS_SIG, SND_SOF_LOGS_SIG_SIZE)) {
1042 log_err("Invalid ldc file signature.\n");
1043 return -EINVAL;
1044 }
1045
1046 ret = verify_fw_ver();
1047 if (ret)
1048 return ret;
1049
1050 /* default logger and ldc_file abi verification */
1051 if (SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_DBG_VERSION,
1052 snd.version.abi_version)) {
1053 log_err("abi version in %s file does not coincide with abi version used by logger.\n",
1054 config->ldc_file);
1055 log_err("logger ABI Version is %d:%d:%d\n",
1056 SOF_ABI_VERSION_MAJOR(SOF_ABI_DBG_VERSION),
1057 SOF_ABI_VERSION_MINOR(SOF_ABI_DBG_VERSION),
1058 SOF_ABI_VERSION_PATCH(SOF_ABI_DBG_VERSION));
1059 log_err("ldc_file ABI Version is %d:%d:%d\n",
1060 SOF_ABI_VERSION_MAJOR(snd.version.abi_version),
1061 SOF_ABI_VERSION_MINOR(snd.version.abi_version),
1062 SOF_ABI_VERSION_PATCH(snd.version.abi_version));
1063 return -EINVAL;
1064 }
1065
1066 /* read uuid section header */
1067 fseek(config->ldc_fd, snd.data_offset + snd.data_length, SEEK_SET);
1068 count = fread(&uids_hdr, sizeof(uids_hdr), 1, config->ldc_fd);
1069 if (!count) {
1070 log_err("Error while reading uuids header from %s.\n", config->ldc_file);
1071 return -ferror(config->ldc_fd);
1072 }
1073 if (strncmp((char *)uids_hdr.sig, SND_SOF_UIDS_SIG,
1074 SND_SOF_UIDS_SIG_SIZE)) {
1075 log_err("invalid uuid section signature.\n");
1076 return -EINVAL;
1077 }
1078 config->uids_dict = calloc(1, sizeof(uids_hdr) + uids_hdr.data_length);
1079 if (!config->uids_dict) {
1080 log_err("failed to alloc memory for uuids.\n");
1081 return -ENOMEM;
1082 }
1083 memcpy(config->uids_dict, &uids_hdr, sizeof(uids_hdr));
1084 count = fread(config->uids_dict + 1, uids_hdr.data_length, 1,
1085 config->ldc_fd);
1086 if (!count) {
1087 log_err("failed to read uuid section data.\n");
1088 ret = -ferror(config->ldc_fd);
1089 goto out;
1090 }
1091
1092 if (config->dump_ldc) {
1093 ret = dump_ldc_info();
1094 goto out;
1095 }
1096
1097 if (config->filter_config) {
1098 ret = filter_update_firmware();
1099 if (ret) {
1100 log_err("failed to apply trace filter, %d.\n", ret);
1101 goto out;
1102 }
1103 }
1104
1105 ret = logger_read();
1106 out:
1107 free(config->uids_dict);
1108 return ret;
1109 }
1110