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