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