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